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