2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
24 #define IDI_MAINICON 200
25 #define IDI_TRAYICON 201
27 #define WM_XUSER (WM_USER + 0x2000)
28 #define WM_SYSTRAY (WM_XUSER + 6)
29 #define WM_SYSTRAY2 (WM_XUSER + 7)
31 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
34 * FIXME: maybe some day we can sort this out ...
36 #define AGENT_MAX_MSGLEN 8192
38 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
39 * wParam are used by Windows, and should be masked off, so we shouldn't
40 * attempt to store information in them. Hence all these identifiers have
41 * the low 4 bits clear. Also, identifiers should < 0xF000. */
43 #define IDM_CLOSE 0x0010
44 #define IDM_VIEWKEYS 0x0020
45 #define IDM_ADDKEY 0x0030
46 #define IDM_HELP 0x0040
47 #define IDM_ABOUT 0x0050
49 #define APPNAME "Pageant"
55 static HMENU systray_menu, session_menu;
56 static int already_running;
58 static char *putty_path;
60 /* CWD for "add key" file requester. */
61 static filereq *keypath = NULL;
63 #define IDM_PUTTY 0x0060
64 #define IDM_SESSIONS_BASE 0x1000
65 #define IDM_SESSIONS_MAX 0x2000
66 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
67 #define PUTTY_DEFAULT "Default%20Settings"
68 static int initial_menuitems_count;
71 * Print a modal (Really Bad) message box and perform a fatal exit.
73 void modalfatalbox(char *fmt, ...)
79 buf = dupvprintf(fmt, ap);
81 MessageBox(hwnd, buf, "Pageant Fatal Error",
82 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
87 /* Un-munge session names out of the registry. */
88 static void unmungestr(char *in, char *out, int outlen)
91 if (*in == '%' && in[1] && in[2]) {
99 *out++ = (i << 4) + j;
113 static tree234 *rsakeys, *ssh2keys;
115 static int has_security;
117 typedef DWORD(WINAPI * gsi_fn_t)
118 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
119 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
120 static gsi_fn_t getsecurityinfo;
126 static void *make_keylist1(int *length);
127 static void *make_keylist2(int *length);
128 static void *get_keylist1(int *length);
129 static void *get_keylist2(int *length);
132 * We need this to link with the RSA code, because rsaencrypt()
133 * pads its data with random bytes. Since we only use rsadecrypt()
134 * and the signing functions, which are deterministic, this should
137 * If it _is_ called, there is a _serious_ problem, because it
138 * won't generate true random numbers. So we must scream, panic,
139 * and exit immediately if that should happen.
141 int random_byte(void)
143 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
145 /* this line can't be reached but it placates MSVC's warnings :-) */
150 * Blob structure for passing to the asymmetric SSH-2 key compare
151 * function, prototyped here.
157 static int cmpkeys_ssh2_asymm(void *av, void *bv);
159 #define GET_32BIT(cp) \
160 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
161 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
162 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
163 ((unsigned long)(unsigned char)(cp)[3]))
165 #define PUT_32BIT(cp, value) { \
166 (cp)[0] = (unsigned char)((value) >> 24); \
167 (cp)[1] = (unsigned char)((value) >> 16); \
168 (cp)[2] = (unsigned char)((value) >> 8); \
169 (cp)[3] = (unsigned char)(value); }
171 #define PASSPHRASE_MAXLEN 512
173 struct PassphraseProcStruct {
178 static tree234 *passphrases = NULL;
181 * After processing a list of filenames, we want to forget the
184 static void forget_passphrases(void)
186 while (count234(passphrases) > 0) {
187 char *pp = index234(passphrases, 0);
188 memset(pp, 0, strlen(pp));
189 delpos234(passphrases, 0);
195 * Dialog-box function for the Licence box.
197 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
198 WPARAM wParam, LPARAM lParam)
204 switch (LOWORD(wParam)) {
219 * Dialog-box function for the About box.
221 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
222 WPARAM wParam, LPARAM lParam)
226 SetDlgItemText(hwnd, 100, ver);
229 switch (LOWORD(wParam)) {
236 EnableWindow(hwnd, 0);
237 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
238 EnableWindow(hwnd, 1);
239 SetActiveWindow(hwnd);
251 static HWND passphrase_box;
254 * Dialog-box function for the passphrase box.
256 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
257 WPARAM wParam, LPARAM lParam)
259 static char *passphrase = NULL;
260 struct PassphraseProcStruct *p;
264 passphrase_box = hwnd;
268 { /* centre the window */
272 hw = GetDesktopWindow();
273 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
275 (rs.right + rs.left + rd.left - rd.right) / 2,
276 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
277 rd.right - rd.left, rd.bottom - rd.top, TRUE);
280 SetForegroundWindow(hwnd);
281 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
282 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
283 p = (struct PassphraseProcStruct *) lParam;
284 passphrase = p->passphrase;
286 SetDlgItemText(hwnd, 101, p->comment);
288 SetDlgItemText(hwnd, 102, passphrase);
291 switch (LOWORD(wParam)) {
301 case 102: /* edit box */
302 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
303 GetDlgItemText(hwnd, 102, passphrase,
304 PASSPHRASE_MAXLEN - 1);
305 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
318 * Warn about the obsolescent key file format.
320 void old_keyfile_warning(void)
322 static const char mbtitle[] = "PuTTY Key File Warning";
323 static const char message[] =
324 "You are loading an SSH-2 private key which has an\n"
325 "old version of the file format. This means your key\n"
326 "file is not fully tamperproof. Future versions of\n"
327 "PuTTY may stop supporting this private key format,\n"
328 "so we recommend you convert your key to the new\n"
331 "You can perform this conversion by loading the key\n"
332 "into PuTTYgen and then saving it again.";
334 MessageBox(NULL, message, mbtitle, MB_OK);
338 * Update the visible key list.
340 static void keylist_update(void)
343 struct ssh2_userkey *skey;
347 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
348 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
349 char listentry[512], *p;
351 * Replace two spaces in the fingerprint with tabs, for
352 * nice alignment in the box.
354 strcpy(listentry, "ssh1\t");
355 p = listentry + strlen(listentry);
356 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
357 p = strchr(listentry, ' ');
360 p = strchr(listentry, ' ');
363 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
364 0, (LPARAM) listentry);
366 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
367 char listentry[512], *p;
370 * Replace two spaces in the fingerprint with tabs, for
371 * nice alignment in the box.
373 p = skey->alg->fingerprint(skey->data);
374 strncpy(listentry, p, sizeof(listentry));
375 p = strchr(listentry, ' ');
378 p = strchr(listentry, ' ');
381 len = strlen(listentry);
382 if (len < sizeof(listentry) - 2) {
383 listentry[len] = '\t';
384 strncpy(listentry + len + 1, skey->comment,
385 sizeof(listentry) - len - 1);
387 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
390 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
395 * This function loads a key from a file and adds it.
397 static void add_keyfile(Filename filename)
399 char passphrase[PASSPHRASE_MAXLEN];
400 struct RSAKey *rkey = NULL;
401 struct ssh2_userkey *skey = NULL;
406 const char *error = NULL;
407 struct PassphraseProcStruct pps;
411 type = key_type(&filename);
412 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
413 char *msg = dupprintf("Couldn't load this key (%s)",
414 key_type_to_str(type));
415 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
416 HELPCTXID(errors_cantloadkey));
422 * See if the key is already loaded (in the primary Pageant,
423 * which may or may not be us).
427 unsigned char *keylist, *p;
428 int i, nkeys, bloblen, keylistlen;
430 if (type == SSH_KEYTYPE_SSH1) {
431 if (!rsakey_pubblob(&filename, &blob, &bloblen, &error)) {
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 keylist = get_keylist1(&keylistlen);
440 unsigned char *blob2;
441 blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, &error);
443 char *msg = dupprintf("Couldn't load private key (%s)", error);
444 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
445 HELPCTXID(errors_cantloadkey));
449 /* For our purposes we want the blob prefixed with its length */
450 blob2 = snewn(bloblen+4, unsigned char);
451 PUT_32BIT(blob2, bloblen);
452 memcpy(blob2 + 4, blob, bloblen);
456 keylist = get_keylist2(&keylistlen);
459 if (keylistlen < 4) {
460 MessageBox(NULL, "Received broken key list?!", APPNAME,
461 MB_OK | MB_ICONERROR);
464 nkeys = GET_32BIT(keylist);
468 for (i = 0; i < nkeys; i++) {
469 if (!memcmp(blob, p, bloblen)) {
470 /* Key is already present; we can now leave. */
475 /* Now skip over public blob */
476 if (type == SSH_KEYTYPE_SSH1) {
477 int n = rsa_public_blob_len(p, keylistlen);
479 MessageBox(NULL, "Received broken key list?!", APPNAME,
480 MB_OK | MB_ICONERROR);
487 if (keylistlen < 4) {
488 MessageBox(NULL, "Received broken key list?!", APPNAME,
489 MB_OK | MB_ICONERROR);
492 n = 4 + GET_32BIT(p);
493 if (keylistlen < n) {
494 MessageBox(NULL, "Received broken key list?!", APPNAME,
495 MB_OK | MB_ICONERROR);
501 /* Now skip over comment field */
504 if (keylistlen < 4) {
505 MessageBox(NULL, "Received broken key list?!", APPNAME,
506 MB_OK | MB_ICONERROR);
509 n = 4 + GET_32BIT(p);
510 if (keylistlen < n) {
511 MessageBox(NULL, "Received broken key list?!", APPNAME,
512 MB_OK | MB_ICONERROR);
527 if (type == SSH_KEYTYPE_SSH1)
528 needs_pass = rsakey_encrypted(&filename, &comment);
530 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
532 if (type == SSH_KEYTYPE_SSH1)
533 rkey = snew(struct RSAKey);
534 pps.passphrase = passphrase;
535 pps.comment = comment;
539 /* try all the remembered passphrases first */
540 char *pp = index234(passphrases, attempts);
542 strcpy(passphrase, pp);
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 */
559 if (type == SSH_KEYTYPE_SSH1)
560 ret = loadrsakey(&filename, rkey, passphrase, &error);
562 skey = ssh2_load_userkey(&filename, passphrase, &error);
563 if (skey == SSH2_WRONG_PASSPHRASE)
573 /* if they typed in an ok passphrase, remember it */
574 if(original_pass && ret) {
575 char *pp = dupstr(passphrase);
576 addpos234(passphrases, pp, 0);
582 char *msg = dupprintf("Couldn't load private key (%s)", error);
583 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
584 HELPCTXID(errors_cantloadkey));
586 if (type == SSH_KEYTYPE_SSH1)
590 if (type == SSH_KEYTYPE_SSH1) {
591 if (already_running) {
592 unsigned char *request, *response;
594 int reqlen, clen, resplen, ret;
596 clen = strlen(rkey->comment);
598 reqlen = 4 + 1 + /* length, message type */
600 ssh1_bignum_length(rkey->modulus) +
601 ssh1_bignum_length(rkey->exponent) +
602 ssh1_bignum_length(rkey->private_exponent) +
603 ssh1_bignum_length(rkey->iqmp) +
604 ssh1_bignum_length(rkey->p) +
605 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
608 request = snewn(reqlen, unsigned char);
610 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
612 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
614 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
615 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
617 ssh1_write_bignum(request + reqlen,
618 rkey->private_exponent);
619 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
620 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
621 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
622 PUT_32BIT(request + reqlen, clen);
623 memcpy(request + reqlen + 4, rkey->comment, clen);
625 PUT_32BIT(request, reqlen - 4);
627 ret = agent_query(request, reqlen, &vresponse, &resplen,
630 response = vresponse;
631 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
632 MessageBox(NULL, "The already running Pageant "
633 "refused to add the key.", APPNAME,
634 MB_OK | MB_ICONERROR);
639 if (add234(rsakeys, rkey) != rkey)
640 sfree(rkey); /* already present, don't waste RAM */
643 if (already_running) {
644 unsigned char *request, *response;
646 int reqlen, alglen, clen, keybloblen, resplen, ret;
647 alglen = strlen(skey->alg->name);
648 clen = strlen(skey->comment);
650 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
652 reqlen = 4 + 1 + /* length, message type */
653 4 + alglen + /* algorithm name */
654 keybloblen + /* key data */
655 4 + clen /* comment */
658 request = snewn(reqlen, unsigned char);
660 request[4] = SSH2_AGENTC_ADD_IDENTITY;
662 PUT_32BIT(request + reqlen, alglen);
664 memcpy(request + reqlen, skey->alg->name, alglen);
666 reqlen += skey->alg->openssh_fmtkey(skey->data,
669 PUT_32BIT(request + reqlen, clen);
670 memcpy(request + reqlen + 4, skey->comment, clen);
672 PUT_32BIT(request, reqlen - 4);
674 ret = agent_query(request, reqlen, &vresponse, &resplen,
677 response = vresponse;
678 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
679 MessageBox(NULL, "The already running Pageant "
680 "refused to add the key.", APPNAME,
681 MB_OK | MB_ICONERROR);
686 if (add234(ssh2keys, skey) != skey) {
687 skey->alg->freekey(skey->data);
688 sfree(skey); /* already present, don't waste RAM */
695 * Create an SSH-1 key list in a malloc'ed buffer; return its
698 static void *make_keylist1(int *length)
702 unsigned char *blob, *p, *ret;
706 * Count up the number and length of keys we hold.
710 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
712 blob = rsa_public_blob(key, &bloblen);
715 len += 4 + strlen(key->comment);
718 /* Allocate the buffer. */
719 p = ret = snewn(len, unsigned char);
720 if (length) *length = len;
724 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
725 blob = rsa_public_blob(key, &bloblen);
726 memcpy(p, blob, bloblen);
729 PUT_32BIT(p, strlen(key->comment));
730 memcpy(p + 4, key->comment, strlen(key->comment));
731 p += 4 + strlen(key->comment);
734 assert(p - ret == len);
739 * Create an SSH-2 key list in a malloc'ed buffer; return its
742 static void *make_keylist2(int *length)
744 struct ssh2_userkey *key;
746 unsigned char *blob, *p, *ret;
750 * Count up the number and length of keys we hold.
754 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
756 len += 4; /* length field */
757 blob = key->alg->public_blob(key->data, &bloblen);
760 len += 4 + strlen(key->comment);
763 /* Allocate the buffer. */
764 p = ret = snewn(len, unsigned char);
765 if (length) *length = len;
768 * Packet header is the obvious five bytes, plus four
769 * bytes for the key count.
773 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
774 blob = key->alg->public_blob(key->data, &bloblen);
775 PUT_32BIT(p, bloblen);
777 memcpy(p, blob, bloblen);
780 PUT_32BIT(p, strlen(key->comment));
781 memcpy(p + 4, key->comment, strlen(key->comment));
782 p += 4 + strlen(key->comment);
785 assert(p - ret == len);
790 * Acquire a keylist1 from the primary Pageant; this means either
791 * calling make_keylist1 (if that's us) or sending a message to the
792 * primary Pageant (if it's not).
794 static void *get_keylist1(int *length)
798 if (already_running) {
799 unsigned char request[5], *response;
802 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
803 PUT_32BIT(request, 4);
805 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
807 response = vresponse;
808 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
811 ret = snewn(resplen-5, unsigned char);
812 memcpy(ret, response+5, resplen-5);
818 ret = make_keylist1(length);
824 * Acquire a keylist2 from the primary Pageant; this means either
825 * calling make_keylist2 (if that's us) or sending a message to the
826 * primary Pageant (if it's not).
828 static void *get_keylist2(int *length)
832 if (already_running) {
833 unsigned char request[5], *response;
837 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
838 PUT_32BIT(request, 4);
840 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
842 response = vresponse;
843 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
846 ret = snewn(resplen-5, unsigned char);
847 memcpy(ret, response+5, resplen-5);
853 ret = make_keylist2(length);
859 * This is the main agent function that answers messages.
861 static void answer_msg(void *msg)
863 unsigned char *p = msg;
864 unsigned char *ret = msg;
865 unsigned char *msgend;
869 * Get the message length.
871 msgend = p + 4 + GET_32BIT(p);
874 * Get the message type.
882 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
884 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
890 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
891 keylist = make_keylist1(&len);
892 if (len + 5 > AGENT_MAX_MSGLEN) {
896 PUT_32BIT(ret, len + 1);
897 memcpy(ret + 5, keylist, len);
901 case SSH2_AGENTC_REQUEST_IDENTITIES:
903 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
909 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
910 keylist = make_keylist2(&len);
911 if (len + 5 > AGENT_MAX_MSGLEN) {
915 PUT_32BIT(ret, len + 1);
916 memcpy(ret + 5, keylist, len);
920 case SSH1_AGENTC_RSA_CHALLENGE:
922 * Reply with either SSH1_AGENT_RSA_RESPONSE or
923 * SSH_AGENT_FAILURE, depending on whether we have that key
927 struct RSAKey reqkey, *key;
928 Bignum challenge, response;
929 unsigned char response_source[48], response_md5[16];
930 struct MD5Context md5c;
934 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
938 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
942 i = ssh1_read_bignum(p, msgend - p, &challenge);
947 freebn(reqkey.exponent);
948 freebn(reqkey.modulus);
952 memcpy(response_source + 32, p, 16);
956 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
957 freebn(reqkey.exponent);
958 freebn(reqkey.modulus);
962 response = rsadecrypt(challenge, key);
963 for (i = 0; i < 32; i++)
964 response_source[i] = bignum_byte(response, 31 - i);
967 MD5Update(&md5c, response_source, 48);
968 MD5Final(response_md5, &md5c);
969 memset(response_source, 0, 48); /* burn the evidence */
970 freebn(response); /* and that evidence */
971 freebn(challenge); /* yes, and that evidence */
972 freebn(reqkey.exponent); /* and free some memory ... */
973 freebn(reqkey.modulus); /* ... while we're at it. */
976 * Packet is the obvious five byte header, plus sixteen
980 PUT_32BIT(ret, len - 4);
981 ret[4] = SSH1_AGENT_RSA_RESPONSE;
982 memcpy(ret + 5, response_md5, 16);
985 case SSH2_AGENTC_SIGN_REQUEST:
987 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
988 * SSH_AGENT_FAILURE, depending on whether we have that key
992 struct ssh2_userkey *key;
994 unsigned char *data, *signature;
995 int datalen, siglen, len;
999 b.len = GET_32BIT(p);
1001 if (msgend < p+b.len)
1007 datalen = GET_32BIT(p);
1009 if (msgend < p+datalen)
1012 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1015 signature = key->alg->sign(key->data, data, datalen, &siglen);
1016 len = 5 + 4 + siglen;
1017 PUT_32BIT(ret, len - 4);
1018 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1019 PUT_32BIT(ret + 5, siglen);
1020 memcpy(ret + 5 + 4, signature, siglen);
1024 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1026 * Add to the list and return SSH_AGENT_SUCCESS, or
1027 * SSH_AGENT_FAILURE if the key was malformed.
1034 key = snew(struct RSAKey);
1035 memset(key, 0, sizeof(struct RSAKey));
1037 n = makekey(p, msgend - p, key, NULL, 1);
1045 n = makeprivate(p, msgend - p, key);
1053 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1061 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1069 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1082 commentlen = GET_32BIT(p);
1084 if (msgend < p+commentlen) {
1090 comment = snewn(commentlen+1, char);
1092 memcpy(comment, p + 4, commentlen);
1093 comment[commentlen] = '\0';
1094 key->comment = comment;
1097 ret[4] = SSH_AGENT_FAILURE;
1098 if (add234(rsakeys, key) == key) {
1100 ret[4] = SSH_AGENT_SUCCESS;
1107 case SSH2_AGENTC_ADD_IDENTITY:
1109 * Add to the list and return SSH_AGENT_SUCCESS, or
1110 * SSH_AGENT_FAILURE if the key was malformed.
1113 struct ssh2_userkey *key;
1114 char *comment, *alg;
1115 int alglen, commlen;
1121 alglen = GET_32BIT(p);
1123 if (msgend < p+alglen)
1128 key = snew(struct ssh2_userkey);
1129 /* Add further algorithm names here. */
1130 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1131 key->alg = &ssh_rsa;
1132 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1133 key->alg = &ssh_dss;
1139 bloblen = msgend - p;
1140 key->data = key->alg->openssh_createkey(&p, &bloblen);
1147 * p has been advanced by openssh_createkey, but
1148 * certainly not _beyond_ the end of the buffer.
1150 assert(p <= msgend);
1153 key->alg->freekey(key->data);
1157 commlen = GET_32BIT(p);
1160 if (msgend < p+commlen) {
1161 key->alg->freekey(key->data);
1165 comment = snewn(commlen + 1, char);
1167 memcpy(comment, p, commlen);
1168 comment[commlen] = '\0';
1170 key->comment = comment;
1173 ret[4] = SSH_AGENT_FAILURE;
1174 if (add234(ssh2keys, key) == key) {
1176 ret[4] = SSH_AGENT_SUCCESS;
1178 key->alg->freekey(key->data);
1179 sfree(key->comment);
1184 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1186 * Remove from the list and return SSH_AGENT_SUCCESS, or
1187 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1191 struct RSAKey reqkey, *key;
1194 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1198 key = find234(rsakeys, &reqkey, NULL);
1199 freebn(reqkey.exponent);
1200 freebn(reqkey.modulus);
1202 ret[4] = SSH_AGENT_FAILURE;
1204 del234(rsakeys, key);
1208 ret[4] = SSH_AGENT_SUCCESS;
1212 case SSH2_AGENTC_REMOVE_IDENTITY:
1214 * Remove from the list and return SSH_AGENT_SUCCESS, or
1215 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1219 struct ssh2_userkey *key;
1224 b.len = GET_32BIT(p);
1227 if (msgend < p+b.len)
1232 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1237 ret[4] = SSH_AGENT_FAILURE;
1239 del234(ssh2keys, key);
1241 key->alg->freekey(key->data);
1243 ret[4] = SSH_AGENT_SUCCESS;
1247 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1249 * Remove all SSH-1 keys. Always returns success.
1252 struct RSAKey *rkey;
1254 while ((rkey = index234(rsakeys, 0)) != NULL) {
1255 del234(rsakeys, rkey);
1262 ret[4] = SSH_AGENT_SUCCESS;
1265 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1267 * Remove all SSH-2 keys. Always returns success.
1270 struct ssh2_userkey *skey;
1272 while ((skey = index234(ssh2keys, 0)) != NULL) {
1273 del234(ssh2keys, skey);
1274 skey->alg->freekey(skey->data);
1280 ret[4] = SSH_AGENT_SUCCESS;
1286 * Unrecognised message. Return SSH_AGENT_FAILURE.
1289 ret[4] = SSH_AGENT_FAILURE;
1295 * Key comparison function for the 2-3-4 tree of RSA keys.
1297 static int cmpkeys_rsa(void *av, void *bv)
1299 struct RSAKey *a = (struct RSAKey *) av;
1300 struct RSAKey *b = (struct RSAKey *) bv;
1307 * Compare by length of moduli.
1309 alen = bignum_bitcount(am);
1310 blen = bignum_bitcount(bm);
1313 else if (alen < blen)
1316 * Now compare by moduli themselves.
1318 alen = (alen + 7) / 8; /* byte count */
1319 while (alen-- > 0) {
1321 abyte = bignum_byte(am, alen);
1322 bbyte = bignum_byte(bm, alen);
1325 else if (abyte < bbyte)
1335 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1337 static int cmpkeys_ssh2(void *av, void *bv)
1339 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1340 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1343 unsigned char *ablob, *bblob;
1347 * Compare purely by public blob.
1349 ablob = a->alg->public_blob(a->data, &alen);
1350 bblob = b->alg->public_blob(b->data, &blen);
1353 for (i = 0; i < alen && i < blen; i++) {
1354 if (ablob[i] < bblob[i]) {
1357 } else if (ablob[i] > bblob[i]) {
1362 if (c == 0 && i < alen)
1363 c = +1; /* a is longer */
1364 if (c == 0 && i < blen)
1365 c = -1; /* a is longer */
1374 * Key comparison function for looking up a blob in the 2-3-4 tree
1377 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1379 struct blob *a = (struct blob *) av;
1380 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1383 unsigned char *ablob, *bblob;
1387 * Compare purely by public blob.
1391 bblob = b->alg->public_blob(b->data, &blen);
1394 for (i = 0; i < alen && i < blen; i++) {
1395 if (ablob[i] < bblob[i]) {
1398 } else if (ablob[i] > bblob[i]) {
1403 if (c == 0 && i < alen)
1404 c = +1; /* a is longer */
1405 if (c == 0 && i < blen)
1406 c = -1; /* a is longer */
1414 * Prompt for a key file to add, and add it.
1416 static void prompt_add_keyfile(void)
1419 char *filelist = snewn(8192, char);
1421 if (!keypath) keypath = filereq_new();
1422 memset(&of, 0, sizeof(of));
1423 of.hwndOwner = hwnd;
1424 of.lpstrFilter = FILTER_KEY_FILES;
1425 of.lpstrCustomFilter = NULL;
1426 of.nFilterIndex = 1;
1427 of.lpstrFile = filelist;
1430 of.lpstrFileTitle = NULL;
1431 of.lpstrTitle = "Select Private Key File";
1432 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1433 if (request_file(keypath, &of, TRUE, FALSE)) {
1434 if(strlen(filelist) > of.nFileOffset)
1435 /* Only one filename returned? */
1436 add_keyfile(filename_from_str(filelist));
1438 /* we are returned a bunch of strings, end to
1439 * end. first string is the directory, the
1440 * rest the filenames. terminated with an
1443 char *dir = filelist;
1444 char *filewalker = filelist + strlen(dir) + 1;
1445 while (*filewalker != '\0') {
1446 char *filename = dupcat(dir, "\\", filewalker, NULL);
1447 add_keyfile(filename_from_str(filename));
1449 filewalker += strlen(filewalker) + 1;
1454 forget_passphrases();
1460 * Dialog-box function for the key list box.
1462 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1463 WPARAM wParam, LPARAM lParam)
1465 struct RSAKey *rkey;
1466 struct ssh2_userkey *skey;
1471 * Centre the window.
1473 { /* centre the window */
1477 hw = GetDesktopWindow();
1478 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1480 (rs.right + rs.left + rd.left - rd.right) / 2,
1481 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1482 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1486 SetWindowLong(hwnd, GWL_EXSTYLE,
1487 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1489 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1491 DestroyWindow(item);
1493 requested_help = FALSE;
1497 static int tabs[] = { 35, 60, 210 };
1498 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1499 sizeof(tabs) / sizeof(*tabs),
1505 switch (LOWORD(wParam)) {
1509 DestroyWindow(hwnd);
1511 case 101: /* add key */
1512 if (HIWORD(wParam) == BN_CLICKED ||
1513 HIWORD(wParam) == BN_DOUBLECLICKED) {
1514 if (passphrase_box) {
1515 MessageBeep(MB_ICONERROR);
1516 SetForegroundWindow(passphrase_box);
1519 prompt_add_keyfile();
1522 case 102: /* remove key */
1523 if (HIWORD(wParam) == BN_CLICKED ||
1524 HIWORD(wParam) == BN_DOUBLECLICKED) {
1529 /* our counter within the array of selected items */
1532 /* get the number of items selected in the list */
1534 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1536 /* none selected? that was silly */
1537 if (numSelected == 0) {
1542 /* get item indices in an array */
1543 selectedArray = snewn(numSelected, int);
1544 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1545 numSelected, (WPARAM)selectedArray);
1547 itemNum = numSelected - 1;
1548 rCount = count234(rsakeys);
1549 sCount = count234(ssh2keys);
1551 /* go through the non-rsakeys until we've covered them all,
1552 * and/or we're out of selected items to check. note that
1553 * we go *backwards*, to avoid complications from deleting
1554 * things hence altering the offset of subsequent items
1556 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1557 skey = index234(ssh2keys, i);
1559 if (selectedArray[itemNum] == rCount + i) {
1560 del234(ssh2keys, skey);
1561 skey->alg->freekey(skey->data);
1567 /* do the same for the rsa keys */
1568 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1569 rkey = index234(rsakeys, i);
1571 if(selectedArray[itemNum] == i) {
1572 del234(rsakeys, rkey);
1579 sfree(selectedArray);
1583 case 103: /* help */
1584 if (HIWORD(wParam) == BN_CLICKED ||
1585 HIWORD(wParam) == BN_DOUBLECLICKED) {
1587 WinHelp(hwnd, help_path, HELP_COMMAND,
1588 (DWORD)"JI(`',`pageant.general')");
1589 requested_help = TRUE;
1597 int id = ((LPHELPINFO)lParam)->iCtrlId;
1600 case 100: topic = "pageant.keylist"; break;
1601 case 101: topic = "pageant.addkey"; break;
1602 case 102: topic = "pageant.remkey"; break;
1605 char *cmd = dupprintf("JI(`',`%s')", topic);
1606 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1608 requested_help = TRUE;
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);
1702 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1703 WPARAM wParam, LPARAM lParam)
1706 static int menuinprogress;
1707 static UINT msgTaskbarCreated = 0;
1711 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1714 if (message==msgTaskbarCreated) {
1716 * Explorer has been restarted, so the tray icon will
1724 if (lParam == WM_RBUTTONUP) {
1726 GetCursorPos(&cursorpos);
1727 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1728 } else if (lParam == WM_LBUTTONDBLCLK) {
1729 /* Run the default menu item. */
1730 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1732 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1736 if (!menuinprogress) {
1739 SetForegroundWindow(hwnd);
1740 ret = TrackPopupMenu(systray_menu,
1741 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1743 wParam, lParam, 0, hwnd, NULL);
1749 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1751 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1753 MessageBox(NULL, "Unable to execute PuTTY!",
1754 "Error", MB_OK | MB_ICONERROR);
1759 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1760 SendMessage(hwnd, WM_CLOSE, 0, 0);
1764 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1766 ShowWindow(keylist, SW_SHOWNORMAL);
1769 * Sometimes the window comes up minimised / hidden for
1770 * no obvious reason. Prevent this. This also brings it
1771 * to the front if it's already present (the user
1772 * selected View Keys because they wanted to _see_ the
1775 SetForegroundWindow(keylist);
1776 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1777 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1780 if (passphrase_box) {
1781 MessageBeep(MB_ICONERROR);
1782 SetForegroundWindow(passphrase_box);
1785 prompt_add_keyfile();
1789 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1791 ShowWindow(aboutbox, SW_SHOWNORMAL);
1793 * Sometimes the window comes up minimised / hidden
1794 * for no obvious reason. Prevent this.
1796 SetForegroundWindow(aboutbox);
1797 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1798 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1803 WinHelp(hwnd, help_path, HELP_COMMAND,
1804 (DWORD)"JI(`',`pageant.general')");
1805 requested_help = TRUE;
1810 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1812 TCHAR buf[MAX_PATH + 1];
1813 TCHAR param[MAX_PATH + 1];
1814 memset(&mii, 0, sizeof(mii));
1815 mii.cbSize = sizeof(mii);
1816 mii.fMask = MIIM_TYPE;
1818 mii.dwTypeData = buf;
1819 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1821 strcat(param, mii.dwTypeData);
1822 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1823 _T(""), SW_SHOW) <= 32) {
1824 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1825 MB_OK | MB_ICONERROR);
1833 if (requested_help) {
1834 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1835 requested_help = FALSE;
1841 COPYDATASTRUCT *cds;
1847 PSID mapowner, procowner;
1848 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1852 cds = (COPYDATASTRUCT *) lParam;
1853 if (cds->dwData != AGENT_COPYDATA_ID)
1854 return 0; /* not our message, mate */
1855 mapname = (char *) cds->lpData;
1856 if (mapname[cds->cbData - 1] != '\0')
1857 return 0; /* failure to be ASCIZ! */
1859 debug(("mapname is :%s:\n", mapname));
1861 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1863 debug(("filemap is %p\n", filemap));
1865 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1869 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1870 GetCurrentProcessId())) ==
1873 debug(("couldn't get handle for process\n"));
1877 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1878 OWNER_SECURITY_INFORMATION,
1879 &procowner, NULL, NULL, NULL,
1880 &psd2) != ERROR_SUCCESS) {
1882 debug(("couldn't get owner info for process\n"));
1885 return 0; /* unable to get security info */
1888 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1889 OWNER_SECURITY_INFORMATION,
1890 &mapowner, NULL, NULL, NULL,
1891 &psd1) != ERROR_SUCCESS)) {
1894 ("couldn't get owner info for filemap: %d\n",
1900 debug(("got security stuff\n"));
1902 if (!EqualSid(mapowner, procowner))
1903 return 0; /* security ID mismatch! */
1905 debug(("security stuff matched\n"));
1911 debug(("security APIs not present\n"));
1915 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1917 debug(("p is %p\n", p));
1920 for (i = 0; i < 5; i++)
1923 ((unsigned char *) p)[i]));}
1929 CloseHandle(filemap);
1934 return DefWindowProc(hwnd, message, wParam, lParam);
1938 * Fork and Exec the command in cmdline. [DBW]
1940 void spawn_cmd(char *cmdline, char * args, int show)
1942 if (ShellExecute(NULL, _T("open"), cmdline,
1943 args, NULL, show) <= (HINSTANCE) 32) {
1945 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1946 (int)GetLastError());
1947 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1953 * This is a can't-happen stub, since Pageant never makes
1954 * asynchronous agent requests.
1956 void agent_schedule_callback(void (*callback)(void *, void *, int),
1957 void *callback_ctx, void *data, int len)
1959 assert(!"We shouldn't get here");
1962 void cleanup_exit(int code) { exit(code); }
1964 int flags = FLAG_SYNCAGENT;
1966 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1971 char *command = NULL;
1974 char **argv, **argstart;
1980 * Determine whether we're an NT system (should have security
1981 * APIs) or a non-NT system (don't do security).
1985 modalfatalbox("Windows refuses to report a version");
1987 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1988 has_security = TRUE;
1990 has_security = FALSE;
1995 * Attempt to get the security API we need.
1997 advapi = LoadLibrary("ADVAPI32.DLL");
1999 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
2000 if (!getsecurityinfo) {
2002 "Unable to access security APIs. Pageant will\n"
2003 "not run, in case it causes a security breach.",
2004 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2009 "This program has been compiled for Win9X and will\n"
2010 "not run on NT, in case it causes a security breach.",
2011 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2018 * See if we can find our Help file.
2021 char b[2048], *p, *q, *r;
2023 GetModuleFileName(NULL, b, sizeof(b) - 1);
2025 p = strrchr(b, '\\');
2026 if (p && p >= r) r = p+1;
2027 q = strrchr(b, ':');
2028 if (q && q >= r) r = q+1;
2029 strcpy(r, PUTTY_HELP_FILE);
2030 if ( (fp = fopen(b, "r")) != NULL) {
2031 help_path = dupstr(b);
2038 * Look for the PuTTY binary (we will enable the saved session
2039 * submenu if we find it).
2042 char b[2048], *p, *q, *r;
2044 GetModuleFileName(NULL, b, sizeof(b) - 1);
2046 p = strrchr(b, '\\');
2047 if (p && p >= r) r = p+1;
2048 q = strrchr(b, ':');
2049 if (q && q >= r) r = q+1;
2050 strcpy(r, "putty.exe");
2051 if ( (fp = fopen(b, "r")) != NULL) {
2052 putty_path = dupstr(b);
2059 * Find out if Pageant is already running.
2061 already_running = agent_exists();
2064 * Initialise storage for RSA keys.
2066 if (!already_running) {
2067 rsakeys = newtree234(cmpkeys_rsa);
2068 ssh2keys = newtree234(cmpkeys_ssh2);
2072 * Initialise storage for short-term passphrase cache.
2074 passphrases = newtree234(NULL);
2077 * Process the command line and add keys as listed on it.
2079 split_into_argv(cmdline, &argc, &argv, &argstart);
2080 for (i = 0; i < argc; i++) {
2081 if (!strcmp(argv[i], "-pgpfp")) {
2084 FreeLibrary(advapi);
2086 } else if (!strcmp(argv[i], "-c")) {
2088 * If we see `-c', then the rest of the
2089 * command line should be treated as a
2090 * command to be spawned.
2093 command = argstart[i+1];
2098 add_keyfile(filename_from_str(argv[i]));
2104 * Forget any passphrase that we retained while going over
2105 * command line keyfiles.
2107 forget_passphrases();
2111 if (command[0] == '"')
2112 args = strchr(++command, '"');
2114 args = strchr(command, ' ');
2117 while(*args && isspace(*args)) args++;
2119 spawn_cmd(command, args, show);
2123 * If Pageant was already running, we leave now. If we haven't
2124 * even taken any auxiliary action (spawned a command or added
2127 if (already_running) {
2128 if (!command && !added_keys) {
2129 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2130 MB_ICONERROR | MB_OK);
2133 FreeLibrary(advapi);
2139 wndclass.lpfnWndProc = WndProc;
2140 wndclass.cbClsExtra = 0;
2141 wndclass.cbWndExtra = 0;
2142 wndclass.hInstance = inst;
2143 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2144 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2145 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2146 wndclass.lpszMenuName = NULL;
2147 wndclass.lpszClassName = APPNAME;
2149 RegisterClass(&wndclass);
2154 hwnd = CreateWindow(APPNAME, APPNAME,
2155 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2156 CW_USEDEFAULT, CW_USEDEFAULT,
2157 100, 100, NULL, NULL, inst, NULL);
2159 /* Set up a system tray icon */
2162 /* Accelerators used: nsvkxa */
2163 systray_menu = CreatePopupMenu();
2165 session_menu = CreateMenu();
2166 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2167 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2168 (UINT) session_menu, "&Saved Sessions");
2169 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2171 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2173 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2174 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2176 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2177 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2178 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2179 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2180 initial_menuitems_count = GetMenuItemCount(session_menu);
2182 /* Set the default menu item. */
2183 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2185 ShowWindow(hwnd, SW_HIDE);
2188 * Main message loop.
2190 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2191 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2192 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2193 TranslateMessage(&msg);
2194 DispatchMessage(&msg);
2198 /* Clean up the system tray icon */
2200 NOTIFYICONDATA tnid;
2202 tnid.cbSize = sizeof(NOTIFYICONDATA);
2206 Shell_NotifyIcon(NIM_DELETE, &tnid);
2208 DestroyMenu(systray_menu);
2211 if (keypath) filereq_free(keypath);
2214 FreeLibrary(advapi);