2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
24 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
29 #define IDI_MAINICON 200
30 #define IDI_TRAYICON 201
32 #define WM_SYSTRAY (WM_APP + 6)
33 #define WM_SYSTRAY2 (WM_APP + 7)
35 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
38 * FIXME: maybe some day we can sort this out ...
40 #define AGENT_MAX_MSGLEN 8192
42 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
43 * wParam are used by Windows, and should be masked off, so we shouldn't
44 * attempt to store information in them. Hence all these identifiers have
45 * the low 4 bits clear. Also, identifiers should < 0xF000. */
47 #define IDM_CLOSE 0x0010
48 #define IDM_VIEWKEYS 0x0020
49 #define IDM_ADDKEY 0x0030
50 #define IDM_HELP 0x0040
51 #define IDM_ABOUT 0x0050
53 #define APPNAME "Pageant"
59 static HMENU systray_menu, session_menu;
60 static int already_running;
62 static char *putty_path;
64 /* CWD for "add key" file requester. */
65 static filereq *keypath = NULL;
67 #define IDM_PUTTY 0x0060
68 #define IDM_SESSIONS_BASE 0x1000
69 #define IDM_SESSIONS_MAX 0x2000
70 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
71 #define PUTTY_DEFAULT "Default%20Settings"
72 static int initial_menuitems_count;
75 * Print a modal (Really Bad) message box and perform a fatal exit.
77 void modalfatalbox(char *fmt, ...)
83 buf = dupvprintf(fmt, ap);
85 MessageBox(hwnd, buf, "Pageant Fatal Error",
86 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
91 /* Un-munge session names out of the registry. */
92 static void unmungestr(char *in, char *out, int outlen)
95 if (*in == '%' && in[1] && in[2]) {
101 j -= (j > 9 ? 7 : 0);
103 *out++ = (i << 4) + j;
117 static tree234 *rsakeys, *ssh2keys;
119 static int has_security;
124 static void *make_keylist1(int *length);
125 static void *make_keylist2(int *length);
126 static void *get_keylist1(int *length);
127 static void *get_keylist2(int *length);
130 * We need this to link with the RSA code, because rsaencrypt()
131 * pads its data with random bytes. Since we only use rsadecrypt()
132 * and the signing functions, which are deterministic, this should
135 * If it _is_ called, there is a _serious_ problem, because it
136 * won't generate true random numbers. So we must scream, panic,
137 * and exit immediately if that should happen.
139 int random_byte(void)
141 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
143 /* this line can't be reached but it placates MSVC's warnings :-) */
148 * Blob structure for passing to the asymmetric SSH-2 key compare
149 * function, prototyped here.
155 static int cmpkeys_ssh2_asymm(void *av, void *bv);
157 struct PassphraseProcStruct {
162 static tree234 *passphrases = NULL;
165 * After processing a list of filenames, we want to forget the
168 static void forget_passphrases(void)
170 while (count234(passphrases) > 0) {
171 char *pp = index234(passphrases, 0);
172 smemclr(pp, strlen(pp));
173 delpos234(passphrases, 0);
179 * Dialog-box function for the Licence box.
181 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
182 WPARAM wParam, LPARAM lParam)
186 SetDlgItemText(hwnd, 1000,
187 "Copyright 1997-2015 Simon Tatham.\r\n\r\n"
189 "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
190 "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
191 "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
192 "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\r\n\r\n"
194 "Permission is hereby granted, free of charge, to any person "
195 "obtaining a copy of this software and associated documentation "
196 "files (the ""Software""), to deal in the Software without restriction, "
197 "including without limitation the rights to use, copy, modify, merge, "
198 "publish, distribute, sublicense, and/or sell copies of the Software, "
199 "and to permit persons to whom the Software is furnished to do so, "
200 "subject to the following conditions:\r\n\r\n"
202 "The above copyright notice and this permission notice shall be "
203 "included in all copies or substantial portions of the Software.\r\n\r\n"
205 "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
206 "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
207 "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
208 "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
209 "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
210 "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
211 "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
212 "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
213 "CONNECTION WITH THE SOFTWARE OR THE USE OR "
214 "OTHER DEALINGS IN THE SOFTWARE."
218 switch (LOWORD(wParam)) {
233 * Dialog-box function for the About box.
235 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
236 WPARAM wParam, LPARAM lParam)
241 char *text = dupprintf
242 ("Pageant\r\n\r\n%s\r\n\r\n%s",
244 "\251 1997-2015 Simon Tatham. All rights reserved.");
245 SetDlgItemText(hwnd, 1000, text);
250 switch (LOWORD(wParam)) {
257 EnableWindow(hwnd, 0);
258 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
259 EnableWindow(hwnd, 1);
260 SetActiveWindow(hwnd);
272 static HWND passphrase_box;
275 * Dialog-box function for the passphrase box.
277 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
278 WPARAM wParam, LPARAM lParam)
280 static char **passphrase = NULL;
281 struct PassphraseProcStruct *p;
285 passphrase_box = hwnd;
289 { /* centre the window */
293 hw = GetDesktopWindow();
294 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
296 (rs.right + rs.left + rd.left - rd.right) / 2,
297 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
298 rd.right - rd.left, rd.bottom - rd.top, TRUE);
301 SetForegroundWindow(hwnd);
302 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
303 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
304 p = (struct PassphraseProcStruct *) lParam;
305 passphrase = p->passphrase;
307 SetDlgItemText(hwnd, 101, p->comment);
308 burnstr(*passphrase);
309 *passphrase = dupstr("");
310 SetDlgItemText(hwnd, 102, *passphrase);
313 switch (LOWORD(wParam)) {
323 case 102: /* edit box */
324 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
325 burnstr(*passphrase);
326 *passphrase = GetDlgItemText_alloc(hwnd, 102);
339 * Warn about the obsolescent key file format.
341 void old_keyfile_warning(void)
343 static const char mbtitle[] = "PuTTY Key File Warning";
344 static const char message[] =
345 "You are loading an SSH-2 private key which has an\n"
346 "old version of the file format. This means your key\n"
347 "file is not fully tamperproof. Future versions of\n"
348 "PuTTY may stop supporting this private key format,\n"
349 "so we recommend you convert your key to the new\n"
352 "You can perform this conversion by loading the key\n"
353 "into PuTTYgen and then saving it again.";
355 MessageBox(NULL, message, mbtitle, MB_OK);
359 * Update the visible key list.
361 static void keylist_update(void)
364 struct ssh2_userkey *skey;
368 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
369 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
370 char listentry[512], *p;
372 * Replace two spaces in the fingerprint with tabs, for
373 * nice alignment in the box.
375 strcpy(listentry, "ssh1\t");
376 p = listentry + strlen(listentry);
377 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
378 p = strchr(listentry, ' ');
381 p = strchr(listentry, ' ');
384 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
385 0, (LPARAM) listentry);
387 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
391 * Replace two spaces in the fingerprint with tabs, for
392 * nice alignment in the box.
394 p = skey->alg->fingerprint(skey->data);
395 listentry = dupprintf("%s\t%s", p, skey->comment);
396 fp_len = strlen(listentry);
399 p = strchr(listentry, ' ');
400 if (p && p < listentry + fp_len)
402 p = strchr(listentry, ' ');
403 if (p && p < listentry + fp_len)
406 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
410 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
415 * This function loads a key from a file and adds it.
417 static void add_keyfile(Filename *filename)
420 struct RSAKey *rkey = NULL;
421 struct ssh2_userkey *skey = NULL;
426 const char *error = NULL;
430 type = key_type(filename);
431 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
432 char *msg = dupprintf("Couldn't load this key (%s)",
433 key_type_to_str(type));
434 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
435 HELPCTXID(errors_cantloadkey));
441 * See if the key is already loaded (in the primary Pageant,
442 * which may or may not be us).
446 unsigned char *keylist, *p;
447 int i, nkeys, bloblen, keylistlen;
449 if (type == SSH_KEYTYPE_SSH1) {
450 if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
451 char *msg = dupprintf("Couldn't load private key (%s)", error);
452 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
453 HELPCTXID(errors_cantloadkey));
457 keylist = get_keylist1(&keylistlen);
459 unsigned char *blob2;
460 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
463 char *msg = dupprintf("Couldn't load private key (%s)", error);
464 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
465 HELPCTXID(errors_cantloadkey));
469 /* For our purposes we want the blob prefixed with its length */
470 blob2 = snewn(bloblen+4, unsigned char);
471 PUT_32BIT(blob2, bloblen);
472 memcpy(blob2 + 4, blob, bloblen);
476 keylist = get_keylist2(&keylistlen);
479 if (keylistlen < 4) {
480 MessageBox(NULL, "Received broken key list?!", APPNAME,
481 MB_OK | MB_ICONERROR);
484 nkeys = toint(GET_32BIT(keylist));
486 MessageBox(NULL, "Received broken key list?!", APPNAME,
487 MB_OK | MB_ICONERROR);
493 for (i = 0; i < nkeys; i++) {
494 if (!memcmp(blob, p, bloblen)) {
495 /* Key is already present; we can now leave. */
500 /* Now skip over public blob */
501 if (type == SSH_KEYTYPE_SSH1) {
502 int n = rsa_public_blob_len(p, keylistlen);
504 MessageBox(NULL, "Received broken key list?!", APPNAME,
505 MB_OK | MB_ICONERROR);
512 if (keylistlen < 4) {
513 MessageBox(NULL, "Received broken key list?!", APPNAME,
514 MB_OK | MB_ICONERROR);
517 n = toint(4 + GET_32BIT(p));
518 if (n < 0 || keylistlen < n) {
519 MessageBox(NULL, "Received broken key list?!", APPNAME,
520 MB_OK | MB_ICONERROR);
526 /* Now skip over comment field */
529 if (keylistlen < 4) {
530 MessageBox(NULL, "Received broken key list?!", APPNAME,
531 MB_OK | MB_ICONERROR);
534 n = toint(4 + GET_32BIT(p));
535 if (n < 0 || keylistlen < n) {
536 MessageBox(NULL, "Received broken key list?!", APPNAME,
537 MB_OK | MB_ICONERROR);
552 if (type == SSH_KEYTYPE_SSH1)
553 needs_pass = rsakey_encrypted(filename, &comment);
555 needs_pass = ssh2_userkey_encrypted(filename, &comment);
557 if (type == SSH_KEYTYPE_SSH1)
558 rkey = snew(struct RSAKey);
566 /* try all the remembered passphrases first */
567 char *pp = index234(passphrases, attempts);
569 passphrase = dupstr(pp);
572 struct PassphraseProcStruct pps;
574 pps.passphrase = &passphrase;
575 pps.comment = comment;
578 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
579 NULL, PassphraseProc, (LPARAM) &pps);
580 passphrase_box = NULL;
584 if (type == SSH_KEYTYPE_SSH1)
586 return; /* operation cancelled */
589 assert(passphrase != NULL);
592 passphrase = dupstr("");
594 if (type == SSH_KEYTYPE_SSH1)
595 ret = loadrsakey(filename, rkey, passphrase, &error);
597 skey = ssh2_load_userkey(filename, passphrase, &error);
598 if (skey == SSH2_WRONG_PASSPHRASE)
608 if(original_pass && ret) {
609 /* If they typed in an ok passphrase, remember it */
610 addpos234(passphrases, passphrase, 0);
612 /* Otherwise, destroy it */
620 char *msg = dupprintf("Couldn't load private key (%s)", error);
621 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
622 HELPCTXID(errors_cantloadkey));
624 if (type == SSH_KEYTYPE_SSH1)
628 if (type == SSH_KEYTYPE_SSH1) {
629 if (already_running) {
630 unsigned char *request, *response;
632 int reqlen, clen, resplen, ret;
634 clen = strlen(rkey->comment);
636 reqlen = 4 + 1 + /* length, message type */
638 ssh1_bignum_length(rkey->modulus) +
639 ssh1_bignum_length(rkey->exponent) +
640 ssh1_bignum_length(rkey->private_exponent) +
641 ssh1_bignum_length(rkey->iqmp) +
642 ssh1_bignum_length(rkey->p) +
643 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
646 request = snewn(reqlen, unsigned char);
648 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
650 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
652 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
653 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
655 ssh1_write_bignum(request + reqlen,
656 rkey->private_exponent);
657 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
658 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
659 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
660 PUT_32BIT(request + reqlen, clen);
661 memcpy(request + reqlen + 4, rkey->comment, clen);
663 PUT_32BIT(request, reqlen - 4);
665 ret = agent_query(request, reqlen, &vresponse, &resplen,
668 response = vresponse;
669 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
670 MessageBox(NULL, "The already running Pageant "
671 "refused to add the key.", APPNAME,
672 MB_OK | MB_ICONERROR);
677 if (add234(rsakeys, rkey) != rkey)
678 sfree(rkey); /* already present, don't waste RAM */
681 if (already_running) {
682 unsigned char *request, *response;
684 int reqlen, alglen, clen, keybloblen, resplen, ret;
685 alglen = strlen(skey->alg->name);
686 clen = strlen(skey->comment);
688 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
690 reqlen = 4 + 1 + /* length, message type */
691 4 + alglen + /* algorithm name */
692 keybloblen + /* key data */
693 4 + clen /* comment */
696 request = snewn(reqlen, unsigned char);
698 request[4] = SSH2_AGENTC_ADD_IDENTITY;
700 PUT_32BIT(request + reqlen, alglen);
702 memcpy(request + reqlen, skey->alg->name, alglen);
704 reqlen += skey->alg->openssh_fmtkey(skey->data,
707 PUT_32BIT(request + reqlen, clen);
708 memcpy(request + reqlen + 4, skey->comment, clen);
710 PUT_32BIT(request, reqlen - 4);
712 ret = agent_query(request, reqlen, &vresponse, &resplen,
715 response = vresponse;
716 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
717 MessageBox(NULL, "The already running Pageant "
718 "refused to add the key.", APPNAME,
719 MB_OK | MB_ICONERROR);
724 if (add234(ssh2keys, skey) != skey) {
725 skey->alg->freekey(skey->data);
726 sfree(skey); /* already present, don't waste RAM */
733 * Create an SSH-1 key list in a malloc'ed buffer; return its
736 static void *make_keylist1(int *length)
740 unsigned char *blob, *p, *ret;
744 * Count up the number and length of keys we hold.
748 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
750 blob = rsa_public_blob(key, &bloblen);
753 len += 4 + strlen(key->comment);
756 /* Allocate the buffer. */
757 p = ret = snewn(len, unsigned char);
758 if (length) *length = len;
762 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
763 blob = rsa_public_blob(key, &bloblen);
764 memcpy(p, blob, bloblen);
767 PUT_32BIT(p, strlen(key->comment));
768 memcpy(p + 4, key->comment, strlen(key->comment));
769 p += 4 + strlen(key->comment);
772 assert(p - ret == len);
777 * Create an SSH-2 key list in a malloc'ed buffer; return its
780 static void *make_keylist2(int *length)
782 struct ssh2_userkey *key;
784 unsigned char *blob, *p, *ret;
788 * Count up the number and length of keys we hold.
792 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
794 len += 4; /* length field */
795 blob = key->alg->public_blob(key->data, &bloblen);
798 len += 4 + strlen(key->comment);
801 /* Allocate the buffer. */
802 p = ret = snewn(len, unsigned char);
803 if (length) *length = len;
806 * Packet header is the obvious five bytes, plus four
807 * bytes for the key count.
811 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
812 blob = key->alg->public_blob(key->data, &bloblen);
813 PUT_32BIT(p, bloblen);
815 memcpy(p, blob, bloblen);
818 PUT_32BIT(p, strlen(key->comment));
819 memcpy(p + 4, key->comment, strlen(key->comment));
820 p += 4 + strlen(key->comment);
823 assert(p - ret == len);
828 * Acquire a keylist1 from the primary Pageant; this means either
829 * calling make_keylist1 (if that's us) or sending a message to the
830 * primary Pageant (if it's not).
832 static void *get_keylist1(int *length)
836 if (already_running) {
837 unsigned char request[5], *response;
840 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
841 PUT_32BIT(request, 4);
843 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
845 response = vresponse;
846 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
851 ret = snewn(resplen-5, unsigned char);
852 memcpy(ret, response+5, resplen-5);
858 ret = make_keylist1(length);
864 * Acquire a keylist2 from the primary Pageant; this means either
865 * calling make_keylist2 (if that's us) or sending a message to the
866 * primary Pageant (if it's not).
868 static void *get_keylist2(int *length)
872 if (already_running) {
873 unsigned char request[5], *response;
877 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
878 PUT_32BIT(request, 4);
880 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
882 response = vresponse;
883 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
888 ret = snewn(resplen-5, unsigned char);
889 memcpy(ret, response+5, resplen-5);
895 ret = make_keylist2(length);
901 * This is the main agent function that answers messages.
903 static void answer_msg(void *msg)
905 unsigned char *p = msg;
906 unsigned char *ret = msg;
907 unsigned char *msgend;
911 * Get the message length.
913 msgend = p + 4 + GET_32BIT(p);
916 * Get the message type.
924 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
926 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
932 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
933 keylist = make_keylist1(&len);
934 if (len + 5 > AGENT_MAX_MSGLEN) {
938 PUT_32BIT(ret, len + 1);
939 memcpy(ret + 5, keylist, len);
943 case SSH2_AGENTC_REQUEST_IDENTITIES:
945 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
951 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
952 keylist = make_keylist2(&len);
953 if (len + 5 > AGENT_MAX_MSGLEN) {
957 PUT_32BIT(ret, len + 1);
958 memcpy(ret + 5, keylist, len);
962 case SSH1_AGENTC_RSA_CHALLENGE:
964 * Reply with either SSH1_AGENT_RSA_RESPONSE or
965 * SSH_AGENT_FAILURE, depending on whether we have that key
969 struct RSAKey reqkey, *key;
970 Bignum challenge, response;
971 unsigned char response_source[48], response_md5[16];
972 struct MD5Context md5c;
976 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
980 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
982 freebn(reqkey.exponent);
986 i = ssh1_read_bignum(p, msgend - p, &challenge);
988 freebn(reqkey.exponent);
989 freebn(reqkey.modulus);
994 freebn(reqkey.exponent);
995 freebn(reqkey.modulus);
999 memcpy(response_source + 32, p, 16);
1002 GET_32BIT(p) != 1 ||
1003 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
1004 freebn(reqkey.exponent);
1005 freebn(reqkey.modulus);
1009 response = rsadecrypt(challenge, key);
1010 for (i = 0; i < 32; i++)
1011 response_source[i] = bignum_byte(response, 31 - i);
1014 MD5Update(&md5c, response_source, 48);
1015 MD5Final(response_md5, &md5c);
1016 smemclr(response_source, 48); /* burn the evidence */
1017 freebn(response); /* and that evidence */
1018 freebn(challenge); /* yes, and that evidence */
1019 freebn(reqkey.exponent); /* and free some memory ... */
1020 freebn(reqkey.modulus); /* ... while we're at it. */
1023 * Packet is the obvious five byte header, plus sixteen
1027 PUT_32BIT(ret, len - 4);
1028 ret[4] = SSH1_AGENT_RSA_RESPONSE;
1029 memcpy(ret + 5, response_md5, 16);
1032 case SSH2_AGENTC_SIGN_REQUEST:
1034 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
1035 * SSH_AGENT_FAILURE, depending on whether we have that key
1039 struct ssh2_userkey *key;
1041 unsigned char *data, *signature;
1042 int datalen, siglen, len;
1046 b.len = toint(GET_32BIT(p));
1047 if (b.len < 0 || b.len > msgend - (p+4))
1054 datalen = toint(GET_32BIT(p));
1056 if (datalen < 0 || datalen > msgend - p)
1059 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1062 signature = key->alg->sign(key->data, data, datalen, &siglen);
1063 len = 5 + 4 + siglen;
1064 PUT_32BIT(ret, len - 4);
1065 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1066 PUT_32BIT(ret + 5, siglen);
1067 memcpy(ret + 5 + 4, signature, siglen);
1071 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1073 * Add to the list and return SSH_AGENT_SUCCESS, or
1074 * SSH_AGENT_FAILURE if the key was malformed.
1081 key = snew(struct RSAKey);
1082 memset(key, 0, sizeof(struct RSAKey));
1084 n = makekey(p, msgend - p, key, NULL, 1);
1092 n = makeprivate(p, msgend - p, key);
1100 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1108 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1116 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1129 commentlen = toint(GET_32BIT(p));
1131 if (commentlen < 0 || commentlen > msgend - p) {
1137 comment = snewn(commentlen+1, char);
1139 memcpy(comment, p + 4, commentlen);
1140 comment[commentlen] = '\0';
1141 key->comment = comment;
1144 ret[4] = SSH_AGENT_FAILURE;
1145 if (add234(rsakeys, key) == key) {
1147 ret[4] = SSH_AGENT_SUCCESS;
1154 case SSH2_AGENTC_ADD_IDENTITY:
1156 * Add to the list and return SSH_AGENT_SUCCESS, or
1157 * SSH_AGENT_FAILURE if the key was malformed.
1160 struct ssh2_userkey *key;
1161 char *comment, *alg;
1162 int alglen, commlen;
1168 alglen = toint(GET_32BIT(p));
1170 if (alglen < 0 || alglen > msgend - p)
1175 key = snew(struct ssh2_userkey);
1176 /* Add further algorithm names here. */
1177 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1178 key->alg = &ssh_rsa;
1179 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1180 key->alg = &ssh_dss;
1186 bloblen = msgend - p;
1187 key->data = key->alg->openssh_createkey(&p, &bloblen);
1194 * p has been advanced by openssh_createkey, but
1195 * certainly not _beyond_ the end of the buffer.
1197 assert(p <= msgend);
1200 key->alg->freekey(key->data);
1204 commlen = toint(GET_32BIT(p));
1207 if (commlen < 0 || commlen > msgend - p) {
1208 key->alg->freekey(key->data);
1212 comment = snewn(commlen + 1, char);
1214 memcpy(comment, p, commlen);
1215 comment[commlen] = '\0';
1217 key->comment = comment;
1220 ret[4] = SSH_AGENT_FAILURE;
1221 if (add234(ssh2keys, key) == key) {
1223 ret[4] = SSH_AGENT_SUCCESS;
1225 key->alg->freekey(key->data);
1226 sfree(key->comment);
1231 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1233 * Remove from the list and return SSH_AGENT_SUCCESS, or
1234 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1238 struct RSAKey reqkey, *key;
1241 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1245 key = find234(rsakeys, &reqkey, NULL);
1246 freebn(reqkey.exponent);
1247 freebn(reqkey.modulus);
1249 ret[4] = SSH_AGENT_FAILURE;
1251 del234(rsakeys, key);
1255 ret[4] = SSH_AGENT_SUCCESS;
1259 case SSH2_AGENTC_REMOVE_IDENTITY:
1261 * Remove from the list and return SSH_AGENT_SUCCESS, or
1262 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1266 struct ssh2_userkey *key;
1271 b.len = toint(GET_32BIT(p));
1274 if (b.len < 0 || b.len > msgend - p)
1279 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1284 ret[4] = SSH_AGENT_FAILURE;
1286 del234(ssh2keys, key);
1288 key->alg->freekey(key->data);
1290 ret[4] = SSH_AGENT_SUCCESS;
1294 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1296 * Remove all SSH-1 keys. Always returns success.
1299 struct RSAKey *rkey;
1301 while ((rkey = index234(rsakeys, 0)) != NULL) {
1302 del234(rsakeys, rkey);
1309 ret[4] = SSH_AGENT_SUCCESS;
1312 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1314 * Remove all SSH-2 keys. Always returns success.
1317 struct ssh2_userkey *skey;
1319 while ((skey = index234(ssh2keys, 0)) != NULL) {
1320 del234(ssh2keys, skey);
1321 skey->alg->freekey(skey->data);
1327 ret[4] = SSH_AGENT_SUCCESS;
1333 * Unrecognised message. Return SSH_AGENT_FAILURE.
1336 ret[4] = SSH_AGENT_FAILURE;
1342 * Key comparison function for the 2-3-4 tree of RSA keys.
1344 static int cmpkeys_rsa(void *av, void *bv)
1346 struct RSAKey *a = (struct RSAKey *) av;
1347 struct RSAKey *b = (struct RSAKey *) bv;
1354 * Compare by length of moduli.
1356 alen = bignum_bitcount(am);
1357 blen = bignum_bitcount(bm);
1360 else if (alen < blen)
1363 * Now compare by moduli themselves.
1365 alen = (alen + 7) / 8; /* byte count */
1366 while (alen-- > 0) {
1368 abyte = bignum_byte(am, alen);
1369 bbyte = bignum_byte(bm, alen);
1372 else if (abyte < bbyte)
1382 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1384 static int cmpkeys_ssh2(void *av, void *bv)
1386 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1387 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1390 unsigned char *ablob, *bblob;
1394 * Compare purely by public blob.
1396 ablob = a->alg->public_blob(a->data, &alen);
1397 bblob = b->alg->public_blob(b->data, &blen);
1400 for (i = 0; i < alen && i < blen; i++) {
1401 if (ablob[i] < bblob[i]) {
1404 } else if (ablob[i] > bblob[i]) {
1409 if (c == 0 && i < alen)
1410 c = +1; /* a is longer */
1411 if (c == 0 && i < blen)
1412 c = -1; /* a is longer */
1421 * Key comparison function for looking up a blob in the 2-3-4 tree
1424 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1426 struct blob *a = (struct blob *) av;
1427 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1430 unsigned char *ablob, *bblob;
1434 * Compare purely by public blob.
1438 bblob = b->alg->public_blob(b->data, &blen);
1441 for (i = 0; i < alen && i < blen; i++) {
1442 if (ablob[i] < bblob[i]) {
1445 } else if (ablob[i] > bblob[i]) {
1450 if (c == 0 && i < alen)
1451 c = +1; /* a is longer */
1452 if (c == 0 && i < blen)
1453 c = -1; /* a is longer */
1461 * Prompt for a key file to add, and add it.
1463 static void prompt_add_keyfile(void)
1466 char *filelist = snewn(8192, char);
1468 if (!keypath) keypath = filereq_new();
1469 memset(&of, 0, sizeof(of));
1470 of.hwndOwner = hwnd;
1471 of.lpstrFilter = FILTER_KEY_FILES;
1472 of.lpstrCustomFilter = NULL;
1473 of.nFilterIndex = 1;
1474 of.lpstrFile = filelist;
1477 of.lpstrFileTitle = NULL;
1478 of.lpstrTitle = "Select Private Key File";
1479 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1480 if (request_file(keypath, &of, TRUE, FALSE)) {
1481 if(strlen(filelist) > of.nFileOffset) {
1482 /* Only one filename returned? */
1483 Filename *fn = filename_from_str(filelist);
1487 /* we are returned a bunch of strings, end to
1488 * end. first string is the directory, the
1489 * rest the filenames. terminated with an
1492 char *dir = filelist;
1493 char *filewalker = filelist + strlen(dir) + 1;
1494 while (*filewalker != '\0') {
1495 char *filename = dupcat(dir, "\\", filewalker, NULL);
1496 Filename *fn = filename_from_str(filename);
1500 filewalker += strlen(filewalker) + 1;
1505 forget_passphrases();
1511 * Dialog-box function for the key list box.
1513 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1514 WPARAM wParam, LPARAM lParam)
1516 struct RSAKey *rkey;
1517 struct ssh2_userkey *skey;
1522 * Centre the window.
1524 { /* centre the window */
1528 hw = GetDesktopWindow();
1529 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1531 (rs.right + rs.left + rd.left - rd.right) / 2,
1532 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1533 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1537 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
1538 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
1541 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1543 DestroyWindow(item);
1548 static int tabs[] = { 35, 60, 210 };
1549 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1550 sizeof(tabs) / sizeof(*tabs),
1556 switch (LOWORD(wParam)) {
1560 DestroyWindow(hwnd);
1562 case 101: /* add key */
1563 if (HIWORD(wParam) == BN_CLICKED ||
1564 HIWORD(wParam) == BN_DOUBLECLICKED) {
1565 if (passphrase_box) {
1566 MessageBeep(MB_ICONERROR);
1567 SetForegroundWindow(passphrase_box);
1570 prompt_add_keyfile();
1573 case 102: /* remove key */
1574 if (HIWORD(wParam) == BN_CLICKED ||
1575 HIWORD(wParam) == BN_DOUBLECLICKED) {
1580 /* our counter within the array of selected items */
1583 /* get the number of items selected in the list */
1585 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1587 /* none selected? that was silly */
1588 if (numSelected == 0) {
1593 /* get item indices in an array */
1594 selectedArray = snewn(numSelected, int);
1595 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1596 numSelected, (WPARAM)selectedArray);
1598 itemNum = numSelected - 1;
1599 rCount = count234(rsakeys);
1600 sCount = count234(ssh2keys);
1602 /* go through the non-rsakeys until we've covered them all,
1603 * and/or we're out of selected items to check. note that
1604 * we go *backwards*, to avoid complications from deleting
1605 * things hence altering the offset of subsequent items
1607 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1608 skey = index234(ssh2keys, i);
1610 if (selectedArray[itemNum] == rCount + i) {
1611 del234(ssh2keys, skey);
1612 skey->alg->freekey(skey->data);
1618 /* do the same for the rsa keys */
1619 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1620 rkey = index234(rsakeys, i);
1622 if(selectedArray[itemNum] == i) {
1623 del234(rsakeys, rkey);
1630 sfree(selectedArray);
1634 case 103: /* help */
1635 if (HIWORD(wParam) == BN_CLICKED ||
1636 HIWORD(wParam) == BN_DOUBLECLICKED) {
1637 launch_help(hwnd, WINHELP_CTX_pageant_general);
1644 int id = ((LPHELPINFO)lParam)->iCtrlId;
1647 case 100: topic = WINHELP_CTX_pageant_keylist; break;
1648 case 101: topic = WINHELP_CTX_pageant_addkey; break;
1649 case 102: topic = WINHELP_CTX_pageant_remkey; break;
1652 launch_help(hwnd, topic);
1660 DestroyWindow(hwnd);
1666 /* Set up a system tray icon */
1667 static BOOL AddTrayIcon(HWND hwnd)
1670 NOTIFYICONDATA tnid;
1673 #ifdef NIM_SETVERSION
1675 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1678 tnid.cbSize = sizeof(NOTIFYICONDATA);
1680 tnid.uID = 1; /* unique within this systray use */
1681 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1682 tnid.uCallbackMessage = WM_SYSTRAY;
1683 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
1684 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1686 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1688 if (hicon) DestroyIcon(hicon);
1693 /* Update the saved-sessions menu. */
1694 static void update_sessions(void)
1698 TCHAR buf[MAX_PATH + 1];
1701 int index_key, index_menu;
1706 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1709 for(num_entries = GetMenuItemCount(session_menu);
1710 num_entries > initial_menuitems_count;
1712 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1717 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1718 TCHAR session_name[MAX_PATH + 1];
1719 unmungestr(buf, session_name, MAX_PATH);
1720 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1721 memset(&mii, 0, sizeof(mii));
1722 mii.cbSize = sizeof(mii);
1723 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1724 mii.fType = MFT_STRING;
1725 mii.fState = MFS_ENABLED;
1726 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1727 mii.dwTypeData = session_name;
1728 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1736 if(index_menu == 0) {
1737 mii.cbSize = sizeof(mii);
1738 mii.fMask = MIIM_TYPE | MIIM_STATE;
1739 mii.fType = MFT_STRING;
1740 mii.fState = MFS_GRAYED;
1741 mii.dwTypeData = _T("(No sessions)");
1742 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1748 * Versions of Pageant prior to 0.61 expected this SID on incoming
1749 * communications. For backwards compatibility, and more particularly
1750 * for compatibility with derived works of PuTTY still using the old
1751 * Pageant client code, we accept it as an alternative to the one
1752 * returned from get_user_sid() in winpgntc.c.
1754 PSID get_default_sid(void)
1758 PSECURITY_DESCRIPTOR psd = NULL;
1759 PSID sid = NULL, copy = NULL, ret = NULL;
1761 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1762 GetCurrentProcessId())) == NULL)
1765 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1766 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1769 sidlen = GetLengthSid(sid);
1771 copy = (PSID)smalloc(sidlen);
1773 if (!CopySid(sidlen, copy, sid))
1776 /* Success. Move sid into the return value slot, and null it out
1777 * to stop the cleanup code freeing it. */
1793 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1794 WPARAM wParam, LPARAM lParam)
1797 static int menuinprogress;
1798 static UINT msgTaskbarCreated = 0;
1802 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1805 if (message==msgTaskbarCreated) {
1807 * Explorer has been restarted, so the tray icon will
1815 if (lParam == WM_RBUTTONUP) {
1817 GetCursorPos(&cursorpos);
1818 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1819 } else if (lParam == WM_LBUTTONDBLCLK) {
1820 /* Run the default menu item. */
1821 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1823 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1827 if (!menuinprogress) {
1830 SetForegroundWindow(hwnd);
1831 ret = TrackPopupMenu(systray_menu,
1832 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1834 wParam, lParam, 0, hwnd, NULL);
1840 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1842 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1844 MessageBox(NULL, "Unable to execute PuTTY!",
1845 "Error", MB_OK | MB_ICONERROR);
1850 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1851 SendMessage(hwnd, WM_CLOSE, 0, 0);
1855 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1857 ShowWindow(keylist, SW_SHOWNORMAL);
1860 * Sometimes the window comes up minimised / hidden for
1861 * no obvious reason. Prevent this. This also brings it
1862 * to the front if it's already present (the user
1863 * selected View Keys because they wanted to _see_ the
1866 SetForegroundWindow(keylist);
1867 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1868 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1871 if (passphrase_box) {
1872 MessageBeep(MB_ICONERROR);
1873 SetForegroundWindow(passphrase_box);
1876 prompt_add_keyfile();
1880 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1882 ShowWindow(aboutbox, SW_SHOWNORMAL);
1884 * Sometimes the window comes up minimised / hidden
1885 * for no obvious reason. Prevent this.
1887 SetForegroundWindow(aboutbox);
1888 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1889 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1893 launch_help(hwnd, WINHELP_CTX_pageant_general);
1897 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1899 TCHAR buf[MAX_PATH + 1];
1900 TCHAR param[MAX_PATH + 1];
1901 memset(&mii, 0, sizeof(mii));
1902 mii.cbSize = sizeof(mii);
1903 mii.fMask = MIIM_TYPE;
1905 mii.dwTypeData = buf;
1906 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1908 strcat(param, mii.dwTypeData);
1909 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1910 _T(""), SW_SHOW) <= 32) {
1911 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1912 MB_OK | MB_ICONERROR);
1925 COPYDATASTRUCT *cds;
1930 PSID mapowner, ourself, ourself2;
1932 PSECURITY_DESCRIPTOR psd = NULL;
1935 cds = (COPYDATASTRUCT *) lParam;
1936 if (cds->dwData != AGENT_COPYDATA_ID)
1937 return 0; /* not our message, mate */
1938 mapname = (char *) cds->lpData;
1939 if (mapname[cds->cbData - 1] != '\0')
1940 return 0; /* failure to be ASCIZ! */
1942 debug(("mapname is :%s:\n", mapname));
1944 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1946 debug(("filemap is %p\n", filemap));
1948 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1952 if ((ourself = get_user_sid()) == NULL) {
1954 debug(("couldn't get user SID\n"));
1956 CloseHandle(filemap);
1960 if ((ourself2 = get_default_sid()) == NULL) {
1962 debug(("couldn't get default SID\n"));
1964 CloseHandle(filemap);
1969 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1970 OWNER_SECURITY_INFORMATION,
1971 &mapowner, NULL, NULL, NULL,
1972 &psd) != ERROR_SUCCESS)) {
1974 debug(("couldn't get owner info for filemap: %d\n",
1977 CloseHandle(filemap);
1984 LPTSTR ours, ours2, theirs;
1985 ConvertSidToStringSid(mapowner, &theirs);
1986 ConvertSidToStringSid(ourself, &ours);
1987 ConvertSidToStringSid(ourself2, &ours2);
1988 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1989 " theirs=%s\n", ours, ours2, theirs));
1995 if (!EqualSid(mapowner, ourself) &&
1996 !EqualSid(mapowner, ourself2)) {
1997 CloseHandle(filemap);
2001 return 0; /* security ID mismatch! */
2004 debug(("security stuff matched\n"));
2011 debug(("security APIs not present\n"));
2015 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
2017 debug(("p is %p\n", p));
2020 for (i = 0; i < 5; i++)
2021 debug(("p[%d]=%02x\n", i,
2022 ((unsigned char *) p)[i]));
2029 CloseHandle(filemap);
2034 return DefWindowProc(hwnd, message, wParam, lParam);
2038 * Fork and Exec the command in cmdline. [DBW]
2040 void spawn_cmd(char *cmdline, char * args, int show)
2042 if (ShellExecute(NULL, _T("open"), cmdline,
2043 args, NULL, show) <= (HINSTANCE) 32) {
2045 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
2046 (int)GetLastError());
2047 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
2053 * This is a can't-happen stub, since Pageant never makes
2054 * asynchronous agent requests.
2056 void agent_schedule_callback(void (*callback)(void *, void *, int),
2057 void *callback_ctx, void *data, int len)
2059 assert(!"We shouldn't get here");
2062 void cleanup_exit(int code)
2068 int flags = FLAG_SYNCAGENT;
2070 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
2074 char *command = NULL;
2077 char **argv, **argstart;
2083 * Determine whether we're an NT system (should have security
2084 * APIs) or a non-NT system (don't do security).
2088 modalfatalbox("Windows refuses to report a version");
2090 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
2091 has_security = TRUE;
2093 has_security = FALSE;
2098 * Attempt to get the security API we need.
2100 if (!got_advapi()) {
2102 "Unable to access security APIs. Pageant will\n"
2103 "not run, in case it causes a security breach.",
2104 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2109 "This program has been compiled for Win9X and will\n"
2110 "not run on NT, in case it causes a security breach.",
2111 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2117 * See if we can find our Help file.
2122 * Look for the PuTTY binary (we will enable the saved session
2123 * submenu if we find it).
2126 char b[2048], *p, *q, *r;
2128 GetModuleFileName(NULL, b, sizeof(b) - 16);
2130 p = strrchr(b, '\\');
2131 if (p && p >= r) r = p+1;
2132 q = strrchr(b, ':');
2133 if (q && q >= r) r = q+1;
2134 strcpy(r, "putty.exe");
2135 if ( (fp = fopen(b, "r")) != NULL) {
2136 putty_path = dupstr(b);
2143 * Find out if Pageant is already running.
2145 already_running = agent_exists();
2148 * Initialise storage for RSA keys.
2150 if (!already_running) {
2151 rsakeys = newtree234(cmpkeys_rsa);
2152 ssh2keys = newtree234(cmpkeys_ssh2);
2156 * Initialise storage for short-term passphrase cache.
2158 passphrases = newtree234(NULL);
2161 * Process the command line and add keys as listed on it.
2163 split_into_argv(cmdline, &argc, &argv, &argstart);
2164 for (i = 0; i < argc; i++) {
2165 if (!strcmp(argv[i], "-pgpfp")) {
2168 } else if (!strcmp(argv[i], "-c")) {
2170 * If we see `-c', then the rest of the
2171 * command line should be treated as a
2172 * command to be spawned.
2175 command = argstart[i+1];
2180 Filename *fn = filename_from_str(argv[i]);
2188 * Forget any passphrase that we retained while going over
2189 * command line keyfiles.
2191 forget_passphrases();
2195 if (command[0] == '"')
2196 args = strchr(++command, '"');
2198 args = strchr(command, ' ');
2201 while(*args && isspace(*args)) args++;
2203 spawn_cmd(command, args, show);
2207 * If Pageant was already running, we leave now. If we haven't
2208 * even taken any auxiliary action (spawned a command or added
2211 if (already_running) {
2212 if (!command && !added_keys) {
2213 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2214 MB_ICONERROR | MB_OK);
2221 wndclass.lpfnWndProc = WndProc;
2222 wndclass.cbClsExtra = 0;
2223 wndclass.cbWndExtra = 0;
2224 wndclass.hInstance = inst;
2225 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2226 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2227 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2228 wndclass.lpszMenuName = NULL;
2229 wndclass.lpszClassName = APPNAME;
2231 RegisterClass(&wndclass);
2236 hwnd = CreateWindow(APPNAME, APPNAME,
2237 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2238 CW_USEDEFAULT, CW_USEDEFAULT,
2239 100, 100, NULL, NULL, inst, NULL);
2241 /* Set up a system tray icon */
2244 /* Accelerators used: nsvkxa */
2245 systray_menu = CreatePopupMenu();
2247 session_menu = CreateMenu();
2248 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2249 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2250 (UINT) session_menu, "&Saved Sessions");
2251 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2253 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2255 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2256 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2258 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2259 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2260 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2261 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2262 initial_menuitems_count = GetMenuItemCount(session_menu);
2264 /* Set the default menu item. */
2265 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2267 ShowWindow(hwnd, SW_HIDE);
2270 * Main message loop.
2272 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2273 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2274 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2275 TranslateMessage(&msg);
2276 DispatchMessage(&msg);
2280 /* Clean up the system tray icon */
2282 NOTIFYICONDATA tnid;
2284 tnid.cbSize = sizeof(NOTIFYICONDATA);
2288 Shell_NotifyIcon(NIM_DELETE, &tnid);
2290 DestroyMenu(systray_menu);
2293 if (keypath) filereq_free(keypath);
2295 cleanup_exit(msg.wParam);
2296 return msg.wParam; /* just in case optimiser complains */