Fix a very old bug nobody ever noticed: multiple About boxes :-)
[u/mdw/putty] / pageant.c
CommitLineData
5c58ad2d 1/*
2 * Pageant: the PuTTY Authentication Agent.
3 */
4
5#include <windows.h>
d70f60ae 6#include <aclapi.h>
9b581c37 7#include <stdio.h>
5c58ad2d 8#include "ssh.h"
9#include "tree234.h"
10
11#define IDI_MAINICON 200
12#define IDI_TRAYICON 201
13
14#define WM_XUSER (WM_USER + 0x2000)
15#define WM_SYSTRAY (WM_XUSER + 6)
16#define WM_SYSTRAY2 (WM_XUSER + 7)
d70f60ae 17
18#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
19
20/*
21 * FIXME: maybe some day we can sort this out ...
22 */
23#define AGENT_MAX_MSGLEN 8192
5c58ad2d 24
25#define IDM_CLOSE 0x0010
26#define IDM_VIEWKEYS 0x0020
27
28#define APPNAME "Pageant"
29
5c58ad2d 30#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
31#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
32#define SSH_AGENTC_RSA_CHALLENGE 3
33#define SSH_AGENT_RSA_RESPONSE 4
34#define SSH_AGENT_FAILURE 5
35#define SSH_AGENT_SUCCESS 6
36#define SSH_AGENTC_ADD_RSA_IDENTITY 7
37#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
38
39HINSTANCE instance;
40HWND hwnd;
41HWND keylist;
42HMENU systray_menu;
43
44tree234 *rsakeys;
45
016ef8ab 46int has_security;
47typedef DWORD (WINAPI *gsi_fn_t)
48 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
49 PSID *, PSID *, PACL *, PACL *,
50 PSECURITY_DESCRIPTOR *);
51gsi_fn_t getsecurityinfo;
52
5c58ad2d 53/*
54 * We need this to link with the RSA code, because rsaencrypt()
55 * pads its data with random bytes. Since we only use rsadecrypt(),
56 * which is deterministic, this should never be called.
57 *
58 * If it _is_ called, there is a _serious_ problem, because it
59 * won't generate true random numbers. So we must scream, panic,
60 * and exit immediately if that should happen.
61 */
62int random_byte(void) {
63 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
64 exit(0);
65}
66
67/*
68 * This function is needed to link with the DES code. We need not
69 * have it do anything at all.
70 */
71void logevent(char *msg) {
72}
73
74#define GET_32BIT(cp) \
75 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
76 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
77 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
78 ((unsigned long)(unsigned char)(cp)[3]))
79
80#define PUT_32BIT(cp, value) { \
81 (cp)[0] = (unsigned char)((value) >> 24); \
82 (cp)[1] = (unsigned char)((value) >> 16); \
83 (cp)[2] = (unsigned char)((value) >> 8); \
84 (cp)[3] = (unsigned char)(value); }
85
86#define PASSPHRASE_MAXLEN 512
87
d4de2d2a 88struct PassphraseProcStruct {
89 char *passphrase;
90 char *comment;
91};
92
5c58ad2d 93/*
94 * Dialog-box function for the passphrase box.
95 */
96static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
97 WPARAM wParam, LPARAM lParam) {
98 static char *passphrase;
d4de2d2a 99 struct PassphraseProcStruct *p;
5c58ad2d 100
101 switch (msg) {
102 case WM_INITDIALOG:
d4de2d2a 103 p = (struct PassphraseProcStruct *)lParam;
104 passphrase = p->passphrase;
105 if (p->comment)
106 SetDlgItemText(hwnd, 101, p->comment);
5c58ad2d 107 *passphrase = 0;
108 return 0;
109 case WM_COMMAND:
110 switch (LOWORD(wParam)) {
111 case IDOK:
112 if (*passphrase)
113 EndDialog (hwnd, 1);
114 else
115 MessageBeep (0);
116 return 0;
117 case IDCANCEL:
118 EndDialog (hwnd, 0);
119 return 0;
120 case 102: /* edit box */
121 if (HIWORD(wParam) == EN_CHANGE) {
122 GetDlgItemText (hwnd, 102, passphrase, PASSPHRASE_MAXLEN-1);
123 passphrase[PASSPHRASE_MAXLEN-1] = '\0';
124 }
125 return 0;
126 }
127 return 0;
128 case WM_CLOSE:
129 EndDialog (hwnd, 0);
130 return 0;
131 }
132 return 0;
133}
134
135/*
3c0b3d06 136 * Update the visible key list.
137 */
138void keylist_update(void) {
139 struct RSAKey *key;
140 enum234 e;
141
142 if (keylist) {
143 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
144 for (key = first234(rsakeys, &e); key; key = next234(&e)) {
1c2a93c4 145 char listentry[512], *p;
146 /*
147 * Replace two spaces in the fingerprint with tabs, for
148 * nice alignment in the box.
149 */
150 rsa_fingerprint(listentry, sizeof(listentry), key);
151 p = strchr(listentry, ' '); if (p) *p = '\t';
152 p = strchr(listentry, ' '); if (p) *p = '\t';
3c0b3d06 153 SendDlgItemMessage (keylist, 100, LB_ADDSTRING,
1c2a93c4 154 0, (LPARAM)listentry);
3c0b3d06 155 }
156 SendDlgItemMessage (keylist, 100, LB_SETCURSEL, (WPARAM) -1, 0);
157 }
158}
159
160/*
5c58ad2d 161 * This function loads a key from a file and adds it.
162 */
163void add_keyfile(char *filename) {
164 char passphrase[PASSPHRASE_MAXLEN];
165 struct RSAKey *key;
166 int needs_pass;
167 int ret;
168 int attempts;
d4de2d2a 169 char *comment;
170 struct PassphraseProcStruct pps;
5c58ad2d 171
d4de2d2a 172 needs_pass = rsakey_encrypted(filename, &comment);
5c58ad2d 173 attempts = 0;
174 key = malloc(sizeof(*key));
d4de2d2a 175 pps.passphrase = passphrase;
176 pps.comment = comment;
5c58ad2d 177 do {
178 if (needs_pass) {
179 int dlgret;
180 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
181 NULL, PassphraseProc,
d4de2d2a 182 (LPARAM)&pps);
5c58ad2d 183 if (!dlgret) {
d4de2d2a 184 if (comment) free(comment);
5c58ad2d 185 free(key);
186 return; /* operation cancelled */
187 }
188 } else
189 *passphrase = '\0';
190 ret = loadrsakey(filename, key, passphrase);
191 attempts++;
192 } while (ret == -1);
d4de2d2a 193 if (comment) free(comment);
5c58ad2d 194 if (ret == 0) {
195 MessageBox(NULL, "Couldn't load public key.", APPNAME,
196 MB_OK | MB_ICONERROR);
197 free(key);
198 return;
199 }
200 if (add234(rsakeys, key) != key)
201 free(key); /* already present, don't waste RAM */
202}
203
204/*
205 * This is the main agent function that answers messages.
206 */
d70f60ae 207void answer_msg(void *msg) {
208 unsigned char *p = msg;
209 unsigned char *ret = msg;
5c58ad2d 210 int type;
211
5c58ad2d 212 /*
213 * Get the message type.
214 */
215 type = p[4];
216
217 p += 5;
5c58ad2d 218 switch (type) {
219 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
220 /*
221 * Reply with SSH_AGENT_RSA_IDENTITIES_ANSWER.
222 */
223 {
224 enum234 e;
225 struct RSAKey *key;
226 int len, nkeys;
227
228 /*
229 * Count up the number and length of keys we hold.
230 */
231 len = nkeys = 0;
232 for (key = first234(rsakeys, &e); key; key = next234(&e)) {
233 nkeys++;
234 len += 4; /* length field */
235 len += ssh1_bignum_length(key->exponent);
236 len += ssh1_bignum_length(key->modulus);
237 len += 4 + strlen(key->comment);
238 }
239
240 /*
241 * Packet header is the obvious five bytes, plus four
242 * bytes for the key count.
243 */
244 len += 5 + 4;
d70f60ae 245 if (len > AGENT_MAX_MSGLEN)
246 goto failure; /* aaargh! too much stuff! */
247 PUT_32BIT(ret, len-4);
248 ret[4] = SSH_AGENT_RSA_IDENTITIES_ANSWER;
249 PUT_32BIT(ret+5, nkeys);
250 p = ret + 5 + 4;
251 for (key = first234(rsakeys, &e); key; key = next234(&e)) {
252 PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus));
253 p += 4;
254 p += ssh1_write_bignum(p, key->exponent);
255 p += ssh1_write_bignum(p, key->modulus);
256 PUT_32BIT(p, strlen(key->comment));
257 memcpy(p+4, key->comment, strlen(key->comment));
258 p += 4 + strlen(key->comment);
5c58ad2d 259 }
260 }
261 break;
262 case SSH_AGENTC_RSA_CHALLENGE:
263 /*
264 * Reply with either SSH_AGENT_RSA_RESPONSE or
265 * SSH_AGENT_FAILURE, depending on whether we have that key
266 * or not.
267 */
268 {
269 struct RSAKey reqkey, *key;
270 Bignum challenge, response;
271 unsigned char response_source[48], response_md5[16];
272 struct MD5Context md5c;
273 int i, len;
274
275 p += 4;
276 p += ssh1_read_bignum(p, &reqkey.exponent);
277 p += ssh1_read_bignum(p, &reqkey.modulus);
278 p += ssh1_read_bignum(p, &challenge);
279 memcpy(response_source+32, p, 16); p += 16;
280 if (GET_32BIT(p) != 1 ||
281 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
282 freebn(reqkey.exponent);
283 freebn(reqkey.modulus);
284 freebn(challenge);
285 goto failure;
286 }
287 response = rsadecrypt(challenge, key);
288 for (i = 0; i < 32; i++)
289 response_source[i] = bignum_byte(response, 31-i);
290
291 MD5Init(&md5c);
292 MD5Update(&md5c, response_source, 48);
293 MD5Final(response_md5, &md5c);
294 memset(response_source, 0, 48); /* burn the evidence */
295 freebn(response); /* and that evidence */
296 freebn(challenge); /* yes, and that evidence */
297 freebn(reqkey.exponent); /* and free some memory ... */
298 freebn(reqkey.modulus); /* ... while we're at it. */
299
300 /*
301 * Packet is the obvious five byte header, plus sixteen
302 * bytes of MD5.
303 */
304 len = 5 + 16;
d70f60ae 305 PUT_32BIT(ret, len-4);
306 ret[4] = SSH_AGENT_RSA_RESPONSE;
307 memcpy(ret+5, response_md5, 16);
5c58ad2d 308 }
309 break;
5c58ad2d 310 case SSH_AGENTC_ADD_RSA_IDENTITY:
311 /*
312 * Add to the list and return SSH_AGENT_SUCCESS, or
313 * SSH_AGENT_FAILURE if the key was malformed.
314 */
3c0b3d06 315 {
316 struct RSAKey *key;
317 char *comment;
318 key = malloc(sizeof(struct RSAKey));
319 memset(key, 0, sizeof(key));
320 p += makekey(p, key, NULL, 1);
321 p += makeprivate(p, key);
322 p += ssh1_read_bignum(p, NULL); /* p^-1 mod q */
323 p += ssh1_read_bignum(p, NULL); /* p */
324 p += ssh1_read_bignum(p, NULL); /* q */
325 comment = malloc(GET_32BIT(p));
326 if (comment) {
327 memcpy(comment, p+4, GET_32BIT(p));
328 key->comment = comment;
329 }
330 PUT_32BIT(ret, 1);
331 ret[4] = SSH_AGENT_FAILURE;
332 if (add234(rsakeys, key) == key) {
333 keylist_update();
334 ret[4] = SSH_AGENT_SUCCESS;
335 } else {
336 freersakey(key);
1c2a93c4 337 free(key);
3c0b3d06 338 }
339 }
5c58ad2d 340 break;
341 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
342 /*
343 * Remove from the list and return SSH_AGENT_SUCCESS, or
344 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
345 * start with.
346 */
3c0b3d06 347 {
348 struct RSAKey reqkey, *key;
349
350 p += makekey(p, &reqkey, NULL, 0);
351 key = find234(rsakeys, &reqkey, NULL);
352 freebn(reqkey.exponent);
353 freebn(reqkey.modulus);
354 PUT_32BIT(ret, 1);
355 ret[4] = SSH_AGENT_FAILURE;
356 if (key) {
357 del234(rsakeys, key);
358 keylist_update();
d4de2d2a 359 freersakey(key);
3c0b3d06 360 ret[4] = SSH_AGENT_SUCCESS;
361 }
362 }
5c58ad2d 363 break;
5c58ad2d 364 default:
365 failure:
366 /*
367 * Unrecognised message. Return SSH_AGENT_FAILURE.
368 */
d70f60ae 369 PUT_32BIT(ret, 1);
370 ret[4] = SSH_AGENT_FAILURE;
5c58ad2d 371 break;
372 }
5c58ad2d 373}
374
375/*
376 * Key comparison function for the 2-3-4 tree of RSA keys.
377 */
378int cmpkeys(void *av, void *bv) {
379 struct RSAKey *a = (struct RSAKey *)av;
380 struct RSAKey *b = (struct RSAKey *)bv;
381 Bignum am, bm;
382 int alen, blen;
383
384 am = a->modulus;
385 bm = b->modulus;
386 /*
387 * Compare by length of moduli.
388 */
389 alen = ssh1_bignum_bitcount(am);
390 blen = ssh1_bignum_bitcount(bm);
391 if (alen > blen) return +1; else if (alen < blen) return -1;
392 /*
393 * Now compare by moduli themselves.
394 */
395 alen = (alen + 7) / 8; /* byte count */
396 while (alen-- > 0) {
397 int abyte, bbyte;
398 abyte = bignum_byte(am, alen);
399 bbyte = bignum_byte(bm, alen);
400 if (abyte > bbyte) return +1; else if (abyte < bbyte) return -1;
401 }
402 /*
403 * Give up.
404 */
405 return 0;
406}
407
408static void error(char *s) {
409 MessageBox(hwnd, s, APPNAME, MB_OK | MB_ICONERROR);
410}
411
412/*
413 * Dialog-box function for the key list box.
414 */
415static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
416 WPARAM wParam, LPARAM lParam) {
417 enum234 e;
418 struct RSAKey *key;
419 OPENFILENAME of;
420 char filename[FILENAME_MAX];
421
422 switch (msg) {
423 case WM_INITDIALOG:
1c2a93c4 424 keylist = hwnd;
425 {
426 static int tabs[2] = {25, 175};
427 SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS, 2,
428 (LPARAM) tabs);
429 }
430 keylist_update();
5c58ad2d 431 return 0;
432 case WM_COMMAND:
433 switch (LOWORD(wParam)) {
434 case IDOK:
435 case IDCANCEL:
436 keylist = NULL;
437 DestroyWindow(hwnd);
438 return 0;
439 case 101: /* add key */
440 if (HIWORD(wParam) == BN_CLICKED ||
441 HIWORD(wParam) == BN_DOUBLECLICKED) {
442 memset(&of, 0, sizeof(of));
443#ifdef OPENFILENAME_SIZE_VERSION_400
444 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
445#else
446 of.lStructSize = sizeof(of);
447#endif
448 of.hwndOwner = hwnd;
449 of.lpstrFilter = "All Files\0*\0\0\0";
450 of.lpstrCustomFilter = NULL;
451 of.nFilterIndex = 1;
452 of.lpstrFile = filename; *filename = '\0';
453 of.nMaxFile = sizeof(filename);
454 of.lpstrFileTitle = NULL;
455 of.lpstrInitialDir = NULL;
456 of.lpstrTitle = "Select Public Key File";
457 of.Flags = 0;
458 if (GetOpenFileName(&of)) {
459 add_keyfile(filename);
3c0b3d06 460 keylist_update();
5c58ad2d 461 }
5c58ad2d 462 }
463 return 0;
464 case 102: /* remove key */
465 if (HIWORD(wParam) == BN_CLICKED ||
466 HIWORD(wParam) == BN_DOUBLECLICKED) {
467 int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0);
468 if (n == LB_ERR || n == 0) {
469 MessageBeep(0);
470 break;
471 }
472 for (key = first234(rsakeys, &e); key; key = next234(&e))
473 if (n-- == 0)
474 break;
475 del234(rsakeys, key);
476 freersakey(key); free(key);
1c2a93c4 477 keylist_update();
5c58ad2d 478 }
479 return 0;
480 }
481 return 0;
482 case WM_CLOSE:
483 keylist = NULL;
484 DestroyWindow(hwnd);
485 return 0;
486 }
487 return 0;
488}
489
490static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
491 WPARAM wParam, LPARAM lParam) {
492 int ret;
493 static int menuinprogress;
494
495 switch (message) {
496 case WM_SYSTRAY:
497 if (lParam == WM_RBUTTONUP) {
498 POINT cursorpos;
499 GetCursorPos(&cursorpos);
500 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
905beba5 501 } else if (lParam == WM_LBUTTONDBLCLK) {
502 /* Equivalent to IDM_VIEWKEYS. */
503 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
5c58ad2d 504 }
505 break;
506 case WM_SYSTRAY2:
507 if (!menuinprogress) {
508 menuinprogress = 1;
509 SetForegroundWindow(hwnd);
510 ret = TrackPopupMenu(systray_menu,
511 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
512 TPM_RIGHTBUTTON,
513 wParam, lParam, 0, hwnd, NULL);
514 menuinprogress = 0;
515 }
516 break;
517 case WM_COMMAND:
518 case WM_SYSCOMMAND:
519 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
520 case IDM_CLOSE:
521 SendMessage(hwnd, WM_CLOSE, 0, 0);
522 break;
523 case IDM_VIEWKEYS:
524 if (!keylist) {
525 keylist = CreateDialog (instance, MAKEINTRESOURCE(211),
526 NULL, KeyListProc);
527 ShowWindow (keylist, SW_SHOWNORMAL);
905beba5 528 /*
529 * Sometimes the window comes up minimised / hidden
530 * for no obvious reason. Prevent this.
531 */
532 SetForegroundWindow(keylist);
533 SetWindowPos (keylist, HWND_TOP, 0, 0, 0, 0,
534 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
5c58ad2d 535 }
536 break;
537 }
538 break;
539 case WM_DESTROY:
540 PostQuitMessage (0);
541 return 0;
542 case WM_COPYDATA:
543 {
544 COPYDATASTRUCT *cds;
d70f60ae 545 char *mapname;
546 void *p;
547 HANDLE filemap, proc;
548 PSID mapowner, procowner;
549 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
550 int ret = 0;
5c58ad2d 551
552 cds = (COPYDATASTRUCT *)lParam;
d70f60ae 553 if (cds->dwData != AGENT_COPYDATA_ID)
554 return 0; /* not our message, mate */
555 mapname = (char *)cds->lpData;
556 if (mapname[cds->cbData - 1] != '\0')
557 return 0; /* failure to be ASCIZ! */
558#ifdef DEBUG_IPC
559 debug(("mapname is :%s:\r\n", mapname));
560#endif
561 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
562#ifdef DEBUG_IPC
563 debug(("filemap is %p\r\n", filemap));
564#endif
565 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
566 int rc;
016ef8ab 567 if (has_security) {
568 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
569 GetCurrentProcessId())) == NULL) {
d70f60ae 570#ifdef DEBUG_IPC
016ef8ab 571 debug(("couldn't get handle for process\r\n"));
d70f60ae 572#endif
016ef8ab 573 return 0;
574 }
575 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
576 OWNER_SECURITY_INFORMATION,
577 &procowner, NULL, NULL, NULL,
578 &psd2) != ERROR_SUCCESS) {
d70f60ae 579#ifdef DEBUG_IPC
016ef8ab 580 debug(("couldn't get owner info for process\r\n"));
d70f60ae 581#endif
016ef8ab 582 CloseHandle(proc);
583 return 0; /* unable to get security info */
584 }
d70f60ae 585 CloseHandle(proc);
016ef8ab 586 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
587 OWNER_SECURITY_INFORMATION,
588 &mapowner, NULL, NULL, NULL,
589 &psd1) != ERROR_SUCCESS)) {
d70f60ae 590#ifdef DEBUG_IPC
016ef8ab 591 debug(("couldn't get owner info for filemap: %d\r\n", rc));
d70f60ae 592#endif
016ef8ab 593 return 0;
594 }
595#ifdef DEBUG_IPC
596 debug(("got security stuff\r\n"));
597#endif
598 if (!EqualSid(mapowner, procowner))
599 return 0; /* security ID mismatch! */
d70f60ae 600#ifdef DEBUG_IPC
016ef8ab 601 debug(("security stuff matched\r\n"));
d70f60ae 602#endif
016ef8ab 603 LocalFree(psd1);
604 LocalFree(psd2);
605 } else {
d70f60ae 606#ifdef DEBUG_IPC
016ef8ab 607 debug(("security APIs not present\r\n"));
d70f60ae 608#endif
016ef8ab 609 }
d70f60ae 610 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
611#ifdef DEBUG_IPC
612 debug(("p is %p\r\n", p));
613 {int i; for(i=0;i<5;i++)debug(("p[%d]=%02x\r\n", i, ((unsigned char *)p)[i]));}
614#endif
615 answer_msg(p);
616 ret = 1;
617 UnmapViewOfFile(p);
618 }
619 CloseHandle(filemap);
620 return ret;
5c58ad2d 621 }
5c58ad2d 622 }
623
624 return DefWindowProc (hwnd, message, wParam, lParam);
625}
626
627int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
628 WNDCLASS wndclass;
5c58ad2d 629 MSG msg;
016ef8ab 630 OSVERSIONINFO osi;
631 HMODULE advapi;
632
633 /*
634 * Determine whether we're an NT system (should have security
635 * APIs) or a non-NT system (don't do security).
636 */
637 memset(&osi, 0, sizeof(OSVERSIONINFO));
638 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
639 if (GetVersionEx(&osi) && osi.dwPlatformId==VER_PLATFORM_WIN32_NT) {
640 has_security = TRUE;
641 } else
642 has_security = FALSE;
643
644 if (has_security) {
645 /*
646 * Attempt to ge the security API we need.
647 */
648 advapi = LoadLibrary("ADVAPI32.DLL");
649 getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo");
650 if (!getsecurityinfo) {
651 MessageBox(NULL,
652 "Unable to access security APIs. Pageant will\n"
653 "not run, in case it causes a security breach.",
654 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
655 return 1;
656 }
657 } else
658 advapi = NULL;
5c58ad2d 659
2faac2e1 660 /*
661 * First bomb out totally if we are already running.
662 */
663 if (FindWindow("Pageant", "Pageant")) {
664 MessageBox(NULL, "Pageant is already running", "Pageant Error",
665 MB_ICONERROR | MB_OK);
016ef8ab 666 if (advapi) FreeLibrary(advapi);
2faac2e1 667 return 0;
668 }
669
5c58ad2d 670 instance = inst;
671
672 if (!prev) {
673 wndclass.style = 0;
674 wndclass.lpfnWndProc = WndProc;
675 wndclass.cbClsExtra = 0;
676 wndclass.cbWndExtra = 0;
677 wndclass.hInstance = inst;
678 wndclass.hIcon = LoadIcon (inst,
679 MAKEINTRESOURCE(IDI_MAINICON));
680 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
681 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
682 wndclass.lpszMenuName = NULL;
683 wndclass.lpszClassName = APPNAME;
684
685 RegisterClass (&wndclass);
686 }
687
688 hwnd = keylist = NULL;
689
690 hwnd = CreateWindow (APPNAME, APPNAME,
691 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
692 CW_USEDEFAULT, CW_USEDEFAULT,
693 100, 100, NULL, NULL, inst, NULL);
694
695 /* Set up a system tray icon */
696 {
697 BOOL res;
698 NOTIFYICONDATA tnid;
699 HICON hicon;
700
701#ifdef NIM_SETVERSION
702 tnid.uVersion = 0;
703 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
704#endif
705
706 tnid.cbSize = sizeof(NOTIFYICONDATA);
707 tnid.hWnd = hwnd;
708 tnid.uID = 1; /* unique within this systray use */
709 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
710 tnid.uCallbackMessage = WM_SYSTRAY;
711 tnid.hIcon = hicon = LoadIcon (instance, MAKEINTRESOURCE(201));
712 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
713
714 res = Shell_NotifyIcon(NIM_ADD, &tnid);
715
716 if (hicon)
717 DestroyIcon(hicon);
718
719 systray_menu = CreatePopupMenu();
70acd63f 720 AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
721 AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
5c58ad2d 722 }
723
724 ShowWindow (hwnd, SW_HIDE);
725
726 /*
dacbd0e8 727 * Initialise storage for RSA keys.
728 */
729 rsakeys = newtree234(cmpkeys);
730
731 /*
732 * Process the command line and add RSA keys as listed on it.
5c58ad2d 733 */
734 {
8c42f28c 735 char *p;
736 int inquotes = 0;
737 p = cmdline;
dacbd0e8 738 while (*p) {
739 while (*p && isspace(*p)) p++;
740 if (*p && !isspace(*p)) {
8c42f28c 741 char *q = p, *pp = p;
742 while (*p && (inquotes || !isspace(*p)))
743 {
744 if (*p == '"') {
745 inquotes = !inquotes;
746 p++;
747 continue;
748 }
749 *pp++ = *p++;
750 }
751 if (*pp) {
752 if (*p) p++;
753 *pp++ = '\0';
754 }
dacbd0e8 755 add_keyfile(q);
756 }
757 }
5c58ad2d 758 }
759
760 /*
dacbd0e8 761 * Main message loop.
5c58ad2d 762 */
5c58ad2d 763 while (GetMessage(&msg, NULL, 0, 0) == 1) {
764 TranslateMessage(&msg);
765 DispatchMessage(&msg);
766 }
767
768 /* Clean up the system tray icon */
769 {
770 NOTIFYICONDATA tnid;
771
772 tnid.cbSize = sizeof(NOTIFYICONDATA);
773 tnid.hWnd = hwnd;
774 tnid.uID = 1;
775
776 Shell_NotifyIcon(NIM_DELETE, &tnid);
777
778 DestroyMenu(systray_menu);
779 }
780
016ef8ab 781 if (advapi) FreeLibrary(advapi);
5c58ad2d 782 exit(msg.wParam);
783}