]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - pageant.c
INCOMPATIBLE CHANGE to the SSH2 private key file format. There is
[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 <tchar.h>
13
14 #include "ssh.h"
15 #include "tree234.h"
16
17 #define IDI_MAINICON 200
18 #define IDI_TRAYICON 201
19
20 #define WM_XUSER     (WM_USER + 0x2000)
21 #define WM_SYSTRAY   (WM_XUSER + 6)
22 #define WM_SYSTRAY2  (WM_XUSER + 7)
23
24 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
25
26 /*
27  * FIXME: maybe some day we can sort this out ...
28  */
29 #define AGENT_MAX_MSGLEN  8192
30
31 #define IDM_CLOSE    0x0010
32 #define IDM_VIEWKEYS 0x0020
33 #define IDM_ADDKEY   0x0030
34 #define IDM_ABOUT    0x0040
35
36 #define APPNAME "Pageant"
37
38 extern char ver[];
39
40 static HINSTANCE instance;
41 static HWND hwnd;
42 static HWND keylist;
43 static HWND aboutbox;
44 static HMENU systray_menu;
45 static int already_running;
46
47 static tree234 *rsakeys, *ssh2keys;
48
49 static int has_security;
50 #ifndef NO_SECURITY
51 typedef DWORD(WINAPI * gsi_fn_t)
52  (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
53   PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
54 static gsi_fn_t getsecurityinfo;
55 #endif
56
57 /*
58  * Exports from pageantc.c
59  */
60 void agent_query(void *in, int inlen, void **out, int *outlen);
61 int agent_exists(void);
62
63 /*
64  * We need this to link with the RSA code, because rsaencrypt()
65  * pads its data with random bytes. Since we only use rsadecrypt()
66  * and the signing functions, which are deterministic, this should
67  * 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 {
75     MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
76     exit(0);
77     /* this line can't be reached but it placates MSVC's warnings :-) */
78     return 0;
79 }
80
81 /*
82  * Blob structure for passing to the asymmetric SSH2 key compare
83  * function, prototyped here.
84  */
85 struct blob {
86     unsigned char *blob;
87     int len;
88 };
89 static int cmpkeys_ssh2_asymm(void *av, void *bv);
90
91 /*
92  * This function is needed to link with the DES code. We need not
93  * have it do anything at all.
94  */
95 void logevent(char *msg)
96 {
97 }
98
99 #define GET_32BIT(cp) \
100     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
101     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
102     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
103     ((unsigned long)(unsigned char)(cp)[3]))
104
105 #define PUT_32BIT(cp, value) { \
106     (cp)[0] = (unsigned char)((value) >> 24); \
107     (cp)[1] = (unsigned char)((value) >> 16); \
108     (cp)[2] = (unsigned char)((value) >> 8); \
109     (cp)[3] = (unsigned char)(value); }
110
111 #define PASSPHRASE_MAXLEN 512
112
113 struct PassphraseProcStruct {
114     char *passphrase;
115     char *comment;
116 };
117
118 /*
119  * Dialog-box function for the Licence box.
120  */
121 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
122                                 WPARAM wParam, LPARAM lParam)
123 {
124     switch (msg) {
125       case WM_INITDIALOG:
126         return 1;
127       case WM_COMMAND:
128         switch (LOWORD(wParam)) {
129           case IDOK:
130             EndDialog(hwnd, 1);
131             return 0;
132         }
133         return 0;
134       case WM_CLOSE:
135         EndDialog(hwnd, 1);
136         return 0;
137     }
138     return 0;
139 }
140
141 /*
142  * Dialog-box function for the About box.
143  */
144 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
145                               WPARAM wParam, LPARAM lParam)
146 {
147     switch (msg) {
148       case WM_INITDIALOG:
149         SetDlgItemText(hwnd, 100, ver);
150         return 1;
151       case WM_COMMAND:
152         switch (LOWORD(wParam)) {
153           case IDOK:
154             aboutbox = NULL;
155             DestroyWindow(hwnd);
156             return 0;
157           case 101:
158             EnableWindow(hwnd, 0);
159             DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
160             EnableWindow(hwnd, 1);
161             SetActiveWindow(hwnd);
162             return 0;
163         }
164         return 0;
165       case WM_CLOSE:
166         aboutbox = NULL;
167         DestroyWindow(hwnd);
168         return 0;
169     }
170     return 0;
171 }
172
173 static HWND passphrase_box;
174
175 /*
176  * Dialog-box function for the passphrase box.
177  */
178 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
179                                    WPARAM wParam, LPARAM lParam)
180 {
181     static char *passphrase = NULL;
182     struct PassphraseProcStruct *p;
183
184     switch (msg) {
185       case WM_INITDIALOG:
186         passphrase_box = hwnd;
187         /*
188          * Centre the window.
189          */
190         {                              /* centre the window */
191             RECT rs, rd;
192             HWND hw;
193
194             hw = GetDesktopWindow();
195             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
196                 MoveWindow(hwnd,
197                            (rs.right + rs.left + rd.left - rd.right) / 2,
198                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
199                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
200         }
201
202         SetForegroundWindow(hwnd);
203         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
204                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
205         p = (struct PassphraseProcStruct *) lParam;
206         passphrase = p->passphrase;
207         if (p->comment)
208             SetDlgItemText(hwnd, 101, p->comment);
209         *passphrase = 0;
210         SetDlgItemText(hwnd, 102, passphrase);
211         return 0;
212       case WM_COMMAND:
213         switch (LOWORD(wParam)) {
214           case IDOK:
215             if (*passphrase)
216                 EndDialog(hwnd, 1);
217             else
218                 MessageBeep(0);
219             return 0;
220           case IDCANCEL:
221             EndDialog(hwnd, 0);
222             return 0;
223           case 102:                    /* edit box */
224             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
225                 GetDlgItemText(hwnd, 102, passphrase,
226                                PASSPHRASE_MAXLEN - 1);
227                 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
228             }
229             return 0;
230         }
231         return 0;
232       case WM_CLOSE:
233         EndDialog(hwnd, 0);
234         return 0;
235     }
236     return 0;
237 }
238
239 /*
240  * Warn about the obsolescent key file format.
241  */
242 void old_keyfile_warning(void)
243 {
244     static const char mbtitle[] = "PuTTY Key File Warning";
245     static const char message[] =
246         "You are loading an SSH 2 private key which has an\n"
247         "old version of the file format. This means your key\n"
248         "file is not fully tamperproof. Future versions of\n"
249         "PuTTY may stop supporting this private key format,\n"
250         "so we recommend you convert your key to the new\n"
251         "format.\n"
252         "\n"
253         "You can perform this conversion by loading the key\n"
254         "into PuTTYgen and then saving it again.";
255
256     MessageBox(NULL, message, mbtitle, MB_OK);
257 }
258
259 /*
260  * Update the visible key list.
261  */
262 static void keylist_update(void)
263 {
264     struct RSAKey *rkey;
265     struct ssh2_userkey *skey;
266     int i;
267
268     if (keylist) {
269         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
270         for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
271             char listentry[512], *p;
272             /*
273              * Replace two spaces in the fingerprint with tabs, for
274              * nice alignment in the box.
275              */
276             strcpy(listentry, "ssh1\t");
277             p = listentry + strlen(listentry);
278             rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
279             p = strchr(listentry, ' ');
280             if (p)
281                 *p = '\t';
282             p = strchr(listentry, ' ');
283             if (p)
284                 *p = '\t';
285             SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
286                                0, (LPARAM) listentry);
287         }
288         for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
289             char listentry[512], *p;
290             int len;
291             /*
292              * Replace two spaces in the fingerprint with tabs, for
293              * nice alignment in the box.
294              */
295             p = skey->alg->fingerprint(skey->data);
296             strncpy(listentry, p, sizeof(listentry));
297             p = strchr(listentry, ' ');
298             if (p)
299                 *p = '\t';
300             p = strchr(listentry, ' ');
301             if (p)
302                 *p = '\t';
303             len = strlen(listentry);
304             if (len < sizeof(listentry) - 2) {
305                 listentry[len] = '\t';
306                 strncpy(listentry + len + 1, skey->comment,
307                         sizeof(listentry) - len - 1);
308             }
309             SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
310                                (LPARAM) listentry);
311         }
312         SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
313     }
314 }
315
316 /*
317  * This function loads a key from a file and adds it.
318  */
319 static void add_keyfile(char *filename)
320 {
321     char passphrase[PASSPHRASE_MAXLEN];
322     struct RSAKey *rkey = NULL;
323     struct ssh2_userkey *skey = NULL;
324     int needs_pass;
325     int ret;
326     int attempts;
327     char *comment;
328     struct PassphraseProcStruct pps;
329     int ver;
330
331     ver = keyfile_version(filename);
332     if (ver == 0) {
333         MessageBox(NULL, "Couldn't load private key.", APPNAME,
334                    MB_OK | MB_ICONERROR);
335         return;
336     }
337
338     if (ver == 1)
339         needs_pass = rsakey_encrypted(filename, &comment);
340     else
341         needs_pass = ssh2_userkey_encrypted(filename, &comment);
342     attempts = 0;
343     if (ver == 1)
344         rkey = smalloc(sizeof(*rkey));
345     pps.passphrase = passphrase;
346     pps.comment = comment;
347     do {
348         if (needs_pass) {
349             int dlgret;
350             dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
351                                     NULL, PassphraseProc, (LPARAM) & pps);
352             passphrase_box = NULL;
353             if (!dlgret) {
354                 if (comment)
355                     sfree(comment);
356                 if (ver == 1)
357                     sfree(rkey);
358                 return;                /* operation cancelled */
359             }
360         } else
361             *passphrase = '\0';
362         if (ver == 1)
363             ret = loadrsakey(filename, rkey, passphrase);
364         else {
365             skey = ssh2_load_userkey(filename, passphrase);
366             if (skey == SSH2_WRONG_PASSPHRASE)
367                 ret = -1;
368             else if (!skey)
369                 ret = 0;
370             else
371                 ret = 1;
372         }
373         attempts++;
374     } while (ret == -1);
375     if (comment)
376         sfree(comment);
377     if (ret == 0) {
378         MessageBox(NULL, "Couldn't load private key.", APPNAME,
379                    MB_OK | MB_ICONERROR);
380         if (ver == 1)
381             sfree(rkey);
382         return;
383     }
384     if (ver == 1) {
385         if (already_running) {
386             unsigned char *request, *response;
387             void *vresponse;
388             int reqlen, clen, resplen;
389
390             clen = strlen(rkey->comment);
391
392             reqlen = 4 + 1 +           /* length, message type */
393                 4 +                    /* bit count */
394                 ssh1_bignum_length(rkey->modulus) +
395                 ssh1_bignum_length(rkey->exponent) +
396                 ssh1_bignum_length(rkey->private_exponent) +
397                 ssh1_bignum_length(rkey->iqmp) +
398                 ssh1_bignum_length(rkey->p) +
399                 ssh1_bignum_length(rkey->q) + 4 + clen  /* comment */
400                 ;
401
402             request = smalloc(reqlen);
403
404             request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
405             reqlen = 5;
406             PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
407             reqlen += 4;
408             reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
409             reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
410             reqlen +=
411                 ssh1_write_bignum(request + reqlen,
412                                   rkey->private_exponent);
413             reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
414             reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
415             reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
416             PUT_32BIT(request + reqlen, clen);
417             memcpy(request + reqlen + 4, rkey->comment, clen);
418             reqlen += 4 + clen;
419             PUT_32BIT(request, reqlen - 4);
420
421             agent_query(request, reqlen, &vresponse, &resplen);
422             response = vresponse;
423             if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
424                 MessageBox(NULL, "The already running Pageant "
425                            "refused to add the key.", APPNAME,
426                            MB_OK | MB_ICONERROR);
427         } else {
428             if (add234(rsakeys, rkey) != rkey)
429                 sfree(rkey);           /* already present, don't waste RAM */
430         }
431     } else {
432         if (already_running) {
433             unsigned char *request, *response;
434             void *vresponse;
435             int reqlen, alglen, clen, keybloblen, resplen;
436             alglen = strlen(skey->alg->name);
437             clen = strlen(skey->comment);
438
439             keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
440
441             reqlen = 4 + 1 +           /* length, message type */
442                 4 + alglen +           /* algorithm name */
443                 keybloblen +           /* key data */
444                 4 + clen               /* comment */
445                 ;
446
447             request = smalloc(reqlen);
448
449             request[4] = SSH2_AGENTC_ADD_IDENTITY;
450             reqlen = 5;
451             PUT_32BIT(request + reqlen, alglen);
452             reqlen += 4;
453             memcpy(request + reqlen, skey->alg->name, alglen);
454             reqlen += alglen;
455             reqlen += skey->alg->openssh_fmtkey(skey->data,
456                                                 request + reqlen,
457                                                 keybloblen);
458             PUT_32BIT(request + reqlen, clen);
459             memcpy(request + reqlen + 4, skey->comment, clen);
460             PUT_32BIT(request, reqlen - 4);
461             reqlen += clen + 4;
462
463             agent_query(request, reqlen, &vresponse, &resplen);
464             response = vresponse;
465             if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
466                 MessageBox(NULL, "The already running Pageant"
467                            "refused to add the key.", APPNAME,
468                            MB_OK | MB_ICONERROR);
469         } else {
470             if (add234(ssh2keys, skey) != skey) {
471                 skey->alg->freekey(skey->data);
472                 sfree(skey);           /* already present, don't waste RAM */
473             }
474         }
475     }
476 }
477
478 /*
479  * This is the main agent function that answers messages.
480  */
481 static void answer_msg(void *msg)
482 {
483     unsigned char *p = msg;
484     unsigned char *ret = msg;
485     int type;
486
487     /*
488      * Get the message type.
489      */
490     type = p[4];
491
492     p += 5;
493     switch (type) {
494       case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
495         /*
496          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
497          */
498         {
499             struct RSAKey *key;
500             int len, nkeys;
501             int i;
502
503             /*
504              * Count up the number and length of keys we hold.
505              */
506             len = nkeys = 0;
507             for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
508                 nkeys++;
509                 len += 4;              /* length field */
510                 len += ssh1_bignum_length(key->exponent);
511                 len += ssh1_bignum_length(key->modulus);
512                 len += 4 + strlen(key->comment);
513             }
514
515             /*
516              * Packet header is the obvious five bytes, plus four
517              * bytes for the key count.
518              */
519             len += 5 + 4;
520             if (len > AGENT_MAX_MSGLEN)
521                 goto failure;          /* aaargh! too much stuff! */
522             PUT_32BIT(ret, len - 4);
523             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
524             PUT_32BIT(ret + 5, nkeys);
525             p = ret + 5 + 4;
526             for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
527                 PUT_32BIT(p, bignum_bitcount(key->modulus));
528                 p += 4;
529                 p += ssh1_write_bignum(p, key->exponent);
530                 p += ssh1_write_bignum(p, key->modulus);
531                 PUT_32BIT(p, strlen(key->comment));
532                 memcpy(p + 4, key->comment, strlen(key->comment));
533                 p += 4 + strlen(key->comment);
534             }
535         }
536         break;
537       case SSH2_AGENTC_REQUEST_IDENTITIES:
538         /*
539          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
540          */
541         {
542             struct ssh2_userkey *key;
543             int len, nkeys;
544             unsigned char *blob;
545             int bloblen;
546             int i;
547
548             /*
549              * Count up the number and length of keys we hold.
550              */
551             len = nkeys = 0;
552             for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
553                 nkeys++;
554                 len += 4;              /* length field */
555                 blob = key->alg->public_blob(key->data, &bloblen);
556                 len += bloblen;
557                 sfree(blob);
558                 len += 4 + strlen(key->comment);
559             }
560
561             /*
562              * Packet header is the obvious five bytes, plus four
563              * bytes for the key count.
564              */
565             len += 5 + 4;
566             if (len > AGENT_MAX_MSGLEN)
567                 goto failure;          /* aaargh! too much stuff! */
568             PUT_32BIT(ret, len - 4);
569             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
570             PUT_32BIT(ret + 5, nkeys);
571             p = ret + 5 + 4;
572             for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
573                 blob = key->alg->public_blob(key->data, &bloblen);
574                 PUT_32BIT(p, bloblen);
575                 p += 4;
576                 memcpy(p, blob, bloblen);
577                 p += bloblen;
578                 sfree(blob);
579                 PUT_32BIT(p, strlen(key->comment));
580                 memcpy(p + 4, key->comment, strlen(key->comment));
581                 p += 4 + strlen(key->comment);
582             }
583         }
584         break;
585       case SSH1_AGENTC_RSA_CHALLENGE:
586         /*
587          * Reply with either SSH1_AGENT_RSA_RESPONSE or
588          * SSH_AGENT_FAILURE, depending on whether we have that key
589          * or not.
590          */
591         {
592             struct RSAKey reqkey, *key;
593             Bignum challenge, response;
594             unsigned char response_source[48], response_md5[16];
595             struct MD5Context md5c;
596             int i, len;
597
598             p += 4;
599             p += ssh1_read_bignum(p, &reqkey.exponent);
600             p += ssh1_read_bignum(p, &reqkey.modulus);
601             p += ssh1_read_bignum(p, &challenge);
602             memcpy(response_source + 32, p, 16);
603             p += 16;
604             if (GET_32BIT(p) != 1 ||
605                 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
606                 freebn(reqkey.exponent);
607                 freebn(reqkey.modulus);
608                 freebn(challenge);
609                 goto failure;
610             }
611             response = rsadecrypt(challenge, key);
612             for (i = 0; i < 32; i++)
613                 response_source[i] = bignum_byte(response, 31 - i);
614
615             MD5Init(&md5c);
616             MD5Update(&md5c, response_source, 48);
617             MD5Final(response_md5, &md5c);
618             memset(response_source, 0, 48);     /* burn the evidence */
619             freebn(response);          /* and that evidence */
620             freebn(challenge);         /* yes, and that evidence */
621             freebn(reqkey.exponent);   /* and free some memory ... */
622             freebn(reqkey.modulus);    /* ... while we're at it. */
623
624             /*
625              * Packet is the obvious five byte header, plus sixteen
626              * bytes of MD5.
627              */
628             len = 5 + 16;
629             PUT_32BIT(ret, len - 4);
630             ret[4] = SSH1_AGENT_RSA_RESPONSE;
631             memcpy(ret + 5, response_md5, 16);
632         }
633         break;
634       case SSH2_AGENTC_SIGN_REQUEST:
635         /*
636          * Reply with either SSH2_AGENT_SIGN_RESPONSE or
637          * SSH_AGENT_FAILURE, depending on whether we have that key
638          * or not.
639          */
640         {
641             struct ssh2_userkey *key;
642             struct blob b;
643             unsigned char *data, *signature;
644             int datalen, siglen, len;
645
646             b.len = GET_32BIT(p);
647             p += 4;
648             b.blob = p;
649             p += b.len;
650             datalen = GET_32BIT(p);
651             p += 4;
652             data = p;
653             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
654             if (!key)
655                 goto failure;
656             signature = key->alg->sign(key->data, data, datalen, &siglen);
657             len = 5 + 4 + siglen;
658             PUT_32BIT(ret, len - 4);
659             ret[4] = SSH2_AGENT_SIGN_RESPONSE;
660             PUT_32BIT(ret + 5, siglen);
661             memcpy(ret + 5 + 4, signature, siglen);
662             sfree(signature);
663         }
664         break;
665       case SSH1_AGENTC_ADD_RSA_IDENTITY:
666         /*
667          * Add to the list and return SSH_AGENT_SUCCESS, or
668          * SSH_AGENT_FAILURE if the key was malformed.
669          */
670         {
671             struct RSAKey *key;
672             char *comment;
673             int commentlen;
674             key = smalloc(sizeof(struct RSAKey));
675             memset(key, 0, sizeof(struct RSAKey));
676             p += makekey(p, key, NULL, 1);
677             p += makeprivate(p, key);
678             p += ssh1_read_bignum(p, &key->iqmp);       /* p^-1 mod q */
679             p += ssh1_read_bignum(p, &key->p);  /* p */
680             p += ssh1_read_bignum(p, &key->q);  /* q */
681             commentlen = GET_32BIT(p);
682             comment = smalloc(commentlen+1);
683             if (comment) {
684                 memcpy(comment, p + 4, commentlen);
685                 comment[commentlen] = '\0';
686                 key->comment = comment;
687             }
688             PUT_32BIT(ret, 1);
689             ret[4] = SSH_AGENT_FAILURE;
690             if (add234(rsakeys, key) == key) {
691                 keylist_update();
692                 ret[4] = SSH_AGENT_SUCCESS;
693             } else {
694                 freersakey(key);
695                 sfree(key);
696             }
697         }
698         break;
699       case SSH2_AGENTC_ADD_IDENTITY:
700         /*
701          * Add to the list and return SSH_AGENT_SUCCESS, or
702          * SSH_AGENT_FAILURE if the key was malformed.
703          */
704         {
705             struct ssh2_userkey *key;
706             char *comment, *alg;
707             int alglen, commlen;
708             int bloblen;
709
710             key = smalloc(sizeof(struct ssh2_userkey));
711
712             alglen = GET_32BIT(p);
713             p += 4;
714             alg = p;
715             p += alglen;
716             /* Add further algorithm names here. */
717             if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
718                 key->alg = &ssh_rsa;
719             else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
720                 key->alg = &ssh_dss;
721             else {
722                 sfree(key);
723                 goto failure;
724             }
725
726             bloblen =
727                 GET_32BIT((unsigned char *) msg) - (p -
728                                                     (unsigned char *) msg -
729                                                     4);
730             key->data = key->alg->openssh_createkey(&p, &bloblen);
731             if (!key->data) {
732                 sfree(key);
733                 goto failure;
734             }
735             commlen = GET_32BIT(p);
736             p += 4;
737
738             comment = smalloc(commlen + 1);
739             if (comment) {
740                 memcpy(comment, p, commlen);
741                 comment[commlen] = '\0';
742             }
743             key->comment = comment;
744
745             PUT_32BIT(ret, 1);
746             ret[4] = SSH_AGENT_FAILURE;
747             if (add234(ssh2keys, key) == key) {
748                 keylist_update();
749                 ret[4] = SSH_AGENT_SUCCESS;
750             } else {
751                 key->alg->freekey(key->data);
752                 sfree(key->comment);
753                 sfree(key);
754             }
755         }
756         break;
757       case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
758         /*
759          * Remove from the list and return SSH_AGENT_SUCCESS, or
760          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
761          * start with.
762          */
763         {
764             struct RSAKey reqkey, *key;
765
766             p += makekey(p, &reqkey, NULL, 0);
767             key = find234(rsakeys, &reqkey, NULL);
768             freebn(reqkey.exponent);
769             freebn(reqkey.modulus);
770             PUT_32BIT(ret, 1);
771             ret[4] = SSH_AGENT_FAILURE;
772             if (key) {
773                 del234(rsakeys, key);
774                 keylist_update();
775                 freersakey(key);
776                 sfree(key);
777                 ret[4] = SSH_AGENT_SUCCESS;
778             }
779         }
780         break;
781       case SSH2_AGENTC_REMOVE_IDENTITY:
782         /*
783          * Remove from the list and return SSH_AGENT_SUCCESS, or
784          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
785          * start with.
786          */
787         {
788             struct ssh2_userkey *key;
789             struct blob b;
790
791             b.len = GET_32BIT(p);
792             p += 4;
793             b.blob = p;
794             p += b.len;
795             key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
796             if (!key)
797                 goto failure;
798
799             PUT_32BIT(ret, 1);
800             ret[4] = SSH_AGENT_FAILURE;
801             if (key) {
802                 del234(ssh2keys, key);
803                 keylist_update();
804                 key->alg->freekey(key->data);
805                 sfree(key);
806                 ret[4] = SSH_AGENT_SUCCESS;
807             }
808         }
809         break;
810       case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
811         /*
812          * Remove all SSH1 keys. Always returns success.
813          */
814         {
815             struct RSAKey *rkey;
816
817             while ((rkey = index234(rsakeys, 0)) != NULL) {
818                 del234(rsakeys, rkey);
819                 freersakey(rkey);
820                 sfree(rkey);
821             }
822             keylist_update();
823
824             PUT_32BIT(ret, 1);
825             ret[4] = SSH_AGENT_SUCCESS;
826         }
827         break;
828       case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
829         /*
830          * Remove all SSH2 keys. Always returns success.
831          */
832         {
833             struct ssh2_userkey *skey;
834
835             while ((skey = index234(ssh2keys, 0)) != NULL) {
836                 del234(ssh2keys, skey);
837                 skey->alg->freekey(skey->data);
838                 sfree(skey);
839             }
840             keylist_update();
841
842             PUT_32BIT(ret, 1);
843             ret[4] = SSH_AGENT_SUCCESS;
844         }
845         break;
846       default:
847       failure:
848         /*
849          * Unrecognised message. Return SSH_AGENT_FAILURE.
850          */
851         PUT_32BIT(ret, 1);
852         ret[4] = SSH_AGENT_FAILURE;
853         break;
854     }
855 }
856
857 /*
858  * Key comparison function for the 2-3-4 tree of RSA keys.
859  */
860 static int cmpkeys_rsa(void *av, void *bv)
861 {
862     struct RSAKey *a = (struct RSAKey *) av;
863     struct RSAKey *b = (struct RSAKey *) bv;
864     Bignum am, bm;
865     int alen, blen;
866
867     am = a->modulus;
868     bm = b->modulus;
869     /*
870      * Compare by length of moduli.
871      */
872     alen = bignum_bitcount(am);
873     blen = bignum_bitcount(bm);
874     if (alen > blen)
875         return +1;
876     else if (alen < blen)
877         return -1;
878     /*
879      * Now compare by moduli themselves.
880      */
881     alen = (alen + 7) / 8;             /* byte count */
882     while (alen-- > 0) {
883         int abyte, bbyte;
884         abyte = bignum_byte(am, alen);
885         bbyte = bignum_byte(bm, alen);
886         if (abyte > bbyte)
887             return +1;
888         else if (abyte < bbyte)
889             return -1;
890     }
891     /*
892      * Give up.
893      */
894     return 0;
895 }
896
897 /*
898  * Key comparison function for the 2-3-4 tree of SSH2 keys.
899  */
900 static int cmpkeys_ssh2(void *av, void *bv)
901 {
902     struct ssh2_userkey *a = (struct ssh2_userkey *) av;
903     struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
904     int i;
905     int alen, blen;
906     unsigned char *ablob, *bblob;
907     int c;
908
909     /*
910      * Compare purely by public blob.
911      */
912     ablob = a->alg->public_blob(a->data, &alen);
913     bblob = b->alg->public_blob(b->data, &blen);
914
915     c = 0;
916     for (i = 0; i < alen && i < blen; i++) {
917         if (ablob[i] < bblob[i]) {
918             c = -1;
919             break;
920         } else if (ablob[i] > bblob[i]) {
921             c = +1;
922             break;
923         }
924     }
925     if (c == 0 && i < alen)
926         c = +1;                        /* a is longer */
927     if (c == 0 && i < blen)
928         c = -1;                        /* a is longer */
929
930     sfree(ablob);
931     sfree(bblob);
932
933     return c;
934 }
935
936 /*
937  * Key comparison function for looking up a blob in the 2-3-4 tree
938  * of SSH2 keys.
939  */
940 static int cmpkeys_ssh2_asymm(void *av, void *bv)
941 {
942     struct blob *a = (struct blob *) av;
943     struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
944     int i;
945     int alen, blen;
946     unsigned char *ablob, *bblob;
947     int c;
948
949     /*
950      * Compare purely by public blob.
951      */
952     ablob = a->blob;
953     alen = a->len;
954     bblob = b->alg->public_blob(b->data, &blen);
955
956     c = 0;
957     for (i = 0; i < alen && i < blen; i++) {
958         if (ablob[i] < bblob[i]) {
959             c = -1;
960             break;
961         } else if (ablob[i] > bblob[i]) {
962             c = +1;
963             break;
964         }
965     }
966     if (c == 0 && i < alen)
967         c = +1;                        /* a is longer */
968     if (c == 0 && i < blen)
969         c = -1;                        /* a is longer */
970
971     sfree(bblob);
972
973     return c;
974 }
975
976 /*
977  * Prompt for a key file to add, and add it.
978  */
979 static void prompt_add_keyfile(void)
980 {
981     OPENFILENAME of;
982     char filename[FILENAME_MAX];
983     memset(&of, 0, sizeof(of));
984 #ifdef OPENFILENAME_SIZE_VERSION_400
985     of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
986 #else
987     of.lStructSize = sizeof(of);
988 #endif
989     of.hwndOwner = hwnd;
990     of.lpstrFilter = "All Files\0*\0\0\0";
991     of.lpstrCustomFilter = NULL;
992     of.nFilterIndex = 1;
993     of.lpstrFile = filename;
994     *filename = '\0';
995     of.nMaxFile = sizeof(filename);
996     of.lpstrFileTitle = NULL;
997     of.lpstrInitialDir = NULL;
998     of.lpstrTitle = "Select Private Key File";
999     of.Flags = 0;
1000     if (GetOpenFileName(&of)) {
1001         add_keyfile(filename);
1002         keylist_update();
1003     }
1004 }
1005
1006 /*
1007  * Dialog-box function for the key list box.
1008  */
1009 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1010                                 WPARAM wParam, LPARAM lParam)
1011 {
1012     struct RSAKey *rkey;
1013     struct ssh2_userkey *skey;
1014
1015     switch (msg) {
1016       case WM_INITDIALOG:
1017         /*
1018          * Centre the window.
1019          */
1020         {                              /* centre the window */
1021             RECT rs, rd;
1022             HWND hw;
1023
1024             hw = GetDesktopWindow();
1025             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1026                 MoveWindow(hwnd,
1027                            (rs.right + rs.left + rd.left - rd.right) / 2,
1028                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1029                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
1030         }
1031
1032         keylist = hwnd;
1033         {
1034             static int tabs[] = { 35, 60, 210 };
1035             SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1036                                sizeof(tabs) / sizeof(*tabs),
1037                                (LPARAM) tabs);
1038         }
1039         keylist_update();
1040         return 0;
1041       case WM_COMMAND:
1042         switch (LOWORD(wParam)) {
1043           case IDOK:
1044           case IDCANCEL:
1045             keylist = NULL;
1046             DestroyWindow(hwnd);
1047             return 0;
1048           case 101:                    /* add key */
1049             if (HIWORD(wParam) == BN_CLICKED ||
1050                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1051                 if (passphrase_box) {
1052                     MessageBeep(MB_ICONERROR);
1053                     SetForegroundWindow(passphrase_box);
1054                     break;
1055                 }
1056                 prompt_add_keyfile();
1057             }
1058             return 0;
1059           case 102:                    /* remove key */
1060             if (HIWORD(wParam) == BN_CLICKED ||
1061                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1062                 int n = SendDlgItemMessage(hwnd, 100, LB_GETCURSEL, 0, 0);
1063                 int i;
1064                 if (n == LB_ERR) {
1065                     MessageBeep(0);
1066                     break;
1067                 }
1068                 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++)
1069                     if (n-- == 0)
1070                         break;
1071                 if (rkey) {
1072                     del234(rsakeys, rkey);
1073                     freersakey(rkey);
1074                     sfree(rkey);
1075                 } else {
1076                     for (i = 0; NULL != (skey = index234(ssh2keys, i));
1077                          i++) if (n-- == 0)
1078                             break;
1079                     if (skey) {
1080                         del234(ssh2keys, skey);
1081                         skey->alg->freekey(skey->data);
1082                         sfree(skey);
1083                     }
1084                 }
1085                 keylist_update();
1086             }
1087             return 0;
1088         }
1089         return 0;
1090       case WM_CLOSE:
1091         keylist = NULL;
1092         DestroyWindow(hwnd);
1093         return 0;
1094     }
1095     return 0;
1096 }
1097
1098 /* Set up a system tray icon */
1099 static BOOL AddTrayIcon(HWND hwnd)
1100 {
1101     BOOL res;
1102     NOTIFYICONDATA tnid;
1103     HICON hicon;
1104
1105 #ifdef NIM_SETVERSION
1106     tnid.uVersion = 0;
1107     res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1108 #endif
1109
1110     tnid.cbSize = sizeof(NOTIFYICONDATA);
1111     tnid.hWnd = hwnd;
1112     tnid.uID = 1;              /* unique within this systray use */
1113     tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1114     tnid.uCallbackMessage = WM_SYSTRAY;
1115     tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1116     strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1117
1118     res = Shell_NotifyIcon(NIM_ADD, &tnid);
1119
1120     if (hicon) DestroyIcon(hicon);
1121     
1122     return res;
1123 }
1124
1125 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1126                                 WPARAM wParam, LPARAM lParam)
1127 {
1128     int ret;
1129     static int menuinprogress;
1130     static UINT msgTaskbarCreated = 0;
1131
1132     switch (message) {
1133       case WM_CREATE:
1134         msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1135         break;
1136       default:
1137         if (message==msgTaskbarCreated) {
1138             /*
1139              * Explorer has been restarted, so the tray icon will
1140              * have been lost.
1141              */
1142             AddTrayIcon(hwnd);
1143         }
1144         break;
1145         
1146       case WM_SYSTRAY:
1147         if (lParam == WM_RBUTTONUP) {
1148             POINT cursorpos;
1149             GetCursorPos(&cursorpos);
1150             PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1151         } else if (lParam == WM_LBUTTONDBLCLK) {
1152             /* Equivalent to IDM_VIEWKEYS. */
1153             PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1154         }
1155         break;
1156       case WM_SYSTRAY2:
1157         if (!menuinprogress) {
1158             menuinprogress = 1;
1159             SetForegroundWindow(hwnd);
1160             ret = TrackPopupMenu(systray_menu,
1161                                  TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1162                                  TPM_RIGHTBUTTON,
1163                                  wParam, lParam, 0, hwnd, NULL);
1164             menuinprogress = 0;
1165         }
1166         break;
1167       case WM_COMMAND:
1168       case WM_SYSCOMMAND:
1169         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1170           case IDM_CLOSE:
1171             if (passphrase_box)
1172                 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1173             SendMessage(hwnd, WM_CLOSE, 0, 0);
1174             break;
1175           case IDM_VIEWKEYS:
1176             if (!keylist) {
1177                 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1178                                        NULL, KeyListProc);
1179                 ShowWindow(keylist, SW_SHOWNORMAL);
1180                 /* 
1181                  * Sometimes the window comes up minimised / hidden
1182                  * for no obvious reason. Prevent this.
1183                  */
1184                 SetForegroundWindow(keylist);
1185                 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1186                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1187             }
1188             break;
1189           case IDM_ADDKEY:
1190             if (passphrase_box) {
1191                 MessageBeep(MB_ICONERROR);
1192                 SetForegroundWindow(passphrase_box);
1193                 break;
1194             }
1195             prompt_add_keyfile();
1196             break;
1197           case IDM_ABOUT:
1198             if (!aboutbox) {
1199                 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1200                                         NULL, AboutProc);
1201                 ShowWindow(aboutbox, SW_SHOWNORMAL);
1202                 /* 
1203                  * Sometimes the window comes up minimised / hidden
1204                  * for no obvious reason. Prevent this.
1205                  */
1206                 SetForegroundWindow(aboutbox);
1207                 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1208                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1209             }
1210             break;
1211         }
1212         break;
1213       case WM_DESTROY:
1214         PostQuitMessage(0);
1215         return 0;
1216       case WM_COPYDATA:
1217         {
1218             COPYDATASTRUCT *cds;
1219             char *mapname;
1220             void *p;
1221             HANDLE filemap;
1222 #ifndef NO_SECURITY
1223             HANDLE proc;
1224             PSID mapowner, procowner;
1225             PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1226 #endif
1227             int ret = 0;
1228
1229             cds = (COPYDATASTRUCT *) lParam;
1230             if (cds->dwData != AGENT_COPYDATA_ID)
1231                 return 0;              /* not our message, mate */
1232             mapname = (char *) cds->lpData;
1233             if (mapname[cds->cbData - 1] != '\0')
1234                 return 0;              /* failure to be ASCIZ! */
1235 #ifdef DEBUG_IPC
1236             debug(("mapname is :%s:\n", mapname));
1237 #endif
1238             filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1239 #ifdef DEBUG_IPC
1240             debug(("filemap is %p\n", filemap));
1241 #endif
1242             if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1243 #ifndef NO_SECURITY
1244                 int rc;
1245                 if (has_security) {
1246                     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1247                                             GetCurrentProcessId())) ==
1248                         NULL) {
1249 #ifdef DEBUG_IPC
1250                         debug(("couldn't get handle for process\n"));
1251 #endif
1252                         return 0;
1253                     }
1254                     if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1255                                         OWNER_SECURITY_INFORMATION,
1256                                         &procowner, NULL, NULL, NULL,
1257                                         &psd2) != ERROR_SUCCESS) {
1258 #ifdef DEBUG_IPC
1259                         debug(("couldn't get owner info for process\n"));
1260 #endif
1261                         CloseHandle(proc);
1262                         return 0;      /* unable to get security info */
1263                     }
1264                     CloseHandle(proc);
1265                     if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1266                                               OWNER_SECURITY_INFORMATION,
1267                                               &mapowner, NULL, NULL, NULL,
1268                                               &psd1) != ERROR_SUCCESS)) {
1269 #ifdef DEBUG_IPC
1270                         debug(
1271                               ("couldn't get owner info for filemap: %d\n",
1272                                rc));
1273 #endif
1274                         return 0;
1275                     }
1276 #ifdef DEBUG_IPC
1277                     debug(("got security stuff\n"));
1278 #endif
1279                     if (!EqualSid(mapowner, procowner))
1280                         return 0;      /* security ID mismatch! */
1281 #ifdef DEBUG_IPC
1282                     debug(("security stuff matched\n"));
1283 #endif
1284                     LocalFree(psd1);
1285                     LocalFree(psd2);
1286                 } else {
1287 #ifdef DEBUG_IPC
1288                     debug(("security APIs not present\n"));
1289 #endif
1290                 }
1291 #endif
1292                 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1293 #ifdef DEBUG_IPC
1294                 debug(("p is %p\n", p));
1295                 {
1296                     int i;
1297                     for (i = 0; i < 5; i++)
1298                         debug(
1299                               ("p[%d]=%02x\n", i,
1300                                ((unsigned char *) p)[i]));}
1301 #endif
1302                 answer_msg(p);
1303                 ret = 1;
1304                 UnmapViewOfFile(p);
1305             }
1306             CloseHandle(filemap);
1307             return ret;
1308         }
1309     }
1310
1311     return DefWindowProc(hwnd, message, wParam, lParam);
1312 }
1313
1314 /*
1315  * Fork and Exec the command in cmdline. [DBW]
1316  */
1317 void spawn_cmd(char *cmdline, int show)
1318 {
1319     if (ShellExecute(NULL, _T("open"), cmdline,
1320                      NULL, NULL, show) <= (HINSTANCE) 32) {
1321         TCHAR sMsg[140];
1322         sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
1323                 (int)GetLastError());
1324         MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1325     }
1326 }
1327
1328 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1329 {
1330     WNDCLASS wndclass;
1331     MSG msg;
1332     OSVERSIONINFO osi;
1333     HMODULE advapi;
1334     char *command = NULL;
1335     int added_keys = 0;
1336
1337     /*
1338      * Determine whether we're an NT system (should have security
1339      * APIs) or a non-NT system (don't do security).
1340      */
1341     memset(&osi, 0, sizeof(OSVERSIONINFO));
1342     osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1343     if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1344         has_security = TRUE;
1345     } else
1346         has_security = FALSE;
1347
1348     if (has_security) {
1349 #ifndef NO_SECURITY
1350         /*
1351          * Attempt to get the security API we need.
1352          */
1353         advapi = LoadLibrary("ADVAPI32.DLL");
1354         getsecurityinfo =
1355             (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1356         if (!getsecurityinfo) {
1357             MessageBox(NULL,
1358                        "Unable to access security APIs. Pageant will\n"
1359                        "not run, in case it causes a security breach.",
1360                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1361             return 1;
1362         }
1363 #else
1364         MessageBox(NULL,
1365                    "This program has been compiled for Win9X and will\n"
1366                    "not run on NT, in case it causes a security breach.",
1367                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1368         return 1;
1369 #endif
1370     } else
1371         advapi = NULL;
1372
1373     instance = inst;
1374
1375     /*
1376      * Find out if Pageant is already running.
1377      */
1378     already_running = FALSE;
1379     if (agent_exists())
1380         already_running = TRUE;
1381     else {
1382
1383         if (!prev) {
1384             wndclass.style = 0;
1385             wndclass.lpfnWndProc = WndProc;
1386             wndclass.cbClsExtra = 0;
1387             wndclass.cbWndExtra = 0;
1388             wndclass.hInstance = inst;
1389             wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1390             wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1391             wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1392             wndclass.lpszMenuName = NULL;
1393             wndclass.lpszClassName = APPNAME;
1394
1395             RegisterClass(&wndclass);
1396         }
1397
1398         hwnd = keylist = NULL;
1399
1400         hwnd = CreateWindow(APPNAME, APPNAME,
1401                             WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1402                             CW_USEDEFAULT, CW_USEDEFAULT,
1403                             100, 100, NULL, NULL, inst, NULL);
1404
1405         /* Set up a system tray icon */
1406         AddTrayIcon(hwnd);
1407
1408         systray_menu = CreatePopupMenu();
1409         /* accelerators used: vkxa */
1410         AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1411                "&View Keys");
1412         AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1413         AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1414         AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1415
1416         ShowWindow(hwnd, SW_HIDE);
1417
1418         /*
1419          * Initialise storage for RSA keys.
1420          */
1421         rsakeys = newtree234(cmpkeys_rsa);
1422         ssh2keys = newtree234(cmpkeys_ssh2);
1423
1424     }
1425
1426     /*
1427      * Process the command line and add keys as listed on it.
1428      * If we already determined that we need to spawn a program from above we
1429      * need to ignore the first two arguments. [DBW]
1430      */
1431     {
1432         char *p;
1433         int inquotes = 0;
1434         p = cmdline;
1435         while (*p) {
1436             while (*p && isspace(*p))
1437                 p++;
1438             if (*p && !isspace(*p)) {
1439                 char *q = p, *pp = p;
1440                 while (*p && (inquotes || !isspace(*p))) {
1441                     if (*p == '"') {
1442                         inquotes = !inquotes;
1443                         p++;
1444                         continue;
1445                     }
1446                     *pp++ = *p++;
1447                 }
1448                 if (*pp) {
1449                     if (*p)
1450                         p++;
1451                     *pp++ = '\0';
1452                 }
1453                 if (!strcmp(q, "-c")) {
1454                     /*
1455                      * If we see `-c', then the rest of the
1456                      * command line should be treated as a
1457                      * command to be spawned.
1458                      */
1459                     while (*p && isspace(*p))
1460                         p++;
1461                     command = p;
1462                     break;
1463                 } else {
1464                     add_keyfile(q);
1465                     added_keys = TRUE;
1466                 }
1467             }
1468         }
1469     }
1470
1471     if (command)
1472         spawn_cmd(command, show);
1473
1474     /*
1475      * If Pageant was already running, we leave now. If we haven't
1476      * even taken any auxiliary action (spawned a command or added
1477      * keys), complain.
1478      */
1479     if (already_running) {
1480         if (!command && !added_keys) {
1481             MessageBox(NULL, "Pageant is already running", "Pageant Error",
1482                        MB_ICONERROR | MB_OK);
1483         }
1484         if (advapi)
1485             FreeLibrary(advapi);
1486         return 0;
1487     }
1488
1489     /*
1490      * Main message loop.
1491      */
1492     while (GetMessage(&msg, NULL, 0, 0) == 1) {
1493         TranslateMessage(&msg);
1494         DispatchMessage(&msg);
1495     }
1496
1497     /* Clean up the system tray icon */
1498     {
1499         NOTIFYICONDATA tnid;
1500
1501         tnid.cbSize = sizeof(NOTIFYICONDATA);
1502         tnid.hWnd = hwnd;
1503         tnid.uID = 1;
1504
1505         Shell_NotifyIcon(NIM_DELETE, &tnid);
1506
1507         DestroyMenu(systray_menu);
1508     }
1509
1510     if (advapi)
1511         FreeLibrary(advapi);
1512     exit(msg.wParam);
1513 }