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++) {
359 * Replace two spaces in the fingerprint with tabs, for
360 * nice alignment in the box.
362 p = skey->alg->fingerprint(skey->data);
363 listentry = dupprintf("%s\t%s", p, skey->comment);
364 fp_len = strlen(listentry);
367 p = strchr(listentry, ' ');
368 if (p && p < listentry + fp_len)
370 p = strchr(listentry, ' ');
371 if (p && p < listentry + fp_len)
374 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
378 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
383 * This function loads a key from a file and adds it.
385 static void add_keyfile(Filename *filename)
388 struct RSAKey *rkey = NULL;
389 struct ssh2_userkey *skey = NULL;
394 const char *error = NULL;
398 type = key_type(filename);
399 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
400 char *msg = dupprintf("Couldn't load this key (%s)",
401 key_type_to_str(type));
402 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
403 HELPCTXID(errors_cantloadkey));
409 * See if the key is already loaded (in the primary Pageant,
410 * which may or may not be us).
414 unsigned char *keylist, *p;
415 int i, nkeys, bloblen, keylistlen;
417 if (type == SSH_KEYTYPE_SSH1) {
418 if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
419 char *msg = dupprintf("Couldn't load private key (%s)", error);
420 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
421 HELPCTXID(errors_cantloadkey));
425 keylist = get_keylist1(&keylistlen);
427 unsigned char *blob2;
428 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
431 char *msg = dupprintf("Couldn't load private key (%s)", error);
432 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
433 HELPCTXID(errors_cantloadkey));
437 /* For our purposes we want the blob prefixed with its length */
438 blob2 = snewn(bloblen+4, unsigned char);
439 PUT_32BIT(blob2, bloblen);
440 memcpy(blob2 + 4, blob, bloblen);
444 keylist = get_keylist2(&keylistlen);
447 if (keylistlen < 4) {
448 MessageBox(NULL, "Received broken key list?!", APPNAME,
449 MB_OK | MB_ICONERROR);
452 nkeys = toint(GET_32BIT(keylist));
454 MessageBox(NULL, "Received broken key list?!", APPNAME,
455 MB_OK | MB_ICONERROR);
461 for (i = 0; i < nkeys; i++) {
462 if (!memcmp(blob, p, bloblen)) {
463 /* Key is already present; we can now leave. */
468 /* Now skip over public blob */
469 if (type == SSH_KEYTYPE_SSH1) {
470 int n = rsa_public_blob_len(p, keylistlen);
472 MessageBox(NULL, "Received broken key list?!", APPNAME,
473 MB_OK | MB_ICONERROR);
480 if (keylistlen < 4) {
481 MessageBox(NULL, "Received broken key list?!", APPNAME,
482 MB_OK | MB_ICONERROR);
485 n = toint(4 + GET_32BIT(p));
486 if (n < 0 || keylistlen < n) {
487 MessageBox(NULL, "Received broken key list?!", APPNAME,
488 MB_OK | MB_ICONERROR);
494 /* Now skip over comment field */
497 if (keylistlen < 4) {
498 MessageBox(NULL, "Received broken key list?!", APPNAME,
499 MB_OK | MB_ICONERROR);
502 n = toint(4 + GET_32BIT(p));
503 if (n < 0 || keylistlen < n) {
504 MessageBox(NULL, "Received broken key list?!", APPNAME,
505 MB_OK | MB_ICONERROR);
520 if (type == SSH_KEYTYPE_SSH1)
521 needs_pass = rsakey_encrypted(filename, &comment);
523 needs_pass = ssh2_userkey_encrypted(filename, &comment);
525 if (type == SSH_KEYTYPE_SSH1)
526 rkey = snew(struct RSAKey);
534 /* try all the remembered passphrases first */
535 char *pp = index234(passphrases, attempts);
537 passphrase = dupstr(pp);
540 struct PassphraseProcStruct pps;
542 pps.passphrase = &passphrase;
543 pps.comment = comment;
546 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
547 NULL, PassphraseProc, (LPARAM) &pps);
548 passphrase_box = NULL;
552 if (type == SSH_KEYTYPE_SSH1)
554 return; /* operation cancelled */
557 assert(passphrase != NULL);
560 passphrase = dupstr("");
562 if (type == SSH_KEYTYPE_SSH1)
563 ret = loadrsakey(filename, rkey, passphrase, &error);
565 skey = ssh2_load_userkey(filename, passphrase, &error);
566 if (skey == SSH2_WRONG_PASSPHRASE)
576 if(original_pass && ret) {
577 /* If they typed in an ok passphrase, remember it */
578 addpos234(passphrases, passphrase, 0);
580 /* Otherwise, destroy it */
588 char *msg = dupprintf("Couldn't load private key (%s)", error);
589 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
590 HELPCTXID(errors_cantloadkey));
592 if (type == SSH_KEYTYPE_SSH1)
596 if (type == SSH_KEYTYPE_SSH1) {
597 if (already_running) {
598 unsigned char *request, *response;
600 int reqlen, clen, resplen, ret;
602 clen = strlen(rkey->comment);
604 reqlen = 4 + 1 + /* length, message type */
606 ssh1_bignum_length(rkey->modulus) +
607 ssh1_bignum_length(rkey->exponent) +
608 ssh1_bignum_length(rkey->private_exponent) +
609 ssh1_bignum_length(rkey->iqmp) +
610 ssh1_bignum_length(rkey->p) +
611 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
614 request = snewn(reqlen, unsigned char);
616 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
618 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
620 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
621 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
623 ssh1_write_bignum(request + reqlen,
624 rkey->private_exponent);
625 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
626 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
627 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
628 PUT_32BIT(request + reqlen, clen);
629 memcpy(request + reqlen + 4, rkey->comment, clen);
631 PUT_32BIT(request, reqlen - 4);
633 ret = agent_query(request, reqlen, &vresponse, &resplen,
636 response = vresponse;
637 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
638 MessageBox(NULL, "The already running Pageant "
639 "refused to add the key.", APPNAME,
640 MB_OK | MB_ICONERROR);
645 if (add234(rsakeys, rkey) != rkey)
646 sfree(rkey); /* already present, don't waste RAM */
649 if (already_running) {
650 unsigned char *request, *response;
652 int reqlen, alglen, clen, keybloblen, resplen, ret;
653 alglen = strlen(skey->alg->name);
654 clen = strlen(skey->comment);
656 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
658 reqlen = 4 + 1 + /* length, message type */
659 4 + alglen + /* algorithm name */
660 keybloblen + /* key data */
661 4 + clen /* comment */
664 request = snewn(reqlen, unsigned char);
666 request[4] = SSH2_AGENTC_ADD_IDENTITY;
668 PUT_32BIT(request + reqlen, alglen);
670 memcpy(request + reqlen, skey->alg->name, alglen);
672 reqlen += skey->alg->openssh_fmtkey(skey->data,
675 PUT_32BIT(request + reqlen, clen);
676 memcpy(request + reqlen + 4, skey->comment, clen);
678 PUT_32BIT(request, reqlen - 4);
680 ret = agent_query(request, reqlen, &vresponse, &resplen,
683 response = vresponse;
684 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
685 MessageBox(NULL, "The already running Pageant "
686 "refused to add the key.", APPNAME,
687 MB_OK | MB_ICONERROR);
692 if (add234(ssh2keys, skey) != skey) {
693 skey->alg->freekey(skey->data);
694 sfree(skey); /* already present, don't waste RAM */
701 * Create an SSH-1 key list in a malloc'ed buffer; return its
704 static void *make_keylist1(int *length)
708 unsigned char *blob, *p, *ret;
712 * Count up the number and length of keys we hold.
716 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
718 blob = rsa_public_blob(key, &bloblen);
721 len += 4 + strlen(key->comment);
724 /* Allocate the buffer. */
725 p = ret = snewn(len, unsigned char);
726 if (length) *length = len;
730 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
731 blob = rsa_public_blob(key, &bloblen);
732 memcpy(p, blob, bloblen);
735 PUT_32BIT(p, strlen(key->comment));
736 memcpy(p + 4, key->comment, strlen(key->comment));
737 p += 4 + strlen(key->comment);
740 assert(p - ret == len);
745 * Create an SSH-2 key list in a malloc'ed buffer; return its
748 static void *make_keylist2(int *length)
750 struct ssh2_userkey *key;
752 unsigned char *blob, *p, *ret;
756 * Count up the number and length of keys we hold.
760 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
762 len += 4; /* length field */
763 blob = key->alg->public_blob(key->data, &bloblen);
766 len += 4 + strlen(key->comment);
769 /* Allocate the buffer. */
770 p = ret = snewn(len, unsigned char);
771 if (length) *length = len;
774 * Packet header is the obvious five bytes, plus four
775 * bytes for the key count.
779 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
780 blob = key->alg->public_blob(key->data, &bloblen);
781 PUT_32BIT(p, bloblen);
783 memcpy(p, blob, bloblen);
786 PUT_32BIT(p, strlen(key->comment));
787 memcpy(p + 4, key->comment, strlen(key->comment));
788 p += 4 + strlen(key->comment);
791 assert(p - ret == len);
796 * Acquire a keylist1 from the primary Pageant; this means either
797 * calling make_keylist1 (if that's us) or sending a message to the
798 * primary Pageant (if it's not).
800 static void *get_keylist1(int *length)
804 if (already_running) {
805 unsigned char request[5], *response;
808 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
809 PUT_32BIT(request, 4);
811 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
813 response = vresponse;
814 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
819 ret = snewn(resplen-5, unsigned char);
820 memcpy(ret, response+5, resplen-5);
826 ret = make_keylist1(length);
832 * Acquire a keylist2 from the primary Pageant; this means either
833 * calling make_keylist2 (if that's us) or sending a message to the
834 * primary Pageant (if it's not).
836 static void *get_keylist2(int *length)
840 if (already_running) {
841 unsigned char request[5], *response;
845 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
846 PUT_32BIT(request, 4);
848 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
850 response = vresponse;
851 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
856 ret = snewn(resplen-5, unsigned char);
857 memcpy(ret, response+5, resplen-5);
863 ret = make_keylist2(length);
869 * This is the main agent function that answers messages.
871 static void answer_msg(void *msg)
873 unsigned char *p = msg;
874 unsigned char *ret = msg;
875 unsigned char *msgend;
879 * Get the message length.
881 msgend = p + 4 + GET_32BIT(p);
884 * Get the message type.
892 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
894 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
900 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
901 keylist = make_keylist1(&len);
902 if (len + 5 > AGENT_MAX_MSGLEN) {
906 PUT_32BIT(ret, len + 1);
907 memcpy(ret + 5, keylist, len);
911 case SSH2_AGENTC_REQUEST_IDENTITIES:
913 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
919 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
920 keylist = make_keylist2(&len);
921 if (len + 5 > AGENT_MAX_MSGLEN) {
925 PUT_32BIT(ret, len + 1);
926 memcpy(ret + 5, keylist, len);
930 case SSH1_AGENTC_RSA_CHALLENGE:
932 * Reply with either SSH1_AGENT_RSA_RESPONSE or
933 * SSH_AGENT_FAILURE, depending on whether we have that key
937 struct RSAKey reqkey, *key;
938 Bignum challenge, response;
939 unsigned char response_source[48], response_md5[16];
940 struct MD5Context md5c;
944 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
948 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
950 freebn(reqkey.exponent);
954 i = ssh1_read_bignum(p, msgend - p, &challenge);
956 freebn(reqkey.exponent);
957 freebn(reqkey.modulus);
962 freebn(reqkey.exponent);
963 freebn(reqkey.modulus);
967 memcpy(response_source + 32, p, 16);
971 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
972 freebn(reqkey.exponent);
973 freebn(reqkey.modulus);
977 response = rsadecrypt(challenge, key);
978 for (i = 0; i < 32; i++)
979 response_source[i] = bignum_byte(response, 31 - i);
982 MD5Update(&md5c, response_source, 48);
983 MD5Final(response_md5, &md5c);
984 smemclr(response_source, 48); /* burn the evidence */
985 freebn(response); /* and that evidence */
986 freebn(challenge); /* yes, and that evidence */
987 freebn(reqkey.exponent); /* and free some memory ... */
988 freebn(reqkey.modulus); /* ... while we're at it. */
991 * Packet is the obvious five byte header, plus sixteen
995 PUT_32BIT(ret, len - 4);
996 ret[4] = SSH1_AGENT_RSA_RESPONSE;
997 memcpy(ret + 5, response_md5, 16);
1000 case SSH2_AGENTC_SIGN_REQUEST:
1002 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
1003 * SSH_AGENT_FAILURE, depending on whether we have that key
1007 struct ssh2_userkey *key;
1009 unsigned char *data, *signature;
1010 int datalen, siglen, len;
1014 b.len = toint(GET_32BIT(p));
1015 if (b.len < 0 || b.len > msgend - (p+4))
1022 datalen = toint(GET_32BIT(p));
1024 if (datalen < 0 || datalen > msgend - p)
1027 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1030 signature = key->alg->sign(key->data, data, datalen, &siglen);
1031 len = 5 + 4 + siglen;
1032 PUT_32BIT(ret, len - 4);
1033 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1034 PUT_32BIT(ret + 5, siglen);
1035 memcpy(ret + 5 + 4, signature, siglen);
1039 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1041 * Add to the list and return SSH_AGENT_SUCCESS, or
1042 * SSH_AGENT_FAILURE if the key was malformed.
1049 key = snew(struct RSAKey);
1050 memset(key, 0, sizeof(struct RSAKey));
1052 n = makekey(p, msgend - p, key, NULL, 1);
1060 n = makeprivate(p, msgend - p, key);
1068 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1076 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1084 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1097 commentlen = toint(GET_32BIT(p));
1099 if (commentlen < 0 || commentlen > msgend - p) {
1105 comment = snewn(commentlen+1, char);
1107 memcpy(comment, p + 4, commentlen);
1108 comment[commentlen] = '\0';
1109 key->comment = comment;
1112 ret[4] = SSH_AGENT_FAILURE;
1113 if (add234(rsakeys, key) == key) {
1115 ret[4] = SSH_AGENT_SUCCESS;
1122 case SSH2_AGENTC_ADD_IDENTITY:
1124 * Add to the list and return SSH_AGENT_SUCCESS, or
1125 * SSH_AGENT_FAILURE if the key was malformed.
1128 struct ssh2_userkey *key;
1129 char *comment, *alg;
1130 int alglen, commlen;
1136 alglen = toint(GET_32BIT(p));
1138 if (alglen < 0 || alglen > msgend - p)
1143 key = snew(struct ssh2_userkey);
1144 /* Add further algorithm names here. */
1145 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1146 key->alg = &ssh_rsa;
1147 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1148 key->alg = &ssh_dss;
1154 bloblen = msgend - p;
1155 key->data = key->alg->openssh_createkey(&p, &bloblen);
1162 * p has been advanced by openssh_createkey, but
1163 * certainly not _beyond_ the end of the buffer.
1165 assert(p <= msgend);
1168 key->alg->freekey(key->data);
1172 commlen = toint(GET_32BIT(p));
1175 if (commlen < 0 || commlen > msgend - p) {
1176 key->alg->freekey(key->data);
1180 comment = snewn(commlen + 1, char);
1182 memcpy(comment, p, commlen);
1183 comment[commlen] = '\0';
1185 key->comment = comment;
1188 ret[4] = SSH_AGENT_FAILURE;
1189 if (add234(ssh2keys, key) == key) {
1191 ret[4] = SSH_AGENT_SUCCESS;
1193 key->alg->freekey(key->data);
1194 sfree(key->comment);
1199 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1201 * Remove from the list and return SSH_AGENT_SUCCESS, or
1202 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1206 struct RSAKey reqkey, *key;
1209 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1213 key = find234(rsakeys, &reqkey, NULL);
1214 freebn(reqkey.exponent);
1215 freebn(reqkey.modulus);
1217 ret[4] = SSH_AGENT_FAILURE;
1219 del234(rsakeys, key);
1223 ret[4] = SSH_AGENT_SUCCESS;
1227 case SSH2_AGENTC_REMOVE_IDENTITY:
1229 * Remove from the list and return SSH_AGENT_SUCCESS, or
1230 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1234 struct ssh2_userkey *key;
1239 b.len = toint(GET_32BIT(p));
1242 if (b.len < 0 || b.len > msgend - p)
1247 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1252 ret[4] = SSH_AGENT_FAILURE;
1254 del234(ssh2keys, key);
1256 key->alg->freekey(key->data);
1258 ret[4] = SSH_AGENT_SUCCESS;
1262 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1264 * Remove all SSH-1 keys. Always returns success.
1267 struct RSAKey *rkey;
1269 while ((rkey = index234(rsakeys, 0)) != NULL) {
1270 del234(rsakeys, rkey);
1277 ret[4] = SSH_AGENT_SUCCESS;
1280 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1282 * Remove all SSH-2 keys. Always returns success.
1285 struct ssh2_userkey *skey;
1287 while ((skey = index234(ssh2keys, 0)) != NULL) {
1288 del234(ssh2keys, skey);
1289 skey->alg->freekey(skey->data);
1295 ret[4] = SSH_AGENT_SUCCESS;
1301 * Unrecognised message. Return SSH_AGENT_FAILURE.
1304 ret[4] = SSH_AGENT_FAILURE;
1310 * Key comparison function for the 2-3-4 tree of RSA keys.
1312 static int cmpkeys_rsa(void *av, void *bv)
1314 struct RSAKey *a = (struct RSAKey *) av;
1315 struct RSAKey *b = (struct RSAKey *) bv;
1322 * Compare by length of moduli.
1324 alen = bignum_bitcount(am);
1325 blen = bignum_bitcount(bm);
1328 else if (alen < blen)
1331 * Now compare by moduli themselves.
1333 alen = (alen + 7) / 8; /* byte count */
1334 while (alen-- > 0) {
1336 abyte = bignum_byte(am, alen);
1337 bbyte = bignum_byte(bm, alen);
1340 else if (abyte < bbyte)
1350 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1352 static int cmpkeys_ssh2(void *av, void *bv)
1354 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1355 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1358 unsigned char *ablob, *bblob;
1362 * Compare purely by public blob.
1364 ablob = a->alg->public_blob(a->data, &alen);
1365 bblob = b->alg->public_blob(b->data, &blen);
1368 for (i = 0; i < alen && i < blen; i++) {
1369 if (ablob[i] < bblob[i]) {
1372 } else if (ablob[i] > bblob[i]) {
1377 if (c == 0 && i < alen)
1378 c = +1; /* a is longer */
1379 if (c == 0 && i < blen)
1380 c = -1; /* a is longer */
1389 * Key comparison function for looking up a blob in the 2-3-4 tree
1392 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1394 struct blob *a = (struct blob *) av;
1395 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1398 unsigned char *ablob, *bblob;
1402 * Compare purely by public blob.
1406 bblob = b->alg->public_blob(b->data, &blen);
1409 for (i = 0; i < alen && i < blen; i++) {
1410 if (ablob[i] < bblob[i]) {
1413 } else if (ablob[i] > bblob[i]) {
1418 if (c == 0 && i < alen)
1419 c = +1; /* a is longer */
1420 if (c == 0 && i < blen)
1421 c = -1; /* a is longer */
1429 * Prompt for a key file to add, and add it.
1431 static void prompt_add_keyfile(void)
1434 char *filelist = snewn(8192, char);
1436 if (!keypath) keypath = filereq_new();
1437 memset(&of, 0, sizeof(of));
1438 of.hwndOwner = hwnd;
1439 of.lpstrFilter = FILTER_KEY_FILES;
1440 of.lpstrCustomFilter = NULL;
1441 of.nFilterIndex = 1;
1442 of.lpstrFile = filelist;
1445 of.lpstrFileTitle = NULL;
1446 of.lpstrTitle = "Select Private Key File";
1447 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1448 if (request_file(keypath, &of, TRUE, FALSE)) {
1449 if(strlen(filelist) > of.nFileOffset) {
1450 /* Only one filename returned? */
1451 Filename *fn = filename_from_str(filelist);
1455 /* we are returned a bunch of strings, end to
1456 * end. first string is the directory, the
1457 * rest the filenames. terminated with an
1460 char *dir = filelist;
1461 char *filewalker = filelist + strlen(dir) + 1;
1462 while (*filewalker != '\0') {
1463 char *filename = dupcat(dir, "\\", filewalker, NULL);
1464 Filename *fn = filename_from_str(filename);
1468 filewalker += strlen(filewalker) + 1;
1473 forget_passphrases();
1479 * Dialog-box function for the key list box.
1481 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1482 WPARAM wParam, LPARAM lParam)
1484 struct RSAKey *rkey;
1485 struct ssh2_userkey *skey;
1490 * Centre the window.
1492 { /* centre the window */
1496 hw = GetDesktopWindow();
1497 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1499 (rs.right + rs.left + rd.left - rd.right) / 2,
1500 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1501 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1505 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1506 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1509 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1511 DestroyWindow(item);
1516 static int tabs[] = { 35, 60, 210 };
1517 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1518 sizeof(tabs) / sizeof(*tabs),
1524 switch (LOWORD(wParam)) {
1528 DestroyWindow(hwnd);
1530 case 101: /* add key */
1531 if (HIWORD(wParam) == BN_CLICKED ||
1532 HIWORD(wParam) == BN_DOUBLECLICKED) {
1533 if (passphrase_box) {
1534 MessageBeep(MB_ICONERROR);
1535 SetForegroundWindow(passphrase_box);
1538 prompt_add_keyfile();
1541 case 102: /* remove key */
1542 if (HIWORD(wParam) == BN_CLICKED ||
1543 HIWORD(wParam) == BN_DOUBLECLICKED) {
1548 /* our counter within the array of selected items */
1551 /* get the number of items selected in the list */
1553 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1555 /* none selected? that was silly */
1556 if (numSelected == 0) {
1561 /* get item indices in an array */
1562 selectedArray = snewn(numSelected, int);
1563 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1564 numSelected, (WPARAM)selectedArray);
1566 itemNum = numSelected - 1;
1567 rCount = count234(rsakeys);
1568 sCount = count234(ssh2keys);
1570 /* go through the non-rsakeys until we've covered them all,
1571 * and/or we're out of selected items to check. note that
1572 * we go *backwards*, to avoid complications from deleting
1573 * things hence altering the offset of subsequent items
1575 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1576 skey = index234(ssh2keys, i);
1578 if (selectedArray[itemNum] == rCount + i) {
1579 del234(ssh2keys, skey);
1580 skey->alg->freekey(skey->data);
1586 /* do the same for the rsa keys */
1587 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1588 rkey = index234(rsakeys, i);
1590 if(selectedArray[itemNum] == i) {
1591 del234(rsakeys, rkey);
1598 sfree(selectedArray);
1602 case 103: /* help */
1603 if (HIWORD(wParam) == BN_CLICKED ||
1604 HIWORD(wParam) == BN_DOUBLECLICKED) {
1605 launch_help(hwnd, WINHELP_CTX_pageant_general);
1612 int id = ((LPHELPINFO)lParam)->iCtrlId;
1615 case 100: topic = WINHELP_CTX_pageant_keylist; break;
1616 case 101: topic = WINHELP_CTX_pageant_addkey; break;
1617 case 102: topic = WINHELP_CTX_pageant_remkey; break;
1620 launch_help(hwnd, topic);
1628 DestroyWindow(hwnd);
1634 /* Set up a system tray icon */
1635 static BOOL AddTrayIcon(HWND hwnd)
1638 NOTIFYICONDATA tnid;
1641 #ifdef NIM_SETVERSION
1643 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1646 tnid.cbSize = sizeof(NOTIFYICONDATA);
1648 tnid.uID = 1; /* unique within this systray use */
1649 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1650 tnid.uCallbackMessage = WM_SYSTRAY;
1651 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
1652 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1654 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1656 if (hicon) DestroyIcon(hicon);
1661 /* Update the saved-sessions menu. */
1662 static void update_sessions(void)
1666 TCHAR buf[MAX_PATH + 1];
1669 int index_key, index_menu;
1674 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1677 for(num_entries = GetMenuItemCount(session_menu);
1678 num_entries > initial_menuitems_count;
1680 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1685 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1686 TCHAR session_name[MAX_PATH + 1];
1687 unmungestr(buf, session_name, MAX_PATH);
1688 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1689 memset(&mii, 0, sizeof(mii));
1690 mii.cbSize = sizeof(mii);
1691 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1692 mii.fType = MFT_STRING;
1693 mii.fState = MFS_ENABLED;
1694 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1695 mii.dwTypeData = session_name;
1696 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1704 if(index_menu == 0) {
1705 mii.cbSize = sizeof(mii);
1706 mii.fMask = MIIM_TYPE | MIIM_STATE;
1707 mii.fType = MFT_STRING;
1708 mii.fState = MFS_GRAYED;
1709 mii.dwTypeData = _T("(No sessions)");
1710 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1716 * Versions of Pageant prior to 0.61 expected this SID on incoming
1717 * communications. For backwards compatibility, and more particularly
1718 * for compatibility with derived works of PuTTY still using the old
1719 * Pageant client code, we accept it as an alternative to the one
1720 * returned from get_user_sid() in winpgntc.c.
1722 PSID get_default_sid(void)
1726 PSECURITY_DESCRIPTOR psd = NULL;
1727 PSID sid = NULL, copy = NULL, ret = NULL;
1729 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1730 GetCurrentProcessId())) == NULL)
1733 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1734 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1737 sidlen = GetLengthSid(sid);
1739 copy = (PSID)smalloc(sidlen);
1741 if (!CopySid(sidlen, copy, sid))
1744 /* Success. Move sid into the return value slot, and null it out
1745 * to stop the cleanup code freeing it. */
1761 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1762 WPARAM wParam, LPARAM lParam)
1765 static int menuinprogress;
1766 static UINT msgTaskbarCreated = 0;
1770 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1773 if (message==msgTaskbarCreated) {
1775 * Explorer has been restarted, so the tray icon will
1783 if (lParam == WM_RBUTTONUP) {
1785 GetCursorPos(&cursorpos);
1786 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1787 } else if (lParam == WM_LBUTTONDBLCLK) {
1788 /* Run the default menu item. */
1789 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1791 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1795 if (!menuinprogress) {
1798 SetForegroundWindow(hwnd);
1799 ret = TrackPopupMenu(systray_menu,
1800 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1802 wParam, lParam, 0, hwnd, NULL);
1808 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1810 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1812 MessageBox(NULL, "Unable to execute PuTTY!",
1813 "Error", MB_OK | MB_ICONERROR);
1818 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1819 SendMessage(hwnd, WM_CLOSE, 0, 0);
1823 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1825 ShowWindow(keylist, SW_SHOWNORMAL);
1828 * Sometimes the window comes up minimised / hidden for
1829 * no obvious reason. Prevent this. This also brings it
1830 * to the front if it's already present (the user
1831 * selected View Keys because they wanted to _see_ the
1834 SetForegroundWindow(keylist);
1835 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1836 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1839 if (passphrase_box) {
1840 MessageBeep(MB_ICONERROR);
1841 SetForegroundWindow(passphrase_box);
1844 prompt_add_keyfile();
1848 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1850 ShowWindow(aboutbox, SW_SHOWNORMAL);
1852 * Sometimes the window comes up minimised / hidden
1853 * for no obvious reason. Prevent this.
1855 SetForegroundWindow(aboutbox);
1856 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1857 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1861 launch_help(hwnd, WINHELP_CTX_pageant_general);
1865 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1867 TCHAR buf[MAX_PATH + 1];
1868 TCHAR param[MAX_PATH + 1];
1869 memset(&mii, 0, sizeof(mii));
1870 mii.cbSize = sizeof(mii);
1871 mii.fMask = MIIM_TYPE;
1873 mii.dwTypeData = buf;
1874 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1876 strcat(param, mii.dwTypeData);
1877 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1878 _T(""), SW_SHOW) <= 32) {
1879 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1880 MB_OK | MB_ICONERROR);
1893 COPYDATASTRUCT *cds;
1898 PSID mapowner, ourself, ourself2;
1900 PSECURITY_DESCRIPTOR psd = NULL;
1903 cds = (COPYDATASTRUCT *) lParam;
1904 if (cds->dwData != AGENT_COPYDATA_ID)
1905 return 0; /* not our message, mate */
1906 mapname = (char *) cds->lpData;
1907 if (mapname[cds->cbData - 1] != '\0')
1908 return 0; /* failure to be ASCIZ! */
1910 debug(("mapname is :%s:\n", mapname));
1912 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1914 debug(("filemap is %p\n", filemap));
1916 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1920 if ((ourself = get_user_sid()) == NULL) {
1922 debug(("couldn't get user SID\n"));
1924 CloseHandle(filemap);
1928 if ((ourself2 = get_default_sid()) == NULL) {
1930 debug(("couldn't get default SID\n"));
1932 CloseHandle(filemap);
1937 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1938 OWNER_SECURITY_INFORMATION,
1939 &mapowner, NULL, NULL, NULL,
1940 &psd) != ERROR_SUCCESS)) {
1942 debug(("couldn't get owner info for filemap: %d\n",
1945 CloseHandle(filemap);
1952 LPTSTR ours, ours2, theirs;
1953 ConvertSidToStringSid(mapowner, &theirs);
1954 ConvertSidToStringSid(ourself, &ours);
1955 ConvertSidToStringSid(ourself2, &ours2);
1956 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1957 " theirs=%s\n", ours, ours2, theirs));
1963 if (!EqualSid(mapowner, ourself) &&
1964 !EqualSid(mapowner, ourself2)) {
1965 CloseHandle(filemap);
1969 return 0; /* security ID mismatch! */
1972 debug(("security stuff matched\n"));
1979 debug(("security APIs not present\n"));
1983 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1985 debug(("p is %p\n", p));
1988 for (i = 0; i < 5; i++)
1989 debug(("p[%d]=%02x\n", i,
1990 ((unsigned char *) p)[i]));
1997 CloseHandle(filemap);
2002 return DefWindowProc(hwnd, message, wParam, lParam);
2006 * Fork and Exec the command in cmdline. [DBW]
2008 void spawn_cmd(char *cmdline, char * args, int show)
2010 if (ShellExecute(NULL, _T("open"), cmdline,
2011 args, NULL, show) <= (HINSTANCE) 32) {
2013 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
2014 (int)GetLastError());
2015 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
2021 * This is a can't-happen stub, since Pageant never makes
2022 * asynchronous agent requests.
2024 void agent_schedule_callback(void (*callback)(void *, void *, int),
2025 void *callback_ctx, void *data, int len)
2027 assert(!"We shouldn't get here");
2030 void cleanup_exit(int code)
2036 int flags = FLAG_SYNCAGENT;
2038 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
2042 char *command = NULL;
2045 char **argv, **argstart;
2051 * Determine whether we're an NT system (should have security
2052 * APIs) or a non-NT system (don't do security).
2056 modalfatalbox("Windows refuses to report a version");
2058 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
2059 has_security = TRUE;
2061 has_security = FALSE;
2066 * Attempt to get the security API we need.
2068 if (!init_advapi()) {
2070 "Unable to access security APIs. Pageant will\n"
2071 "not run, in case it causes a security breach.",
2072 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2077 "This program has been compiled for Win9X and will\n"
2078 "not run on NT, in case it causes a security breach.",
2079 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2085 * See if we can find our Help file.
2090 * Look for the PuTTY binary (we will enable the saved session
2091 * submenu if we find it).
2094 char b[2048], *p, *q, *r;
2096 GetModuleFileName(NULL, b, sizeof(b) - 16);
2098 p = strrchr(b, '\\');
2099 if (p && p >= r) r = p+1;
2100 q = strrchr(b, ':');
2101 if (q && q >= r) r = q+1;
2102 strcpy(r, "putty.exe");
2103 if ( (fp = fopen(b, "r")) != NULL) {
2104 putty_path = dupstr(b);
2111 * Find out if Pageant is already running.
2113 already_running = agent_exists();
2116 * Initialise storage for RSA keys.
2118 if (!already_running) {
2119 rsakeys = newtree234(cmpkeys_rsa);
2120 ssh2keys = newtree234(cmpkeys_ssh2);
2124 * Initialise storage for short-term passphrase cache.
2126 passphrases = newtree234(NULL);
2129 * Process the command line and add keys as listed on it.
2131 split_into_argv(cmdline, &argc, &argv, &argstart);
2132 for (i = 0; i < argc; i++) {
2133 if (!strcmp(argv[i], "-pgpfp")) {
2136 } else if (!strcmp(argv[i], "-c")) {
2138 * If we see `-c', then the rest of the
2139 * command line should be treated as a
2140 * command to be spawned.
2143 command = argstart[i+1];
2148 Filename *fn = filename_from_str(argv[i]);
2156 * Forget any passphrase that we retained while going over
2157 * command line keyfiles.
2159 forget_passphrases();
2163 if (command[0] == '"')
2164 args = strchr(++command, '"');
2166 args = strchr(command, ' ');
2169 while(*args && isspace(*args)) args++;
2171 spawn_cmd(command, args, show);
2175 * If Pageant was already running, we leave now. If we haven't
2176 * even taken any auxiliary action (spawned a command or added
2179 if (already_running) {
2180 if (!command && !added_keys) {
2181 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2182 MB_ICONERROR | MB_OK);
2189 wndclass.lpfnWndProc = WndProc;
2190 wndclass.cbClsExtra = 0;
2191 wndclass.cbWndExtra = 0;
2192 wndclass.hInstance = inst;
2193 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2194 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2195 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2196 wndclass.lpszMenuName = NULL;
2197 wndclass.lpszClassName = APPNAME;
2199 RegisterClass(&wndclass);
2204 hwnd = CreateWindow(APPNAME, APPNAME,
2205 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2206 CW_USEDEFAULT, CW_USEDEFAULT,
2207 100, 100, NULL, NULL, inst, NULL);
2209 /* Set up a system tray icon */
2212 /* Accelerators used: nsvkxa */
2213 systray_menu = CreatePopupMenu();
2215 session_menu = CreateMenu();
2216 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2217 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2218 (UINT) session_menu, "&Saved Sessions");
2219 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2221 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2223 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2224 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2226 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2227 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2228 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2229 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2230 initial_menuitems_count = GetMenuItemCount(session_menu);
2232 /* Set the default menu item. */
2233 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2235 ShowWindow(hwnd, SW_HIDE);
2238 * Main message loop.
2240 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2241 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2242 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2243 TranslateMessage(&msg);
2244 DispatchMessage(&msg);
2248 /* Clean up the system tray icon */
2250 NOTIFYICONDATA tnid;
2252 tnid.cbSize = sizeof(NOTIFYICONDATA);
2256 Shell_NotifyIcon(NIM_DELETE, &tnid);
2258 DestroyMenu(systray_menu);
2261 if (keypath) filereq_free(keypath);
2263 cleanup_exit(msg.wParam);
2264 return msg.wParam; /* just in case optimiser complains */