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