2 * Pageant: the PuTTY Authentication Agent.
17 #define IDI_MAINICON 200
18 #define IDI_TRAYICON 201
20 #define WM_XUSER (WM_USER + 0x2000)
21 #define WM_SYSTRAY (WM_XUSER + 6)
22 #define WM_SYSTRAY2 (WM_XUSER + 7)
24 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
27 * FIXME: maybe some day we can sort this out ...
29 #define AGENT_MAX_MSGLEN 8192
31 #define IDM_CLOSE 0x0010
32 #define IDM_VIEWKEYS 0x0020
33 #define IDM_ADDKEY 0x0030
34 #define IDM_ABOUT 0x0040
36 #define APPNAME "Pageant"
40 static HINSTANCE instance;
44 static HMENU systray_menu;
45 static int already_running;
47 static tree234 *rsakeys, *ssh2keys;
49 static int has_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;
58 * Exports from pageantc.c
60 void agent_query(void *in, int inlen, void **out, int *outlen);
61 int agent_exists(void);
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
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.
75 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
77 /* this line can't be reached but it placates MSVC's warnings :-) */
82 * Blob structure for passing to the asymmetric SSH2 key compare
83 * function, prototyped here.
89 static int cmpkeys_ssh2_asymm(void *av, void *bv);
92 * This function is needed to link with the DES code. We need not
93 * have it do anything at all.
95 void logevent(char *msg)
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]))
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); }
111 #define PASSPHRASE_MAXLEN 512
113 struct PassphraseProcStruct {
119 * Dialog-box function for the Licence box.
121 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
122 WPARAM wParam, LPARAM lParam)
128 switch (LOWORD(wParam)) {
142 * Dialog-box function for the About box.
144 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
145 WPARAM wParam, LPARAM lParam)
149 SetDlgItemText(hwnd, 100, ver);
152 switch (LOWORD(wParam)) {
158 EnableWindow(hwnd, 0);
159 DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
160 EnableWindow(hwnd, 1);
161 SetActiveWindow(hwnd);
173 static HWND passphrase_box;
176 * Dialog-box function for the passphrase box.
178 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
179 WPARAM wParam, LPARAM lParam)
181 static char *passphrase = NULL;
182 struct PassphraseProcStruct *p;
186 passphrase_box = hwnd;
190 { /* centre the window */
194 hw = GetDesktopWindow();
195 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
197 (rs.right + rs.left + rd.left - rd.right) / 2,
198 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
199 rd.right - rd.left, rd.bottom - rd.top, TRUE);
202 SetForegroundWindow(hwnd);
203 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
204 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
205 p = (struct PassphraseProcStruct *) lParam;
206 passphrase = p->passphrase;
208 SetDlgItemText(hwnd, 101, p->comment);
210 SetDlgItemText(hwnd, 102, passphrase);
213 switch (LOWORD(wParam)) {
223 case 102: /* edit box */
224 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
225 GetDlgItemText(hwnd, 102, passphrase,
226 PASSPHRASE_MAXLEN - 1);
227 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
240 * Warn about the obsolescent key file format.
242 void old_keyfile_warning(void)
244 static const char mbtitle[] = "PuTTY Key File Warning";
245 static const char message[] =
246 "You are loading an SSH 2 private key which has an\n"
247 "old version of the file format. This means your key\n"
248 "file is not fully tamperproof. Future versions of\n"
249 "PuTTY may stop supporting this private key format,\n"
250 "so we recommend you convert your key to the new\n"
253 "You can perform this conversion by loading the key\n"
254 "into PuTTYgen and then saving it again.";
256 MessageBox(NULL, message, mbtitle, MB_OK);
260 * Update the visible key list.
262 static void keylist_update(void)
265 struct ssh2_userkey *skey;
269 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
270 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
271 char listentry[512], *p;
273 * Replace two spaces in the fingerprint with tabs, for
274 * nice alignment in the box.
276 strcpy(listentry, "ssh1\t");
277 p = listentry + strlen(listentry);
278 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
279 p = strchr(listentry, ' ');
282 p = strchr(listentry, ' ');
285 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
286 0, (LPARAM) listentry);
288 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
289 char listentry[512], *p;
292 * Replace two spaces in the fingerprint with tabs, for
293 * nice alignment in the box.
295 p = skey->alg->fingerprint(skey->data);
296 strncpy(listentry, p, sizeof(listentry));
297 p = strchr(listentry, ' ');
300 p = strchr(listentry, ' ');
303 len = strlen(listentry);
304 if (len < sizeof(listentry) - 2) {
305 listentry[len] = '\t';
306 strncpy(listentry + len + 1, skey->comment,
307 sizeof(listentry) - len - 1);
309 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
312 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
317 * This function loads a key from a file and adds it.
319 static void add_keyfile(char *filename)
321 char passphrase[PASSPHRASE_MAXLEN];
322 struct RSAKey *rkey = NULL;
323 struct ssh2_userkey *skey = NULL;
328 struct PassphraseProcStruct pps;
331 ver = keyfile_version(filename);
333 MessageBox(NULL, "Couldn't load private key.", APPNAME,
334 MB_OK | MB_ICONERROR);
339 needs_pass = rsakey_encrypted(filename, &comment);
341 needs_pass = ssh2_userkey_encrypted(filename, &comment);
344 rkey = smalloc(sizeof(*rkey));
345 pps.passphrase = passphrase;
346 pps.comment = comment;
350 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
351 NULL, PassphraseProc, (LPARAM) & pps);
352 passphrase_box = NULL;
358 return; /* operation cancelled */
363 ret = loadrsakey(filename, rkey, passphrase);
365 skey = ssh2_load_userkey(filename, passphrase);
366 if (skey == SSH2_WRONG_PASSPHRASE)
378 MessageBox(NULL, "Couldn't load private key.", APPNAME,
379 MB_OK | MB_ICONERROR);
385 if (already_running) {
386 unsigned char *request, *response;
388 int reqlen, clen, resplen;
390 clen = strlen(rkey->comment);
392 reqlen = 4 + 1 + /* length, message type */
394 ssh1_bignum_length(rkey->modulus) +
395 ssh1_bignum_length(rkey->exponent) +
396 ssh1_bignum_length(rkey->private_exponent) +
397 ssh1_bignum_length(rkey->iqmp) +
398 ssh1_bignum_length(rkey->p) +
399 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
402 request = smalloc(reqlen);
404 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
406 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
408 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
409 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
411 ssh1_write_bignum(request + reqlen,
412 rkey->private_exponent);
413 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
414 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
415 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
416 PUT_32BIT(request + reqlen, clen);
417 memcpy(request + reqlen + 4, rkey->comment, clen);
419 PUT_32BIT(request, reqlen - 4);
421 agent_query(request, reqlen, &vresponse, &resplen);
422 response = vresponse;
423 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
424 MessageBox(NULL, "The already running Pageant "
425 "refused to add the key.", APPNAME,
426 MB_OK | MB_ICONERROR);
428 if (add234(rsakeys, rkey) != rkey)
429 sfree(rkey); /* already present, don't waste RAM */
432 if (already_running) {
433 unsigned char *request, *response;
435 int reqlen, alglen, clen, keybloblen, resplen;
436 alglen = strlen(skey->alg->name);
437 clen = strlen(skey->comment);
439 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
441 reqlen = 4 + 1 + /* length, message type */
442 4 + alglen + /* algorithm name */
443 keybloblen + /* key data */
444 4 + clen /* comment */
447 request = smalloc(reqlen);
449 request[4] = SSH2_AGENTC_ADD_IDENTITY;
451 PUT_32BIT(request + reqlen, alglen);
453 memcpy(request + reqlen, skey->alg->name, alglen);
455 reqlen += skey->alg->openssh_fmtkey(skey->data,
458 PUT_32BIT(request + reqlen, clen);
459 memcpy(request + reqlen + 4, skey->comment, clen);
460 PUT_32BIT(request, reqlen - 4);
463 agent_query(request, reqlen, &vresponse, &resplen);
464 response = vresponse;
465 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
466 MessageBox(NULL, "The already running Pageant"
467 "refused to add the key.", APPNAME,
468 MB_OK | MB_ICONERROR);
470 if (add234(ssh2keys, skey) != skey) {
471 skey->alg->freekey(skey->data);
472 sfree(skey); /* already present, don't waste RAM */
479 * This is the main agent function that answers messages.
481 static void answer_msg(void *msg)
483 unsigned char *p = msg;
484 unsigned char *ret = msg;
488 * Get the message type.
494 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
496 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
504 * Count up the number and length of keys we hold.
507 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
509 len += 4; /* length field */
510 len += ssh1_bignum_length(key->exponent);
511 len += ssh1_bignum_length(key->modulus);
512 len += 4 + strlen(key->comment);
516 * Packet header is the obvious five bytes, plus four
517 * bytes for the key count.
520 if (len > AGENT_MAX_MSGLEN)
521 goto failure; /* aaargh! too much stuff! */
522 PUT_32BIT(ret, len - 4);
523 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
524 PUT_32BIT(ret + 5, nkeys);
526 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
527 PUT_32BIT(p, bignum_bitcount(key->modulus));
529 p += ssh1_write_bignum(p, key->exponent);
530 p += ssh1_write_bignum(p, key->modulus);
531 PUT_32BIT(p, strlen(key->comment));
532 memcpy(p + 4, key->comment, strlen(key->comment));
533 p += 4 + strlen(key->comment);
537 case SSH2_AGENTC_REQUEST_IDENTITIES:
539 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
542 struct ssh2_userkey *key;
549 * Count up the number and length of keys we hold.
552 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
554 len += 4; /* length field */
555 blob = key->alg->public_blob(key->data, &bloblen);
558 len += 4 + strlen(key->comment);
562 * Packet header is the obvious five bytes, plus four
563 * bytes for the key count.
566 if (len > AGENT_MAX_MSGLEN)
567 goto failure; /* aaargh! too much stuff! */
568 PUT_32BIT(ret, len - 4);
569 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
570 PUT_32BIT(ret + 5, nkeys);
572 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
573 blob = key->alg->public_blob(key->data, &bloblen);
574 PUT_32BIT(p, bloblen);
576 memcpy(p, blob, bloblen);
579 PUT_32BIT(p, strlen(key->comment));
580 memcpy(p + 4, key->comment, strlen(key->comment));
581 p += 4 + strlen(key->comment);
585 case SSH1_AGENTC_RSA_CHALLENGE:
587 * Reply with either SSH1_AGENT_RSA_RESPONSE or
588 * SSH_AGENT_FAILURE, depending on whether we have that key
592 struct RSAKey reqkey, *key;
593 Bignum challenge, response;
594 unsigned char response_source[48], response_md5[16];
595 struct MD5Context md5c;
599 p += ssh1_read_bignum(p, &reqkey.exponent);
600 p += ssh1_read_bignum(p, &reqkey.modulus);
601 p += ssh1_read_bignum(p, &challenge);
602 memcpy(response_source + 32, p, 16);
604 if (GET_32BIT(p) != 1 ||
605 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
606 freebn(reqkey.exponent);
607 freebn(reqkey.modulus);
611 response = rsadecrypt(challenge, key);
612 for (i = 0; i < 32; i++)
613 response_source[i] = bignum_byte(response, 31 - i);
616 MD5Update(&md5c, response_source, 48);
617 MD5Final(response_md5, &md5c);
618 memset(response_source, 0, 48); /* burn the evidence */
619 freebn(response); /* and that evidence */
620 freebn(challenge); /* yes, and that evidence */
621 freebn(reqkey.exponent); /* and free some memory ... */
622 freebn(reqkey.modulus); /* ... while we're at it. */
625 * Packet is the obvious five byte header, plus sixteen
629 PUT_32BIT(ret, len - 4);
630 ret[4] = SSH1_AGENT_RSA_RESPONSE;
631 memcpy(ret + 5, response_md5, 16);
634 case SSH2_AGENTC_SIGN_REQUEST:
636 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
637 * SSH_AGENT_FAILURE, depending on whether we have that key
641 struct ssh2_userkey *key;
643 unsigned char *data, *signature;
644 int datalen, siglen, len;
646 b.len = GET_32BIT(p);
650 datalen = GET_32BIT(p);
653 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
656 signature = key->alg->sign(key->data, data, datalen, &siglen);
657 len = 5 + 4 + siglen;
658 PUT_32BIT(ret, len - 4);
659 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
660 PUT_32BIT(ret + 5, siglen);
661 memcpy(ret + 5 + 4, signature, siglen);
665 case SSH1_AGENTC_ADD_RSA_IDENTITY:
667 * Add to the list and return SSH_AGENT_SUCCESS, or
668 * SSH_AGENT_FAILURE if the key was malformed.
674 key = smalloc(sizeof(struct RSAKey));
675 memset(key, 0, sizeof(struct RSAKey));
676 p += makekey(p, key, NULL, 1);
677 p += makeprivate(p, key);
678 p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
679 p += ssh1_read_bignum(p, &key->p); /* p */
680 p += ssh1_read_bignum(p, &key->q); /* q */
681 commentlen = GET_32BIT(p);
682 comment = smalloc(commentlen+1);
684 memcpy(comment, p + 4, commentlen);
685 comment[commentlen] = '\0';
686 key->comment = comment;
689 ret[4] = SSH_AGENT_FAILURE;
690 if (add234(rsakeys, key) == key) {
692 ret[4] = SSH_AGENT_SUCCESS;
699 case SSH2_AGENTC_ADD_IDENTITY:
701 * Add to the list and return SSH_AGENT_SUCCESS, or
702 * SSH_AGENT_FAILURE if the key was malformed.
705 struct ssh2_userkey *key;
710 key = smalloc(sizeof(struct ssh2_userkey));
712 alglen = GET_32BIT(p);
716 /* Add further algorithm names here. */
717 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
719 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
727 GET_32BIT((unsigned char *) msg) - (p -
728 (unsigned char *) msg -
730 key->data = key->alg->openssh_createkey(&p, &bloblen);
735 commlen = GET_32BIT(p);
738 comment = smalloc(commlen + 1);
740 memcpy(comment, p, commlen);
741 comment[commlen] = '\0';
743 key->comment = comment;
746 ret[4] = SSH_AGENT_FAILURE;
747 if (add234(ssh2keys, key) == key) {
749 ret[4] = SSH_AGENT_SUCCESS;
751 key->alg->freekey(key->data);
757 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
759 * Remove from the list and return SSH_AGENT_SUCCESS, or
760 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
764 struct RSAKey reqkey, *key;
766 p += makekey(p, &reqkey, NULL, 0);
767 key = find234(rsakeys, &reqkey, NULL);
768 freebn(reqkey.exponent);
769 freebn(reqkey.modulus);
771 ret[4] = SSH_AGENT_FAILURE;
773 del234(rsakeys, key);
777 ret[4] = SSH_AGENT_SUCCESS;
781 case SSH2_AGENTC_REMOVE_IDENTITY:
783 * Remove from the list and return SSH_AGENT_SUCCESS, or
784 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
788 struct ssh2_userkey *key;
791 b.len = GET_32BIT(p);
795 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
800 ret[4] = SSH_AGENT_FAILURE;
802 del234(ssh2keys, key);
804 key->alg->freekey(key->data);
806 ret[4] = SSH_AGENT_SUCCESS;
810 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
812 * Remove all SSH1 keys. Always returns success.
817 while ((rkey = index234(rsakeys, 0)) != NULL) {
818 del234(rsakeys, rkey);
825 ret[4] = SSH_AGENT_SUCCESS;
828 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
830 * Remove all SSH2 keys. Always returns success.
833 struct ssh2_userkey *skey;
835 while ((skey = index234(ssh2keys, 0)) != NULL) {
836 del234(ssh2keys, skey);
837 skey->alg->freekey(skey->data);
843 ret[4] = SSH_AGENT_SUCCESS;
849 * Unrecognised message. Return SSH_AGENT_FAILURE.
852 ret[4] = SSH_AGENT_FAILURE;
858 * Key comparison function for the 2-3-4 tree of RSA keys.
860 static int cmpkeys_rsa(void *av, void *bv)
862 struct RSAKey *a = (struct RSAKey *) av;
863 struct RSAKey *b = (struct RSAKey *) bv;
870 * Compare by length of moduli.
872 alen = bignum_bitcount(am);
873 blen = bignum_bitcount(bm);
876 else if (alen < blen)
879 * Now compare by moduli themselves.
881 alen = (alen + 7) / 8; /* byte count */
884 abyte = bignum_byte(am, alen);
885 bbyte = bignum_byte(bm, alen);
888 else if (abyte < bbyte)
898 * Key comparison function for the 2-3-4 tree of SSH2 keys.
900 static int cmpkeys_ssh2(void *av, void *bv)
902 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
903 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
906 unsigned char *ablob, *bblob;
910 * Compare purely by public blob.
912 ablob = a->alg->public_blob(a->data, &alen);
913 bblob = b->alg->public_blob(b->data, &blen);
916 for (i = 0; i < alen && i < blen; i++) {
917 if (ablob[i] < bblob[i]) {
920 } else if (ablob[i] > bblob[i]) {
925 if (c == 0 && i < alen)
926 c = +1; /* a is longer */
927 if (c == 0 && i < blen)
928 c = -1; /* a is longer */
937 * Key comparison function for looking up a blob in the 2-3-4 tree
940 static int cmpkeys_ssh2_asymm(void *av, void *bv)
942 struct blob *a = (struct blob *) av;
943 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
946 unsigned char *ablob, *bblob;
950 * Compare purely by public blob.
954 bblob = b->alg->public_blob(b->data, &blen);
957 for (i = 0; i < alen && i < blen; i++) {
958 if (ablob[i] < bblob[i]) {
961 } else if (ablob[i] > bblob[i]) {
966 if (c == 0 && i < alen)
967 c = +1; /* a is longer */
968 if (c == 0 && i < blen)
969 c = -1; /* a is longer */
977 * Prompt for a key file to add, and add it.
979 static void prompt_add_keyfile(void)
982 char filename[FILENAME_MAX];
983 memset(&of, 0, sizeof(of));
984 #ifdef OPENFILENAME_SIZE_VERSION_400
985 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
987 of.lStructSize = sizeof(of);
990 of.lpstrFilter = "All Files\0*\0\0\0";
991 of.lpstrCustomFilter = NULL;
993 of.lpstrFile = filename;
995 of.nMaxFile = sizeof(filename);
996 of.lpstrFileTitle = NULL;
997 of.lpstrInitialDir = NULL;
998 of.lpstrTitle = "Select Private Key File";
1000 if (GetOpenFileName(&of)) {
1001 add_keyfile(filename);
1007 * Dialog-box function for the key list box.
1009 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1010 WPARAM wParam, LPARAM lParam)
1012 struct RSAKey *rkey;
1013 struct ssh2_userkey *skey;
1018 * Centre the window.
1020 { /* centre the window */
1024 hw = GetDesktopWindow();
1025 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1027 (rs.right + rs.left + rd.left - rd.right) / 2,
1028 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1029 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1034 static int tabs[] = { 35, 60, 210 };
1035 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1036 sizeof(tabs) / sizeof(*tabs),
1042 switch (LOWORD(wParam)) {
1046 DestroyWindow(hwnd);
1048 case 101: /* add key */
1049 if (HIWORD(wParam) == BN_CLICKED ||
1050 HIWORD(wParam) == BN_DOUBLECLICKED) {
1051 if (passphrase_box) {
1052 MessageBeep(MB_ICONERROR);
1053 SetForegroundWindow(passphrase_box);
1056 prompt_add_keyfile();
1059 case 102: /* remove key */
1060 if (HIWORD(wParam) == BN_CLICKED ||
1061 HIWORD(wParam) == BN_DOUBLECLICKED) {
1062 int n = SendDlgItemMessage(hwnd, 100, LB_GETCURSEL, 0, 0);
1068 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++)
1072 del234(rsakeys, rkey);
1076 for (i = 0; NULL != (skey = index234(ssh2keys, i));
1080 del234(ssh2keys, skey);
1081 skey->alg->freekey(skey->data);
1092 DestroyWindow(hwnd);
1098 /* Set up a system tray icon */
1099 static BOOL AddTrayIcon(HWND hwnd)
1102 NOTIFYICONDATA tnid;
1105 #ifdef NIM_SETVERSION
1107 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1110 tnid.cbSize = sizeof(NOTIFYICONDATA);
1112 tnid.uID = 1; /* unique within this systray use */
1113 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1114 tnid.uCallbackMessage = WM_SYSTRAY;
1115 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1116 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1118 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1120 if (hicon) DestroyIcon(hicon);
1125 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1126 WPARAM wParam, LPARAM lParam)
1129 static int menuinprogress;
1130 static UINT msgTaskbarCreated = 0;
1134 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1137 if (message==msgTaskbarCreated) {
1139 * Explorer has been restarted, so the tray icon will
1147 if (lParam == WM_RBUTTONUP) {
1149 GetCursorPos(&cursorpos);
1150 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1151 } else if (lParam == WM_LBUTTONDBLCLK) {
1152 /* Equivalent to IDM_VIEWKEYS. */
1153 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1157 if (!menuinprogress) {
1159 SetForegroundWindow(hwnd);
1160 ret = TrackPopupMenu(systray_menu,
1161 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1163 wParam, lParam, 0, hwnd, NULL);
1169 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1172 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1173 SendMessage(hwnd, WM_CLOSE, 0, 0);
1177 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1179 ShowWindow(keylist, SW_SHOWNORMAL);
1181 * Sometimes the window comes up minimised / hidden
1182 * for no obvious reason. Prevent this.
1184 SetForegroundWindow(keylist);
1185 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1186 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1190 if (passphrase_box) {
1191 MessageBeep(MB_ICONERROR);
1192 SetForegroundWindow(passphrase_box);
1195 prompt_add_keyfile();
1199 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1201 ShowWindow(aboutbox, SW_SHOWNORMAL);
1203 * Sometimes the window comes up minimised / hidden
1204 * for no obvious reason. Prevent this.
1206 SetForegroundWindow(aboutbox);
1207 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1208 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1218 COPYDATASTRUCT *cds;
1224 PSID mapowner, procowner;
1225 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1229 cds = (COPYDATASTRUCT *) lParam;
1230 if (cds->dwData != AGENT_COPYDATA_ID)
1231 return 0; /* not our message, mate */
1232 mapname = (char *) cds->lpData;
1233 if (mapname[cds->cbData - 1] != '\0')
1234 return 0; /* failure to be ASCIZ! */
1236 debug(("mapname is :%s:\n", mapname));
1238 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1240 debug(("filemap is %p\n", filemap));
1242 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1246 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1247 GetCurrentProcessId())) ==
1250 debug(("couldn't get handle for process\n"));
1254 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1255 OWNER_SECURITY_INFORMATION,
1256 &procowner, NULL, NULL, NULL,
1257 &psd2) != ERROR_SUCCESS) {
1259 debug(("couldn't get owner info for process\n"));
1262 return 0; /* unable to get security info */
1265 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1266 OWNER_SECURITY_INFORMATION,
1267 &mapowner, NULL, NULL, NULL,
1268 &psd1) != ERROR_SUCCESS)) {
1271 ("couldn't get owner info for filemap: %d\n",
1277 debug(("got security stuff\n"));
1279 if (!EqualSid(mapowner, procowner))
1280 return 0; /* security ID mismatch! */
1282 debug(("security stuff matched\n"));
1288 debug(("security APIs not present\n"));
1292 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1294 debug(("p is %p\n", p));
1297 for (i = 0; i < 5; i++)
1300 ((unsigned char *) p)[i]));}
1306 CloseHandle(filemap);
1311 return DefWindowProc(hwnd, message, wParam, lParam);
1315 * Fork and Exec the command in cmdline. [DBW]
1317 void spawn_cmd(char *cmdline, int show)
1319 if (ShellExecute(NULL, _T("open"), cmdline,
1320 NULL, NULL, show) <= (HINSTANCE) 32) {
1322 sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
1323 (int)GetLastError());
1324 MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1328 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1334 char *command = NULL;
1338 * Determine whether we're an NT system (should have security
1339 * APIs) or a non-NT system (don't do security).
1341 memset(&osi, 0, sizeof(OSVERSIONINFO));
1342 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1343 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1344 has_security = TRUE;
1346 has_security = FALSE;
1351 * Attempt to get the security API we need.
1353 advapi = LoadLibrary("ADVAPI32.DLL");
1355 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1356 if (!getsecurityinfo) {
1358 "Unable to access security APIs. Pageant will\n"
1359 "not run, in case it causes a security breach.",
1360 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1365 "This program has been compiled for Win9X and will\n"
1366 "not run on NT, in case it causes a security breach.",
1367 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1376 * Find out if Pageant is already running.
1378 already_running = FALSE;
1380 already_running = TRUE;
1385 wndclass.lpfnWndProc = WndProc;
1386 wndclass.cbClsExtra = 0;
1387 wndclass.cbWndExtra = 0;
1388 wndclass.hInstance = inst;
1389 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1390 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1391 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1392 wndclass.lpszMenuName = NULL;
1393 wndclass.lpszClassName = APPNAME;
1395 RegisterClass(&wndclass);
1398 hwnd = keylist = NULL;
1400 hwnd = CreateWindow(APPNAME, APPNAME,
1401 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1402 CW_USEDEFAULT, CW_USEDEFAULT,
1403 100, 100, NULL, NULL, inst, NULL);
1405 /* Set up a system tray icon */
1408 systray_menu = CreatePopupMenu();
1409 /* accelerators used: vkxa */
1410 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1412 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1413 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1414 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1416 ShowWindow(hwnd, SW_HIDE);
1419 * Initialise storage for RSA keys.
1421 rsakeys = newtree234(cmpkeys_rsa);
1422 ssh2keys = newtree234(cmpkeys_ssh2);
1427 * Process the command line and add keys as listed on it.
1428 * If we already determined that we need to spawn a program from above we
1429 * need to ignore the first two arguments. [DBW]
1436 while (*p && isspace(*p))
1438 if (*p && !isspace(*p)) {
1439 char *q = p, *pp = p;
1440 while (*p && (inquotes || !isspace(*p))) {
1442 inquotes = !inquotes;
1453 if (!strcmp(q, "-c")) {
1455 * If we see `-c', then the rest of the
1456 * command line should be treated as a
1457 * command to be spawned.
1459 while (*p && isspace(*p))
1472 spawn_cmd(command, show);
1475 * If Pageant was already running, we leave now. If we haven't
1476 * even taken any auxiliary action (spawned a command or added
1479 if (already_running) {
1480 if (!command && !added_keys) {
1481 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1482 MB_ICONERROR | MB_OK);
1485 FreeLibrary(advapi);
1490 * Main message loop.
1492 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1493 TranslateMessage(&msg);
1494 DispatchMessage(&msg);
1497 /* Clean up the system tray icon */
1499 NOTIFYICONDATA tnid;
1501 tnid.cbSize = sizeof(NOTIFYICONDATA);
1505 Shell_NotifyIcon(NIM_DELETE, &tnid);
1507 DestroyMenu(systray_menu);
1511 FreeLibrary(advapi);