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