2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
25 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
30 #define IDI_MAINICON 200
31 #define IDI_TRAYICON 201
33 #define WM_SYSTRAY (WM_APP + 6)
34 #define WM_SYSTRAY2 (WM_APP + 7)
36 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
39 * FIXME: maybe some day we can sort this out ...
41 #define AGENT_MAX_MSGLEN 8192
43 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
44 * wParam are used by Windows, and should be masked off, so we shouldn't
45 * attempt to store information in them. Hence all these identifiers have
46 * the low 4 bits clear. Also, identifiers should < 0xF000. */
48 #define IDM_CLOSE 0x0010
49 #define IDM_VIEWKEYS 0x0020
50 #define IDM_ADDKEY 0x0030
51 #define IDM_HELP 0x0040
52 #define IDM_ABOUT 0x0050
54 #define APPNAME "Pageant"
60 static HMENU systray_menu, session_menu;
61 static int already_running;
63 static char *putty_path;
65 /* CWD for "add key" file requester. */
66 static filereq *keypath = NULL;
68 #define IDM_PUTTY 0x0060
69 #define IDM_SESSIONS_BASE 0x1000
70 #define IDM_SESSIONS_MAX 0x2000
71 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
72 #define PUTTY_DEFAULT "Default%20Settings"
73 static int initial_menuitems_count;
76 * Print a modal (Really Bad) message box and perform a fatal exit.
78 void modalfatalbox(char *fmt, ...)
84 buf = dupvprintf(fmt, ap);
86 MessageBox(hwnd, buf, "Pageant Fatal Error",
87 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
92 /* Un-munge session names out of the registry. */
93 static void unmungestr(char *in, char *out, int outlen)
96 if (*in == '%' && in[1] && in[2]) {
100 i -= (i > 9 ? 7 : 0);
102 j -= (j > 9 ? 7 : 0);
104 *out++ = (i << 4) + j;
118 static tree234 *rsakeys, *ssh2keys;
120 static int has_security;
125 static void *make_keylist1(int *length);
126 static void *make_keylist2(int *length);
127 static void *get_keylist1(int *length);
128 static void *get_keylist2(int *length);
131 * We need this to link with the RSA code, because rsaencrypt()
132 * pads its data with random bytes. Since we only use rsadecrypt()
133 * and the signing functions, which are deterministic, this should
136 * If it _is_ called, there is a _serious_ problem, because it
137 * won't generate true random numbers. So we must scream, panic,
138 * and exit immediately if that should happen.
140 int random_byte(void)
142 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
144 /* this line can't be reached but it placates MSVC's warnings :-) */
149 * Blob structure for passing to the asymmetric SSH-2 key compare
150 * function, prototyped here.
156 static int cmpkeys_ssh2_asymm(void *av, void *bv);
158 struct PassphraseProcStruct {
163 static tree234 *passphrases = NULL;
166 * After processing a list of filenames, we want to forget the
169 static void forget_passphrases(void)
171 while (count234(passphrases) > 0) {
172 char *pp = index234(passphrases, 0);
173 smemclr(pp, strlen(pp));
174 delpos234(passphrases, 0);
180 * Dialog-box function for the Licence box.
182 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
183 WPARAM wParam, LPARAM lParam)
187 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
190 switch (LOWORD(wParam)) {
205 * Dialog-box function for the About box.
207 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
208 WPARAM wParam, LPARAM lParam)
213 char *text = dupprintf
214 ("Pageant\r\n\r\n%s\r\n\r\n%s",
216 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
217 SetDlgItemText(hwnd, 1000, text);
222 switch (LOWORD(wParam)) {
229 EnableWindow(hwnd, 0);
230 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
231 EnableWindow(hwnd, 1);
232 SetActiveWindow(hwnd);
244 static HWND passphrase_box;
247 * Dialog-box function for the passphrase box.
249 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
250 WPARAM wParam, LPARAM lParam)
252 static char **passphrase = NULL;
253 struct PassphraseProcStruct *p;
257 passphrase_box = hwnd;
261 { /* centre the window */
265 hw = GetDesktopWindow();
266 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
268 (rs.right + rs.left + rd.left - rd.right) / 2,
269 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
270 rd.right - rd.left, rd.bottom - rd.top, TRUE);
273 SetForegroundWindow(hwnd);
274 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
275 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
276 p = (struct PassphraseProcStruct *) lParam;
277 passphrase = p->passphrase;
279 SetDlgItemText(hwnd, 101, p->comment);
280 burnstr(*passphrase);
281 *passphrase = dupstr("");
282 SetDlgItemText(hwnd, 102, *passphrase);
285 switch (LOWORD(wParam)) {
295 case 102: /* edit box */
296 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
297 burnstr(*passphrase);
298 *passphrase = GetDlgItemText_alloc(hwnd, 102);
311 * Warn about the obsolescent key file format.
313 void old_keyfile_warning(void)
315 static const char mbtitle[] = "PuTTY Key File Warning";
316 static const char message[] =
317 "You are loading an SSH-2 private key which has an\n"
318 "old version of the file format. This means your key\n"
319 "file is not fully tamperproof. Future versions of\n"
320 "PuTTY may stop supporting this private key format,\n"
321 "so we recommend you convert your key to the new\n"
324 "You can perform this conversion by loading the key\n"
325 "into PuTTYgen and then saving it again.";
327 MessageBox(NULL, message, mbtitle, MB_OK);
331 * Update the visible key list.
333 static void keylist_update(void)
336 struct ssh2_userkey *skey;
340 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
341 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
342 char listentry[512], *p;
344 * Replace two spaces in the fingerprint with tabs, for
345 * nice alignment in the box.
347 strcpy(listentry, "ssh1\t");
348 p = listentry + strlen(listentry);
349 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
350 p = strchr(listentry, ' ');
353 p = strchr(listentry, ' ');
356 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
357 0, (LPARAM) listentry);
359 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
363 * Replace two spaces in the fingerprint with tabs, for
364 * nice alignment in the box.
366 p = skey->alg->fingerprint(skey->data);
367 listentry = dupprintf("%s\t%s", p, skey->comment);
368 fp_len = strlen(listentry);
371 p = strchr(listentry, ' ');
372 if (p && p < listentry + fp_len)
374 p = strchr(listentry, ' ');
375 if (p && p < listentry + fp_len)
378 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
382 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
387 * This function loads a key from a file and adds it.
389 static void add_keyfile(Filename *filename)
392 struct RSAKey *rkey = NULL;
393 struct ssh2_userkey *skey = NULL;
398 const char *error = NULL;
402 type = key_type(filename);
403 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
404 char *msg = dupprintf("Couldn't load this key (%s)",
405 key_type_to_str(type));
406 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
407 HELPCTXID(errors_cantloadkey));
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, &error)) {
423 char *msg = dupprintf("Couldn't load private key (%s)", error);
424 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
425 HELPCTXID(errors_cantloadkey));
429 keylist = get_keylist1(&keylistlen);
431 unsigned char *blob2;
432 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
435 char *msg = dupprintf("Couldn't load private key (%s)", error);
436 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
437 HELPCTXID(errors_cantloadkey));
441 /* For our purposes we want the blob prefixed with its length */
442 blob2 = snewn(bloblen+4, unsigned char);
443 PUT_32BIT(blob2, bloblen);
444 memcpy(blob2 + 4, blob, bloblen);
448 keylist = get_keylist2(&keylistlen);
451 if (keylistlen < 4) {
452 MessageBox(NULL, "Received broken key list?!", APPNAME,
453 MB_OK | MB_ICONERROR);
456 nkeys = toint(GET_32BIT(keylist));
458 MessageBox(NULL, "Received broken key list?!", APPNAME,
459 MB_OK | MB_ICONERROR);
465 for (i = 0; i < nkeys; i++) {
466 if (!memcmp(blob, p, bloblen)) {
467 /* Key is already present; we can now leave. */
472 /* Now skip over public blob */
473 if (type == SSH_KEYTYPE_SSH1) {
474 int n = rsa_public_blob_len(p, keylistlen);
476 MessageBox(NULL, "Received broken key list?!", APPNAME,
477 MB_OK | MB_ICONERROR);
484 if (keylistlen < 4) {
485 MessageBox(NULL, "Received broken key list?!", APPNAME,
486 MB_OK | MB_ICONERROR);
489 n = toint(4 + GET_32BIT(p));
490 if (n < 0 || keylistlen < n) {
491 MessageBox(NULL, "Received broken key list?!", APPNAME,
492 MB_OK | MB_ICONERROR);
498 /* Now skip over comment field */
501 if (keylistlen < 4) {
502 MessageBox(NULL, "Received broken key list?!", APPNAME,
503 MB_OK | MB_ICONERROR);
506 n = toint(4 + GET_32BIT(p));
507 if (n < 0 || keylistlen < n) {
508 MessageBox(NULL, "Received broken key list?!", APPNAME,
509 MB_OK | MB_ICONERROR);
524 if (type == SSH_KEYTYPE_SSH1)
525 needs_pass = rsakey_encrypted(filename, &comment);
527 needs_pass = ssh2_userkey_encrypted(filename, &comment);
529 if (type == SSH_KEYTYPE_SSH1)
530 rkey = snew(struct RSAKey);
538 /* try all the remembered passphrases first */
539 char *pp = index234(passphrases, attempts);
541 passphrase = dupstr(pp);
544 struct PassphraseProcStruct pps;
546 pps.passphrase = &passphrase;
547 pps.comment = comment;
550 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
551 NULL, PassphraseProc, (LPARAM) &pps);
552 passphrase_box = NULL;
556 if (type == SSH_KEYTYPE_SSH1)
558 return; /* operation cancelled */
561 assert(passphrase != NULL);
564 passphrase = dupstr("");
566 if (type == SSH_KEYTYPE_SSH1)
567 ret = loadrsakey(filename, rkey, passphrase, &error);
569 skey = ssh2_load_userkey(filename, passphrase, &error);
570 if (skey == SSH2_WRONG_PASSPHRASE)
580 if(original_pass && ret) {
581 /* If they typed in an ok passphrase, remember it */
582 addpos234(passphrases, passphrase, 0);
584 /* Otherwise, destroy it */
592 char *msg = dupprintf("Couldn't load private key (%s)", error);
593 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
594 HELPCTXID(errors_cantloadkey));
596 if (type == SSH_KEYTYPE_SSH1)
600 if (type == SSH_KEYTYPE_SSH1) {
601 if (already_running) {
602 unsigned char *request, *response;
604 int reqlen, clen, resplen, ret;
606 clen = strlen(rkey->comment);
608 reqlen = 4 + 1 + /* length, message type */
610 ssh1_bignum_length(rkey->modulus) +
611 ssh1_bignum_length(rkey->exponent) +
612 ssh1_bignum_length(rkey->private_exponent) +
613 ssh1_bignum_length(rkey->iqmp) +
614 ssh1_bignum_length(rkey->p) +
615 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
618 request = snewn(reqlen, unsigned char);
620 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
622 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
624 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
625 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
627 ssh1_write_bignum(request + reqlen,
628 rkey->private_exponent);
629 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
630 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
631 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
632 PUT_32BIT(request + reqlen, clen);
633 memcpy(request + reqlen + 4, rkey->comment, clen);
635 PUT_32BIT(request, reqlen - 4);
637 ret = agent_query(request, reqlen, &vresponse, &resplen,
640 response = vresponse;
641 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
642 MessageBox(NULL, "The already running Pageant "
643 "refused to add the key.", APPNAME,
644 MB_OK | MB_ICONERROR);
649 if (add234(rsakeys, rkey) != rkey)
650 sfree(rkey); /* already present, don't waste RAM */
653 if (already_running) {
654 unsigned char *request, *response;
656 int reqlen, alglen, clen, keybloblen, resplen, ret;
657 alglen = strlen(skey->alg->name);
658 clen = strlen(skey->comment);
660 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
662 reqlen = 4 + 1 + /* length, message type */
663 4 + alglen + /* algorithm name */
664 keybloblen + /* key data */
665 4 + clen /* comment */
668 request = snewn(reqlen, unsigned char);
670 request[4] = SSH2_AGENTC_ADD_IDENTITY;
672 PUT_32BIT(request + reqlen, alglen);
674 memcpy(request + reqlen, skey->alg->name, alglen);
676 reqlen += skey->alg->openssh_fmtkey(skey->data,
679 PUT_32BIT(request + reqlen, clen);
680 memcpy(request + reqlen + 4, skey->comment, clen);
682 PUT_32BIT(request, reqlen - 4);
684 ret = agent_query(request, reqlen, &vresponse, &resplen,
687 response = vresponse;
688 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
689 MessageBox(NULL, "The already running Pageant "
690 "refused to add the key.", APPNAME,
691 MB_OK | MB_ICONERROR);
696 if (add234(ssh2keys, skey) != skey) {
697 skey->alg->freekey(skey->data);
698 sfree(skey); /* already present, don't waste RAM */
705 * Create an SSH-1 key list in a malloc'ed buffer; return its
708 static void *make_keylist1(int *length)
712 unsigned char *blob, *p, *ret;
716 * Count up the number and length of keys we hold.
720 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
722 blob = rsa_public_blob(key, &bloblen);
725 len += 4 + strlen(key->comment);
728 /* Allocate the buffer. */
729 p = ret = snewn(len, unsigned char);
730 if (length) *length = len;
734 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
735 blob = rsa_public_blob(key, &bloblen);
736 memcpy(p, blob, bloblen);
739 PUT_32BIT(p, strlen(key->comment));
740 memcpy(p + 4, key->comment, strlen(key->comment));
741 p += 4 + strlen(key->comment);
744 assert(p - ret == len);
749 * Create an SSH-2 key list in a malloc'ed buffer; return its
752 static void *make_keylist2(int *length)
754 struct ssh2_userkey *key;
756 unsigned char *blob, *p, *ret;
760 * Count up the number and length of keys we hold.
764 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
766 len += 4; /* length field */
767 blob = key->alg->public_blob(key->data, &bloblen);
770 len += 4 + strlen(key->comment);
773 /* Allocate the buffer. */
774 p = ret = snewn(len, unsigned char);
775 if (length) *length = len;
778 * Packet header is the obvious five bytes, plus four
779 * bytes for the key count.
783 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
784 blob = key->alg->public_blob(key->data, &bloblen);
785 PUT_32BIT(p, bloblen);
787 memcpy(p, blob, bloblen);
790 PUT_32BIT(p, strlen(key->comment));
791 memcpy(p + 4, key->comment, strlen(key->comment));
792 p += 4 + strlen(key->comment);
795 assert(p - ret == len);
800 * Acquire a keylist1 from the primary Pageant; this means either
801 * calling make_keylist1 (if that's us) or sending a message to the
802 * primary Pageant (if it's not).
804 static void *get_keylist1(int *length)
808 if (already_running) {
809 unsigned char request[5], *response;
812 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
813 PUT_32BIT(request, 4);
815 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
817 response = vresponse;
818 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
823 ret = snewn(resplen-5, unsigned char);
824 memcpy(ret, response+5, resplen-5);
830 ret = make_keylist1(length);
836 * Acquire a keylist2 from the primary Pageant; this means either
837 * calling make_keylist2 (if that's us) or sending a message to the
838 * primary Pageant (if it's not).
840 static void *get_keylist2(int *length)
844 if (already_running) {
845 unsigned char request[5], *response;
849 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
850 PUT_32BIT(request, 4);
852 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
854 response = vresponse;
855 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
860 ret = snewn(resplen-5, unsigned char);
861 memcpy(ret, response+5, resplen-5);
867 ret = make_keylist2(length);
873 * This is the main agent function that answers messages.
875 static void answer_msg(void *msg)
877 unsigned char *p = msg;
878 unsigned char *ret = msg;
879 unsigned char *msgend;
883 * Get the message length.
885 msgend = p + 4 + GET_32BIT(p);
888 * Get the message type.
896 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
898 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
904 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
905 keylist = make_keylist1(&len);
906 if (len + 5 > AGENT_MAX_MSGLEN) {
910 PUT_32BIT(ret, len + 1);
911 memcpy(ret + 5, keylist, len);
915 case SSH2_AGENTC_REQUEST_IDENTITIES:
917 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
923 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
924 keylist = make_keylist2(&len);
925 if (len + 5 > AGENT_MAX_MSGLEN) {
929 PUT_32BIT(ret, len + 1);
930 memcpy(ret + 5, keylist, len);
934 case SSH1_AGENTC_RSA_CHALLENGE:
936 * Reply with either SSH1_AGENT_RSA_RESPONSE or
937 * SSH_AGENT_FAILURE, depending on whether we have that key
941 struct RSAKey reqkey, *key;
942 Bignum challenge, response;
943 unsigned char response_source[48], response_md5[16];
944 struct MD5Context md5c;
948 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
952 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
954 freebn(reqkey.exponent);
958 i = ssh1_read_bignum(p, msgend - p, &challenge);
960 freebn(reqkey.exponent);
961 freebn(reqkey.modulus);
966 freebn(reqkey.exponent);
967 freebn(reqkey.modulus);
971 memcpy(response_source + 32, p, 16);
975 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
976 freebn(reqkey.exponent);
977 freebn(reqkey.modulus);
981 response = rsadecrypt(challenge, key);
982 for (i = 0; i < 32; i++)
983 response_source[i] = bignum_byte(response, 31 - i);
986 MD5Update(&md5c, response_source, 48);
987 MD5Final(response_md5, &md5c);
988 smemclr(response_source, 48); /* burn the evidence */
989 freebn(response); /* and that evidence */
990 freebn(challenge); /* yes, and that evidence */
991 freebn(reqkey.exponent); /* and free some memory ... */
992 freebn(reqkey.modulus); /* ... while we're at it. */
995 * Packet is the obvious five byte header, plus sixteen
999 PUT_32BIT(ret, len - 4);
1000 ret[4] = SSH1_AGENT_RSA_RESPONSE;
1001 memcpy(ret + 5, response_md5, 16);
1004 case SSH2_AGENTC_SIGN_REQUEST:
1006 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
1007 * SSH_AGENT_FAILURE, depending on whether we have that key
1011 struct ssh2_userkey *key;
1013 unsigned char *data, *signature;
1014 int datalen, siglen, len;
1018 b.len = toint(GET_32BIT(p));
1019 if (b.len < 0 || b.len > msgend - (p+4))
1026 datalen = toint(GET_32BIT(p));
1028 if (datalen < 0 || datalen > msgend - p)
1031 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1034 signature = key->alg->sign(key->data, data, datalen, &siglen);
1035 len = 5 + 4 + siglen;
1036 PUT_32BIT(ret, len - 4);
1037 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1038 PUT_32BIT(ret + 5, siglen);
1039 memcpy(ret + 5 + 4, signature, siglen);
1043 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1045 * Add to the list and return SSH_AGENT_SUCCESS, or
1046 * SSH_AGENT_FAILURE if the key was malformed.
1053 key = snew(struct RSAKey);
1054 memset(key, 0, sizeof(struct RSAKey));
1056 n = makekey(p, msgend - p, key, NULL, 1);
1064 n = makeprivate(p, msgend - p, key);
1072 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1080 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1088 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1101 commentlen = toint(GET_32BIT(p));
1103 if (commentlen < 0 || commentlen > msgend - p) {
1109 comment = snewn(commentlen+1, char);
1111 memcpy(comment, p + 4, commentlen);
1112 comment[commentlen] = '\0';
1113 key->comment = comment;
1116 ret[4] = SSH_AGENT_FAILURE;
1117 if (add234(rsakeys, key) == key) {
1119 ret[4] = SSH_AGENT_SUCCESS;
1126 case SSH2_AGENTC_ADD_IDENTITY:
1128 * Add to the list and return SSH_AGENT_SUCCESS, or
1129 * SSH_AGENT_FAILURE if the key was malformed.
1132 struct ssh2_userkey *key;
1133 char *comment, *alg;
1134 int alglen, commlen;
1140 alglen = toint(GET_32BIT(p));
1142 if (alglen < 0 || alglen > msgend - p)
1147 key = snew(struct ssh2_userkey);
1148 /* Add further algorithm names here. */
1149 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1150 key->alg = &ssh_rsa;
1151 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1152 key->alg = &ssh_dss;
1158 bloblen = msgend - p;
1159 key->data = key->alg->openssh_createkey(&p, &bloblen);
1166 * p has been advanced by openssh_createkey, but
1167 * certainly not _beyond_ the end of the buffer.
1169 assert(p <= msgend);
1172 key->alg->freekey(key->data);
1176 commlen = toint(GET_32BIT(p));
1179 if (commlen < 0 || commlen > msgend - p) {
1180 key->alg->freekey(key->data);
1184 comment = snewn(commlen + 1, char);
1186 memcpy(comment, p, commlen);
1187 comment[commlen] = '\0';
1189 key->comment = comment;
1192 ret[4] = SSH_AGENT_FAILURE;
1193 if (add234(ssh2keys, key) == key) {
1195 ret[4] = SSH_AGENT_SUCCESS;
1197 key->alg->freekey(key->data);
1198 sfree(key->comment);
1203 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1205 * Remove from the list and return SSH_AGENT_SUCCESS, or
1206 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1210 struct RSAKey reqkey, *key;
1213 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1217 key = find234(rsakeys, &reqkey, NULL);
1218 freebn(reqkey.exponent);
1219 freebn(reqkey.modulus);
1221 ret[4] = SSH_AGENT_FAILURE;
1223 del234(rsakeys, key);
1227 ret[4] = SSH_AGENT_SUCCESS;
1231 case SSH2_AGENTC_REMOVE_IDENTITY:
1233 * Remove from the list and return SSH_AGENT_SUCCESS, or
1234 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1238 struct ssh2_userkey *key;
1243 b.len = toint(GET_32BIT(p));
1246 if (b.len < 0 || b.len > msgend - p)
1251 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1256 ret[4] = SSH_AGENT_FAILURE;
1258 del234(ssh2keys, key);
1260 key->alg->freekey(key->data);
1262 ret[4] = SSH_AGENT_SUCCESS;
1266 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1268 * Remove all SSH-1 keys. Always returns success.
1271 struct RSAKey *rkey;
1273 while ((rkey = index234(rsakeys, 0)) != NULL) {
1274 del234(rsakeys, rkey);
1281 ret[4] = SSH_AGENT_SUCCESS;
1284 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1286 * Remove all SSH-2 keys. Always returns success.
1289 struct ssh2_userkey *skey;
1291 while ((skey = index234(ssh2keys, 0)) != NULL) {
1292 del234(ssh2keys, skey);
1293 skey->alg->freekey(skey->data);
1299 ret[4] = SSH_AGENT_SUCCESS;
1305 * Unrecognised message. Return SSH_AGENT_FAILURE.
1308 ret[4] = SSH_AGENT_FAILURE;
1314 * Key comparison function for the 2-3-4 tree of RSA keys.
1316 static int cmpkeys_rsa(void *av, void *bv)
1318 struct RSAKey *a = (struct RSAKey *) av;
1319 struct RSAKey *b = (struct RSAKey *) bv;
1326 * Compare by length of moduli.
1328 alen = bignum_bitcount(am);
1329 blen = bignum_bitcount(bm);
1332 else if (alen < blen)
1335 * Now compare by moduli themselves.
1337 alen = (alen + 7) / 8; /* byte count */
1338 while (alen-- > 0) {
1340 abyte = bignum_byte(am, alen);
1341 bbyte = bignum_byte(bm, alen);
1344 else if (abyte < bbyte)
1354 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1356 static int cmpkeys_ssh2(void *av, void *bv)
1358 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1359 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1362 unsigned char *ablob, *bblob;
1366 * Compare purely by public blob.
1368 ablob = a->alg->public_blob(a->data, &alen);
1369 bblob = b->alg->public_blob(b->data, &blen);
1372 for (i = 0; i < alen && i < blen; i++) {
1373 if (ablob[i] < bblob[i]) {
1376 } else if (ablob[i] > bblob[i]) {
1381 if (c == 0 && i < alen)
1382 c = +1; /* a is longer */
1383 if (c == 0 && i < blen)
1384 c = -1; /* a is longer */
1393 * Key comparison function for looking up a blob in the 2-3-4 tree
1396 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1398 struct blob *a = (struct blob *) av;
1399 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1402 unsigned char *ablob, *bblob;
1406 * Compare purely by public blob.
1410 bblob = b->alg->public_blob(b->data, &blen);
1413 for (i = 0; i < alen && i < blen; i++) {
1414 if (ablob[i] < bblob[i]) {
1417 } else if (ablob[i] > bblob[i]) {
1422 if (c == 0 && i < alen)
1423 c = +1; /* a is longer */
1424 if (c == 0 && i < blen)
1425 c = -1; /* a is longer */
1433 * Prompt for a key file to add, and add it.
1435 static void prompt_add_keyfile(void)
1438 char *filelist = snewn(8192, char);
1440 if (!keypath) keypath = filereq_new();
1441 memset(&of, 0, sizeof(of));
1442 of.hwndOwner = hwnd;
1443 of.lpstrFilter = FILTER_KEY_FILES;
1444 of.lpstrCustomFilter = NULL;
1445 of.nFilterIndex = 1;
1446 of.lpstrFile = filelist;
1449 of.lpstrFileTitle = NULL;
1450 of.lpstrTitle = "Select Private Key File";
1451 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1452 if (request_file(keypath, &of, TRUE, FALSE)) {
1453 if(strlen(filelist) > of.nFileOffset) {
1454 /* Only one filename returned? */
1455 Filename *fn = filename_from_str(filelist);
1459 /* we are returned a bunch of strings, end to
1460 * end. first string is the directory, the
1461 * rest the filenames. terminated with an
1464 char *dir = filelist;
1465 char *filewalker = filelist + strlen(dir) + 1;
1466 while (*filewalker != '\0') {
1467 char *filename = dupcat(dir, "\\", filewalker, NULL);
1468 Filename *fn = filename_from_str(filename);
1472 filewalker += strlen(filewalker) + 1;
1477 forget_passphrases();
1483 * Dialog-box function for the key list box.
1485 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1486 WPARAM wParam, LPARAM lParam)
1488 struct RSAKey *rkey;
1489 struct ssh2_userkey *skey;
1494 * Centre the window.
1496 { /* centre the window */
1500 hw = GetDesktopWindow();
1501 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1503 (rs.right + rs.left + rd.left - rd.right) / 2,
1504 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1505 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1509 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1510 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1513 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1515 DestroyWindow(item);
1520 static int tabs[] = { 35, 60, 210 };
1521 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1522 sizeof(tabs) / sizeof(*tabs),
1528 switch (LOWORD(wParam)) {
1532 DestroyWindow(hwnd);
1534 case 101: /* add key */
1535 if (HIWORD(wParam) == BN_CLICKED ||
1536 HIWORD(wParam) == BN_DOUBLECLICKED) {
1537 if (passphrase_box) {
1538 MessageBeep(MB_ICONERROR);
1539 SetForegroundWindow(passphrase_box);
1542 prompt_add_keyfile();
1545 case 102: /* remove key */
1546 if (HIWORD(wParam) == BN_CLICKED ||
1547 HIWORD(wParam) == BN_DOUBLECLICKED) {
1552 /* our counter within the array of selected items */
1555 /* get the number of items selected in the list */
1557 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1559 /* none selected? that was silly */
1560 if (numSelected == 0) {
1565 /* get item indices in an array */
1566 selectedArray = snewn(numSelected, int);
1567 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1568 numSelected, (WPARAM)selectedArray);
1570 itemNum = numSelected - 1;
1571 rCount = count234(rsakeys);
1572 sCount = count234(ssh2keys);
1574 /* go through the non-rsakeys until we've covered them all,
1575 * and/or we're out of selected items to check. note that
1576 * we go *backwards*, to avoid complications from deleting
1577 * things hence altering the offset of subsequent items
1579 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1580 skey = index234(ssh2keys, i);
1582 if (selectedArray[itemNum] == rCount + i) {
1583 del234(ssh2keys, skey);
1584 skey->alg->freekey(skey->data);
1590 /* do the same for the rsa keys */
1591 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1592 rkey = index234(rsakeys, i);
1594 if(selectedArray[itemNum] == i) {
1595 del234(rsakeys, rkey);
1602 sfree(selectedArray);
1606 case 103: /* help */
1607 if (HIWORD(wParam) == BN_CLICKED ||
1608 HIWORD(wParam) == BN_DOUBLECLICKED) {
1609 launch_help(hwnd, WINHELP_CTX_pageant_general);
1616 int id = ((LPHELPINFO)lParam)->iCtrlId;
1619 case 100: topic = WINHELP_CTX_pageant_keylist; break;
1620 case 101: topic = WINHELP_CTX_pageant_addkey; break;
1621 case 102: topic = WINHELP_CTX_pageant_remkey; break;
1624 launch_help(hwnd, topic);
1632 DestroyWindow(hwnd);
1638 /* Set up a system tray icon */
1639 static BOOL AddTrayIcon(HWND hwnd)
1642 NOTIFYICONDATA tnid;
1645 #ifdef NIM_SETVERSION
1647 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1650 tnid.cbSize = sizeof(NOTIFYICONDATA);
1652 tnid.uID = 1; /* unique within this systray use */
1653 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1654 tnid.uCallbackMessage = WM_SYSTRAY;
1655 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
1656 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1658 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1660 if (hicon) DestroyIcon(hicon);
1665 /* Update the saved-sessions menu. */
1666 static void update_sessions(void)
1670 TCHAR buf[MAX_PATH + 1];
1673 int index_key, index_menu;
1678 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1681 for(num_entries = GetMenuItemCount(session_menu);
1682 num_entries > initial_menuitems_count;
1684 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1689 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1690 TCHAR session_name[MAX_PATH + 1];
1691 unmungestr(buf, session_name, MAX_PATH);
1692 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1693 memset(&mii, 0, sizeof(mii));
1694 mii.cbSize = sizeof(mii);
1695 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1696 mii.fType = MFT_STRING;
1697 mii.fState = MFS_ENABLED;
1698 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1699 mii.dwTypeData = session_name;
1700 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1708 if(index_menu == 0) {
1709 mii.cbSize = sizeof(mii);
1710 mii.fMask = MIIM_TYPE | MIIM_STATE;
1711 mii.fType = MFT_STRING;
1712 mii.fState = MFS_GRAYED;
1713 mii.dwTypeData = _T("(No sessions)");
1714 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1720 * Versions of Pageant prior to 0.61 expected this SID on incoming
1721 * communications. For backwards compatibility, and more particularly
1722 * for compatibility with derived works of PuTTY still using the old
1723 * Pageant client code, we accept it as an alternative to the one
1724 * returned from get_user_sid() in winpgntc.c.
1726 PSID get_default_sid(void)
1730 PSECURITY_DESCRIPTOR psd = NULL;
1731 PSID sid = NULL, copy = NULL, ret = NULL;
1733 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1734 GetCurrentProcessId())) == NULL)
1737 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1738 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1741 sidlen = GetLengthSid(sid);
1743 copy = (PSID)smalloc(sidlen);
1745 if (!CopySid(sidlen, copy, sid))
1748 /* Success. Move sid into the return value slot, and null it out
1749 * to stop the cleanup code freeing it. */
1765 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1766 WPARAM wParam, LPARAM lParam)
1769 static int menuinprogress;
1770 static UINT msgTaskbarCreated = 0;
1774 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1777 if (message==msgTaskbarCreated) {
1779 * Explorer has been restarted, so the tray icon will
1787 if (lParam == WM_RBUTTONUP) {
1789 GetCursorPos(&cursorpos);
1790 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1791 } else if (lParam == WM_LBUTTONDBLCLK) {
1792 /* Run the default menu item. */
1793 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1795 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1799 if (!menuinprogress) {
1802 SetForegroundWindow(hwnd);
1803 ret = TrackPopupMenu(systray_menu,
1804 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1806 wParam, lParam, 0, hwnd, NULL);
1812 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1814 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1816 MessageBox(NULL, "Unable to execute PuTTY!",
1817 "Error", MB_OK | MB_ICONERROR);
1822 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1823 SendMessage(hwnd, WM_CLOSE, 0, 0);
1827 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1829 ShowWindow(keylist, SW_SHOWNORMAL);
1832 * Sometimes the window comes up minimised / hidden for
1833 * no obvious reason. Prevent this. This also brings it
1834 * to the front if it's already present (the user
1835 * selected View Keys because they wanted to _see_ the
1838 SetForegroundWindow(keylist);
1839 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1840 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1843 if (passphrase_box) {
1844 MessageBeep(MB_ICONERROR);
1845 SetForegroundWindow(passphrase_box);
1848 prompt_add_keyfile();
1852 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1854 ShowWindow(aboutbox, SW_SHOWNORMAL);
1856 * Sometimes the window comes up minimised / hidden
1857 * for no obvious reason. Prevent this.
1859 SetForegroundWindow(aboutbox);
1860 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1861 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1865 launch_help(hwnd, WINHELP_CTX_pageant_general);
1869 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1871 TCHAR buf[MAX_PATH + 1];
1872 TCHAR param[MAX_PATH + 1];
1873 memset(&mii, 0, sizeof(mii));
1874 mii.cbSize = sizeof(mii);
1875 mii.fMask = MIIM_TYPE;
1877 mii.dwTypeData = buf;
1878 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1880 strcat(param, mii.dwTypeData);
1881 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1882 _T(""), SW_SHOW) <= 32) {
1883 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1884 MB_OK | MB_ICONERROR);
1897 COPYDATASTRUCT *cds;
1902 PSID mapowner, ourself, ourself2;
1904 PSECURITY_DESCRIPTOR psd = NULL;
1907 cds = (COPYDATASTRUCT *) lParam;
1908 if (cds->dwData != AGENT_COPYDATA_ID)
1909 return 0; /* not our message, mate */
1910 mapname = (char *) cds->lpData;
1911 if (mapname[cds->cbData - 1] != '\0')
1912 return 0; /* failure to be ASCIZ! */
1914 debug(("mapname is :%s:\n", mapname));
1916 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1918 debug(("filemap is %p\n", filemap));
1920 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1924 if ((ourself = get_user_sid()) == NULL) {
1926 debug(("couldn't get user SID\n"));
1928 CloseHandle(filemap);
1932 if ((ourself2 = get_default_sid()) == NULL) {
1934 debug(("couldn't get default SID\n"));
1936 CloseHandle(filemap);
1941 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1942 OWNER_SECURITY_INFORMATION,
1943 &mapowner, NULL, NULL, NULL,
1944 &psd) != ERROR_SUCCESS)) {
1946 debug(("couldn't get owner info for filemap: %d\n",
1949 CloseHandle(filemap);
1956 LPTSTR ours, ours2, theirs;
1957 ConvertSidToStringSid(mapowner, &theirs);
1958 ConvertSidToStringSid(ourself, &ours);
1959 ConvertSidToStringSid(ourself2, &ours2);
1960 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1961 " theirs=%s\n", ours, ours2, theirs));
1967 if (!EqualSid(mapowner, ourself) &&
1968 !EqualSid(mapowner, ourself2)) {
1969 CloseHandle(filemap);
1973 return 0; /* security ID mismatch! */
1976 debug(("security stuff matched\n"));
1983 debug(("security APIs not present\n"));
1987 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1989 debug(("p is %p\n", p));
1992 for (i = 0; i < 5; i++)
1993 debug(("p[%d]=%02x\n", i,
1994 ((unsigned char *) p)[i]));
2001 CloseHandle(filemap);
2006 return DefWindowProc(hwnd, message, wParam, lParam);
2010 * Fork and Exec the command in cmdline. [DBW]
2012 void spawn_cmd(char *cmdline, char * args, int show)
2014 if (ShellExecute(NULL, _T("open"), cmdline,
2015 args, NULL, show) <= (HINSTANCE) 32) {
2017 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
2018 (int)GetLastError());
2019 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
2025 * This is a can't-happen stub, since Pageant never makes
2026 * asynchronous agent requests.
2028 void agent_schedule_callback(void (*callback)(void *, void *, int),
2029 void *callback_ctx, void *data, int len)
2031 assert(!"We shouldn't get here");
2034 void cleanup_exit(int code)
2040 int flags = FLAG_SYNCAGENT;
2042 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
2046 char *command = NULL;
2049 char **argv, **argstart;
2055 * Determine whether we're an NT system (should have security
2056 * APIs) or a non-NT system (don't do security).
2060 modalfatalbox("Windows refuses to report a version");
2062 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
2063 has_security = TRUE;
2065 has_security = FALSE;
2070 * Attempt to get the security API we need.
2072 if (!got_advapi()) {
2074 "Unable to access security APIs. Pageant will\n"
2075 "not run, in case it causes a security breach.",
2076 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2081 "This program has been compiled for Win9X and will\n"
2082 "not run on NT, in case it causes a security breach.",
2083 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2089 * See if we can find our Help file.
2094 * Look for the PuTTY binary (we will enable the saved session
2095 * submenu if we find it).
2098 char b[2048], *p, *q, *r;
2100 GetModuleFileName(NULL, b, sizeof(b) - 16);
2102 p = strrchr(b, '\\');
2103 if (p && p >= r) r = p+1;
2104 q = strrchr(b, ':');
2105 if (q && q >= r) r = q+1;
2106 strcpy(r, "putty.exe");
2107 if ( (fp = fopen(b, "r")) != NULL) {
2108 putty_path = dupstr(b);
2115 * Find out if Pageant is already running.
2117 already_running = agent_exists();
2120 * Initialise storage for RSA keys.
2122 if (!already_running) {
2123 rsakeys = newtree234(cmpkeys_rsa);
2124 ssh2keys = newtree234(cmpkeys_ssh2);
2128 * Initialise storage for short-term passphrase cache.
2130 passphrases = newtree234(NULL);
2133 * Process the command line and add keys as listed on it.
2135 split_into_argv(cmdline, &argc, &argv, &argstart);
2136 for (i = 0; i < argc; i++) {
2137 if (!strcmp(argv[i], "-pgpfp")) {
2140 } else if (!strcmp(argv[i], "-c")) {
2142 * If we see `-c', then the rest of the
2143 * command line should be treated as a
2144 * command to be spawned.
2147 command = argstart[i+1];
2152 Filename *fn = filename_from_str(argv[i]);
2160 * Forget any passphrase that we retained while going over
2161 * command line keyfiles.
2163 forget_passphrases();
2167 if (command[0] == '"')
2168 args = strchr(++command, '"');
2170 args = strchr(command, ' ');
2173 while(*args && isspace(*args)) args++;
2175 spawn_cmd(command, args, show);
2179 * If Pageant was already running, we leave now. If we haven't
2180 * even taken any auxiliary action (spawned a command or added
2183 if (already_running) {
2184 if (!command && !added_keys) {
2185 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2186 MB_ICONERROR | MB_OK);
2193 wndclass.lpfnWndProc = WndProc;
2194 wndclass.cbClsExtra = 0;
2195 wndclass.cbWndExtra = 0;
2196 wndclass.hInstance = inst;
2197 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2198 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2199 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2200 wndclass.lpszMenuName = NULL;
2201 wndclass.lpszClassName = APPNAME;
2203 RegisterClass(&wndclass);
2208 hwnd = CreateWindow(APPNAME, APPNAME,
2209 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2210 CW_USEDEFAULT, CW_USEDEFAULT,
2211 100, 100, NULL, NULL, inst, NULL);
2213 /* Set up a system tray icon */
2216 /* Accelerators used: nsvkxa */
2217 systray_menu = CreatePopupMenu();
2219 session_menu = CreateMenu();
2220 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2221 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2222 (UINT) session_menu, "&Saved Sessions");
2223 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2225 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2227 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2228 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2230 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2231 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2232 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2233 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2234 initial_menuitems_count = GetMenuItemCount(session_menu);
2236 /* Set the default menu item. */
2237 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2239 ShowWindow(hwnd, SW_HIDE);
2242 * Main message loop.
2244 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2245 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2246 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2247 TranslateMessage(&msg);
2248 DispatchMessage(&msg);
2252 /* Clean up the system tray icon */
2254 NOTIFYICONDATA tnid;
2256 tnid.cbSize = sizeof(NOTIFYICONDATA);
2260 Shell_NotifyIcon(NIM_DELETE, &tnid);
2262 DestroyMenu(systray_menu);
2265 if (keypath) filereq_free(keypath);
2267 cleanup_exit(msg.wParam);
2268 return msg.wParam; /* just in case optimiser complains */