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