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