2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
24 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
29 #define IDI_MAINICON 200
30 #define IDI_TRAYICON 201
32 #define WM_SYSTRAY (WM_APP + 6)
33 #define WM_SYSTRAY2 (WM_APP + 7)
35 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
38 * FIXME: maybe some day we can sort this out ...
40 #define AGENT_MAX_MSGLEN 8192
42 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
43 * wParam are used by Windows, and should be masked off, so we shouldn't
44 * attempt to store information in them. Hence all these identifiers have
45 * the low 4 bits clear. Also, identifiers should < 0xF000. */
47 #define IDM_CLOSE 0x0010
48 #define IDM_VIEWKEYS 0x0020
49 #define IDM_ADDKEY 0x0030
50 #define IDM_HELP 0x0040
51 #define IDM_ABOUT 0x0050
53 #define APPNAME "Pageant"
59 static HMENU systray_menu, session_menu;
60 static int already_running;
62 static char *putty_path;
64 /* CWD for "add key" file requester. */
65 static filereq *keypath = NULL;
67 #define IDM_PUTTY 0x0060
68 #define IDM_SESSIONS_BASE 0x1000
69 #define IDM_SESSIONS_MAX 0x2000
70 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
71 #define PUTTY_DEFAULT "Default%20Settings"
72 static int initial_menuitems_count;
75 * Print a modal (Really Bad) message box and perform a fatal exit.
77 void modalfatalbox(char *fmt, ...)
83 buf = dupvprintf(fmt, ap);
85 MessageBox(hwnd, buf, "Pageant Fatal Error",
86 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
91 /* Un-munge session names out of the registry. */
92 static void unmungestr(char *in, char *out, int outlen)
95 if (*in == '%' && in[1] && in[2]) {
101 j -= (j > 9 ? 7 : 0);
103 *out++ = (i << 4) + j;
117 static tree234 *rsakeys, *ssh2keys;
119 static int has_security;
124 static void *make_keylist1(int *length);
125 static void *make_keylist2(int *length);
126 static void *get_keylist1(int *length);
127 static void *get_keylist2(int *length);
130 * We need this to link with the RSA code, because rsaencrypt()
131 * pads its data with random bytes. Since we only use rsadecrypt()
132 * and the signing functions, which are deterministic, this should
135 * If it _is_ called, there is a _serious_ problem, because it
136 * won't generate true random numbers. So we must scream, panic,
137 * and exit immediately if that should happen.
139 int random_byte(void)
141 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
143 /* this line can't be reached but it placates MSVC's warnings :-) */
148 * Blob structure for passing to the asymmetric SSH-2 key compare
149 * function, prototyped here.
155 static int cmpkeys_ssh2_asymm(void *av, void *bv);
157 struct PassphraseProcStruct {
162 static tree234 *passphrases = NULL;
165 * After processing a list of filenames, we want to forget the
168 static void forget_passphrases(void)
170 while (count234(passphrases) > 0) {
171 char *pp = index234(passphrases, 0);
172 smemclr(pp, strlen(pp));
173 delpos234(passphrases, 0);
179 * Dialog-box function for the Licence box.
181 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
182 WPARAM wParam, LPARAM lParam)
188 switch (LOWORD(wParam)) {
203 * Dialog-box function for the About box.
205 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
206 WPARAM wParam, LPARAM lParam)
210 SetDlgItemText(hwnd, 100, ver);
213 switch (LOWORD(wParam)) {
220 EnableWindow(hwnd, 0);
221 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
222 EnableWindow(hwnd, 1);
223 SetActiveWindow(hwnd);
235 static HWND passphrase_box;
238 * Dialog-box function for the passphrase box.
240 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
241 WPARAM wParam, LPARAM lParam)
243 static char **passphrase = NULL;
244 struct PassphraseProcStruct *p;
248 passphrase_box = hwnd;
252 { /* centre the window */
256 hw = GetDesktopWindow();
257 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
259 (rs.right + rs.left + rd.left - rd.right) / 2,
260 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
261 rd.right - rd.left, rd.bottom - rd.top, TRUE);
264 SetForegroundWindow(hwnd);
265 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
266 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
267 p = (struct PassphraseProcStruct *) lParam;
268 passphrase = p->passphrase;
270 SetDlgItemText(hwnd, 101, p->comment);
271 burnstr(*passphrase);
272 *passphrase = dupstr("");
273 SetDlgItemText(hwnd, 102, *passphrase);
276 switch (LOWORD(wParam)) {
286 case 102: /* edit box */
287 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
288 burnstr(*passphrase);
289 *passphrase = GetDlgItemText_alloc(hwnd, 102);
302 * Warn about the obsolescent key file format.
304 void old_keyfile_warning(void)
306 static const char mbtitle[] = "PuTTY Key File Warning";
307 static const char message[] =
308 "You are loading an SSH-2 private key which has an\n"
309 "old version of the file format. This means your key\n"
310 "file is not fully tamperproof. Future versions of\n"
311 "PuTTY may stop supporting this private key format,\n"
312 "so we recommend you convert your key to the new\n"
315 "You can perform this conversion by loading the key\n"
316 "into PuTTYgen and then saving it again.";
318 MessageBox(NULL, message, mbtitle, MB_OK);
322 * Update the visible key list.
324 static void keylist_update(void)
327 struct ssh2_userkey *skey;
331 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
332 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
333 char listentry[512], *p;
335 * Replace two spaces in the fingerprint with tabs, for
336 * nice alignment in the box.
338 strcpy(listentry, "ssh1\t");
339 p = listentry + strlen(listentry);
340 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
341 p = strchr(listentry, ' ');
344 p = strchr(listentry, ' ');
347 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
348 0, (LPARAM) listentry);
350 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
354 * Replace spaces with tabs in the fingerprint prefix, for
355 * nice alignment in the list box, until we encounter a :
356 * meaning we're into the fingerprint proper.
358 p = skey->alg->fingerprint(skey->data);
359 listentry = dupprintf("%s\t%s", p, skey->comment);
360 fp_len = strlen(listentry);
365 pos += strcspn(listentry + pos, " :");
366 if (listentry[pos] == ':')
368 listentry[pos++] = '\t';
371 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
375 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
376 (LPARAM)"0\t1\t2\t3\t4\t5\t6\t7\t8\t9\t10");
377 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
382 * This function loads a key from a file and adds it.
384 static void add_keyfile(Filename *filename)
387 struct RSAKey *rkey = NULL;
388 struct ssh2_userkey *skey = NULL;
393 const char *error = NULL;
397 type = key_type(filename);
398 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
399 char *msg = dupprintf("Couldn't load this key (%s)",
400 key_type_to_str(type));
401 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
402 HELPCTXID(errors_cantloadkey));
408 * See if the key is already loaded (in the primary Pageant,
409 * which may or may not be us).
413 unsigned char *keylist, *p;
414 int i, nkeys, bloblen, keylistlen;
416 if (type == SSH_KEYTYPE_SSH1) {
417 if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
418 char *msg = dupprintf("Couldn't load private key (%s)", error);
419 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
420 HELPCTXID(errors_cantloadkey));
424 keylist = get_keylist1(&keylistlen);
426 unsigned char *blob2;
427 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
430 char *msg = dupprintf("Couldn't load private key (%s)", error);
431 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
432 HELPCTXID(errors_cantloadkey));
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 = toint(GET_32BIT(keylist));
453 MessageBox(NULL, "Received broken key list?!", APPNAME,
454 MB_OK | MB_ICONERROR);
460 for (i = 0; i < nkeys; i++) {
461 if (!memcmp(blob, p, bloblen)) {
462 /* Key is already present; we can now leave. */
467 /* Now skip over public blob */
468 if (type == SSH_KEYTYPE_SSH1) {
469 int n = rsa_public_blob_len(p, keylistlen);
471 MessageBox(NULL, "Received broken key list?!", APPNAME,
472 MB_OK | MB_ICONERROR);
479 if (keylistlen < 4) {
480 MessageBox(NULL, "Received broken key list?!", APPNAME,
481 MB_OK | MB_ICONERROR);
484 n = toint(4 + GET_32BIT(p));
485 if (n < 0 || keylistlen < n) {
486 MessageBox(NULL, "Received broken key list?!", APPNAME,
487 MB_OK | MB_ICONERROR);
493 /* Now skip over comment field */
496 if (keylistlen < 4) {
497 MessageBox(NULL, "Received broken key list?!", APPNAME,
498 MB_OK | MB_ICONERROR);
501 n = toint(4 + GET_32BIT(p));
502 if (n < 0 || keylistlen < n) {
503 MessageBox(NULL, "Received broken key list?!", APPNAME,
504 MB_OK | MB_ICONERROR);
519 if (type == SSH_KEYTYPE_SSH1)
520 needs_pass = rsakey_encrypted(filename, &comment);
522 needs_pass = ssh2_userkey_encrypted(filename, &comment);
524 if (type == SSH_KEYTYPE_SSH1)
525 rkey = snew(struct RSAKey);
533 /* try all the remembered passphrases first */
534 char *pp = index234(passphrases, attempts);
536 passphrase = dupstr(pp);
539 struct PassphraseProcStruct pps;
541 pps.passphrase = &passphrase;
542 pps.comment = comment;
545 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
546 NULL, PassphraseProc, (LPARAM) &pps);
547 passphrase_box = NULL;
551 if (type == SSH_KEYTYPE_SSH1)
553 return; /* operation cancelled */
556 assert(passphrase != NULL);
559 passphrase = dupstr("");
561 if (type == SSH_KEYTYPE_SSH1)
562 ret = loadrsakey(filename, rkey, passphrase, &error);
564 skey = ssh2_load_userkey(filename, passphrase, &error);
565 if (skey == SSH2_WRONG_PASSPHRASE)
575 if(original_pass && ret) {
576 /* If they typed in an ok passphrase, remember it */
577 addpos234(passphrases, passphrase, 0);
579 /* Otherwise, destroy it */
587 char *msg = dupprintf("Couldn't load private key (%s)", error);
588 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
589 HELPCTXID(errors_cantloadkey));
591 if (type == SSH_KEYTYPE_SSH1)
595 if (type == SSH_KEYTYPE_SSH1) {
596 if (already_running) {
597 unsigned char *request, *response;
599 int reqlen, clen, resplen, ret;
601 clen = strlen(rkey->comment);
603 reqlen = 4 + 1 + /* length, message type */
605 ssh1_bignum_length(rkey->modulus) +
606 ssh1_bignum_length(rkey->exponent) +
607 ssh1_bignum_length(rkey->private_exponent) +
608 ssh1_bignum_length(rkey->iqmp) +
609 ssh1_bignum_length(rkey->p) +
610 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
613 request = snewn(reqlen, unsigned char);
615 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
617 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
619 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
620 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
622 ssh1_write_bignum(request + reqlen,
623 rkey->private_exponent);
624 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
625 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
626 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
627 PUT_32BIT(request + reqlen, clen);
628 memcpy(request + reqlen + 4, rkey->comment, clen);
630 PUT_32BIT(request, reqlen - 4);
632 ret = agent_query(request, reqlen, &vresponse, &resplen,
635 response = vresponse;
636 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
637 MessageBox(NULL, "The already running Pageant "
638 "refused to add the key.", APPNAME,
639 MB_OK | MB_ICONERROR);
644 if (add234(rsakeys, rkey) != rkey)
645 sfree(rkey); /* already present, don't waste RAM */
648 if (already_running) {
649 unsigned char *request, *response;
651 int reqlen, alglen, clen, keybloblen, resplen, ret;
652 alglen = strlen(skey->alg->name);
653 clen = strlen(skey->comment);
655 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
657 reqlen = 4 + 1 + /* length, message type */
658 4 + alglen + /* algorithm name */
659 keybloblen + /* key data */
660 4 + clen /* comment */
663 request = snewn(reqlen, unsigned char);
665 request[4] = SSH2_AGENTC_ADD_IDENTITY;
667 PUT_32BIT(request + reqlen, alglen);
669 memcpy(request + reqlen, skey->alg->name, alglen);
671 reqlen += skey->alg->openssh_fmtkey(skey->data,
674 PUT_32BIT(request + reqlen, clen);
675 memcpy(request + reqlen + 4, skey->comment, clen);
677 PUT_32BIT(request, reqlen - 4);
679 ret = agent_query(request, reqlen, &vresponse, &resplen,
682 response = vresponse;
683 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
684 MessageBox(NULL, "The already running Pageant "
685 "refused to add the key.", APPNAME,
686 MB_OK | MB_ICONERROR);
691 if (add234(ssh2keys, skey) != skey) {
692 skey->alg->freekey(skey->data);
693 sfree(skey); /* already present, don't waste RAM */
700 * Create an SSH-1 key list in a malloc'ed buffer; return its
703 static void *make_keylist1(int *length)
707 unsigned char *blob, *p, *ret;
711 * Count up the number and length of keys we hold.
715 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
717 blob = rsa_public_blob(key, &bloblen);
720 len += 4 + strlen(key->comment);
723 /* Allocate the buffer. */
724 p = ret = snewn(len, unsigned char);
725 if (length) *length = len;
729 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
730 blob = rsa_public_blob(key, &bloblen);
731 memcpy(p, blob, bloblen);
734 PUT_32BIT(p, strlen(key->comment));
735 memcpy(p + 4, key->comment, strlen(key->comment));
736 p += 4 + strlen(key->comment);
739 assert(p - ret == len);
744 * Create an SSH-2 key list in a malloc'ed buffer; return its
747 static void *make_keylist2(int *length)
749 struct ssh2_userkey *key;
751 unsigned char *blob, *p, *ret;
755 * Count up the number and length of keys we hold.
759 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
761 len += 4; /* length field */
762 blob = key->alg->public_blob(key->data, &bloblen);
765 len += 4 + strlen(key->comment);
768 /* Allocate the buffer. */
769 p = ret = snewn(len, unsigned char);
770 if (length) *length = len;
773 * Packet header is the obvious five bytes, plus four
774 * bytes for the key count.
778 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
779 blob = key->alg->public_blob(key->data, &bloblen);
780 PUT_32BIT(p, bloblen);
782 memcpy(p, blob, bloblen);
785 PUT_32BIT(p, strlen(key->comment));
786 memcpy(p + 4, key->comment, strlen(key->comment));
787 p += 4 + strlen(key->comment);
790 assert(p - ret == len);
795 * Acquire a keylist1 from the primary Pageant; this means either
796 * calling make_keylist1 (if that's us) or sending a message to the
797 * primary Pageant (if it's not).
799 static void *get_keylist1(int *length)
803 if (already_running) {
804 unsigned char request[5], *response;
807 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
808 PUT_32BIT(request, 4);
810 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
812 response = vresponse;
813 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
818 ret = snewn(resplen-5, unsigned char);
819 memcpy(ret, response+5, resplen-5);
825 ret = make_keylist1(length);
831 * Acquire a keylist2 from the primary Pageant; this means either
832 * calling make_keylist2 (if that's us) or sending a message to the
833 * primary Pageant (if it's not).
835 static void *get_keylist2(int *length)
839 if (already_running) {
840 unsigned char request[5], *response;
844 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
845 PUT_32BIT(request, 4);
847 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
849 response = vresponse;
850 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
855 ret = snewn(resplen-5, unsigned char);
856 memcpy(ret, response+5, resplen-5);
862 ret = make_keylist2(length);
868 * This is the main agent function that answers messages.
870 static void answer_msg(void *msg)
872 unsigned char *p = msg;
873 unsigned char *ret = msg;
874 unsigned char *msgend;
878 * Get the message length.
880 msgend = p + 4 + GET_32BIT(p);
883 * Get the message type.
891 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
893 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
899 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
900 keylist = make_keylist1(&len);
901 if (len + 5 > AGENT_MAX_MSGLEN) {
905 PUT_32BIT(ret, len + 1);
906 memcpy(ret + 5, keylist, len);
910 case SSH2_AGENTC_REQUEST_IDENTITIES:
912 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
918 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
919 keylist = make_keylist2(&len);
920 if (len + 5 > AGENT_MAX_MSGLEN) {
924 PUT_32BIT(ret, len + 1);
925 memcpy(ret + 5, keylist, len);
929 case SSH1_AGENTC_RSA_CHALLENGE:
931 * Reply with either SSH1_AGENT_RSA_RESPONSE or
932 * SSH_AGENT_FAILURE, depending on whether we have that key
936 struct RSAKey reqkey, *key;
937 Bignum challenge, response;
938 unsigned char response_source[48], response_md5[16];
939 struct MD5Context md5c;
943 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
947 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
949 freebn(reqkey.exponent);
953 i = ssh1_read_bignum(p, msgend - p, &challenge);
955 freebn(reqkey.exponent);
956 freebn(reqkey.modulus);
961 freebn(reqkey.exponent);
962 freebn(reqkey.modulus);
966 memcpy(response_source + 32, p, 16);
970 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
971 freebn(reqkey.exponent);
972 freebn(reqkey.modulus);
976 response = rsadecrypt(challenge, key);
977 for (i = 0; i < 32; i++)
978 response_source[i] = bignum_byte(response, 31 - i);
981 MD5Update(&md5c, response_source, 48);
982 MD5Final(response_md5, &md5c);
983 smemclr(response_source, 48); /* burn the evidence */
984 freebn(response); /* and that evidence */
985 freebn(challenge); /* yes, and that evidence */
986 freebn(reqkey.exponent); /* and free some memory ... */
987 freebn(reqkey.modulus); /* ... while we're at it. */
990 * Packet is the obvious five byte header, plus sixteen
994 PUT_32BIT(ret, len - 4);
995 ret[4] = SSH1_AGENT_RSA_RESPONSE;
996 memcpy(ret + 5, response_md5, 16);
999 case SSH2_AGENTC_SIGN_REQUEST:
1001 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
1002 * SSH_AGENT_FAILURE, depending on whether we have that key
1006 struct ssh2_userkey *key;
1008 unsigned char *data, *signature;
1009 int datalen, siglen, len;
1013 b.len = toint(GET_32BIT(p));
1014 if (b.len < 0 || b.len > msgend - (p+4))
1021 datalen = toint(GET_32BIT(p));
1023 if (datalen < 0 || datalen > msgend - p)
1026 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1029 signature = key->alg->sign(key->data, data, datalen, &siglen);
1030 len = 5 + 4 + siglen;
1031 PUT_32BIT(ret, len - 4);
1032 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1033 PUT_32BIT(ret + 5, siglen);
1034 memcpy(ret + 5 + 4, signature, siglen);
1038 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1040 * Add to the list and return SSH_AGENT_SUCCESS, or
1041 * SSH_AGENT_FAILURE if the key was malformed.
1048 key = snew(struct RSAKey);
1049 memset(key, 0, sizeof(struct RSAKey));
1051 n = makekey(p, msgend - p, key, NULL, 1);
1059 n = makeprivate(p, msgend - p, key);
1067 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1075 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1083 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1096 commentlen = toint(GET_32BIT(p));
1098 if (commentlen < 0 || commentlen > msgend - p) {
1104 comment = snewn(commentlen+1, char);
1106 memcpy(comment, p + 4, commentlen);
1107 comment[commentlen] = '\0';
1108 key->comment = comment;
1111 ret[4] = SSH_AGENT_FAILURE;
1112 if (add234(rsakeys, key) == key) {
1114 ret[4] = SSH_AGENT_SUCCESS;
1121 case SSH2_AGENTC_ADD_IDENTITY:
1123 * Add to the list and return SSH_AGENT_SUCCESS, or
1124 * SSH_AGENT_FAILURE if the key was malformed.
1127 struct ssh2_userkey *key;
1128 char *comment, *alg;
1129 int alglen, commlen;
1135 alglen = toint(GET_32BIT(p));
1137 if (alglen < 0 || alglen > msgend - p)
1142 key = snew(struct ssh2_userkey);
1143 /* Add further algorithm names here. */
1144 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1145 key->alg = &ssh_rsa;
1146 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1147 key->alg = &ssh_dss;
1148 else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19))
1149 key->alg = &ssh_ecdsa_nistp256;
1150 else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19))
1151 key->alg = &ssh_ecdsa_nistp384;
1152 else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19))
1153 key->alg = &ssh_ecdsa_nistp521;
1159 bloblen = msgend - p;
1160 key->data = key->alg->openssh_createkey(&p, &bloblen);
1167 * p has been advanced by openssh_createkey, but
1168 * certainly not _beyond_ the end of the buffer.
1170 assert(p <= msgend);
1173 key->alg->freekey(key->data);
1177 commlen = toint(GET_32BIT(p));
1180 if (commlen < 0 || commlen > msgend - p) {
1181 key->alg->freekey(key->data);
1185 comment = snewn(commlen + 1, char);
1187 memcpy(comment, p, commlen);
1188 comment[commlen] = '\0';
1190 key->comment = comment;
1193 ret[4] = SSH_AGENT_FAILURE;
1194 if (add234(ssh2keys, key) == key) {
1196 ret[4] = SSH_AGENT_SUCCESS;
1198 key->alg->freekey(key->data);
1199 sfree(key->comment);
1204 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1206 * Remove from the list and return SSH_AGENT_SUCCESS, or
1207 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1211 struct RSAKey reqkey, *key;
1214 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1218 key = find234(rsakeys, &reqkey, NULL);
1219 freebn(reqkey.exponent);
1220 freebn(reqkey.modulus);
1222 ret[4] = SSH_AGENT_FAILURE;
1224 del234(rsakeys, key);
1228 ret[4] = SSH_AGENT_SUCCESS;
1232 case SSH2_AGENTC_REMOVE_IDENTITY:
1234 * Remove from the list and return SSH_AGENT_SUCCESS, or
1235 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1239 struct ssh2_userkey *key;
1244 b.len = toint(GET_32BIT(p));
1247 if (b.len < 0 || b.len > msgend - p)
1252 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1257 ret[4] = SSH_AGENT_FAILURE;
1259 del234(ssh2keys, key);
1261 key->alg->freekey(key->data);
1263 ret[4] = SSH_AGENT_SUCCESS;
1267 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1269 * Remove all SSH-1 keys. Always returns success.
1272 struct RSAKey *rkey;
1274 while ((rkey = index234(rsakeys, 0)) != NULL) {
1275 del234(rsakeys, rkey);
1282 ret[4] = SSH_AGENT_SUCCESS;
1285 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1287 * Remove all SSH-2 keys. Always returns success.
1290 struct ssh2_userkey *skey;
1292 while ((skey = index234(ssh2keys, 0)) != NULL) {
1293 del234(ssh2keys, skey);
1294 skey->alg->freekey(skey->data);
1300 ret[4] = SSH_AGENT_SUCCESS;
1306 * Unrecognised message. Return SSH_AGENT_FAILURE.
1309 ret[4] = SSH_AGENT_FAILURE;
1315 * Key comparison function for the 2-3-4 tree of RSA keys.
1317 static int cmpkeys_rsa(void *av, void *bv)
1319 struct RSAKey *a = (struct RSAKey *) av;
1320 struct RSAKey *b = (struct RSAKey *) bv;
1327 * Compare by length of moduli.
1329 alen = bignum_bitcount(am);
1330 blen = bignum_bitcount(bm);
1333 else if (alen < blen)
1336 * Now compare by moduli themselves.
1338 alen = (alen + 7) / 8; /* byte count */
1339 while (alen-- > 0) {
1341 abyte = bignum_byte(am, alen);
1342 bbyte = bignum_byte(bm, alen);
1345 else if (abyte < bbyte)
1355 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1357 static int cmpkeys_ssh2(void *av, void *bv)
1359 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1360 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1363 unsigned char *ablob, *bblob;
1367 * Compare purely by public blob.
1369 ablob = a->alg->public_blob(a->data, &alen);
1370 bblob = b->alg->public_blob(b->data, &blen);
1373 for (i = 0; i < alen && i < blen; i++) {
1374 if (ablob[i] < bblob[i]) {
1377 } else if (ablob[i] > bblob[i]) {
1382 if (c == 0 && i < alen)
1383 c = +1; /* a is longer */
1384 if (c == 0 && i < blen)
1385 c = -1; /* a is longer */
1394 * Key comparison function for looking up a blob in the 2-3-4 tree
1397 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1399 struct blob *a = (struct blob *) av;
1400 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1403 unsigned char *ablob, *bblob;
1407 * Compare purely by public blob.
1411 bblob = b->alg->public_blob(b->data, &blen);
1414 for (i = 0; i < alen && i < blen; i++) {
1415 if (ablob[i] < bblob[i]) {
1418 } else if (ablob[i] > bblob[i]) {
1423 if (c == 0 && i < alen)
1424 c = +1; /* a is longer */
1425 if (c == 0 && i < blen)
1426 c = -1; /* a is longer */
1434 * Prompt for a key file to add, and add it.
1436 static void prompt_add_keyfile(void)
1439 char *filelist = snewn(8192, char);
1441 if (!keypath) keypath = filereq_new();
1442 memset(&of, 0, sizeof(of));
1443 of.hwndOwner = hwnd;
1444 of.lpstrFilter = FILTER_KEY_FILES;
1445 of.lpstrCustomFilter = NULL;
1446 of.nFilterIndex = 1;
1447 of.lpstrFile = filelist;
1450 of.lpstrFileTitle = NULL;
1451 of.lpstrTitle = "Select Private Key File";
1452 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1453 if (request_file(keypath, &of, TRUE, FALSE)) {
1454 if(strlen(filelist) > of.nFileOffset) {
1455 /* Only one filename returned? */
1456 Filename *fn = filename_from_str(filelist);
1460 /* we are returned a bunch of strings, end to
1461 * end. first string is the directory, the
1462 * rest the filenames. terminated with an
1465 char *dir = filelist;
1466 char *filewalker = filelist + strlen(dir) + 1;
1467 while (*filewalker != '\0') {
1468 char *filename = dupcat(dir, "\\", filewalker, NULL);
1469 Filename *fn = filename_from_str(filename);
1473 filewalker += strlen(filewalker) + 1;
1478 forget_passphrases();
1484 * Dialog-box function for the key list box.
1486 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1487 WPARAM wParam, LPARAM lParam)
1489 struct RSAKey *rkey;
1490 struct ssh2_userkey *skey;
1495 * Centre the window.
1497 { /* centre the window */
1501 hw = GetDesktopWindow();
1502 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1504 (rs.right + rs.left + rd.left - rd.right) / 2,
1505 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1506 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1510 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1511 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1514 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1516 DestroyWindow(item);
1521 static int tabs[] = { 35, 75, 250 };
1522 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1523 sizeof(tabs) / sizeof(*tabs),
1529 switch (LOWORD(wParam)) {
1533 DestroyWindow(hwnd);
1535 case 101: /* add key */
1536 if (HIWORD(wParam) == BN_CLICKED ||
1537 HIWORD(wParam) == BN_DOUBLECLICKED) {
1538 if (passphrase_box) {
1539 MessageBeep(MB_ICONERROR);
1540 SetForegroundWindow(passphrase_box);
1543 prompt_add_keyfile();
1546 case 102: /* remove key */
1547 if (HIWORD(wParam) == BN_CLICKED ||
1548 HIWORD(wParam) == BN_DOUBLECLICKED) {
1553 /* our counter within the array of selected items */
1556 /* get the number of items selected in the list */
1558 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1560 /* none selected? that was silly */
1561 if (numSelected == 0) {
1566 /* get item indices in an array */
1567 selectedArray = snewn(numSelected, int);
1568 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1569 numSelected, (WPARAM)selectedArray);
1571 itemNum = numSelected - 1;
1572 rCount = count234(rsakeys);
1573 sCount = count234(ssh2keys);
1575 /* go through the non-rsakeys until we've covered them all,
1576 * and/or we're out of selected items to check. note that
1577 * we go *backwards*, to avoid complications from deleting
1578 * things hence altering the offset of subsequent items
1580 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1581 skey = index234(ssh2keys, i);
1583 if (selectedArray[itemNum] == rCount + i) {
1584 del234(ssh2keys, skey);
1585 skey->alg->freekey(skey->data);
1591 /* do the same for the rsa keys */
1592 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1593 rkey = index234(rsakeys, i);
1595 if(selectedArray[itemNum] == i) {
1596 del234(rsakeys, rkey);
1603 sfree(selectedArray);
1607 case 103: /* help */
1608 if (HIWORD(wParam) == BN_CLICKED ||
1609 HIWORD(wParam) == BN_DOUBLECLICKED) {
1610 launch_help(hwnd, WINHELP_CTX_pageant_general);
1617 int id = ((LPHELPINFO)lParam)->iCtrlId;
1620 case 100: topic = WINHELP_CTX_pageant_keylist; break;
1621 case 101: topic = WINHELP_CTX_pageant_addkey; break;
1622 case 102: topic = WINHELP_CTX_pageant_remkey; break;
1625 launch_help(hwnd, topic);
1633 DestroyWindow(hwnd);
1639 /* Set up a system tray icon */
1640 static BOOL AddTrayIcon(HWND hwnd)
1643 NOTIFYICONDATA tnid;
1646 #ifdef NIM_SETVERSION
1648 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1651 tnid.cbSize = sizeof(NOTIFYICONDATA);
1653 tnid.uID = 1; /* unique within this systray use */
1654 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1655 tnid.uCallbackMessage = WM_SYSTRAY;
1656 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
1657 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1659 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1661 if (hicon) DestroyIcon(hicon);
1666 /* Update the saved-sessions menu. */
1667 static void update_sessions(void)
1671 TCHAR buf[MAX_PATH + 1];
1674 int index_key, index_menu;
1679 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1682 for(num_entries = GetMenuItemCount(session_menu);
1683 num_entries > initial_menuitems_count;
1685 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1690 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1691 TCHAR session_name[MAX_PATH + 1];
1692 unmungestr(buf, session_name, MAX_PATH);
1693 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1694 memset(&mii, 0, sizeof(mii));
1695 mii.cbSize = sizeof(mii);
1696 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1697 mii.fType = MFT_STRING;
1698 mii.fState = MFS_ENABLED;
1699 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1700 mii.dwTypeData = session_name;
1701 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1709 if(index_menu == 0) {
1710 mii.cbSize = sizeof(mii);
1711 mii.fMask = MIIM_TYPE | MIIM_STATE;
1712 mii.fType = MFT_STRING;
1713 mii.fState = MFS_GRAYED;
1714 mii.dwTypeData = _T("(No sessions)");
1715 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1721 * Versions of Pageant prior to 0.61 expected this SID on incoming
1722 * communications. For backwards compatibility, and more particularly
1723 * for compatibility with derived works of PuTTY still using the old
1724 * Pageant client code, we accept it as an alternative to the one
1725 * returned from get_user_sid() in winpgntc.c.
1727 PSID get_default_sid(void)
1731 PSECURITY_DESCRIPTOR psd = NULL;
1732 PSID sid = NULL, copy = NULL, ret = NULL;
1734 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1735 GetCurrentProcessId())) == NULL)
1738 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1739 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1742 sidlen = GetLengthSid(sid);
1744 copy = (PSID)smalloc(sidlen);
1746 if (!CopySid(sidlen, copy, sid))
1749 /* Success. Move sid into the return value slot, and null it out
1750 * to stop the cleanup code freeing it. */
1766 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1767 WPARAM wParam, LPARAM lParam)
1770 static int menuinprogress;
1771 static UINT msgTaskbarCreated = 0;
1775 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1778 if (message==msgTaskbarCreated) {
1780 * Explorer has been restarted, so the tray icon will
1788 if (lParam == WM_RBUTTONUP) {
1790 GetCursorPos(&cursorpos);
1791 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1792 } else if (lParam == WM_LBUTTONDBLCLK) {
1793 /* Run the default menu item. */
1794 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1796 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1800 if (!menuinprogress) {
1803 SetForegroundWindow(hwnd);
1804 ret = TrackPopupMenu(systray_menu,
1805 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1807 wParam, lParam, 0, hwnd, NULL);
1813 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1815 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1817 MessageBox(NULL, "Unable to execute PuTTY!",
1818 "Error", MB_OK | MB_ICONERROR);
1823 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1824 SendMessage(hwnd, WM_CLOSE, 0, 0);
1828 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1830 ShowWindow(keylist, SW_SHOWNORMAL);
1833 * Sometimes the window comes up minimised / hidden for
1834 * no obvious reason. Prevent this. This also brings it
1835 * to the front if it's already present (the user
1836 * selected View Keys because they wanted to _see_ the
1839 SetForegroundWindow(keylist);
1840 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1841 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1844 if (passphrase_box) {
1845 MessageBeep(MB_ICONERROR);
1846 SetForegroundWindow(passphrase_box);
1849 prompt_add_keyfile();
1853 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1855 ShowWindow(aboutbox, SW_SHOWNORMAL);
1857 * Sometimes the window comes up minimised / hidden
1858 * for no obvious reason. Prevent this.
1860 SetForegroundWindow(aboutbox);
1861 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1862 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1866 launch_help(hwnd, WINHELP_CTX_pageant_general);
1870 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1872 TCHAR buf[MAX_PATH + 1];
1873 TCHAR param[MAX_PATH + 1];
1874 memset(&mii, 0, sizeof(mii));
1875 mii.cbSize = sizeof(mii);
1876 mii.fMask = MIIM_TYPE;
1878 mii.dwTypeData = buf;
1879 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1881 strcat(param, mii.dwTypeData);
1882 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1883 _T(""), SW_SHOW) <= 32) {
1884 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1885 MB_OK | MB_ICONERROR);
1898 COPYDATASTRUCT *cds;
1903 PSID mapowner, ourself, ourself2;
1905 PSECURITY_DESCRIPTOR psd = NULL;
1908 cds = (COPYDATASTRUCT *) lParam;
1909 if (cds->dwData != AGENT_COPYDATA_ID)
1910 return 0; /* not our message, mate */
1911 mapname = (char *) cds->lpData;
1912 if (mapname[cds->cbData - 1] != '\0')
1913 return 0; /* failure to be ASCIZ! */
1915 debug(("mapname is :%s:\n", mapname));
1917 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1919 debug(("filemap is %p\n", filemap));
1921 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1925 if ((ourself = get_user_sid()) == NULL) {
1927 debug(("couldn't get user SID\n"));
1929 CloseHandle(filemap);
1933 if ((ourself2 = get_default_sid()) == NULL) {
1935 debug(("couldn't get default SID\n"));
1937 CloseHandle(filemap);
1942 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1943 OWNER_SECURITY_INFORMATION,
1944 &mapowner, NULL, NULL, NULL,
1945 &psd) != ERROR_SUCCESS)) {
1947 debug(("couldn't get owner info for filemap: %d\n",
1950 CloseHandle(filemap);
1957 LPTSTR ours, ours2, theirs;
1958 ConvertSidToStringSid(mapowner, &theirs);
1959 ConvertSidToStringSid(ourself, &ours);
1960 ConvertSidToStringSid(ourself2, &ours2);
1961 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1962 " theirs=%s\n", ours, ours2, theirs));
1968 if (!EqualSid(mapowner, ourself) &&
1969 !EqualSid(mapowner, ourself2)) {
1970 CloseHandle(filemap);
1974 return 0; /* security ID mismatch! */
1977 debug(("security stuff matched\n"));
1984 debug(("security APIs not present\n"));
1988 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1990 debug(("p is %p\n", p));
1993 for (i = 0; i < 5; i++)
1994 debug(("p[%d]=%02x\n", i,
1995 ((unsigned char *) p)[i]));
2002 CloseHandle(filemap);
2007 return DefWindowProc(hwnd, message, wParam, lParam);
2011 * Fork and Exec the command in cmdline. [DBW]
2013 void spawn_cmd(char *cmdline, char * args, int show)
2015 if (ShellExecute(NULL, _T("open"), cmdline,
2016 args, NULL, show) <= (HINSTANCE) 32) {
2018 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
2019 (int)GetLastError());
2020 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
2026 * This is a can't-happen stub, since Pageant never makes
2027 * asynchronous agent requests.
2029 void agent_schedule_callback(void (*callback)(void *, void *, int),
2030 void *callback_ctx, void *data, int len)
2032 assert(!"We shouldn't get here");
2035 void cleanup_exit(int code)
2041 int flags = FLAG_SYNCAGENT;
2043 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
2047 char *command = NULL;
2050 char **argv, **argstart;
2056 * Determine whether we're an NT system (should have security
2057 * APIs) or a non-NT system (don't do security).
2061 modalfatalbox("Windows refuses to report a version");
2063 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
2064 has_security = TRUE;
2066 has_security = FALSE;
2071 * Attempt to get the security API we need.
2073 if (!got_advapi()) {
2075 "Unable to access security APIs. Pageant will\n"
2076 "not run, in case it causes a security breach.",
2077 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2082 "This program has been compiled for Win9X and will\n"
2083 "not run on NT, in case it causes a security breach.",
2084 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2090 * See if we can find our Help file.
2095 * Look for the PuTTY binary (we will enable the saved session
2096 * submenu if we find it).
2099 char b[2048], *p, *q, *r;
2101 GetModuleFileName(NULL, b, sizeof(b) - 16);
2103 p = strrchr(b, '\\');
2104 if (p && p >= r) r = p+1;
2105 q = strrchr(b, ':');
2106 if (q && q >= r) r = q+1;
2107 strcpy(r, "putty.exe");
2108 if ( (fp = fopen(b, "r")) != NULL) {
2109 putty_path = dupstr(b);
2116 * Find out if Pageant is already running.
2118 already_running = agent_exists();
2121 * Initialise storage for RSA keys.
2123 if (!already_running) {
2124 rsakeys = newtree234(cmpkeys_rsa);
2125 ssh2keys = newtree234(cmpkeys_ssh2);
2129 * Initialise storage for short-term passphrase cache.
2131 passphrases = newtree234(NULL);
2134 * Process the command line and add keys as listed on it.
2136 split_into_argv(cmdline, &argc, &argv, &argstart);
2137 for (i = 0; i < argc; i++) {
2138 if (!strcmp(argv[i], "-pgpfp")) {
2141 } else if (!strcmp(argv[i], "-c")) {
2143 * If we see `-c', then the rest of the
2144 * command line should be treated as a
2145 * command to be spawned.
2148 command = argstart[i+1];
2153 Filename *fn = filename_from_str(argv[i]);
2161 * Forget any passphrase that we retained while going over
2162 * command line keyfiles.
2164 forget_passphrases();
2168 if (command[0] == '"')
2169 args = strchr(++command, '"');
2171 args = strchr(command, ' ');
2174 while(*args && isspace(*args)) args++;
2176 spawn_cmd(command, args, show);
2180 * If Pageant was already running, we leave now. If we haven't
2181 * even taken any auxiliary action (spawned a command or added
2184 if (already_running) {
2185 if (!command && !added_keys) {
2186 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2187 MB_ICONERROR | MB_OK);
2194 wndclass.lpfnWndProc = WndProc;
2195 wndclass.cbClsExtra = 0;
2196 wndclass.cbWndExtra = 0;
2197 wndclass.hInstance = inst;
2198 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2199 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2200 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2201 wndclass.lpszMenuName = NULL;
2202 wndclass.lpszClassName = APPNAME;
2204 RegisterClass(&wndclass);
2209 hwnd = CreateWindow(APPNAME, APPNAME,
2210 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2211 CW_USEDEFAULT, CW_USEDEFAULT,
2212 100, 100, NULL, NULL, inst, NULL);
2214 /* Set up a system tray icon */
2217 /* Accelerators used: nsvkxa */
2218 systray_menu = CreatePopupMenu();
2220 session_menu = CreateMenu();
2221 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2222 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2223 (UINT) session_menu, "&Saved Sessions");
2224 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2226 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2228 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2229 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2231 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2232 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2233 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2234 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2235 initial_menuitems_count = GetMenuItemCount(session_menu);
2237 /* Set the default menu item. */
2238 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2240 ShowWindow(hwnd, SW_HIDE);
2243 * Main message loop.
2245 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2246 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2247 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2248 TranslateMessage(&msg);
2249 DispatchMessage(&msg);
2253 /* Clean up the system tray icon */
2255 NOTIFYICONDATA tnid;
2257 tnid.cbSize = sizeof(NOTIFYICONDATA);
2261 Shell_NotifyIcon(NIM_DELETE, &tnid);
2263 DestroyMenu(systray_menu);
2266 if (keypath) filereq_free(keypath);
2268 cleanup_exit(msg.wParam);
2269 return msg.wParam; /* just in case optimiser complains */