2 * Pageant: the PuTTY Authentication Agent.
22 #define IDI_MAINICON 200
23 #define IDI_TRAYICON 201
25 #define WM_XUSER (WM_USER + 0x2000)
26 #define WM_SYSTRAY (WM_XUSER + 6)
27 #define WM_SYSTRAY2 (WM_XUSER + 7)
29 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
32 * FIXME: maybe some day we can sort this out ...
34 #define AGENT_MAX_MSGLEN 8192
36 #define IDM_CLOSE 0x0010
37 #define IDM_VIEWKEYS 0x0020
38 #define IDM_ADDKEY 0x0030
39 #define IDM_HELP 0x0040
40 #define IDM_ABOUT 0x0050
42 #define APPNAME "Pageant"
46 static HINSTANCE instance;
47 static HWND main_hwnd;
50 static HMENU systray_menu, session_menu;
51 static int already_running;
52 static int requested_help;
55 static char *putty_path;
57 #define IDM_PUTTY 0x0060
58 #define IDM_SESSIONS_BASE 0x1000
59 #define IDM_SESSIONS_MAX 0x2000
60 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
61 #define PUTTY_DEFAULT "Default%20Settings"
62 static int initial_menuitems_count;
65 * Print a modal (Really Bad) message box and perform a fatal exit.
67 void modalfatalbox(char *fmt, ...)
73 buf = dupvprintf(fmt, ap);
75 MessageBox(main_hwnd, buf, "Pageant Fatal Error",
76 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
81 /* Un-munge session names out of the registry. */
82 static void unmungestr(char *in, char *out, int outlen)
85 if (*in == '%' && in[1] && in[2]) {
93 *out++ = (i << 4) + j;
107 static tree234 *rsakeys, *ssh2keys;
109 static int has_security;
111 typedef DWORD(WINAPI * gsi_fn_t)
112 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
113 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
114 static gsi_fn_t getsecurityinfo;
120 static void *make_keylist1(int *length);
121 static void *make_keylist2(int *length);
122 static void *get_keylist1(int *length);
123 static void *get_keylist2(int *length);
126 * We need this to link with the RSA code, because rsaencrypt()
127 * pads its data with random bytes. Since we only use rsadecrypt()
128 * and the signing functions, which are deterministic, this should
131 * If it _is_ called, there is a _serious_ problem, because it
132 * won't generate true random numbers. So we must scream, panic,
133 * and exit immediately if that should happen.
135 int random_byte(void)
137 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
139 /* this line can't be reached but it placates MSVC's warnings :-) */
144 * Blob structure for passing to the asymmetric SSH2 key compare
145 * function, prototyped here.
151 static int cmpkeys_ssh2_asymm(void *av, void *bv);
153 #define GET_32BIT(cp) \
154 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
155 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
156 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
157 ((unsigned long)(unsigned char)(cp)[3]))
159 #define PUT_32BIT(cp, value) { \
160 (cp)[0] = (unsigned char)((value) >> 24); \
161 (cp)[1] = (unsigned char)((value) >> 16); \
162 (cp)[2] = (unsigned char)((value) >> 8); \
163 (cp)[3] = (unsigned char)(value); }
165 #define PASSPHRASE_MAXLEN 512
167 struct PassphraseProcStruct {
172 static tree234 *passphrases = NULL;
175 * After processing a list of filenames, we want to forget the
178 static void forget_passphrases(void)
180 while (count234(passphrases) > 0) {
181 char *pp = index234(passphrases, 0);
182 memset(pp, 0, strlen(pp));
183 delpos234(passphrases, 0);
189 * Dialog-box function for the Licence box.
191 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
192 WPARAM wParam, LPARAM lParam)
198 switch (LOWORD(wParam)) {
213 * Dialog-box function for the About box.
215 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
216 WPARAM wParam, LPARAM lParam)
220 SetDlgItemText(hwnd, 100, ver);
223 switch (LOWORD(wParam)) {
230 EnableWindow(hwnd, 0);
231 DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
232 EnableWindow(hwnd, 1);
233 SetActiveWindow(hwnd);
245 static HWND passphrase_box;
248 * Dialog-box function for the passphrase box.
250 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
251 WPARAM wParam, LPARAM lParam)
253 static char *passphrase = NULL;
254 struct PassphraseProcStruct *p;
258 passphrase_box = hwnd;
262 { /* centre the window */
266 hw = GetDesktopWindow();
267 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
269 (rs.right + rs.left + rd.left - rd.right) / 2,
270 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
271 rd.right - rd.left, rd.bottom - rd.top, TRUE);
274 SetForegroundWindow(hwnd);
275 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
276 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
277 p = (struct PassphraseProcStruct *) lParam;
278 passphrase = p->passphrase;
280 SetDlgItemText(hwnd, 101, p->comment);
282 SetDlgItemText(hwnd, 102, passphrase);
285 switch (LOWORD(wParam)) {
295 case 102: /* edit box */
296 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
297 GetDlgItemText(hwnd, 102, passphrase,
298 PASSPHRASE_MAXLEN - 1);
299 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
312 * Warn about the obsolescent key file format.
314 void old_keyfile_warning(void)
316 static const char mbtitle[] = "PuTTY Key File Warning";
317 static const char message[] =
318 "You are loading an SSH 2 private key which has an\n"
319 "old version of the file format. This means your key\n"
320 "file is not fully tamperproof. Future versions of\n"
321 "PuTTY may stop supporting this private key format,\n"
322 "so we recommend you convert your key to the new\n"
325 "You can perform this conversion by loading the key\n"
326 "into PuTTYgen and then saving it again.";
328 MessageBox(NULL, message, mbtitle, MB_OK);
332 * Update the visible key list.
334 static void keylist_update(void)
337 struct ssh2_userkey *skey;
341 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
342 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
343 char listentry[512], *p;
345 * Replace two spaces in the fingerprint with tabs, for
346 * nice alignment in the box.
348 strcpy(listentry, "ssh1\t");
349 p = listentry + strlen(listentry);
350 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
351 p = strchr(listentry, ' ');
354 p = strchr(listentry, ' ');
357 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
358 0, (LPARAM) listentry);
360 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
361 char listentry[512], *p;
364 * Replace two spaces in the fingerprint with tabs, for
365 * nice alignment in the box.
367 p = skey->alg->fingerprint(skey->data);
368 strncpy(listentry, p, sizeof(listentry));
369 p = strchr(listentry, ' ');
372 p = strchr(listentry, ' ');
375 len = strlen(listentry);
376 if (len < sizeof(listentry) - 2) {
377 listentry[len] = '\t';
378 strncpy(listentry + len + 1, skey->comment,
379 sizeof(listentry) - len - 1);
381 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
384 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
389 * This function loads a key from a file and adds it.
391 static void add_keyfile(Filename filename)
393 char passphrase[PASSPHRASE_MAXLEN];
394 struct RSAKey *rkey = NULL;
395 struct ssh2_userkey *skey = NULL;
400 struct PassphraseProcStruct pps;
404 type = key_type(&filename);
405 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
407 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
408 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
413 * See if the key is already loaded (in the primary Pageant,
414 * which may or may not be us).
418 unsigned char *keylist, *p;
419 int i, nkeys, bloblen, keylistlen;
421 if (type == SSH_KEYTYPE_SSH1) {
422 if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
423 MessageBox(NULL, "Couldn't load private key.", APPNAME,
424 MB_OK | MB_ICONERROR);
427 keylist = get_keylist1(&keylistlen);
429 unsigned char *blob2;
430 blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
432 MessageBox(NULL, "Couldn't load private key.", APPNAME,
433 MB_OK | MB_ICONERROR);
436 /* For our purposes we want the blob prefixed with its length */
437 blob2 = snewn(bloblen+4, unsigned char);
438 PUT_32BIT(blob2, bloblen);
439 memcpy(blob2 + 4, blob, bloblen);
443 keylist = get_keylist2(&keylistlen);
446 if (keylistlen < 4) {
447 MessageBox(NULL, "Received broken key list?!", APPNAME,
448 MB_OK | MB_ICONERROR);
451 nkeys = GET_32BIT(keylist);
455 for (i = 0; i < nkeys; i++) {
456 if (!memcmp(blob, p, bloblen)) {
457 /* Key is already present; we can now leave. */
462 /* Now skip over public blob */
463 if (type == SSH_KEYTYPE_SSH1) {
464 int n = rsa_public_blob_len(p, keylistlen);
466 MessageBox(NULL, "Received broken key list?!", APPNAME,
467 MB_OK | MB_ICONERROR);
474 if (keylistlen < 4) {
475 MessageBox(NULL, "Received broken key list?!", APPNAME,
476 MB_OK | MB_ICONERROR);
479 n = 4 + GET_32BIT(p);
480 if (keylistlen < n) {
481 MessageBox(NULL, "Received broken key list?!", APPNAME,
482 MB_OK | MB_ICONERROR);
488 /* Now skip over comment field */
491 if (keylistlen < 4) {
492 MessageBox(NULL, "Received broken key list?!", APPNAME,
493 MB_OK | MB_ICONERROR);
496 n = 4 + GET_32BIT(p);
497 if (keylistlen < n) {
498 MessageBox(NULL, "Received broken key list?!", APPNAME,
499 MB_OK | MB_ICONERROR);
513 if (type == SSH_KEYTYPE_SSH1)
514 needs_pass = rsakey_encrypted(&filename, &comment);
516 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
518 if (type == SSH_KEYTYPE_SSH1)
519 rkey = snew(struct RSAKey);
520 pps.passphrase = passphrase;
521 pps.comment = comment;
525 /* try all the remembered passphrases first */
526 char *pp = index234(passphrases, attempts);
528 strcpy(passphrase, pp);
532 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
533 NULL, PassphraseProc, (LPARAM) & pps);
534 passphrase_box = NULL;
538 if (type == SSH_KEYTYPE_SSH1)
540 return; /* operation cancelled */
545 if (type == SSH_KEYTYPE_SSH1)
546 ret = loadrsakey(&filename, rkey, passphrase, NULL);
548 skey = ssh2_load_userkey(&filename, passphrase, NULL);
549 if (skey == SSH2_WRONG_PASSPHRASE)
559 /* if they typed in an ok passphrase, remember it */
560 if(original_pass && ret) {
561 char *pp = dupstr(passphrase);
562 addpos234(passphrases, pp, 0);
568 MessageBox(NULL, "Couldn't load private key.", APPNAME,
569 MB_OK | MB_ICONERROR);
570 if (type == SSH_KEYTYPE_SSH1)
574 if (type == SSH_KEYTYPE_SSH1) {
575 if (already_running) {
576 unsigned char *request, *response;
578 int reqlen, clen, resplen, ret;
580 clen = strlen(rkey->comment);
582 reqlen = 4 + 1 + /* length, message type */
584 ssh1_bignum_length(rkey->modulus) +
585 ssh1_bignum_length(rkey->exponent) +
586 ssh1_bignum_length(rkey->private_exponent) +
587 ssh1_bignum_length(rkey->iqmp) +
588 ssh1_bignum_length(rkey->p) +
589 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
592 request = snewn(reqlen, unsigned char);
594 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
596 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
598 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
599 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
601 ssh1_write_bignum(request + reqlen,
602 rkey->private_exponent);
603 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
604 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
605 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
606 PUT_32BIT(request + reqlen, clen);
607 memcpy(request + reqlen + 4, rkey->comment, clen);
609 PUT_32BIT(request, reqlen - 4);
611 ret = agent_query(request, reqlen, &vresponse, &resplen,
614 response = vresponse;
615 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
616 MessageBox(NULL, "The already running Pageant "
617 "refused to add the key.", APPNAME,
618 MB_OK | MB_ICONERROR);
623 if (add234(rsakeys, rkey) != rkey)
624 sfree(rkey); /* already present, don't waste RAM */
627 if (already_running) {
628 unsigned char *request, *response;
630 int reqlen, alglen, clen, keybloblen, resplen, ret;
631 alglen = strlen(skey->alg->name);
632 clen = strlen(skey->comment);
634 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
636 reqlen = 4 + 1 + /* length, message type */
637 4 + alglen + /* algorithm name */
638 keybloblen + /* key data */
639 4 + clen /* comment */
642 request = snewn(reqlen, unsigned char);
644 request[4] = SSH2_AGENTC_ADD_IDENTITY;
646 PUT_32BIT(request + reqlen, alglen);
648 memcpy(request + reqlen, skey->alg->name, alglen);
650 reqlen += skey->alg->openssh_fmtkey(skey->data,
653 PUT_32BIT(request + reqlen, clen);
654 memcpy(request + reqlen + 4, skey->comment, clen);
656 PUT_32BIT(request, reqlen - 4);
658 ret = agent_query(request, reqlen, &vresponse, &resplen,
661 response = vresponse;
662 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
663 MessageBox(NULL, "The already running Pageant "
664 "refused to add the key.", APPNAME,
665 MB_OK | MB_ICONERROR);
670 if (add234(ssh2keys, skey) != skey) {
671 skey->alg->freekey(skey->data);
672 sfree(skey); /* already present, don't waste RAM */
679 * Create an SSH1 key list in a malloc'ed buffer; return its
682 static void *make_keylist1(int *length)
686 unsigned char *blob, *p, *ret;
690 * Count up the number and length of keys we hold.
694 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
696 blob = rsa_public_blob(key, &bloblen);
699 len += 4 + strlen(key->comment);
702 /* Allocate the buffer. */
703 p = ret = snewn(len, unsigned char);
704 if (length) *length = len;
708 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
709 blob = rsa_public_blob(key, &bloblen);
710 memcpy(p, blob, bloblen);
713 PUT_32BIT(p, strlen(key->comment));
714 memcpy(p + 4, key->comment, strlen(key->comment));
715 p += 4 + strlen(key->comment);
718 assert(p - ret == len);
723 * Create an SSH2 key list in a malloc'ed buffer; return its
726 static void *make_keylist2(int *length)
728 struct ssh2_userkey *key;
730 unsigned char *blob, *p, *ret;
734 * Count up the number and length of keys we hold.
738 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
740 len += 4; /* length field */
741 blob = key->alg->public_blob(key->data, &bloblen);
744 len += 4 + strlen(key->comment);
747 /* Allocate the buffer. */
748 p = ret = snewn(len, unsigned char);
749 if (length) *length = len;
752 * Packet header is the obvious five bytes, plus four
753 * bytes for the key count.
757 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
758 blob = key->alg->public_blob(key->data, &bloblen);
759 PUT_32BIT(p, bloblen);
761 memcpy(p, blob, bloblen);
764 PUT_32BIT(p, strlen(key->comment));
765 memcpy(p + 4, key->comment, strlen(key->comment));
766 p += 4 + strlen(key->comment);
769 assert(p - ret == len);
774 * Acquire a keylist1 from the primary Pageant; this means either
775 * calling make_keylist1 (if that's us) or sending a message to the
776 * primary Pageant (if it's not).
778 static void *get_keylist1(int *length)
782 if (already_running) {
783 unsigned char request[5], *response;
786 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
787 PUT_32BIT(request, 4);
789 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
791 response = vresponse;
792 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
795 ret = snewn(resplen-5, unsigned char);
796 memcpy(ret, response+5, resplen-5);
802 ret = make_keylist1(length);
808 * Acquire a keylist2 from the primary Pageant; this means either
809 * calling make_keylist2 (if that's us) or sending a message to the
810 * primary Pageant (if it's not).
812 static void *get_keylist2(int *length)
816 if (already_running) {
817 unsigned char request[5], *response;
821 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
822 PUT_32BIT(request, 4);
824 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
826 response = vresponse;
827 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
830 ret = snewn(resplen-5, unsigned char);
831 memcpy(ret, response+5, resplen-5);
837 ret = make_keylist2(length);
843 * This is the main agent function that answers messages.
845 static void answer_msg(void *msg)
847 unsigned char *p = msg;
848 unsigned char *ret = msg;
849 unsigned char *msgend;
853 * Get the message length.
855 msgend = p + 4 + GET_32BIT(p);
858 * Get the message type.
866 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
868 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
874 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
875 keylist = make_keylist1(&len);
876 if (len + 5 > AGENT_MAX_MSGLEN) {
880 PUT_32BIT(ret, len + 1);
881 memcpy(ret + 5, keylist, len);
885 case SSH2_AGENTC_REQUEST_IDENTITIES:
887 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
893 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
894 keylist = make_keylist2(&len);
895 if (len + 5 > AGENT_MAX_MSGLEN) {
899 PUT_32BIT(ret, len + 1);
900 memcpy(ret + 5, keylist, len);
904 case SSH1_AGENTC_RSA_CHALLENGE:
906 * Reply with either SSH1_AGENT_RSA_RESPONSE or
907 * SSH_AGENT_FAILURE, depending on whether we have that key
911 struct RSAKey reqkey, *key;
912 Bignum challenge, response;
913 unsigned char response_source[48], response_md5[16];
914 struct MD5Context md5c;
918 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
922 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
926 i = ssh1_read_bignum(p, msgend - p, &challenge);
931 freebn(reqkey.exponent);
932 freebn(reqkey.modulus);
936 memcpy(response_source + 32, p, 16);
940 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
941 freebn(reqkey.exponent);
942 freebn(reqkey.modulus);
946 response = rsadecrypt(challenge, key);
947 for (i = 0; i < 32; i++)
948 response_source[i] = bignum_byte(response, 31 - i);
951 MD5Update(&md5c, response_source, 48);
952 MD5Final(response_md5, &md5c);
953 memset(response_source, 0, 48); /* burn the evidence */
954 freebn(response); /* and that evidence */
955 freebn(challenge); /* yes, and that evidence */
956 freebn(reqkey.exponent); /* and free some memory ... */
957 freebn(reqkey.modulus); /* ... while we're at it. */
960 * Packet is the obvious five byte header, plus sixteen
964 PUT_32BIT(ret, len - 4);
965 ret[4] = SSH1_AGENT_RSA_RESPONSE;
966 memcpy(ret + 5, response_md5, 16);
969 case SSH2_AGENTC_SIGN_REQUEST:
971 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
972 * SSH_AGENT_FAILURE, depending on whether we have that key
976 struct ssh2_userkey *key;
978 unsigned char *data, *signature;
979 int datalen, siglen, len;
983 b.len = GET_32BIT(p);
985 if (msgend < p+b.len)
991 datalen = GET_32BIT(p);
993 if (msgend < p+datalen)
996 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
999 signature = key->alg->sign(key->data, data, datalen, &siglen);
1000 len = 5 + 4 + siglen;
1001 PUT_32BIT(ret, len - 4);
1002 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1003 PUT_32BIT(ret + 5, siglen);
1004 memcpy(ret + 5 + 4, signature, siglen);
1008 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1010 * Add to the list and return SSH_AGENT_SUCCESS, or
1011 * SSH_AGENT_FAILURE if the key was malformed.
1018 key = snew(struct RSAKey);
1019 memset(key, 0, sizeof(struct RSAKey));
1021 n = makekey(p, msgend - p, key, NULL, 1);
1029 n = makeprivate(p, msgend - p, key);
1037 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1045 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1053 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1066 commentlen = GET_32BIT(p);
1068 if (msgend < p+commentlen) {
1074 comment = snewn(commentlen+1, char);
1076 memcpy(comment, p + 4, commentlen);
1077 comment[commentlen] = '\0';
1078 key->comment = comment;
1081 ret[4] = SSH_AGENT_FAILURE;
1082 if (add234(rsakeys, key) == key) {
1084 ret[4] = SSH_AGENT_SUCCESS;
1091 case SSH2_AGENTC_ADD_IDENTITY:
1093 * Add to the list and return SSH_AGENT_SUCCESS, or
1094 * SSH_AGENT_FAILURE if the key was malformed.
1097 struct ssh2_userkey *key;
1098 char *comment, *alg;
1099 int alglen, commlen;
1105 alglen = GET_32BIT(p);
1107 if (msgend < p+alglen)
1112 key = snew(struct ssh2_userkey);
1113 /* Add further algorithm names here. */
1114 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1115 key->alg = &ssh_rsa;
1116 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1117 key->alg = &ssh_dss;
1123 bloblen = msgend - p;
1124 key->data = key->alg->openssh_createkey(&p, &bloblen);
1131 * p has been advanced by openssh_createkey, but
1132 * certainly not _beyond_ the end of the buffer.
1134 assert(p <= msgend);
1137 key->alg->freekey(key->data);
1141 commlen = GET_32BIT(p);
1144 if (msgend < p+commlen) {
1145 key->alg->freekey(key->data);
1149 comment = snewn(commlen + 1, char);
1151 memcpy(comment, p, commlen);
1152 comment[commlen] = '\0';
1154 key->comment = comment;
1157 ret[4] = SSH_AGENT_FAILURE;
1158 if (add234(ssh2keys, key) == key) {
1160 ret[4] = SSH_AGENT_SUCCESS;
1162 key->alg->freekey(key->data);
1163 sfree(key->comment);
1168 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1170 * Remove from the list and return SSH_AGENT_SUCCESS, or
1171 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1175 struct RSAKey reqkey, *key;
1178 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1182 key = find234(rsakeys, &reqkey, NULL);
1183 freebn(reqkey.exponent);
1184 freebn(reqkey.modulus);
1186 ret[4] = SSH_AGENT_FAILURE;
1188 del234(rsakeys, key);
1192 ret[4] = SSH_AGENT_SUCCESS;
1196 case SSH2_AGENTC_REMOVE_IDENTITY:
1198 * Remove from the list and return SSH_AGENT_SUCCESS, or
1199 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1203 struct ssh2_userkey *key;
1208 b.len = GET_32BIT(p);
1211 if (msgend < p+b.len)
1216 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1221 ret[4] = SSH_AGENT_FAILURE;
1223 del234(ssh2keys, key);
1225 key->alg->freekey(key->data);
1227 ret[4] = SSH_AGENT_SUCCESS;
1231 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1233 * Remove all SSH1 keys. Always returns success.
1236 struct RSAKey *rkey;
1238 while ((rkey = index234(rsakeys, 0)) != NULL) {
1239 del234(rsakeys, rkey);
1246 ret[4] = SSH_AGENT_SUCCESS;
1249 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1251 * Remove all SSH2 keys. Always returns success.
1254 struct ssh2_userkey *skey;
1256 while ((skey = index234(ssh2keys, 0)) != NULL) {
1257 del234(ssh2keys, skey);
1258 skey->alg->freekey(skey->data);
1264 ret[4] = SSH_AGENT_SUCCESS;
1270 * Unrecognised message. Return SSH_AGENT_FAILURE.
1273 ret[4] = SSH_AGENT_FAILURE;
1279 * Key comparison function for the 2-3-4 tree of RSA keys.
1281 static int cmpkeys_rsa(void *av, void *bv)
1283 struct RSAKey *a = (struct RSAKey *) av;
1284 struct RSAKey *b = (struct RSAKey *) bv;
1291 * Compare by length of moduli.
1293 alen = bignum_bitcount(am);
1294 blen = bignum_bitcount(bm);
1297 else if (alen < blen)
1300 * Now compare by moduli themselves.
1302 alen = (alen + 7) / 8; /* byte count */
1303 while (alen-- > 0) {
1305 abyte = bignum_byte(am, alen);
1306 bbyte = bignum_byte(bm, alen);
1309 else if (abyte < bbyte)
1319 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1321 static int cmpkeys_ssh2(void *av, void *bv)
1323 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1324 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1327 unsigned char *ablob, *bblob;
1331 * Compare purely by public blob.
1333 ablob = a->alg->public_blob(a->data, &alen);
1334 bblob = b->alg->public_blob(b->data, &blen);
1337 for (i = 0; i < alen && i < blen; i++) {
1338 if (ablob[i] < bblob[i]) {
1341 } else if (ablob[i] > bblob[i]) {
1346 if (c == 0 && i < alen)
1347 c = +1; /* a is longer */
1348 if (c == 0 && i < blen)
1349 c = -1; /* a is longer */
1358 * Key comparison function for looking up a blob in the 2-3-4 tree
1361 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1363 struct blob *a = (struct blob *) av;
1364 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1367 unsigned char *ablob, *bblob;
1371 * Compare purely by public blob.
1375 bblob = b->alg->public_blob(b->data, &blen);
1378 for (i = 0; i < alen && i < blen; i++) {
1379 if (ablob[i] < bblob[i]) {
1382 } else if (ablob[i] > bblob[i]) {
1387 if (c == 0 && i < alen)
1388 c = +1; /* a is longer */
1389 if (c == 0 && i < blen)
1390 c = -1; /* a is longer */
1398 * Prompt for a key file to add, and add it.
1400 static void prompt_add_keyfile(void)
1403 char filename[FILENAME_MAX];
1404 char *filelist = snewn(8192, char);
1408 memset(&of, 0, sizeof(of));
1409 #ifdef OPENFILENAME_SIZE_VERSION_400
1410 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1412 of.lStructSize = sizeof(of);
1414 of.hwndOwner = main_hwnd;
1415 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1416 "All Files (*.*)\0*\0\0\0";
1417 of.lpstrCustomFilter = NULL;
1418 of.nFilterIndex = 1;
1419 of.lpstrFile = filelist;
1421 of.nMaxFile = FILENAME_MAX;
1422 of.lpstrFileTitle = NULL;
1423 of.lpstrInitialDir = NULL;
1424 of.lpstrTitle = "Select Private Key File";
1425 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1426 if (GetOpenFileName(&of)) {
1427 if(strlen(filelist) > of.nFileOffset)
1428 /* Only one filename returned? */
1429 add_keyfile(filename_from_str(filelist));
1431 /* we are returned a bunch of strings, end to
1432 * end. first string is the directory, the
1433 * rest the filenames. terminated with an
1436 filewalker = filelist;
1437 dirlen = strlen(filewalker);
1438 if(dirlen > FILENAME_MAX - 8) return;
1439 memcpy(filename, filewalker, dirlen);
1441 filewalker += dirlen + 1;
1442 filename[dirlen++] = '\\';
1444 /* then go over names one by one */
1446 n = strlen(filewalker) + 1;
1447 /* end of the list */
1450 /* too big, shouldn't happen */
1451 if(n + dirlen > FILENAME_MAX)
1454 memcpy(filename + dirlen, filewalker, n);
1457 add_keyfile(filename_from_str(filename));
1462 forget_passphrases();
1468 * Dialog-box function for the key list box.
1470 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1471 WPARAM wParam, LPARAM lParam)
1473 struct RSAKey *rkey;
1474 struct ssh2_userkey *skey;
1479 * Centre the window.
1481 { /* centre the window */
1485 hw = GetDesktopWindow();
1486 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1488 (rs.right + rs.left + rd.left - rd.right) / 2,
1489 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1490 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1494 SetWindowLong(hwnd, GWL_EXSTYLE,
1495 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1497 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1499 DestroyWindow(item);
1501 requested_help = FALSE;
1505 static int tabs[] = { 35, 60, 210 };
1506 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1507 sizeof(tabs) / sizeof(*tabs),
1513 switch (LOWORD(wParam)) {
1517 DestroyWindow(hwnd);
1519 case 101: /* add key */
1520 if (HIWORD(wParam) == BN_CLICKED ||
1521 HIWORD(wParam) == BN_DOUBLECLICKED) {
1522 if (passphrase_box) {
1523 MessageBeep(MB_ICONERROR);
1524 SetForegroundWindow(passphrase_box);
1527 prompt_add_keyfile();
1530 case 102: /* remove key */
1531 if (HIWORD(wParam) == BN_CLICKED ||
1532 HIWORD(wParam) == BN_DOUBLECLICKED) {
1537 /* our counter within the array of selected items */
1540 /* get the number of items selected in the list */
1542 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1544 /* none selected? that was silly */
1545 if (numSelected == 0) {
1550 /* get item indices in an array */
1551 selectedArray = snewn(numSelected, int);
1552 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1553 numSelected, (WPARAM)selectedArray);
1555 itemNum = numSelected - 1;
1556 rCount = count234(rsakeys);
1557 sCount = count234(ssh2keys);
1559 /* go through the non-rsakeys until we've covered them all,
1560 * and/or we're out of selected items to check. note that
1561 * we go *backwards*, to avoid complications from deleting
1562 * things hence altering the offset of subsequent items
1564 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1565 skey = index234(ssh2keys, i);
1567 if (selectedArray[itemNum] == rCount + i) {
1568 del234(ssh2keys, skey);
1569 skey->alg->freekey(skey->data);
1575 /* do the same for the rsa keys */
1576 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1577 rkey = index234(rsakeys, i);
1579 if(selectedArray[itemNum] == i) {
1580 del234(rsakeys, rkey);
1587 sfree(selectedArray);
1591 case 103: /* help */
1592 if (HIWORD(wParam) == BN_CLICKED ||
1593 HIWORD(wParam) == BN_DOUBLECLICKED) {
1595 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1596 (DWORD)"JI(`',`pageant.general')");
1597 requested_help = TRUE;
1605 int id = ((LPHELPINFO)lParam)->iCtrlId;
1608 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1609 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1610 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1613 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1614 requested_help = TRUE;
1622 DestroyWindow(hwnd);
1628 /* Set up a system tray icon */
1629 static BOOL AddTrayIcon(HWND hwnd)
1632 NOTIFYICONDATA tnid;
1635 #ifdef NIM_SETVERSION
1637 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1640 tnid.cbSize = sizeof(NOTIFYICONDATA);
1642 tnid.uID = 1; /* unique within this systray use */
1643 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1644 tnid.uCallbackMessage = WM_SYSTRAY;
1645 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1646 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1648 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1650 if (hicon) DestroyIcon(hicon);
1655 /* Update the saved-sessions menu. */
1656 static void update_sessions(void)
1660 TCHAR buf[MAX_PATH + 1];
1663 int index_key, index_menu;
1668 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1671 for(num_entries = GetMenuItemCount(session_menu);
1672 num_entries > initial_menuitems_count;
1674 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1679 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1680 TCHAR session_name[MAX_PATH + 1];
1681 unmungestr(buf, session_name, MAX_PATH);
1682 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1683 memset(&mii, 0, sizeof(mii));
1684 mii.cbSize = sizeof(mii);
1685 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1686 mii.fType = MFT_STRING;
1687 mii.fState = MFS_ENABLED;
1688 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1689 mii.dwTypeData = session_name;
1690 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1698 if(index_menu == 0) {
1699 mii.cbSize = sizeof(mii);
1700 mii.fMask = MIIM_TYPE | MIIM_STATE;
1701 mii.fType = MFT_STRING;
1702 mii.fState = MFS_GRAYED;
1703 mii.dwTypeData = _T("(No sessions)");
1704 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1708 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1709 WPARAM wParam, LPARAM lParam)
1712 static int menuinprogress;
1713 static UINT msgTaskbarCreated = 0;
1717 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1720 if (message==msgTaskbarCreated) {
1722 * Explorer has been restarted, so the tray icon will
1730 if (lParam == WM_RBUTTONUP) {
1732 GetCursorPos(&cursorpos);
1733 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1734 } else if (lParam == WM_LBUTTONDBLCLK) {
1735 /* Equivalent to IDM_VIEWKEYS. */
1736 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1740 if (!menuinprogress) {
1743 SetForegroundWindow(hwnd);
1744 ret = TrackPopupMenu(systray_menu,
1745 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1747 wParam, lParam, 0, hwnd, NULL);
1753 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1755 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1757 MessageBox(NULL, "Unable to execute PuTTY!",
1758 "Error", MB_OK | MB_ICONERROR);
1763 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1764 SendMessage(hwnd, WM_CLOSE, 0, 0);
1768 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1770 ShowWindow(keylist, SW_SHOWNORMAL);
1773 * Sometimes the window comes up minimised / hidden for
1774 * no obvious reason. Prevent this. This also brings it
1775 * to the front if it's already present (the user
1776 * selected View Keys because they wanted to _see_ the
1779 SetForegroundWindow(keylist);
1780 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1781 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1784 if (passphrase_box) {
1785 MessageBeep(MB_ICONERROR);
1786 SetForegroundWindow(passphrase_box);
1789 prompt_add_keyfile();
1793 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1795 ShowWindow(aboutbox, SW_SHOWNORMAL);
1797 * Sometimes the window comes up minimised / hidden
1798 * for no obvious reason. Prevent this.
1800 SetForegroundWindow(aboutbox);
1801 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1802 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1807 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1808 (DWORD)"JI(`',`pageant.general')");
1809 requested_help = TRUE;
1814 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1816 TCHAR buf[MAX_PATH + 1];
1817 TCHAR param[MAX_PATH + 1];
1818 memset(&mii, 0, sizeof(mii));
1819 mii.cbSize = sizeof(mii);
1820 mii.fMask = MIIM_TYPE;
1822 mii.dwTypeData = buf;
1823 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1825 strcat(param, mii.dwTypeData);
1826 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1827 _T(""), SW_SHOW) <= 32) {
1828 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1829 MB_OK | MB_ICONERROR);
1837 if (requested_help) {
1838 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1839 requested_help = FALSE;
1845 COPYDATASTRUCT *cds;
1851 PSID mapowner, procowner;
1852 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1856 cds = (COPYDATASTRUCT *) lParam;
1857 if (cds->dwData != AGENT_COPYDATA_ID)
1858 return 0; /* not our message, mate */
1859 mapname = (char *) cds->lpData;
1860 if (mapname[cds->cbData - 1] != '\0')
1861 return 0; /* failure to be ASCIZ! */
1863 debug(("mapname is :%s:\n", mapname));
1865 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1867 debug(("filemap is %p\n", filemap));
1869 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1873 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1874 GetCurrentProcessId())) ==
1877 debug(("couldn't get handle for process\n"));
1881 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1882 OWNER_SECURITY_INFORMATION,
1883 &procowner, NULL, NULL, NULL,
1884 &psd2) != ERROR_SUCCESS) {
1886 debug(("couldn't get owner info for process\n"));
1889 return 0; /* unable to get security info */
1892 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1893 OWNER_SECURITY_INFORMATION,
1894 &mapowner, NULL, NULL, NULL,
1895 &psd1) != ERROR_SUCCESS)) {
1898 ("couldn't get owner info for filemap: %d\n",
1904 debug(("got security stuff\n"));
1906 if (!EqualSid(mapowner, procowner))
1907 return 0; /* security ID mismatch! */
1909 debug(("security stuff matched\n"));
1915 debug(("security APIs not present\n"));
1919 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1921 debug(("p is %p\n", p));
1924 for (i = 0; i < 5; i++)
1927 ((unsigned char *) p)[i]));}
1933 CloseHandle(filemap);
1938 return DefWindowProc(hwnd, message, wParam, lParam);
1942 * Fork and Exec the command in cmdline. [DBW]
1944 void spawn_cmd(char *cmdline, char * args, int show)
1946 if (ShellExecute(NULL, _T("open"), cmdline,
1947 args, NULL, show) <= (HINSTANCE) 32) {
1949 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1950 (int)GetLastError());
1951 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1957 * This is a can't-happen stub, since Pageant never makes
1958 * asynchronous agent requests.
1960 void agent_schedule_callback(void (*callback)(void *, void *, int),
1961 void *callback_ctx, void *data, int len)
1963 assert(!"We shouldn't get here");
1966 void cleanup_exit(int code) { exit(code); }
1968 int flags = FLAG_SYNCAGENT;
1970 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1975 char *command = NULL;
1978 char **argv, **argstart;
1981 * Determine whether we're an NT system (should have security
1982 * APIs) or a non-NT system (don't do security).
1986 modalfatalbox("Windows refuses to report a version");
1988 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1989 has_security = TRUE;
1991 has_security = FALSE;
1996 * Attempt to get the security API we need.
1998 advapi = LoadLibrary("ADVAPI32.DLL");
2000 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
2001 if (!getsecurityinfo) {
2003 "Unable to access security APIs. Pageant will\n"
2004 "not run, in case it causes a security breach.",
2005 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2010 "This program has been compiled for Win9X and will\n"
2011 "not run on NT, in case it causes a security breach.",
2012 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2021 * See if we can find our Help file.
2024 char b[2048], *p, *q, *r;
2026 GetModuleFileName(NULL, b, sizeof(b) - 1);
2028 p = strrchr(b, '\\');
2029 if (p && p >= r) r = p+1;
2030 q = strrchr(b, ':');
2031 if (q && q >= r) r = q+1;
2032 strcpy(r, "putty.hlp");
2033 if ( (fp = fopen(b, "r")) != NULL) {
2034 help_path = dupstr(b);
2041 * Look for the PuTTY binary (we will enable the saved session
2042 * submenu if we find it).
2045 char b[2048], *p, *q, *r;
2047 GetModuleFileName(NULL, b, sizeof(b) - 1);
2049 p = strrchr(b, '\\');
2050 if (p && p >= r) r = p+1;
2051 q = strrchr(b, ':');
2052 if (q && q >= r) r = q+1;
2053 strcpy(r, "putty.exe");
2054 if ( (fp = fopen(b, "r")) != NULL) {
2055 putty_path = dupstr(b);
2062 * Find out if Pageant is already running.
2064 already_running = FALSE;
2066 already_running = TRUE;
2071 wndclass.lpfnWndProc = WndProc;
2072 wndclass.cbClsExtra = 0;
2073 wndclass.cbWndExtra = 0;
2074 wndclass.hInstance = inst;
2075 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2076 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2077 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2078 wndclass.lpszMenuName = NULL;
2079 wndclass.lpszClassName = APPNAME;
2081 RegisterClass(&wndclass);
2084 main_hwnd = keylist = NULL;
2086 main_hwnd = CreateWindow(APPNAME, APPNAME,
2087 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2088 CW_USEDEFAULT, CW_USEDEFAULT,
2089 100, 100, NULL, NULL, inst, NULL);
2091 /* Set up a system tray icon */
2092 AddTrayIcon(main_hwnd);
2094 /* Accelerators used: nsvkxa */
2095 systray_menu = CreatePopupMenu();
2097 session_menu = CreateMenu();
2098 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2099 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2100 (UINT) session_menu, "&Saved Sessions");
2101 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2103 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2105 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2106 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2108 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2109 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2110 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2111 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2112 initial_menuitems_count = GetMenuItemCount(session_menu);
2114 ShowWindow(main_hwnd, SW_HIDE);
2117 * Initialise storage for RSA keys.
2119 rsakeys = newtree234(cmpkeys_rsa);
2120 ssh2keys = newtree234(cmpkeys_ssh2);
2125 * Initialise storage for short-term passphrase cache.
2127 passphrases = newtree234(NULL);
2130 * Process the command line and add keys as listed on it.
2132 split_into_argv(cmdline, &argc, &argv, &argstart);
2133 for (i = 0; i < argc; i++) {
2134 if (!strcmp(argv[i], "-c")) {
2136 * If we see `-c', then the rest of the
2137 * command line should be treated as a
2138 * command to be spawned.
2141 command = argstart[i+1];
2146 add_keyfile(filename_from_str(argv[i]));
2152 * Forget any passphrase that we retained while going over
2153 * command line keyfiles.
2155 forget_passphrases();
2159 if (command[0] == '"')
2160 args = strchr(++command, '"');
2162 args = strchr(command, ' ');
2165 while(*args && isspace(*args)) args++;
2167 spawn_cmd(command, args, show);
2171 * If Pageant was already running, we leave now. If we haven't
2172 * even taken any auxiliary action (spawned a command or added
2175 if (already_running) {
2176 if (!command && !added_keys) {
2177 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2178 MB_ICONERROR | MB_OK);
2181 FreeLibrary(advapi);
2186 * Main message loop.
2188 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2189 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2190 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2191 TranslateMessage(&msg);
2192 DispatchMessage(&msg);
2196 /* Clean up the system tray icon */
2198 NOTIFYICONDATA tnid;
2200 tnid.cbSize = sizeof(NOTIFYICONDATA);
2201 tnid.hWnd = main_hwnd;
2204 Shell_NotifyIcon(NIM_DELETE, &tnid);
2206 DestroyMenu(systray_menu);
2210 FreeLibrary(advapi);