2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
23 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
28 #define IDI_MAINICON 200
29 #define IDI_TRAYICON 201
31 #define WM_SYSTRAY (WM_APP + 6)
32 #define WM_SYSTRAY2 (WM_APP + 7)
34 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
37 * FIXME: maybe some day we can sort this out ...
39 #define AGENT_MAX_MSGLEN 8192
41 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
42 * wParam are used by Windows, and should be masked off, so we shouldn't
43 * attempt to store information in them. Hence all these identifiers have
44 * the low 4 bits clear. Also, identifiers should < 0xF000. */
46 #define IDM_CLOSE 0x0010
47 #define IDM_VIEWKEYS 0x0020
48 #define IDM_ADDKEY 0x0030
49 #define IDM_HELP 0x0040
50 #define IDM_ABOUT 0x0050
52 #define APPNAME "Pageant"
58 static HMENU systray_menu, session_menu;
59 static int already_running;
61 static char *putty_path;
63 /* CWD for "add key" file requester. */
64 static filereq *keypath = NULL;
66 #define IDM_PUTTY 0x0060
67 #define IDM_SESSIONS_BASE 0x1000
68 #define IDM_SESSIONS_MAX 0x2000
69 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
70 #define PUTTY_DEFAULT "Default%20Settings"
71 static int initial_menuitems_count;
74 * Print a modal (Really Bad) message box and perform a fatal exit.
76 void modalfatalbox(char *fmt, ...)
82 buf = dupvprintf(fmt, ap);
84 MessageBox(hwnd, buf, "Pageant Fatal Error",
85 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
90 /* Un-munge session names out of the registry. */
91 static void unmungestr(char *in, char *out, int outlen)
94 if (*in == '%' && in[1] && in[2]) {
100 j -= (j > 9 ? 7 : 0);
102 *out++ = (i << 4) + j;
116 static tree234 *rsakeys, *ssh2keys;
118 static int has_security;
120 DECL_WINDOWS_FUNCTION(extern, DWORD, GetSecurityInfo,
121 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
122 PSID *, PSID *, PACL *, PACL *,
123 PSECURITY_DESCRIPTOR *));
129 static void *make_keylist1(int *length);
130 static void *make_keylist2(int *length);
131 static void *get_keylist1(int *length);
132 static void *get_keylist2(int *length);
135 * We need this to link with the RSA code, because rsaencrypt()
136 * pads its data with random bytes. Since we only use rsadecrypt()
137 * and the signing functions, which are deterministic, this should
140 * If it _is_ called, there is a _serious_ problem, because it
141 * won't generate true random numbers. So we must scream, panic,
142 * and exit immediately if that should happen.
144 int random_byte(void)
146 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
148 /* this line can't be reached but it placates MSVC's warnings :-) */
153 * Blob structure for passing to the asymmetric SSH-2 key compare
154 * function, prototyped here.
160 static int cmpkeys_ssh2_asymm(void *av, void *bv);
162 struct PassphraseProcStruct {
167 static tree234 *passphrases = NULL;
170 * After processing a list of filenames, we want to forget the
173 static void forget_passphrases(void)
175 while (count234(passphrases) > 0) {
176 char *pp = index234(passphrases, 0);
177 smemclr(pp, strlen(pp));
178 delpos234(passphrases, 0);
184 * Dialog-box function for the Licence box.
186 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
187 WPARAM wParam, LPARAM lParam)
193 switch (LOWORD(wParam)) {
208 * Dialog-box function for the About box.
210 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
211 WPARAM wParam, LPARAM lParam)
215 SetDlgItemText(hwnd, 100, ver);
218 switch (LOWORD(wParam)) {
225 EnableWindow(hwnd, 0);
226 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
227 EnableWindow(hwnd, 1);
228 SetActiveWindow(hwnd);
240 static HWND passphrase_box;
243 * Dialog-box function for the passphrase box.
245 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
246 WPARAM wParam, LPARAM lParam)
248 static char **passphrase = NULL;
249 struct PassphraseProcStruct *p;
253 passphrase_box = hwnd;
257 { /* centre the window */
261 hw = GetDesktopWindow();
262 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
264 (rs.right + rs.left + rd.left - rd.right) / 2,
265 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
266 rd.right - rd.left, rd.bottom - rd.top, TRUE);
269 SetForegroundWindow(hwnd);
270 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
271 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
272 p = (struct PassphraseProcStruct *) lParam;
273 passphrase = p->passphrase;
275 SetDlgItemText(hwnd, 101, p->comment);
276 burnstr(*passphrase);
277 *passphrase = dupstr("");
278 SetDlgItemText(hwnd, 102, *passphrase);
281 switch (LOWORD(wParam)) {
291 case 102: /* edit box */
292 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
293 burnstr(*passphrase);
294 *passphrase = GetDlgItemText_alloc(hwnd, 102);
307 * Warn about the obsolescent key file format.
309 void old_keyfile_warning(void)
311 static const char mbtitle[] = "PuTTY Key File Warning";
312 static const char message[] =
313 "You are loading an SSH-2 private key which has an\n"
314 "old version of the file format. This means your key\n"
315 "file is not fully tamperproof. Future versions of\n"
316 "PuTTY may stop supporting this private key format,\n"
317 "so we recommend you convert your key to the new\n"
320 "You can perform this conversion by loading the key\n"
321 "into PuTTYgen and then saving it again.";
323 MessageBox(NULL, message, mbtitle, MB_OK);
327 * Update the visible key list.
329 static void keylist_update(void)
332 struct ssh2_userkey *skey;
336 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
337 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
338 char listentry[512], *p;
340 * Replace two spaces in the fingerprint with tabs, for
341 * nice alignment in the box.
343 strcpy(listentry, "ssh1\t");
344 p = listentry + strlen(listentry);
345 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
346 p = strchr(listentry, ' ');
349 p = strchr(listentry, ' ');
352 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
353 0, (LPARAM) listentry);
355 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
356 char listentry[512], *p;
359 * Replace two spaces in the fingerprint with tabs, for
360 * nice alignment in the box.
362 p = skey->alg->fingerprint(skey->data);
363 strncpy(listentry, p, sizeof(listentry));
364 p = strchr(listentry, ' ');
367 p = strchr(listentry, ' ');
370 len = strlen(listentry);
371 if (len < sizeof(listentry) - 2) {
372 listentry[len] = '\t';
373 strncpy(listentry + len + 1, skey->comment,
374 sizeof(listentry) - len - 1);
376 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
379 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
384 * This function loads a key from a file and adds it.
386 static void add_keyfile(Filename *filename)
389 struct RSAKey *rkey = NULL;
390 struct ssh2_userkey *skey = NULL;
395 const char *error = NULL;
399 type = key_type(filename);
400 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
401 char *msg = dupprintf("Couldn't load this key (%s)",
402 key_type_to_str(type));
403 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
404 HELPCTXID(errors_cantloadkey));
410 * See if the key is already loaded (in the primary Pageant,
411 * which may or may not be us).
415 unsigned char *keylist, *p;
416 int i, nkeys, bloblen, keylistlen;
418 if (type == SSH_KEYTYPE_SSH1) {
419 if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
420 char *msg = dupprintf("Couldn't load private key (%s)", error);
421 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
422 HELPCTXID(errors_cantloadkey));
426 keylist = get_keylist1(&keylistlen);
428 unsigned char *blob2;
429 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
432 char *msg = dupprintf("Couldn't load private key (%s)", error);
433 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
434 HELPCTXID(errors_cantloadkey));
438 /* For our purposes we want the blob prefixed with its length */
439 blob2 = snewn(bloblen+4, unsigned char);
440 PUT_32BIT(blob2, bloblen);
441 memcpy(blob2 + 4, blob, bloblen);
445 keylist = get_keylist2(&keylistlen);
448 if (keylistlen < 4) {
449 MessageBox(NULL, "Received broken key list?!", APPNAME,
450 MB_OK | MB_ICONERROR);
453 nkeys = toint(GET_32BIT(keylist));
455 MessageBox(NULL, "Received broken key list?!", APPNAME,
456 MB_OK | MB_ICONERROR);
462 for (i = 0; i < nkeys; i++) {
463 if (!memcmp(blob, p, bloblen)) {
464 /* Key is already present; we can now leave. */
469 /* Now skip over public blob */
470 if (type == SSH_KEYTYPE_SSH1) {
471 int n = rsa_public_blob_len(p, keylistlen);
473 MessageBox(NULL, "Received broken key list?!", APPNAME,
474 MB_OK | MB_ICONERROR);
481 if (keylistlen < 4) {
482 MessageBox(NULL, "Received broken key list?!", APPNAME,
483 MB_OK | MB_ICONERROR);
486 n = toint(4 + GET_32BIT(p));
487 if (n < 0 || keylistlen < n) {
488 MessageBox(NULL, "Received broken key list?!", APPNAME,
489 MB_OK | MB_ICONERROR);
495 /* Now skip over comment field */
498 if (keylistlen < 4) {
499 MessageBox(NULL, "Received broken key list?!", APPNAME,
500 MB_OK | MB_ICONERROR);
503 n = toint(4 + GET_32BIT(p));
504 if (n < 0 || keylistlen < n) {
505 MessageBox(NULL, "Received broken key list?!", APPNAME,
506 MB_OK | MB_ICONERROR);
521 if (type == SSH_KEYTYPE_SSH1)
522 needs_pass = rsakey_encrypted(filename, &comment);
524 needs_pass = ssh2_userkey_encrypted(filename, &comment);
526 if (type == SSH_KEYTYPE_SSH1)
527 rkey = snew(struct RSAKey);
535 /* try all the remembered passphrases first */
536 char *pp = index234(passphrases, attempts);
538 passphrase = dupstr(pp);
541 struct PassphraseProcStruct pps;
543 pps.passphrase = &passphrase;
544 pps.comment = comment;
547 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
548 NULL, PassphraseProc, (LPARAM) &pps);
549 passphrase_box = NULL;
553 if (type == SSH_KEYTYPE_SSH1)
555 return; /* operation cancelled */
558 assert(passphrase != NULL);
561 passphrase = dupstr("");
563 if (type == SSH_KEYTYPE_SSH1)
564 ret = loadrsakey(filename, rkey, passphrase, &error);
566 skey = ssh2_load_userkey(filename, passphrase, &error);
567 if (skey == SSH2_WRONG_PASSPHRASE)
577 if(original_pass && ret) {
578 /* If they typed in an ok passphrase, remember it */
579 addpos234(passphrases, passphrase, 0);
581 /* Otherwise, destroy it */
589 char *msg = dupprintf("Couldn't load private key (%s)", error);
590 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
591 HELPCTXID(errors_cantloadkey));
593 if (type == SSH_KEYTYPE_SSH1)
597 if (type == SSH_KEYTYPE_SSH1) {
598 if (already_running) {
599 unsigned char *request, *response;
601 int reqlen, clen, resplen, ret;
603 clen = strlen(rkey->comment);
605 reqlen = 4 + 1 + /* length, message type */
607 ssh1_bignum_length(rkey->modulus) +
608 ssh1_bignum_length(rkey->exponent) +
609 ssh1_bignum_length(rkey->private_exponent) +
610 ssh1_bignum_length(rkey->iqmp) +
611 ssh1_bignum_length(rkey->p) +
612 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
615 request = snewn(reqlen, unsigned char);
617 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
619 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
621 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
622 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
624 ssh1_write_bignum(request + reqlen,
625 rkey->private_exponent);
626 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
627 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
628 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
629 PUT_32BIT(request + reqlen, clen);
630 memcpy(request + reqlen + 4, rkey->comment, clen);
632 PUT_32BIT(request, reqlen - 4);
634 ret = agent_query(request, reqlen, &vresponse, &resplen,
637 response = vresponse;
638 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
639 MessageBox(NULL, "The already running Pageant "
640 "refused to add the key.", APPNAME,
641 MB_OK | MB_ICONERROR);
646 if (add234(rsakeys, rkey) != rkey)
647 sfree(rkey); /* already present, don't waste RAM */
650 if (already_running) {
651 unsigned char *request, *response;
653 int reqlen, alglen, clen, keybloblen, resplen, ret;
654 alglen = strlen(skey->alg->name);
655 clen = strlen(skey->comment);
657 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
659 reqlen = 4 + 1 + /* length, message type */
660 4 + alglen + /* algorithm name */
661 keybloblen + /* key data */
662 4 + clen /* comment */
665 request = snewn(reqlen, unsigned char);
667 request[4] = SSH2_AGENTC_ADD_IDENTITY;
669 PUT_32BIT(request + reqlen, alglen);
671 memcpy(request + reqlen, skey->alg->name, alglen);
673 reqlen += skey->alg->openssh_fmtkey(skey->data,
676 PUT_32BIT(request + reqlen, clen);
677 memcpy(request + reqlen + 4, skey->comment, clen);
679 PUT_32BIT(request, reqlen - 4);
681 ret = agent_query(request, reqlen, &vresponse, &resplen,
684 response = vresponse;
685 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
686 MessageBox(NULL, "The already running Pageant "
687 "refused to add the key.", APPNAME,
688 MB_OK | MB_ICONERROR);
693 if (add234(ssh2keys, skey) != skey) {
694 skey->alg->freekey(skey->data);
695 sfree(skey); /* already present, don't waste RAM */
702 * Create an SSH-1 key list in a malloc'ed buffer; return its
705 static void *make_keylist1(int *length)
709 unsigned char *blob, *p, *ret;
713 * Count up the number and length of keys we hold.
717 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
719 blob = rsa_public_blob(key, &bloblen);
722 len += 4 + strlen(key->comment);
725 /* Allocate the buffer. */
726 p = ret = snewn(len, unsigned char);
727 if (length) *length = len;
731 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
732 blob = rsa_public_blob(key, &bloblen);
733 memcpy(p, blob, bloblen);
736 PUT_32BIT(p, strlen(key->comment));
737 memcpy(p + 4, key->comment, strlen(key->comment));
738 p += 4 + strlen(key->comment);
741 assert(p - ret == len);
746 * Create an SSH-2 key list in a malloc'ed buffer; return its
749 static void *make_keylist2(int *length)
751 struct ssh2_userkey *key;
753 unsigned char *blob, *p, *ret;
757 * Count up the number and length of keys we hold.
761 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
763 len += 4; /* length field */
764 blob = key->alg->public_blob(key->data, &bloblen);
767 len += 4 + strlen(key->comment);
770 /* Allocate the buffer. */
771 p = ret = snewn(len, unsigned char);
772 if (length) *length = len;
775 * Packet header is the obvious five bytes, plus four
776 * bytes for the key count.
780 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
781 blob = key->alg->public_blob(key->data, &bloblen);
782 PUT_32BIT(p, bloblen);
784 memcpy(p, blob, bloblen);
787 PUT_32BIT(p, strlen(key->comment));
788 memcpy(p + 4, key->comment, strlen(key->comment));
789 p += 4 + strlen(key->comment);
792 assert(p - ret == len);
797 * Acquire a keylist1 from the primary Pageant; this means either
798 * calling make_keylist1 (if that's us) or sending a message to the
799 * primary Pageant (if it's not).
801 static void *get_keylist1(int *length)
805 if (already_running) {
806 unsigned char request[5], *response;
809 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
810 PUT_32BIT(request, 4);
812 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
814 response = vresponse;
815 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)
853 ret = snewn(resplen-5, unsigned char);
854 memcpy(ret, response+5, resplen-5);
860 ret = make_keylist2(length);
866 * This is the main agent function that answers messages.
868 static void answer_msg(void *msg)
870 unsigned char *p = msg;
871 unsigned char *ret = msg;
872 unsigned char *msgend;
876 * Get the message length.
878 msgend = p + 4 + GET_32BIT(p);
881 * Get the message type.
889 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
891 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
897 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
898 keylist = make_keylist1(&len);
899 if (len + 5 > AGENT_MAX_MSGLEN) {
903 PUT_32BIT(ret, len + 1);
904 memcpy(ret + 5, keylist, len);
908 case SSH2_AGENTC_REQUEST_IDENTITIES:
910 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
916 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
917 keylist = make_keylist2(&len);
918 if (len + 5 > AGENT_MAX_MSGLEN) {
922 PUT_32BIT(ret, len + 1);
923 memcpy(ret + 5, keylist, len);
927 case SSH1_AGENTC_RSA_CHALLENGE:
929 * Reply with either SSH1_AGENT_RSA_RESPONSE or
930 * SSH_AGENT_FAILURE, depending on whether we have that key
934 struct RSAKey reqkey, *key;
935 Bignum challenge, response;
936 unsigned char response_source[48], response_md5[16];
937 struct MD5Context md5c;
941 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
945 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
949 i = ssh1_read_bignum(p, msgend - p, &challenge);
954 freebn(reqkey.exponent);
955 freebn(reqkey.modulus);
959 memcpy(response_source + 32, p, 16);
963 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
964 freebn(reqkey.exponent);
965 freebn(reqkey.modulus);
969 response = rsadecrypt(challenge, key);
970 for (i = 0; i < 32; i++)
971 response_source[i] = bignum_byte(response, 31 - i);
974 MD5Update(&md5c, response_source, 48);
975 MD5Final(response_md5, &md5c);
976 smemclr(response_source, 48); /* burn the evidence */
977 freebn(response); /* and that evidence */
978 freebn(challenge); /* yes, and that evidence */
979 freebn(reqkey.exponent); /* and free some memory ... */
980 freebn(reqkey.modulus); /* ... while we're at it. */
983 * Packet is the obvious five byte header, plus sixteen
987 PUT_32BIT(ret, len - 4);
988 ret[4] = SSH1_AGENT_RSA_RESPONSE;
989 memcpy(ret + 5, response_md5, 16);
992 case SSH2_AGENTC_SIGN_REQUEST:
994 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
995 * SSH_AGENT_FAILURE, depending on whether we have that key
999 struct ssh2_userkey *key;
1001 unsigned char *data, *signature;
1002 int datalen, siglen, len;
1006 b.len = toint(GET_32BIT(p));
1007 if (b.len < 0 || b.len > msgend - (p+4))
1014 datalen = toint(GET_32BIT(p));
1016 if (datalen < 0 || datalen > msgend - p)
1019 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1022 signature = key->alg->sign(key->data, data, datalen, &siglen);
1023 len = 5 + 4 + siglen;
1024 PUT_32BIT(ret, len - 4);
1025 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1026 PUT_32BIT(ret + 5, siglen);
1027 memcpy(ret + 5 + 4, signature, siglen);
1031 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1033 * Add to the list and return SSH_AGENT_SUCCESS, or
1034 * SSH_AGENT_FAILURE if the key was malformed.
1041 key = snew(struct RSAKey);
1042 memset(key, 0, sizeof(struct RSAKey));
1044 n = makekey(p, msgend - p, key, NULL, 1);
1052 n = makeprivate(p, msgend - p, key);
1060 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1068 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1076 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1089 commentlen = toint(GET_32BIT(p));
1091 if (commentlen < 0 || commentlen > msgend - p) {
1097 comment = snewn(commentlen+1, char);
1099 memcpy(comment, p + 4, commentlen);
1100 comment[commentlen] = '\0';
1101 key->comment = comment;
1104 ret[4] = SSH_AGENT_FAILURE;
1105 if (add234(rsakeys, key) == key) {
1107 ret[4] = SSH_AGENT_SUCCESS;
1114 case SSH2_AGENTC_ADD_IDENTITY:
1116 * Add to the list and return SSH_AGENT_SUCCESS, or
1117 * SSH_AGENT_FAILURE if the key was malformed.
1120 struct ssh2_userkey *key;
1121 char *comment, *alg;
1122 int alglen, commlen;
1128 alglen = toint(GET_32BIT(p));
1130 if (alglen < 0 || alglen > msgend - p)
1135 key = snew(struct ssh2_userkey);
1136 /* Add further algorithm names here. */
1137 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1138 key->alg = &ssh_rsa;
1139 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1140 key->alg = &ssh_dss;
1146 bloblen = msgend - p;
1147 key->data = key->alg->openssh_createkey(&p, &bloblen);
1154 * p has been advanced by openssh_createkey, but
1155 * certainly not _beyond_ the end of the buffer.
1157 assert(p <= msgend);
1160 key->alg->freekey(key->data);
1164 commlen = toint(GET_32BIT(p));
1167 if (commlen < 0 || commlen > msgend - p) {
1168 key->alg->freekey(key->data);
1172 comment = snewn(commlen + 1, char);
1174 memcpy(comment, p, commlen);
1175 comment[commlen] = '\0';
1177 key->comment = comment;
1180 ret[4] = SSH_AGENT_FAILURE;
1181 if (add234(ssh2keys, key) == key) {
1183 ret[4] = SSH_AGENT_SUCCESS;
1185 key->alg->freekey(key->data);
1186 sfree(key->comment);
1191 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1193 * Remove from the list and return SSH_AGENT_SUCCESS, or
1194 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1198 struct RSAKey reqkey, *key;
1201 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1205 key = find234(rsakeys, &reqkey, NULL);
1206 freebn(reqkey.exponent);
1207 freebn(reqkey.modulus);
1209 ret[4] = SSH_AGENT_FAILURE;
1211 del234(rsakeys, key);
1215 ret[4] = SSH_AGENT_SUCCESS;
1219 case SSH2_AGENTC_REMOVE_IDENTITY:
1221 * Remove from the list and return SSH_AGENT_SUCCESS, or
1222 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1226 struct ssh2_userkey *key;
1231 b.len = toint(GET_32BIT(p));
1234 if (b.len < 0 || b.len > msgend - p)
1239 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1244 ret[4] = SSH_AGENT_FAILURE;
1246 del234(ssh2keys, key);
1248 key->alg->freekey(key->data);
1250 ret[4] = SSH_AGENT_SUCCESS;
1254 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1256 * Remove all SSH-1 keys. Always returns success.
1259 struct RSAKey *rkey;
1261 while ((rkey = index234(rsakeys, 0)) != NULL) {
1262 del234(rsakeys, rkey);
1269 ret[4] = SSH_AGENT_SUCCESS;
1272 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1274 * Remove all SSH-2 keys. Always returns success.
1277 struct ssh2_userkey *skey;
1279 while ((skey = index234(ssh2keys, 0)) != NULL) {
1280 del234(ssh2keys, skey);
1281 skey->alg->freekey(skey->data);
1287 ret[4] = SSH_AGENT_SUCCESS;
1293 * Unrecognised message. Return SSH_AGENT_FAILURE.
1296 ret[4] = SSH_AGENT_FAILURE;
1302 * Key comparison function for the 2-3-4 tree of RSA keys.
1304 static int cmpkeys_rsa(void *av, void *bv)
1306 struct RSAKey *a = (struct RSAKey *) av;
1307 struct RSAKey *b = (struct RSAKey *) bv;
1314 * Compare by length of moduli.
1316 alen = bignum_bitcount(am);
1317 blen = bignum_bitcount(bm);
1320 else if (alen < blen)
1323 * Now compare by moduli themselves.
1325 alen = (alen + 7) / 8; /* byte count */
1326 while (alen-- > 0) {
1328 abyte = bignum_byte(am, alen);
1329 bbyte = bignum_byte(bm, alen);
1332 else if (abyte < bbyte)
1342 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1344 static int cmpkeys_ssh2(void *av, void *bv)
1346 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1347 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1350 unsigned char *ablob, *bblob;
1354 * Compare purely by public blob.
1356 ablob = a->alg->public_blob(a->data, &alen);
1357 bblob = b->alg->public_blob(b->data, &blen);
1360 for (i = 0; i < alen && i < blen; i++) {
1361 if (ablob[i] < bblob[i]) {
1364 } else if (ablob[i] > bblob[i]) {
1369 if (c == 0 && i < alen)
1370 c = +1; /* a is longer */
1371 if (c == 0 && i < blen)
1372 c = -1; /* a is longer */
1381 * Key comparison function for looking up a blob in the 2-3-4 tree
1384 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1386 struct blob *a = (struct blob *) av;
1387 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1390 unsigned char *ablob, *bblob;
1394 * Compare purely by public blob.
1398 bblob = b->alg->public_blob(b->data, &blen);
1401 for (i = 0; i < alen && i < blen; i++) {
1402 if (ablob[i] < bblob[i]) {
1405 } else if (ablob[i] > bblob[i]) {
1410 if (c == 0 && i < alen)
1411 c = +1; /* a is longer */
1412 if (c == 0 && i < blen)
1413 c = -1; /* a is longer */
1421 * Prompt for a key file to add, and add it.
1423 static void prompt_add_keyfile(void)
1426 char *filelist = snewn(8192, char);
1428 if (!keypath) keypath = filereq_new();
1429 memset(&of, 0, sizeof(of));
1430 of.hwndOwner = hwnd;
1431 of.lpstrFilter = FILTER_KEY_FILES;
1432 of.lpstrCustomFilter = NULL;
1433 of.nFilterIndex = 1;
1434 of.lpstrFile = filelist;
1437 of.lpstrFileTitle = NULL;
1438 of.lpstrTitle = "Select Private Key File";
1439 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1440 if (request_file(keypath, &of, TRUE, FALSE)) {
1441 if(strlen(filelist) > of.nFileOffset)
1442 /* Only one filename returned? */
1443 add_keyfile(filename_from_str(filelist));
1445 /* we are returned a bunch of strings, end to
1446 * end. first string is the directory, the
1447 * rest the filenames. terminated with an
1450 char *dir = filelist;
1451 char *filewalker = filelist + strlen(dir) + 1;
1452 while (*filewalker != '\0') {
1453 char *filename = dupcat(dir, "\\", filewalker, NULL);
1454 add_keyfile(filename_from_str(filename));
1456 filewalker += strlen(filewalker) + 1;
1461 forget_passphrases();
1467 * Dialog-box function for the key list box.
1469 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1470 WPARAM wParam, LPARAM lParam)
1472 struct RSAKey *rkey;
1473 struct ssh2_userkey *skey;
1478 * Centre the window.
1480 { /* centre the window */
1484 hw = GetDesktopWindow();
1485 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1487 (rs.right + rs.left + rd.left - rd.right) / 2,
1488 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1489 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1493 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1494 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1497 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1499 DestroyWindow(item);
1504 static int tabs[] = { 35, 60, 210 };
1505 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1506 sizeof(tabs) / sizeof(*tabs),
1512 switch (LOWORD(wParam)) {
1516 DestroyWindow(hwnd);
1518 case 101: /* add key */
1519 if (HIWORD(wParam) == BN_CLICKED ||
1520 HIWORD(wParam) == BN_DOUBLECLICKED) {
1521 if (passphrase_box) {
1522 MessageBeep(MB_ICONERROR);
1523 SetForegroundWindow(passphrase_box);
1526 prompt_add_keyfile();
1529 case 102: /* remove key */
1530 if (HIWORD(wParam) == BN_CLICKED ||
1531 HIWORD(wParam) == BN_DOUBLECLICKED) {
1536 /* our counter within the array of selected items */
1539 /* get the number of items selected in the list */
1541 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1543 /* none selected? that was silly */
1544 if (numSelected == 0) {
1549 /* get item indices in an array */
1550 selectedArray = snewn(numSelected, int);
1551 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1552 numSelected, (WPARAM)selectedArray);
1554 itemNum = numSelected - 1;
1555 rCount = count234(rsakeys);
1556 sCount = count234(ssh2keys);
1558 /* go through the non-rsakeys until we've covered them all,
1559 * and/or we're out of selected items to check. note that
1560 * we go *backwards*, to avoid complications from deleting
1561 * things hence altering the offset of subsequent items
1563 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1564 skey = index234(ssh2keys, i);
1566 if (selectedArray[itemNum] == rCount + i) {
1567 del234(ssh2keys, skey);
1568 skey->alg->freekey(skey->data);
1574 /* do the same for the rsa keys */
1575 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1576 rkey = index234(rsakeys, i);
1578 if(selectedArray[itemNum] == i) {
1579 del234(rsakeys, rkey);
1586 sfree(selectedArray);
1590 case 103: /* help */
1591 if (HIWORD(wParam) == BN_CLICKED ||
1592 HIWORD(wParam) == BN_DOUBLECLICKED) {
1593 launch_help(hwnd, WINHELP_CTX_pageant_general);
1600 int id = ((LPHELPINFO)lParam)->iCtrlId;
1603 case 100: topic = WINHELP_CTX_pageant_keylist; break;
1604 case 101: topic = WINHELP_CTX_pageant_addkey; break;
1605 case 102: topic = WINHELP_CTX_pageant_remkey; break;
1608 launch_help(hwnd, topic);
1616 DestroyWindow(hwnd);
1622 /* Set up a system tray icon */
1623 static BOOL AddTrayIcon(HWND hwnd)
1626 NOTIFYICONDATA tnid;
1629 #ifdef NIM_SETVERSION
1631 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1634 tnid.cbSize = sizeof(NOTIFYICONDATA);
1636 tnid.uID = 1; /* unique within this systray use */
1637 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1638 tnid.uCallbackMessage = WM_SYSTRAY;
1639 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
1640 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1642 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1644 if (hicon) DestroyIcon(hicon);
1649 /* Update the saved-sessions menu. */
1650 static void update_sessions(void)
1654 TCHAR buf[MAX_PATH + 1];
1657 int index_key, index_menu;
1662 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1665 for(num_entries = GetMenuItemCount(session_menu);
1666 num_entries > initial_menuitems_count;
1668 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1673 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1674 TCHAR session_name[MAX_PATH + 1];
1675 unmungestr(buf, session_name, MAX_PATH);
1676 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1677 memset(&mii, 0, sizeof(mii));
1678 mii.cbSize = sizeof(mii);
1679 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1680 mii.fType = MFT_STRING;
1681 mii.fState = MFS_ENABLED;
1682 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1683 mii.dwTypeData = session_name;
1684 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1692 if(index_menu == 0) {
1693 mii.cbSize = sizeof(mii);
1694 mii.fMask = MIIM_TYPE | MIIM_STATE;
1695 mii.fType = MFT_STRING;
1696 mii.fState = MFS_GRAYED;
1697 mii.dwTypeData = _T("(No sessions)");
1698 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1704 * Versions of Pageant prior to 0.61 expected this SID on incoming
1705 * communications. For backwards compatibility, and more particularly
1706 * for compatibility with derived works of PuTTY still using the old
1707 * Pageant client code, we accept it as an alternative to the one
1708 * returned from get_user_sid() in winpgntc.c.
1710 PSID get_default_sid(void)
1714 PSECURITY_DESCRIPTOR psd = NULL;
1715 PSID sid = NULL, copy = NULL, ret = NULL;
1717 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1718 GetCurrentProcessId())) == NULL)
1721 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1722 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1725 sidlen = GetLengthSid(sid);
1727 copy = (PSID)smalloc(sidlen);
1729 if (!CopySid(sidlen, copy, sid))
1732 /* Success. Move sid into the return value slot, and null it out
1733 * to stop the cleanup code freeing it. */
1749 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1750 WPARAM wParam, LPARAM lParam)
1753 static int menuinprogress;
1754 static UINT msgTaskbarCreated = 0;
1758 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1761 if (message==msgTaskbarCreated) {
1763 * Explorer has been restarted, so the tray icon will
1771 if (lParam == WM_RBUTTONUP) {
1773 GetCursorPos(&cursorpos);
1774 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1775 } else if (lParam == WM_LBUTTONDBLCLK) {
1776 /* Run the default menu item. */
1777 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1779 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1783 if (!menuinprogress) {
1786 SetForegroundWindow(hwnd);
1787 ret = TrackPopupMenu(systray_menu,
1788 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1790 wParam, lParam, 0, hwnd, NULL);
1796 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1798 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1800 MessageBox(NULL, "Unable to execute PuTTY!",
1801 "Error", MB_OK | MB_ICONERROR);
1806 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1807 SendMessage(hwnd, WM_CLOSE, 0, 0);
1811 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1813 ShowWindow(keylist, SW_SHOWNORMAL);
1816 * Sometimes the window comes up minimised / hidden for
1817 * no obvious reason. Prevent this. This also brings it
1818 * to the front if it's already present (the user
1819 * selected View Keys because they wanted to _see_ the
1822 SetForegroundWindow(keylist);
1823 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1824 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1827 if (passphrase_box) {
1828 MessageBeep(MB_ICONERROR);
1829 SetForegroundWindow(passphrase_box);
1832 prompt_add_keyfile();
1836 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1838 ShowWindow(aboutbox, SW_SHOWNORMAL);
1840 * Sometimes the window comes up minimised / hidden
1841 * for no obvious reason. Prevent this.
1843 SetForegroundWindow(aboutbox);
1844 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1845 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1849 launch_help(hwnd, WINHELP_CTX_pageant_general);
1853 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1855 TCHAR buf[MAX_PATH + 1];
1856 TCHAR param[MAX_PATH + 1];
1857 memset(&mii, 0, sizeof(mii));
1858 mii.cbSize = sizeof(mii);
1859 mii.fMask = MIIM_TYPE;
1861 mii.dwTypeData = buf;
1862 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1864 strcat(param, mii.dwTypeData);
1865 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1866 _T(""), SW_SHOW) <= 32) {
1867 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1868 MB_OK | MB_ICONERROR);
1881 COPYDATASTRUCT *cds;
1886 PSID mapowner, ourself, ourself2;
1888 PSECURITY_DESCRIPTOR psd = NULL;
1891 cds = (COPYDATASTRUCT *) lParam;
1892 if (cds->dwData != AGENT_COPYDATA_ID)
1893 return 0; /* not our message, mate */
1894 mapname = (char *) cds->lpData;
1895 if (mapname[cds->cbData - 1] != '\0')
1896 return 0; /* failure to be ASCIZ! */
1898 debug(("mapname is :%s:\n", mapname));
1900 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1902 debug(("filemap is %p\n", filemap));
1904 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1908 if ((ourself = get_user_sid()) == NULL) {
1910 debug(("couldn't get user SID\n"));
1915 if ((ourself2 = get_default_sid()) == NULL) {
1917 debug(("couldn't get default SID\n"));
1922 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1923 OWNER_SECURITY_INFORMATION,
1924 &mapowner, NULL, NULL, NULL,
1925 &psd) != ERROR_SUCCESS)) {
1927 debug(("couldn't get owner info for filemap: %d\n",
1934 LPTSTR ours, ours2, theirs;
1935 ConvertSidToStringSid(mapowner, &theirs);
1936 ConvertSidToStringSid(ourself, &ours);
1937 ConvertSidToStringSid(ourself2, &ours2);
1938 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1939 " theirs=%s\n", ours, ours2, theirs));
1945 if (!EqualSid(mapowner, ourself) &&
1946 !EqualSid(mapowner, ourself2)) {
1947 CloseHandle(filemap);
1948 return 0; /* security ID mismatch! */
1951 debug(("security stuff matched\n"));
1958 debug(("security APIs not present\n"));
1962 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1964 debug(("p is %p\n", p));
1967 for (i = 0; i < 5; i++)
1968 debug(("p[%d]=%02x\n", i,
1969 ((unsigned char *) p)[i]));
1976 CloseHandle(filemap);
1981 return DefWindowProc(hwnd, message, wParam, lParam);
1985 * Fork and Exec the command in cmdline. [DBW]
1987 void spawn_cmd(char *cmdline, char * args, int show)
1989 if (ShellExecute(NULL, _T("open"), cmdline,
1990 args, NULL, show) <= (HINSTANCE) 32) {
1992 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1993 (int)GetLastError());
1994 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
2000 * This is a can't-happen stub, since Pageant never makes
2001 * asynchronous agent requests.
2003 void agent_schedule_callback(void (*callback)(void *, void *, int),
2004 void *callback_ctx, void *data, int len)
2006 assert(!"We shouldn't get here");
2009 void cleanup_exit(int code)
2015 int flags = FLAG_SYNCAGENT;
2017 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
2022 char *command = NULL;
2025 char **argv, **argstart;
2031 * Determine whether we're an NT system (should have security
2032 * APIs) or a non-NT system (don't do security).
2036 modalfatalbox("Windows refuses to report a version");
2038 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
2039 has_security = TRUE;
2041 has_security = FALSE;
2046 * Attempt to get the security API we need.
2048 if (!init_advapi()) {
2050 "Unable to access security APIs. Pageant will\n"
2051 "not run, in case it causes a security breach.",
2052 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2057 "This program has been compiled for Win9X and will\n"
2058 "not run on NT, in case it causes a security breach.",
2059 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2066 * See if we can find our Help file.
2071 * Look for the PuTTY binary (we will enable the saved session
2072 * submenu if we find it).
2075 char b[2048], *p, *q, *r;
2077 GetModuleFileName(NULL, b, sizeof(b) - 16);
2079 p = strrchr(b, '\\');
2080 if (p && p >= r) r = p+1;
2081 q = strrchr(b, ':');
2082 if (q && q >= r) r = q+1;
2083 strcpy(r, "putty.exe");
2084 if ( (fp = fopen(b, "r")) != NULL) {
2085 putty_path = dupstr(b);
2092 * Find out if Pageant is already running.
2094 already_running = agent_exists();
2097 * Initialise storage for RSA keys.
2099 if (!already_running) {
2100 rsakeys = newtree234(cmpkeys_rsa);
2101 ssh2keys = newtree234(cmpkeys_ssh2);
2105 * Initialise storage for short-term passphrase cache.
2107 passphrases = newtree234(NULL);
2110 * Process the command line and add keys as listed on it.
2112 split_into_argv(cmdline, &argc, &argv, &argstart);
2113 for (i = 0; i < argc; i++) {
2114 if (!strcmp(argv[i], "-pgpfp")) {
2117 FreeLibrary(advapi);
2119 } else if (!strcmp(argv[i], "-c")) {
2121 * If we see `-c', then the rest of the
2122 * command line should be treated as a
2123 * command to be spawned.
2126 command = argstart[i+1];
2131 add_keyfile(filename_from_str(argv[i]));
2137 * Forget any passphrase that we retained while going over
2138 * command line keyfiles.
2140 forget_passphrases();
2144 if (command[0] == '"')
2145 args = strchr(++command, '"');
2147 args = strchr(command, ' ');
2150 while(*args && isspace(*args)) args++;
2152 spawn_cmd(command, args, show);
2156 * If Pageant was already running, we leave now. If we haven't
2157 * even taken any auxiliary action (spawned a command or added
2160 if (already_running) {
2161 if (!command && !added_keys) {
2162 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2163 MB_ICONERROR | MB_OK);
2166 FreeLibrary(advapi);
2172 wndclass.lpfnWndProc = WndProc;
2173 wndclass.cbClsExtra = 0;
2174 wndclass.cbWndExtra = 0;
2175 wndclass.hInstance = inst;
2176 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2177 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2178 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2179 wndclass.lpszMenuName = NULL;
2180 wndclass.lpszClassName = APPNAME;
2182 RegisterClass(&wndclass);
2187 hwnd = CreateWindow(APPNAME, APPNAME,
2188 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2189 CW_USEDEFAULT, CW_USEDEFAULT,
2190 100, 100, NULL, NULL, inst, NULL);
2192 /* Set up a system tray icon */
2195 /* Accelerators used: nsvkxa */
2196 systray_menu = CreatePopupMenu();
2198 session_menu = CreateMenu();
2199 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2200 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2201 (UINT) session_menu, "&Saved Sessions");
2202 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2204 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2206 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2207 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2209 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2210 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2211 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2212 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2213 initial_menuitems_count = GetMenuItemCount(session_menu);
2215 /* Set the default menu item. */
2216 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2218 ShowWindow(hwnd, SW_HIDE);
2221 * Main message loop.
2223 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2224 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2225 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2226 TranslateMessage(&msg);
2227 DispatchMessage(&msg);
2231 /* Clean up the system tray icon */
2233 NOTIFYICONDATA tnid;
2235 tnid.cbSize = sizeof(NOTIFYICONDATA);
2239 Shell_NotifyIcon(NIM_DELETE, &tnid);
2241 DestroyMenu(systray_menu);
2244 if (keypath) filereq_free(keypath);
2247 FreeLibrary(advapi);
2249 cleanup_exit(msg.wParam);
2250 return msg.wParam; /* just in case optimiser complains */