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