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