2 * Pageant: the PuTTY Authentication Agent.
22 #define IDI_MAINICON 200
23 #define IDI_TRAYICON 201
25 #define WM_XUSER (WM_USER + 0x2000)
26 #define WM_SYSTRAY (WM_XUSER + 6)
27 #define WM_SYSTRAY2 (WM_XUSER + 7)
29 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
32 * FIXME: maybe some day we can sort this out ...
34 #define AGENT_MAX_MSGLEN 8192
36 #define IDM_CLOSE 0x0010
37 #define IDM_VIEWKEYS 0x0020
38 #define IDM_ADDKEY 0x0030
39 #define IDM_HELP 0x0040
40 #define IDM_ABOUT 0x0050
42 #define APPNAME "Pageant"
46 static HINSTANCE instance;
47 static HWND main_hwnd;
50 static HMENU systray_menu, session_menu;
51 static int already_running;
52 static int requested_help;
55 static char *putty_path;
57 #define IDM_PUTTY 0x0060
58 #define IDM_SESSIONS_BASE 0x1000
59 #define IDM_SESSIONS_MAX 0x2000
60 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
61 #define PUTTY_DEFAULT "Default%20Settings"
62 static int initial_menuitems_count;
65 * Print a modal (Really Bad) message box and perform a fatal exit.
67 void modalfatalbox(char *fmt, ...)
73 buf = dupvprintf(fmt, ap);
75 MessageBox(main_hwnd, buf, "Pageant Fatal Error",
76 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
81 /* Un-munge session names out of the registry. */
82 static void unmungestr(char *in, char *out, int outlen)
85 if (*in == '%' && in[1] && in[2]) {
93 *out++ = (i << 4) + j;
107 static tree234 *rsakeys, *ssh2keys;
109 static int has_security;
111 typedef DWORD(WINAPI * gsi_fn_t)
112 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
113 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
114 static gsi_fn_t getsecurityinfo;
120 static void *make_keylist1(int *length);
121 static void *make_keylist2(int *length);
122 static void *get_keylist1(int *length);
123 static void *get_keylist2(int *length);
126 * We need this to link with the RSA code, because rsaencrypt()
127 * pads its data with random bytes. Since we only use rsadecrypt()
128 * and the signing functions, which are deterministic, this should
131 * If it _is_ called, there is a _serious_ problem, because it
132 * won't generate true random numbers. So we must scream, panic,
133 * and exit immediately if that should happen.
135 int random_byte(void)
137 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
139 /* this line can't be reached but it placates MSVC's warnings :-) */
144 * Blob structure for passing to the asymmetric SSH2 key compare
145 * function, prototyped here.
151 static int cmpkeys_ssh2_asymm(void *av, void *bv);
153 #define GET_32BIT(cp) \
154 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
155 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
156 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
157 ((unsigned long)(unsigned char)(cp)[3]))
159 #define PUT_32BIT(cp, value) { \
160 (cp)[0] = (unsigned char)((value) >> 24); \
161 (cp)[1] = (unsigned char)((value) >> 16); \
162 (cp)[2] = (unsigned char)((value) >> 8); \
163 (cp)[3] = (unsigned char)(value); }
165 #define PASSPHRASE_MAXLEN 512
167 struct PassphraseProcStruct {
172 static tree234 *passphrases = NULL;
175 * After processing a list of filenames, we want to forget the
178 static void forget_passphrases(void)
180 while (count234(passphrases) > 0) {
181 char *pp = index234(passphrases, 0);
182 memset(pp, 0, strlen(pp));
183 delpos234(passphrases, 0);
189 * Dialog-box function for the Licence box.
191 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
192 WPARAM wParam, LPARAM lParam)
198 switch (LOWORD(wParam)) {
212 * Dialog-box function for the About box.
214 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
215 WPARAM wParam, LPARAM lParam)
219 SetDlgItemText(hwnd, 100, ver);
222 switch (LOWORD(wParam)) {
228 EnableWindow(hwnd, 0);
229 DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
230 EnableWindow(hwnd, 1);
231 SetActiveWindow(hwnd);
243 static HWND passphrase_box;
246 * Dialog-box function for the passphrase box.
248 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
249 WPARAM wParam, LPARAM lParam)
251 static char *passphrase = NULL;
252 struct PassphraseProcStruct *p;
256 passphrase_box = hwnd;
260 { /* centre the window */
264 hw = GetDesktopWindow();
265 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
267 (rs.right + rs.left + rd.left - rd.right) / 2,
268 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
269 rd.right - rd.left, rd.bottom - rd.top, TRUE);
272 SetForegroundWindow(hwnd);
273 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
274 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
275 p = (struct PassphraseProcStruct *) lParam;
276 passphrase = p->passphrase;
278 SetDlgItemText(hwnd, 101, p->comment);
280 SetDlgItemText(hwnd, 102, passphrase);
283 switch (LOWORD(wParam)) {
293 case 102: /* edit box */
294 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
295 GetDlgItemText(hwnd, 102, passphrase,
296 PASSPHRASE_MAXLEN - 1);
297 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
310 * Warn about the obsolescent key file format.
312 void old_keyfile_warning(void)
314 static const char mbtitle[] = "PuTTY Key File Warning";
315 static const char message[] =
316 "You are loading an SSH 2 private key which has an\n"
317 "old version of the file format. This means your key\n"
318 "file is not fully tamperproof. Future versions of\n"
319 "PuTTY may stop supporting this private key format,\n"
320 "so we recommend you convert your key to the new\n"
323 "You can perform this conversion by loading the key\n"
324 "into PuTTYgen and then saving it again.";
326 MessageBox(NULL, message, mbtitle, MB_OK);
330 * Update the visible key list.
332 static void keylist_update(void)
335 struct ssh2_userkey *skey;
339 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
340 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
341 char listentry[512], *p;
343 * Replace two spaces in the fingerprint with tabs, for
344 * nice alignment in the box.
346 strcpy(listentry, "ssh1\t");
347 p = listentry + strlen(listentry);
348 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
349 p = strchr(listentry, ' ');
352 p = strchr(listentry, ' ');
355 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
356 0, (LPARAM) listentry);
358 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
359 char listentry[512], *p;
362 * Replace two spaces in the fingerprint with tabs, for
363 * nice alignment in the box.
365 p = skey->alg->fingerprint(skey->data);
366 strncpy(listentry, p, sizeof(listentry));
367 p = strchr(listentry, ' ');
370 p = strchr(listentry, ' ');
373 len = strlen(listentry);
374 if (len < sizeof(listentry) - 2) {
375 listentry[len] = '\t';
376 strncpy(listentry + len + 1, skey->comment,
377 sizeof(listentry) - len - 1);
379 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
382 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
387 * This function loads a key from a file and adds it.
389 static void add_keyfile(Filename filename)
391 char passphrase[PASSPHRASE_MAXLEN];
392 struct RSAKey *rkey = NULL;
393 struct ssh2_userkey *skey = NULL;
398 struct PassphraseProcStruct pps;
402 type = key_type(&filename);
403 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
405 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
406 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
411 * See if the key is already loaded (in the primary Pageant,
412 * which may or may not be us).
416 unsigned char *keylist, *p;
417 int i, nkeys, bloblen, keylistlen;
419 if (type == SSH_KEYTYPE_SSH1) {
420 if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
421 MessageBox(NULL, "Couldn't load private key.", APPNAME,
422 MB_OK | MB_ICONERROR);
425 keylist = get_keylist1(&keylistlen);
427 unsigned char *blob2;
428 blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
430 MessageBox(NULL, "Couldn't load private key.", APPNAME,
431 MB_OK | MB_ICONERROR);
434 /* For our purposes we want the blob prefixed with its length */
435 blob2 = snewn(bloblen+4, unsigned char);
436 PUT_32BIT(blob2, bloblen);
437 memcpy(blob2 + 4, blob, bloblen);
441 keylist = get_keylist2(&keylistlen);
444 if (keylistlen < 4) {
445 MessageBox(NULL, "Received broken key list?!", APPNAME,
446 MB_OK | MB_ICONERROR);
449 nkeys = GET_32BIT(keylist);
453 for (i = 0; i < nkeys; i++) {
454 if (!memcmp(blob, p, bloblen)) {
455 /* Key is already present; we can now leave. */
460 /* Now skip over public blob */
461 if (type == SSH_KEYTYPE_SSH1) {
462 int n = rsa_public_blob_len(p, keylistlen);
464 MessageBox(NULL, "Received broken key list?!", APPNAME,
465 MB_OK | MB_ICONERROR);
472 if (keylistlen < 4) {
473 MessageBox(NULL, "Received broken key list?!", APPNAME,
474 MB_OK | MB_ICONERROR);
477 n = 4 + GET_32BIT(p);
478 if (keylistlen < n) {
479 MessageBox(NULL, "Received broken key list?!", APPNAME,
480 MB_OK | MB_ICONERROR);
486 /* Now skip over comment field */
489 if (keylistlen < 4) {
490 MessageBox(NULL, "Received broken key list?!", APPNAME,
491 MB_OK | MB_ICONERROR);
494 n = 4 + GET_32BIT(p);
495 if (keylistlen < n) {
496 MessageBox(NULL, "Received broken key list?!", APPNAME,
497 MB_OK | MB_ICONERROR);
511 if (type == SSH_KEYTYPE_SSH1)
512 needs_pass = rsakey_encrypted(&filename, &comment);
514 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
516 if (type == SSH_KEYTYPE_SSH1)
517 rkey = snew(struct RSAKey);
518 pps.passphrase = passphrase;
519 pps.comment = comment;
523 /* try all the remembered passphrases first */
524 char *pp = index234(passphrases, attempts);
526 strcpy(passphrase, pp);
530 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
531 NULL, PassphraseProc, (LPARAM) & pps);
532 passphrase_box = NULL;
536 if (type == SSH_KEYTYPE_SSH1)
538 return; /* operation cancelled */
543 if (type == SSH_KEYTYPE_SSH1)
544 ret = loadrsakey(&filename, rkey, passphrase, NULL);
546 skey = ssh2_load_userkey(&filename, passphrase, NULL);
547 if (skey == SSH2_WRONG_PASSPHRASE)
557 /* if they typed in an ok passphrase, remember it */
558 if(original_pass && ret) {
559 char *pp = dupstr(passphrase);
560 addpos234(passphrases, pp, 0);
566 MessageBox(NULL, "Couldn't load private key.", APPNAME,
567 MB_OK | MB_ICONERROR);
568 if (type == SSH_KEYTYPE_SSH1)
572 if (type == SSH_KEYTYPE_SSH1) {
573 if (already_running) {
574 unsigned char *request, *response;
576 int reqlen, clen, resplen, ret;
578 clen = strlen(rkey->comment);
580 reqlen = 4 + 1 + /* length, message type */
582 ssh1_bignum_length(rkey->modulus) +
583 ssh1_bignum_length(rkey->exponent) +
584 ssh1_bignum_length(rkey->private_exponent) +
585 ssh1_bignum_length(rkey->iqmp) +
586 ssh1_bignum_length(rkey->p) +
587 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
590 request = snewn(reqlen, unsigned char);
592 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
594 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
596 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
597 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
599 ssh1_write_bignum(request + reqlen,
600 rkey->private_exponent);
601 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
602 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
603 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
604 PUT_32BIT(request + reqlen, clen);
605 memcpy(request + reqlen + 4, rkey->comment, clen);
607 PUT_32BIT(request, reqlen - 4);
609 ret = agent_query(request, reqlen, &vresponse, &resplen,
612 response = vresponse;
613 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
614 MessageBox(NULL, "The already running Pageant "
615 "refused to add the key.", APPNAME,
616 MB_OK | MB_ICONERROR);
621 if (add234(rsakeys, rkey) != rkey)
622 sfree(rkey); /* already present, don't waste RAM */
625 if (already_running) {
626 unsigned char *request, *response;
628 int reqlen, alglen, clen, keybloblen, resplen, ret;
629 alglen = strlen(skey->alg->name);
630 clen = strlen(skey->comment);
632 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
634 reqlen = 4 + 1 + /* length, message type */
635 4 + alglen + /* algorithm name */
636 keybloblen + /* key data */
637 4 + clen /* comment */
640 request = snewn(reqlen, unsigned char);
642 request[4] = SSH2_AGENTC_ADD_IDENTITY;
644 PUT_32BIT(request + reqlen, alglen);
646 memcpy(request + reqlen, skey->alg->name, alglen);
648 reqlen += skey->alg->openssh_fmtkey(skey->data,
651 PUT_32BIT(request + reqlen, clen);
652 memcpy(request + reqlen + 4, skey->comment, clen);
654 PUT_32BIT(request, reqlen - 4);
656 ret = agent_query(request, reqlen, &vresponse, &resplen,
659 response = vresponse;
660 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
661 MessageBox(NULL, "The already running Pageant "
662 "refused to add the key.", APPNAME,
663 MB_OK | MB_ICONERROR);
668 if (add234(ssh2keys, skey) != skey) {
669 skey->alg->freekey(skey->data);
670 sfree(skey); /* already present, don't waste RAM */
677 * Create an SSH1 key list in a malloc'ed buffer; return its
680 static void *make_keylist1(int *length)
684 unsigned char *blob, *p, *ret;
688 * Count up the number and length of keys we hold.
692 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
694 blob = rsa_public_blob(key, &bloblen);
697 len += 4 + strlen(key->comment);
700 /* Allocate the buffer. */
701 p = ret = snewn(len, unsigned char);
702 if (length) *length = len;
706 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
707 blob = rsa_public_blob(key, &bloblen);
708 memcpy(p, blob, bloblen);
711 PUT_32BIT(p, strlen(key->comment));
712 memcpy(p + 4, key->comment, strlen(key->comment));
713 p += 4 + strlen(key->comment);
716 assert(p - ret == len);
721 * Create an SSH2 key list in a malloc'ed buffer; return its
724 static void *make_keylist2(int *length)
726 struct ssh2_userkey *key;
728 unsigned char *blob, *p, *ret;
732 * Count up the number and length of keys we hold.
736 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
738 len += 4; /* length field */
739 blob = key->alg->public_blob(key->data, &bloblen);
742 len += 4 + strlen(key->comment);
745 /* Allocate the buffer. */
746 p = ret = snewn(len, unsigned char);
747 if (length) *length = len;
750 * Packet header is the obvious five bytes, plus four
751 * bytes for the key count.
755 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
756 blob = key->alg->public_blob(key->data, &bloblen);
757 PUT_32BIT(p, bloblen);
759 memcpy(p, blob, bloblen);
762 PUT_32BIT(p, strlen(key->comment));
763 memcpy(p + 4, key->comment, strlen(key->comment));
764 p += 4 + strlen(key->comment);
767 assert(p - ret == len);
772 * Acquire a keylist1 from the primary Pageant; this means either
773 * calling make_keylist1 (if that's us) or sending a message to the
774 * primary Pageant (if it's not).
776 static void *get_keylist1(int *length)
780 if (already_running) {
781 unsigned char request[5], *response;
784 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
785 PUT_32BIT(request, 4);
787 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
789 response = vresponse;
790 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
793 ret = snewn(resplen-5, unsigned char);
794 memcpy(ret, response+5, resplen-5);
800 ret = make_keylist1(length);
806 * Acquire a keylist2 from the primary Pageant; this means either
807 * calling make_keylist2 (if that's us) or sending a message to the
808 * primary Pageant (if it's not).
810 static void *get_keylist2(int *length)
814 if (already_running) {
815 unsigned char request[5], *response;
819 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
820 PUT_32BIT(request, 4);
822 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
824 response = vresponse;
825 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
828 ret = snewn(resplen-5, unsigned char);
829 memcpy(ret, response+5, resplen-5);
835 ret = make_keylist2(length);
841 * This is the main agent function that answers messages.
843 static void answer_msg(void *msg)
845 unsigned char *p = msg;
846 unsigned char *ret = msg;
847 unsigned char *msgend;
851 * Get the message length.
853 msgend = p + 4 + GET_32BIT(p);
856 * Get the message type.
864 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
866 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
872 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
873 keylist = make_keylist1(&len);
874 if (len + 5 > AGENT_MAX_MSGLEN) {
878 PUT_32BIT(ret, len + 1);
879 memcpy(ret + 5, keylist, len);
883 case SSH2_AGENTC_REQUEST_IDENTITIES:
885 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
891 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
892 keylist = make_keylist2(&len);
893 if (len + 5 > AGENT_MAX_MSGLEN) {
897 PUT_32BIT(ret, len + 1);
898 memcpy(ret + 5, keylist, len);
902 case SSH1_AGENTC_RSA_CHALLENGE:
904 * Reply with either SSH1_AGENT_RSA_RESPONSE or
905 * SSH_AGENT_FAILURE, depending on whether we have that key
909 struct RSAKey reqkey, *key;
910 Bignum challenge, response;
911 unsigned char response_source[48], response_md5[16];
912 struct MD5Context md5c;
916 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
920 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
924 i = ssh1_read_bignum(p, msgend - p, &challenge);
929 freebn(reqkey.exponent);
930 freebn(reqkey.modulus);
934 memcpy(response_source + 32, p, 16);
938 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
939 freebn(reqkey.exponent);
940 freebn(reqkey.modulus);
944 response = rsadecrypt(challenge, key);
945 for (i = 0; i < 32; i++)
946 response_source[i] = bignum_byte(response, 31 - i);
949 MD5Update(&md5c, response_source, 48);
950 MD5Final(response_md5, &md5c);
951 memset(response_source, 0, 48); /* burn the evidence */
952 freebn(response); /* and that evidence */
953 freebn(challenge); /* yes, and that evidence */
954 freebn(reqkey.exponent); /* and free some memory ... */
955 freebn(reqkey.modulus); /* ... while we're at it. */
958 * Packet is the obvious five byte header, plus sixteen
962 PUT_32BIT(ret, len - 4);
963 ret[4] = SSH1_AGENT_RSA_RESPONSE;
964 memcpy(ret + 5, response_md5, 16);
967 case SSH2_AGENTC_SIGN_REQUEST:
969 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
970 * SSH_AGENT_FAILURE, depending on whether we have that key
974 struct ssh2_userkey *key;
976 unsigned char *data, *signature;
977 int datalen, siglen, len;
981 b.len = GET_32BIT(p);
983 if (msgend < p+b.len)
989 datalen = GET_32BIT(p);
991 if (msgend < p+datalen)
994 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
997 signature = key->alg->sign(key->data, data, datalen, &siglen);
998 len = 5 + 4 + siglen;
999 PUT_32BIT(ret, len - 4);
1000 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1001 PUT_32BIT(ret + 5, siglen);
1002 memcpy(ret + 5 + 4, signature, siglen);
1006 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1008 * Add to the list and return SSH_AGENT_SUCCESS, or
1009 * SSH_AGENT_FAILURE if the key was malformed.
1016 key = snew(struct RSAKey);
1017 memset(key, 0, sizeof(struct RSAKey));
1019 n = makekey(p, msgend - p, key, NULL, 1);
1027 n = makeprivate(p, msgend - p, key);
1035 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1043 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1051 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1064 commentlen = GET_32BIT(p);
1066 if (msgend < p+commentlen) {
1072 comment = snewn(commentlen+1, char);
1074 memcpy(comment, p + 4, commentlen);
1075 comment[commentlen] = '\0';
1076 key->comment = comment;
1079 ret[4] = SSH_AGENT_FAILURE;
1080 if (add234(rsakeys, key) == key) {
1082 ret[4] = SSH_AGENT_SUCCESS;
1089 case SSH2_AGENTC_ADD_IDENTITY:
1091 * Add to the list and return SSH_AGENT_SUCCESS, or
1092 * SSH_AGENT_FAILURE if the key was malformed.
1095 struct ssh2_userkey *key;
1096 char *comment, *alg;
1097 int alglen, commlen;
1103 alglen = GET_32BIT(p);
1105 if (msgend < p+alglen)
1110 key = snew(struct ssh2_userkey);
1111 /* Add further algorithm names here. */
1112 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1113 key->alg = &ssh_rsa;
1114 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1115 key->alg = &ssh_dss;
1121 bloblen = msgend - p;
1122 key->data = key->alg->openssh_createkey(&p, &bloblen);
1129 * p has been advanced by openssh_createkey, but
1130 * certainly not _beyond_ the end of the buffer.
1132 assert(p <= msgend);
1135 key->alg->freekey(key->data);
1139 commlen = GET_32BIT(p);
1142 if (msgend < p+commlen) {
1143 key->alg->freekey(key->data);
1147 comment = snewn(commlen + 1, char);
1149 memcpy(comment, p, commlen);
1150 comment[commlen] = '\0';
1152 key->comment = comment;
1155 ret[4] = SSH_AGENT_FAILURE;
1156 if (add234(ssh2keys, key) == key) {
1158 ret[4] = SSH_AGENT_SUCCESS;
1160 key->alg->freekey(key->data);
1161 sfree(key->comment);
1166 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1168 * Remove from the list and return SSH_AGENT_SUCCESS, or
1169 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1173 struct RSAKey reqkey, *key;
1176 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1180 key = find234(rsakeys, &reqkey, NULL);
1181 freebn(reqkey.exponent);
1182 freebn(reqkey.modulus);
1184 ret[4] = SSH_AGENT_FAILURE;
1186 del234(rsakeys, key);
1190 ret[4] = SSH_AGENT_SUCCESS;
1194 case SSH2_AGENTC_REMOVE_IDENTITY:
1196 * Remove from the list and return SSH_AGENT_SUCCESS, or
1197 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1201 struct ssh2_userkey *key;
1206 b.len = GET_32BIT(p);
1209 if (msgend < p+b.len)
1214 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1219 ret[4] = SSH_AGENT_FAILURE;
1221 del234(ssh2keys, key);
1223 key->alg->freekey(key->data);
1225 ret[4] = SSH_AGENT_SUCCESS;
1229 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1231 * Remove all SSH1 keys. Always returns success.
1234 struct RSAKey *rkey;
1236 while ((rkey = index234(rsakeys, 0)) != NULL) {
1237 del234(rsakeys, rkey);
1244 ret[4] = SSH_AGENT_SUCCESS;
1247 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1249 * Remove all SSH2 keys. Always returns success.
1252 struct ssh2_userkey *skey;
1254 while ((skey = index234(ssh2keys, 0)) != NULL) {
1255 del234(ssh2keys, skey);
1256 skey->alg->freekey(skey->data);
1262 ret[4] = SSH_AGENT_SUCCESS;
1268 * Unrecognised message. Return SSH_AGENT_FAILURE.
1271 ret[4] = SSH_AGENT_FAILURE;
1277 * Key comparison function for the 2-3-4 tree of RSA keys.
1279 static int cmpkeys_rsa(void *av, void *bv)
1281 struct RSAKey *a = (struct RSAKey *) av;
1282 struct RSAKey *b = (struct RSAKey *) bv;
1289 * Compare by length of moduli.
1291 alen = bignum_bitcount(am);
1292 blen = bignum_bitcount(bm);
1295 else if (alen < blen)
1298 * Now compare by moduli themselves.
1300 alen = (alen + 7) / 8; /* byte count */
1301 while (alen-- > 0) {
1303 abyte = bignum_byte(am, alen);
1304 bbyte = bignum_byte(bm, alen);
1307 else if (abyte < bbyte)
1317 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1319 static int cmpkeys_ssh2(void *av, void *bv)
1321 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1322 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1325 unsigned char *ablob, *bblob;
1329 * Compare purely by public blob.
1331 ablob = a->alg->public_blob(a->data, &alen);
1332 bblob = b->alg->public_blob(b->data, &blen);
1335 for (i = 0; i < alen && i < blen; i++) {
1336 if (ablob[i] < bblob[i]) {
1339 } else if (ablob[i] > bblob[i]) {
1344 if (c == 0 && i < alen)
1345 c = +1; /* a is longer */
1346 if (c == 0 && i < blen)
1347 c = -1; /* a is longer */
1356 * Key comparison function for looking up a blob in the 2-3-4 tree
1359 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1361 struct blob *a = (struct blob *) av;
1362 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1365 unsigned char *ablob, *bblob;
1369 * Compare purely by public blob.
1373 bblob = b->alg->public_blob(b->data, &blen);
1376 for (i = 0; i < alen && i < blen; i++) {
1377 if (ablob[i] < bblob[i]) {
1380 } else if (ablob[i] > bblob[i]) {
1385 if (c == 0 && i < alen)
1386 c = +1; /* a is longer */
1387 if (c == 0 && i < blen)
1388 c = -1; /* a is longer */
1396 * Prompt for a key file to add, and add it.
1398 static void prompt_add_keyfile(void)
1401 char filename[FILENAME_MAX];
1402 char *filelist = snewn(8192, char);
1406 memset(&of, 0, sizeof(of));
1407 #ifdef OPENFILENAME_SIZE_VERSION_400
1408 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1410 of.lStructSize = sizeof(of);
1412 of.hwndOwner = main_hwnd;
1413 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1414 "All Files (*.*)\0*\0\0\0";
1415 of.lpstrCustomFilter = NULL;
1416 of.nFilterIndex = 1;
1417 of.lpstrFile = filelist;
1419 of.nMaxFile = FILENAME_MAX;
1420 of.lpstrFileTitle = NULL;
1421 of.lpstrInitialDir = NULL;
1422 of.lpstrTitle = "Select Private Key File";
1423 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1424 if (GetOpenFileName(&of)) {
1425 if(strlen(filelist) > of.nFileOffset)
1426 /* Only one filename returned? */
1427 add_keyfile(filename_from_str(filelist));
1429 /* we are returned a bunch of strings, end to
1430 * end. first string is the directory, the
1431 * rest the filenames. terminated with an
1434 filewalker = filelist;
1435 dirlen = strlen(filewalker);
1436 if(dirlen > FILENAME_MAX - 8) return;
1437 memcpy(filename, filewalker, dirlen);
1439 filewalker += dirlen + 1;
1440 filename[dirlen++] = '\\';
1442 /* then go over names one by one */
1444 n = strlen(filewalker) + 1;
1445 /* end of the list */
1448 /* too big, shouldn't happen */
1449 if(n + dirlen > FILENAME_MAX)
1452 memcpy(filename + dirlen, filewalker, n);
1455 add_keyfile(filename_from_str(filename));
1460 forget_passphrases();
1466 * Dialog-box function for the key list box.
1468 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1469 WPARAM wParam, LPARAM lParam)
1471 struct RSAKey *rkey;
1472 struct ssh2_userkey *skey;
1477 * Centre the window.
1479 { /* centre the window */
1483 hw = GetDesktopWindow();
1484 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1486 (rs.right + rs.left + rd.left - rd.right) / 2,
1487 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1488 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1492 SetWindowLong(hwnd, GWL_EXSTYLE,
1493 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1495 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1497 DestroyWindow(item);
1499 requested_help = FALSE;
1503 static int tabs[] = { 35, 60, 210 };
1504 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1505 sizeof(tabs) / sizeof(*tabs),
1511 switch (LOWORD(wParam)) {
1515 DestroyWindow(hwnd);
1517 case 101: /* add key */
1518 if (HIWORD(wParam) == BN_CLICKED ||
1519 HIWORD(wParam) == BN_DOUBLECLICKED) {
1520 if (passphrase_box) {
1521 MessageBeep(MB_ICONERROR);
1522 SetForegroundWindow(passphrase_box);
1525 prompt_add_keyfile();
1528 case 102: /* remove key */
1529 if (HIWORD(wParam) == BN_CLICKED ||
1530 HIWORD(wParam) == BN_DOUBLECLICKED) {
1535 /* our counter within the array of selected items */
1538 /* get the number of items selected in the list */
1540 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1542 /* none selected? that was silly */
1543 if (numSelected == 0) {
1548 /* get item indices in an array */
1549 selectedArray = snewn(numSelected, int);
1550 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1551 numSelected, (WPARAM)selectedArray);
1553 itemNum = numSelected - 1;
1554 rCount = count234(rsakeys);
1555 sCount = count234(ssh2keys);
1557 /* go through the non-rsakeys until we've covered them all,
1558 * and/or we're out of selected items to check. note that
1559 * we go *backwards*, to avoid complications from deleting
1560 * things hence altering the offset of subsequent items
1562 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1563 skey = index234(ssh2keys, i);
1565 if (selectedArray[itemNum] == rCount + i) {
1566 del234(ssh2keys, skey);
1567 skey->alg->freekey(skey->data);
1573 /* do the same for the rsa keys */
1574 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1575 rkey = index234(rsakeys, i);
1577 if(selectedArray[itemNum] == i) {
1578 del234(rsakeys, rkey);
1585 sfree(selectedArray);
1589 case 103: /* help */
1590 if (HIWORD(wParam) == BN_CLICKED ||
1591 HIWORD(wParam) == BN_DOUBLECLICKED) {
1593 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1594 (DWORD)"JI(`',`pageant.general')");
1595 requested_help = TRUE;
1603 int id = ((LPHELPINFO)lParam)->iCtrlId;
1606 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1607 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1608 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1611 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1612 requested_help = TRUE;
1620 DestroyWindow(hwnd);
1626 /* Set up a system tray icon */
1627 static BOOL AddTrayIcon(HWND hwnd)
1630 NOTIFYICONDATA tnid;
1633 #ifdef NIM_SETVERSION
1635 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1638 tnid.cbSize = sizeof(NOTIFYICONDATA);
1640 tnid.uID = 1; /* unique within this systray use */
1641 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1642 tnid.uCallbackMessage = WM_SYSTRAY;
1643 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1644 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1646 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1648 if (hicon) DestroyIcon(hicon);
1653 /* Update the saved-sessions menu. */
1654 static void update_sessions(void)
1658 TCHAR buf[MAX_PATH + 1];
1661 int index_key, index_menu;
1666 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1669 for(num_entries = GetMenuItemCount(session_menu);
1670 num_entries > initial_menuitems_count;
1672 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1677 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1678 TCHAR session_name[MAX_PATH + 1];
1679 unmungestr(buf, session_name, MAX_PATH);
1680 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1681 memset(&mii, 0, sizeof(mii));
1682 mii.cbSize = sizeof(mii);
1683 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1684 mii.fType = MFT_STRING;
1685 mii.fState = MFS_ENABLED;
1686 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1687 mii.dwTypeData = session_name;
1688 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1696 if(index_menu == 0) {
1697 mii.cbSize = sizeof(mii);
1698 mii.fMask = MIIM_TYPE | MIIM_STATE;
1699 mii.fType = MFT_STRING;
1700 mii.fState = MFS_GRAYED;
1701 mii.dwTypeData = _T("(No sessions)");
1702 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1706 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1707 WPARAM wParam, LPARAM lParam)
1710 static int menuinprogress;
1711 static UINT msgTaskbarCreated = 0;
1715 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1718 if (message==msgTaskbarCreated) {
1720 * Explorer has been restarted, so the tray icon will
1728 if (lParam == WM_RBUTTONUP) {
1730 GetCursorPos(&cursorpos);
1731 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1732 } else if (lParam == WM_LBUTTONDBLCLK) {
1733 /* Equivalent to IDM_VIEWKEYS. */
1734 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1738 if (!menuinprogress) {
1741 SetForegroundWindow(hwnd);
1742 ret = TrackPopupMenu(systray_menu,
1743 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1745 wParam, lParam, 0, hwnd, NULL);
1751 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1753 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1755 MessageBox(NULL, "Unable to execute PuTTY!",
1756 "Error", MB_OK | MB_ICONERROR);
1761 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1762 SendMessage(hwnd, WM_CLOSE, 0, 0);
1766 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1768 ShowWindow(keylist, SW_SHOWNORMAL);
1771 * Sometimes the window comes up minimised / hidden for
1772 * no obvious reason. Prevent this. This also brings it
1773 * to the front if it's already present (the user
1774 * selected View Keys because they wanted to _see_ the
1777 SetForegroundWindow(keylist);
1778 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1779 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1782 if (passphrase_box) {
1783 MessageBeep(MB_ICONERROR);
1784 SetForegroundWindow(passphrase_box);
1787 prompt_add_keyfile();
1791 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1793 ShowWindow(aboutbox, SW_SHOWNORMAL);
1795 * Sometimes the window comes up minimised / hidden
1796 * for no obvious reason. Prevent this.
1798 SetForegroundWindow(aboutbox);
1799 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1800 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1805 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1806 (DWORD)"JI(`',`pageant.general')");
1807 requested_help = TRUE;
1812 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1814 TCHAR buf[MAX_PATH + 1];
1815 TCHAR param[MAX_PATH + 1];
1816 memset(&mii, 0, sizeof(mii));
1817 mii.cbSize = sizeof(mii);
1818 mii.fMask = MIIM_TYPE;
1820 mii.dwTypeData = buf;
1821 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1823 strcat(param, mii.dwTypeData);
1824 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1825 _T(""), SW_SHOW) <= 32) {
1826 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1827 MB_OK | MB_ICONERROR);
1835 if (requested_help) {
1836 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1837 requested_help = FALSE;
1843 COPYDATASTRUCT *cds;
1849 PSID mapowner, procowner;
1850 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1854 cds = (COPYDATASTRUCT *) lParam;
1855 if (cds->dwData != AGENT_COPYDATA_ID)
1856 return 0; /* not our message, mate */
1857 mapname = (char *) cds->lpData;
1858 if (mapname[cds->cbData - 1] != '\0')
1859 return 0; /* failure to be ASCIZ! */
1861 debug(("mapname is :%s:\n", mapname));
1863 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1865 debug(("filemap is %p\n", filemap));
1867 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1871 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1872 GetCurrentProcessId())) ==
1875 debug(("couldn't get handle for process\n"));
1879 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1880 OWNER_SECURITY_INFORMATION,
1881 &procowner, NULL, NULL, NULL,
1882 &psd2) != ERROR_SUCCESS) {
1884 debug(("couldn't get owner info for process\n"));
1887 return 0; /* unable to get security info */
1890 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1891 OWNER_SECURITY_INFORMATION,
1892 &mapowner, NULL, NULL, NULL,
1893 &psd1) != ERROR_SUCCESS)) {
1896 ("couldn't get owner info for filemap: %d\n",
1902 debug(("got security stuff\n"));
1904 if (!EqualSid(mapowner, procowner))
1905 return 0; /* security ID mismatch! */
1907 debug(("security stuff matched\n"));
1913 debug(("security APIs not present\n"));
1917 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1919 debug(("p is %p\n", p));
1922 for (i = 0; i < 5; i++)
1925 ((unsigned char *) p)[i]));}
1931 CloseHandle(filemap);
1936 return DefWindowProc(hwnd, message, wParam, lParam);
1940 * Fork and Exec the command in cmdline. [DBW]
1942 void spawn_cmd(char *cmdline, char * args, int show)
1944 if (ShellExecute(NULL, _T("open"), cmdline,
1945 args, NULL, show) <= (HINSTANCE) 32) {
1947 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1948 (int)GetLastError());
1949 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1955 * This is a can't-happen stub, since Pageant never makes
1956 * asynchronous agent requests.
1958 void agent_schedule_callback(void (*callback)(void *, void *, int),
1959 void *callback_ctx, void *data, int len)
1961 assert(!"We shouldn't get here");
1964 void cleanup_exit(int code) { exit(code); }
1966 int flags = FLAG_SYNCAGENT;
1968 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1973 char *command = NULL;
1976 char **argv, **argstart;
1979 * Determine whether we're an NT system (should have security
1980 * APIs) or a non-NT system (don't do security).
1984 modalfatalbox("Windows refuses to report a version");
1986 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1987 has_security = TRUE;
1989 has_security = FALSE;
1994 * Attempt to get the security API we need.
1996 advapi = LoadLibrary("ADVAPI32.DLL");
1998 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1999 if (!getsecurityinfo) {
2001 "Unable to access security APIs. Pageant will\n"
2002 "not run, in case it causes a security breach.",
2003 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2008 "This program has been compiled for Win9X and will\n"
2009 "not run on NT, in case it causes a security breach.",
2010 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2019 * See if we can find our Help file.
2022 char b[2048], *p, *q, *r;
2024 GetModuleFileName(NULL, b, sizeof(b) - 1);
2026 p = strrchr(b, '\\');
2027 if (p && p >= r) r = p+1;
2028 q = strrchr(b, ':');
2029 if (q && q >= r) r = q+1;
2030 strcpy(r, "putty.hlp");
2031 if ( (fp = fopen(b, "r")) != NULL) {
2032 help_path = dupstr(b);
2039 * Look for the PuTTY binary (we will enable the saved session
2040 * submenu if we find it).
2043 char b[2048], *p, *q, *r;
2045 GetModuleFileName(NULL, b, sizeof(b) - 1);
2047 p = strrchr(b, '\\');
2048 if (p && p >= r) r = p+1;
2049 q = strrchr(b, ':');
2050 if (q && q >= r) r = q+1;
2051 strcpy(r, "putty.exe");
2052 if ( (fp = fopen(b, "r")) != NULL) {
2053 putty_path = dupstr(b);
2060 * Find out if Pageant is already running.
2062 already_running = FALSE;
2064 already_running = TRUE;
2069 wndclass.lpfnWndProc = WndProc;
2070 wndclass.cbClsExtra = 0;
2071 wndclass.cbWndExtra = 0;
2072 wndclass.hInstance = inst;
2073 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2074 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2075 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2076 wndclass.lpszMenuName = NULL;
2077 wndclass.lpszClassName = APPNAME;
2079 RegisterClass(&wndclass);
2082 main_hwnd = keylist = NULL;
2084 main_hwnd = CreateWindow(APPNAME, APPNAME,
2085 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2086 CW_USEDEFAULT, CW_USEDEFAULT,
2087 100, 100, NULL, NULL, inst, NULL);
2089 /* Set up a system tray icon */
2090 AddTrayIcon(main_hwnd);
2092 /* Accelerators used: nsvkxa */
2093 systray_menu = CreatePopupMenu();
2095 session_menu = CreateMenu();
2096 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2097 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2098 (UINT) session_menu, "&Saved Sessions");
2099 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2101 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2103 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2104 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2106 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2107 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2108 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2109 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2110 initial_menuitems_count = GetMenuItemCount(session_menu);
2112 ShowWindow(main_hwnd, SW_HIDE);
2115 * Initialise storage for RSA keys.
2117 rsakeys = newtree234(cmpkeys_rsa);
2118 ssh2keys = newtree234(cmpkeys_ssh2);
2123 * Initialise storage for short-term passphrase cache.
2125 passphrases = newtree234(NULL);
2128 * Process the command line and add keys as listed on it.
2130 split_into_argv(cmdline, &argc, &argv, &argstart);
2131 for (i = 0; i < argc; i++) {
2132 if (!strcmp(argv[i], "-c")) {
2134 * If we see `-c', then the rest of the
2135 * command line should be treated as a
2136 * command to be spawned.
2139 command = argstart[i+1];
2144 add_keyfile(filename_from_str(argv[i]));
2150 * Forget any passphrase that we retained while going over
2151 * command line keyfiles.
2153 forget_passphrases();
2157 if (command[0] == '"')
2158 args = strchr(++command, '"');
2160 args = strchr(command, ' ');
2163 while(*args && isspace(*args)) args++;
2165 spawn_cmd(command, args, show);
2169 * If Pageant was already running, we leave now. If we haven't
2170 * even taken any auxiliary action (spawned a command or added
2173 if (already_running) {
2174 if (!command && !added_keys) {
2175 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2176 MB_ICONERROR | MB_OK);
2179 FreeLibrary(advapi);
2184 * Main message loop.
2186 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2187 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2188 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2189 TranslateMessage(&msg);
2190 DispatchMessage(&msg);
2194 /* Clean up the system tray icon */
2196 NOTIFYICONDATA tnid;
2198 tnid.cbSize = sizeof(NOTIFYICONDATA);
2199 tnid.hWnd = main_hwnd;
2202 Shell_NotifyIcon(NIM_DELETE, &tnid);
2204 DestroyMenu(systray_menu);
2208 FreeLibrary(advapi);