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