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