2cfcdd0be9cc14627f3906f296c13663efb0360f
[u/mdw/putty] / 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, char * args, int show)
1422 {
1423 if (ShellExecute(NULL, _T("open"), cmdline,
1424 args, 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 char *args;
1588 if (command[0] == '"')
1589 args = strchr(++command, '"');
1590 else
1591 args = strchr(command, ' ');
1592 if (args) {
1593 *args++ = 0;
1594 while(*args && isspace(*args)) args++;
1595 }
1596 spawn_cmd(command, args, show);
1597 }
1598
1599 /*
1600 * If Pageant was already running, we leave now. If we haven't
1601 * even taken any auxiliary action (spawned a command or added
1602 * keys), complain.
1603 */
1604 if (already_running) {
1605 if (!command && !added_keys) {
1606 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1607 MB_ICONERROR | MB_OK);
1608 }
1609 if (advapi)
1610 FreeLibrary(advapi);
1611 return 0;
1612 }
1613
1614 /*
1615 * Main message loop.
1616 */
1617 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1618 TranslateMessage(&msg);
1619 DispatchMessage(&msg);
1620 }
1621
1622 /* Clean up the system tray icon */
1623 {
1624 NOTIFYICONDATA tnid;
1625
1626 tnid.cbSize = sizeof(NOTIFYICONDATA);
1627 tnid.hWnd = hwnd;
1628 tnid.uID = 1;
1629
1630 Shell_NotifyIcon(NIM_DELETE, &tnid);
1631
1632 DestroyMenu(systray_menu);
1633 }
1634
1635 if (advapi)
1636 FreeLibrary(advapi);
1637 exit(msg.wParam);
1638 }