2 * Pageant: the PuTTY Authentication Agent.
21 #define IDI_MAINICON 200
22 #define IDI_TRAYICON 201
24 #define WM_XUSER (WM_USER + 0x2000)
25 #define WM_SYSTRAY (WM_XUSER + 6)
26 #define WM_SYSTRAY2 (WM_XUSER + 7)
28 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
31 * FIXME: maybe some day we can sort this out ...
33 #define AGENT_MAX_MSGLEN 8192
35 #define IDM_CLOSE 0x0010
36 #define IDM_VIEWKEYS 0x0020
37 #define IDM_ADDKEY 0x0030
38 #define IDM_HELP 0x0040
39 #define IDM_ABOUT 0x0050
41 #define APPNAME "Pageant"
45 static HINSTANCE instance;
46 static HWND main_hwnd;
49 static HMENU systray_menu, session_menu;
50 static int already_running;
51 static int requested_help;
53 static char *help_path;
54 static char *putty_path;
56 #define IDM_PUTTY 0x0060
57 #define IDM_SESSIONS_BASE 0x1000
58 #define IDM_SESSIONS_MAX 0x2000
59 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
60 #define PUTTY_DEFAULT "Default%20Settings"
61 static int initial_menuitems_count;
64 * Print a modal (Really Bad) message box and perform a fatal exit.
66 void modalfatalbox(char *fmt, ...)
72 buf = dupvprintf(fmt, ap);
74 MessageBox(main_hwnd, buf, "Pageant Fatal Error",
75 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
80 /* Un-munge session names out of the registry. */
81 static void unmungestr(char *in, char *out, int outlen)
84 if (*in == '%' && in[1] && in[2]) {
92 *out++ = (i << 4) + j;
106 static tree234 *rsakeys, *ssh2keys;
108 static int has_security;
110 typedef DWORD(WINAPI * gsi_fn_t)
111 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
112 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
113 static gsi_fn_t getsecurityinfo;
119 static void *make_keylist1(int *length);
120 static void *make_keylist2(int *length);
121 static void *get_keylist1(void);
122 static void *get_keylist2(void);
125 * We need this to link with the RSA code, because rsaencrypt()
126 * pads its data with random bytes. Since we only use rsadecrypt()
127 * and the signing functions, which are deterministic, this should
130 * If it _is_ called, there is a _serious_ problem, because it
131 * won't generate true random numbers. So we must scream, panic,
132 * and exit immediately if that should happen.
134 int random_byte(void)
136 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
138 /* this line can't be reached but it placates MSVC's warnings :-) */
143 * Blob structure for passing to the asymmetric SSH2 key compare
144 * function, prototyped here.
150 static int cmpkeys_ssh2_asymm(void *av, void *bv);
152 #define GET_32BIT(cp) \
153 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
154 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
155 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
156 ((unsigned long)(unsigned char)(cp)[3]))
158 #define PUT_32BIT(cp, value) { \
159 (cp)[0] = (unsigned char)((value) >> 24); \
160 (cp)[1] = (unsigned char)((value) >> 16); \
161 (cp)[2] = (unsigned char)((value) >> 8); \
162 (cp)[3] = (unsigned char)(value); }
164 #define PASSPHRASE_MAXLEN 512
166 struct PassphraseProcStruct {
171 static tree234 *passphrases = NULL;
174 * After processing a list of filenames, we want to forget the
177 static void forget_passphrases(void)
179 while (count234(passphrases) > 0) {
180 char *pp = index234(passphrases, 0);
181 memset(pp, 0, strlen(pp));
182 delpos234(passphrases, 0);
188 * Dialog-box function for the Licence box.
190 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
191 WPARAM wParam, LPARAM lParam)
197 switch (LOWORD(wParam)) {
211 * Dialog-box function for the About box.
213 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
214 WPARAM wParam, LPARAM lParam)
218 SetDlgItemText(hwnd, 100, ver);
221 switch (LOWORD(wParam)) {
227 EnableWindow(hwnd, 0);
228 DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
229 EnableWindow(hwnd, 1);
230 SetActiveWindow(hwnd);
242 static HWND passphrase_box;
245 * Dialog-box function for the passphrase box.
247 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
248 WPARAM wParam, LPARAM lParam)
250 static char *passphrase = NULL;
251 struct PassphraseProcStruct *p;
255 passphrase_box = hwnd;
259 { /* centre the window */
263 hw = GetDesktopWindow();
264 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
266 (rs.right + rs.left + rd.left - rd.right) / 2,
267 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
268 rd.right - rd.left, rd.bottom - rd.top, TRUE);
271 SetForegroundWindow(hwnd);
272 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
273 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
274 p = (struct PassphraseProcStruct *) lParam;
275 passphrase = p->passphrase;
277 SetDlgItemText(hwnd, 101, p->comment);
279 SetDlgItemText(hwnd, 102, passphrase);
282 switch (LOWORD(wParam)) {
292 case 102: /* edit box */
293 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
294 GetDlgItemText(hwnd, 102, passphrase,
295 PASSPHRASE_MAXLEN - 1);
296 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
309 * Warn about the obsolescent key file format.
311 void old_keyfile_warning(void)
313 static const char mbtitle[] = "PuTTY Key File Warning";
314 static const char message[] =
315 "You are loading an SSH 2 private key which has an\n"
316 "old version of the file format. This means your key\n"
317 "file is not fully tamperproof. Future versions of\n"
318 "PuTTY may stop supporting this private key format,\n"
319 "so we recommend you convert your key to the new\n"
322 "You can perform this conversion by loading the key\n"
323 "into PuTTYgen and then saving it again.";
325 MessageBox(NULL, message, mbtitle, MB_OK);
329 * Update the visible key list.
331 static void keylist_update(void)
334 struct ssh2_userkey *skey;
338 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
339 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
340 char listentry[512], *p;
342 * Replace two spaces in the fingerprint with tabs, for
343 * nice alignment in the box.
345 strcpy(listentry, "ssh1\t");
346 p = listentry + strlen(listentry);
347 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
348 p = strchr(listentry, ' ');
351 p = strchr(listentry, ' ');
354 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
355 0, (LPARAM) listentry);
357 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
358 char listentry[512], *p;
361 * Replace two spaces in the fingerprint with tabs, for
362 * nice alignment in the box.
364 p = skey->alg->fingerprint(skey->data);
365 strncpy(listentry, p, sizeof(listentry));
366 p = strchr(listentry, ' ');
369 p = strchr(listentry, ' ');
372 len = strlen(listentry);
373 if (len < sizeof(listentry) - 2) {
374 listentry[len] = '\t';
375 strncpy(listentry + len + 1, skey->comment,
376 sizeof(listentry) - len - 1);
378 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
381 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
386 * This function loads a key from a file and adds it.
388 static void add_keyfile(Filename filename)
390 char passphrase[PASSPHRASE_MAXLEN];
391 struct RSAKey *rkey = NULL;
392 struct ssh2_userkey *skey = NULL;
397 struct PassphraseProcStruct pps;
401 type = key_type(&filename);
402 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
404 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
405 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
410 * See if the key is already loaded (in the primary Pageant,
411 * which may or may not be us).
415 unsigned char *keylist, *p;
416 int i, nkeys, bloblen;
418 if (type == SSH_KEYTYPE_SSH1) {
419 if (!rsakey_pubblob(&filename, &blob, &bloblen)) {
420 MessageBox(NULL, "Couldn't load private key.", APPNAME,
421 MB_OK | MB_ICONERROR);
424 keylist = get_keylist1();
426 unsigned char *blob2;
427 blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen);
429 MessageBox(NULL, "Couldn't load private key.", APPNAME,
430 MB_OK | MB_ICONERROR);
433 /* For our purposes we want the blob prefixed with its length */
434 blob2 = snewn(bloblen+4, unsigned char);
435 PUT_32BIT(blob2, bloblen);
436 memcpy(blob2 + 4, blob, bloblen);
440 keylist = get_keylist2();
443 nkeys = GET_32BIT(keylist);
446 for (i = 0; i < nkeys; i++) {
447 if (!memcmp(blob, p, bloblen)) {
448 /* Key is already present; we can now leave. */
453 /* Now skip over public blob */
454 if (type == SSH_KEYTYPE_SSH1)
455 p += rsa_public_blob_len(p);
457 p += 4 + GET_32BIT(p);
458 /* Now skip over comment field */
459 p += 4 + GET_32BIT(p);
468 if (type == SSH_KEYTYPE_SSH1)
469 needs_pass = rsakey_encrypted(&filename, &comment);
471 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
473 if (type == SSH_KEYTYPE_SSH1)
474 rkey = snew(struct RSAKey);
475 pps.passphrase = passphrase;
476 pps.comment = comment;
480 /* try all the remembered passphrases first */
481 char *pp = index234(passphrases, attempts);
483 strcpy(passphrase, pp);
487 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
488 NULL, PassphraseProc, (LPARAM) & pps);
489 passphrase_box = NULL;
493 if (type == SSH_KEYTYPE_SSH1)
495 return; /* operation cancelled */
500 if (type == SSH_KEYTYPE_SSH1)
501 ret = loadrsakey(&filename, rkey, passphrase);
503 skey = ssh2_load_userkey(&filename, passphrase);
504 if (skey == SSH2_WRONG_PASSPHRASE)
514 /* if they typed in an ok passphrase, remember it */
515 if(original_pass && ret) {
516 char *pp = dupstr(passphrase);
517 addpos234(passphrases, pp, 0);
523 MessageBox(NULL, "Couldn't load private key.", APPNAME,
524 MB_OK | MB_ICONERROR);
525 if (type == SSH_KEYTYPE_SSH1)
529 if (type == SSH_KEYTYPE_SSH1) {
530 if (already_running) {
531 unsigned char *request, *response;
533 int reqlen, clen, resplen, ret;
535 clen = strlen(rkey->comment);
537 reqlen = 4 + 1 + /* length, message type */
539 ssh1_bignum_length(rkey->modulus) +
540 ssh1_bignum_length(rkey->exponent) +
541 ssh1_bignum_length(rkey->private_exponent) +
542 ssh1_bignum_length(rkey->iqmp) +
543 ssh1_bignum_length(rkey->p) +
544 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
547 request = snewn(reqlen, unsigned char);
549 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
551 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
553 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
554 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
556 ssh1_write_bignum(request + reqlen,
557 rkey->private_exponent);
558 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
559 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
560 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
561 PUT_32BIT(request + reqlen, clen);
562 memcpy(request + reqlen + 4, rkey->comment, clen);
564 PUT_32BIT(request, reqlen - 4);
566 ret = agent_query(request, reqlen, &vresponse, &resplen,
569 response = vresponse;
570 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
571 MessageBox(NULL, "The already running Pageant "
572 "refused to add the key.", APPNAME,
573 MB_OK | MB_ICONERROR);
578 if (add234(rsakeys, rkey) != rkey)
579 sfree(rkey); /* already present, don't waste RAM */
582 if (already_running) {
583 unsigned char *request, *response;
585 int reqlen, alglen, clen, keybloblen, resplen, ret;
586 alglen = strlen(skey->alg->name);
587 clen = strlen(skey->comment);
589 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
591 reqlen = 4 + 1 + /* length, message type */
592 4 + alglen + /* algorithm name */
593 keybloblen + /* key data */
594 4 + clen /* comment */
597 request = snewn(reqlen, unsigned char);
599 request[4] = SSH2_AGENTC_ADD_IDENTITY;
601 PUT_32BIT(request + reqlen, alglen);
603 memcpy(request + reqlen, skey->alg->name, alglen);
605 reqlen += skey->alg->openssh_fmtkey(skey->data,
608 PUT_32BIT(request + reqlen, clen);
609 memcpy(request + reqlen + 4, skey->comment, clen);
610 PUT_32BIT(request, reqlen - 4);
613 ret = agent_query(request, reqlen, &vresponse, &resplen,
616 response = vresponse;
617 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
618 MessageBox(NULL, "The already running Pageant "
619 "refused to add the key.", APPNAME,
620 MB_OK | MB_ICONERROR);
625 if (add234(ssh2keys, skey) != skey) {
626 skey->alg->freekey(skey->data);
627 sfree(skey); /* already present, don't waste RAM */
634 * Create an SSH1 key list in a malloc'ed buffer; return its
637 static void *make_keylist1(int *length)
641 unsigned char *blob, *p, *ret;
645 * Count up the number and length of keys we hold.
649 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
651 blob = rsa_public_blob(key, &bloblen);
654 len += 4 + strlen(key->comment);
657 /* Allocate the buffer. */
658 p = ret = snewn(len, unsigned char);
659 if (length) *length = len;
663 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
664 blob = rsa_public_blob(key, &bloblen);
665 memcpy(p, blob, bloblen);
668 PUT_32BIT(p, strlen(key->comment));
669 memcpy(p + 4, key->comment, strlen(key->comment));
670 p += 4 + strlen(key->comment);
673 assert(p - ret == len);
678 * Create an SSH2 key list in a malloc'ed buffer; return its
681 static void *make_keylist2(int *length)
683 struct ssh2_userkey *key;
685 unsigned char *blob, *p, *ret;
689 * Count up the number and length of keys we hold.
693 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
695 len += 4; /* length field */
696 blob = key->alg->public_blob(key->data, &bloblen);
699 len += 4 + strlen(key->comment);
702 /* Allocate the buffer. */
703 p = ret = snewn(len, unsigned char);
704 if (length) *length = len;
707 * Packet header is the obvious five bytes, plus four
708 * bytes for the key count.
712 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
713 blob = key->alg->public_blob(key->data, &bloblen);
714 PUT_32BIT(p, bloblen);
716 memcpy(p, blob, bloblen);
719 PUT_32BIT(p, strlen(key->comment));
720 memcpy(p + 4, key->comment, strlen(key->comment));
721 p += 4 + strlen(key->comment);
724 assert(p - ret == len);
729 * Acquire a keylist1 from the primary Pageant; this means either
730 * calling make_keylist1 (if that's us) or sending a message to the
731 * primary Pageant (if it's not).
733 static void *get_keylist1(void)
737 if (already_running) {
738 unsigned char request[5], *response;
741 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
742 PUT_32BIT(request, 4);
744 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
746 response = vresponse;
747 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
750 ret = snewn(resplen-5, unsigned char);
751 memcpy(ret, response+5, resplen-5);
754 ret = make_keylist1(NULL);
760 * Acquire a keylist2 from the primary Pageant; this means either
761 * calling make_keylist2 (if that's us) or sending a message to the
762 * primary Pageant (if it's not).
764 static void *get_keylist2(void)
768 if (already_running) {
769 unsigned char request[5], *response;
773 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
774 PUT_32BIT(request, 4);
776 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
778 response = vresponse;
779 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
782 ret = snewn(resplen-5, unsigned char);
783 memcpy(ret, response+5, resplen-5);
786 ret = make_keylist2(NULL);
792 * This is the main agent function that answers messages.
794 static void answer_msg(void *msg)
796 unsigned char *p = msg;
797 unsigned char *ret = msg;
801 * Get the message type.
807 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
809 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
815 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
816 keylist = make_keylist1(&len);
817 if (len + 5 > AGENT_MAX_MSGLEN) {
821 PUT_32BIT(ret, len + 1);
822 memcpy(ret + 5, keylist, len);
826 case SSH2_AGENTC_REQUEST_IDENTITIES:
828 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
834 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
835 keylist = make_keylist2(&len);
836 if (len + 5 > AGENT_MAX_MSGLEN) {
840 PUT_32BIT(ret, len + 1);
841 memcpy(ret + 5, keylist, len);
845 case SSH1_AGENTC_RSA_CHALLENGE:
847 * Reply with either SSH1_AGENT_RSA_RESPONSE or
848 * SSH_AGENT_FAILURE, depending on whether we have that key
852 struct RSAKey reqkey, *key;
853 Bignum challenge, response;
854 unsigned char response_source[48], response_md5[16];
855 struct MD5Context md5c;
859 p += ssh1_read_bignum(p, &reqkey.exponent);
860 p += ssh1_read_bignum(p, &reqkey.modulus);
861 p += ssh1_read_bignum(p, &challenge);
862 memcpy(response_source + 32, p, 16);
864 if (GET_32BIT(p) != 1 ||
865 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
866 freebn(reqkey.exponent);
867 freebn(reqkey.modulus);
871 response = rsadecrypt(challenge, key);
872 for (i = 0; i < 32; i++)
873 response_source[i] = bignum_byte(response, 31 - i);
876 MD5Update(&md5c, response_source, 48);
877 MD5Final(response_md5, &md5c);
878 memset(response_source, 0, 48); /* burn the evidence */
879 freebn(response); /* and that evidence */
880 freebn(challenge); /* yes, and that evidence */
881 freebn(reqkey.exponent); /* and free some memory ... */
882 freebn(reqkey.modulus); /* ... while we're at it. */
885 * Packet is the obvious five byte header, plus sixteen
889 PUT_32BIT(ret, len - 4);
890 ret[4] = SSH1_AGENT_RSA_RESPONSE;
891 memcpy(ret + 5, response_md5, 16);
894 case SSH2_AGENTC_SIGN_REQUEST:
896 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
897 * SSH_AGENT_FAILURE, depending on whether we have that key
901 struct ssh2_userkey *key;
903 unsigned char *data, *signature;
904 int datalen, siglen, len;
906 b.len = GET_32BIT(p);
910 datalen = GET_32BIT(p);
913 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
916 signature = key->alg->sign(key->data, data, datalen, &siglen);
917 len = 5 + 4 + siglen;
918 PUT_32BIT(ret, len - 4);
919 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
920 PUT_32BIT(ret + 5, siglen);
921 memcpy(ret + 5 + 4, signature, siglen);
925 case SSH1_AGENTC_ADD_RSA_IDENTITY:
927 * Add to the list and return SSH_AGENT_SUCCESS, or
928 * SSH_AGENT_FAILURE if the key was malformed.
934 key = snew(struct RSAKey);
935 memset(key, 0, sizeof(struct RSAKey));
936 p += makekey(p, key, NULL, 1);
937 p += makeprivate(p, key);
938 p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
939 p += ssh1_read_bignum(p, &key->p); /* p */
940 p += ssh1_read_bignum(p, &key->q); /* q */
941 commentlen = GET_32BIT(p);
942 comment = snewn(commentlen+1, char);
944 memcpy(comment, p + 4, commentlen);
945 comment[commentlen] = '\0';
946 key->comment = comment;
949 ret[4] = SSH_AGENT_FAILURE;
950 if (add234(rsakeys, key) == key) {
952 ret[4] = SSH_AGENT_SUCCESS;
959 case SSH2_AGENTC_ADD_IDENTITY:
961 * Add to the list and return SSH_AGENT_SUCCESS, or
962 * SSH_AGENT_FAILURE if the key was malformed.
965 struct ssh2_userkey *key;
970 key = snew(struct ssh2_userkey);
972 alglen = GET_32BIT(p);
976 /* Add further algorithm names here. */
977 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
979 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
987 GET_32BIT((unsigned char *) msg) - (p -
988 (unsigned char *) msg -
990 key->data = key->alg->openssh_createkey(&p, &bloblen);
995 commlen = GET_32BIT(p);
998 comment = snewn(commlen + 1, char);
1000 memcpy(comment, p, commlen);
1001 comment[commlen] = '\0';
1003 key->comment = comment;
1006 ret[4] = SSH_AGENT_FAILURE;
1007 if (add234(ssh2keys, key) == key) {
1009 ret[4] = SSH_AGENT_SUCCESS;
1011 key->alg->freekey(key->data);
1012 sfree(key->comment);
1017 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1019 * Remove from the list and return SSH_AGENT_SUCCESS, or
1020 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1024 struct RSAKey reqkey, *key;
1026 p += makekey(p, &reqkey, NULL, 0);
1027 key = find234(rsakeys, &reqkey, NULL);
1028 freebn(reqkey.exponent);
1029 freebn(reqkey.modulus);
1031 ret[4] = SSH_AGENT_FAILURE;
1033 del234(rsakeys, key);
1037 ret[4] = SSH_AGENT_SUCCESS;
1041 case SSH2_AGENTC_REMOVE_IDENTITY:
1043 * Remove from the list and return SSH_AGENT_SUCCESS, or
1044 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1048 struct ssh2_userkey *key;
1051 b.len = GET_32BIT(p);
1055 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1060 ret[4] = SSH_AGENT_FAILURE;
1062 del234(ssh2keys, key);
1064 key->alg->freekey(key->data);
1066 ret[4] = SSH_AGENT_SUCCESS;
1070 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1072 * Remove all SSH1 keys. Always returns success.
1075 struct RSAKey *rkey;
1077 while ((rkey = index234(rsakeys, 0)) != NULL) {
1078 del234(rsakeys, rkey);
1085 ret[4] = SSH_AGENT_SUCCESS;
1088 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1090 * Remove all SSH2 keys. Always returns success.
1093 struct ssh2_userkey *skey;
1095 while ((skey = index234(ssh2keys, 0)) != NULL) {
1096 del234(ssh2keys, skey);
1097 skey->alg->freekey(skey->data);
1103 ret[4] = SSH_AGENT_SUCCESS;
1109 * Unrecognised message. Return SSH_AGENT_FAILURE.
1112 ret[4] = SSH_AGENT_FAILURE;
1118 * Key comparison function for the 2-3-4 tree of RSA keys.
1120 static int cmpkeys_rsa(void *av, void *bv)
1122 struct RSAKey *a = (struct RSAKey *) av;
1123 struct RSAKey *b = (struct RSAKey *) bv;
1130 * Compare by length of moduli.
1132 alen = bignum_bitcount(am);
1133 blen = bignum_bitcount(bm);
1136 else if (alen < blen)
1139 * Now compare by moduli themselves.
1141 alen = (alen + 7) / 8; /* byte count */
1142 while (alen-- > 0) {
1144 abyte = bignum_byte(am, alen);
1145 bbyte = bignum_byte(bm, alen);
1148 else if (abyte < bbyte)
1158 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1160 static int cmpkeys_ssh2(void *av, void *bv)
1162 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1163 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1166 unsigned char *ablob, *bblob;
1170 * Compare purely by public blob.
1172 ablob = a->alg->public_blob(a->data, &alen);
1173 bblob = b->alg->public_blob(b->data, &blen);
1176 for (i = 0; i < alen && i < blen; i++) {
1177 if (ablob[i] < bblob[i]) {
1180 } else if (ablob[i] > bblob[i]) {
1185 if (c == 0 && i < alen)
1186 c = +1; /* a is longer */
1187 if (c == 0 && i < blen)
1188 c = -1; /* a is longer */
1197 * Key comparison function for looking up a blob in the 2-3-4 tree
1200 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1202 struct blob *a = (struct blob *) av;
1203 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1206 unsigned char *ablob, *bblob;
1210 * Compare purely by public blob.
1214 bblob = b->alg->public_blob(b->data, &blen);
1217 for (i = 0; i < alen && i < blen; i++) {
1218 if (ablob[i] < bblob[i]) {
1221 } else if (ablob[i] > bblob[i]) {
1226 if (c == 0 && i < alen)
1227 c = +1; /* a is longer */
1228 if (c == 0 && i < blen)
1229 c = -1; /* a is longer */
1237 * Prompt for a key file to add, and add it.
1239 static void prompt_add_keyfile(void)
1242 char filename[FILENAME_MAX];
1243 char *filelist = snewn(8192, char);
1247 memset(&of, 0, sizeof(of));
1248 #ifdef OPENFILENAME_SIZE_VERSION_400
1249 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1251 of.lStructSize = sizeof(of);
1253 of.hwndOwner = main_hwnd;
1254 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1255 "All Files (*.*)\0*\0\0\0";
1256 of.lpstrCustomFilter = NULL;
1257 of.nFilterIndex = 1;
1258 of.lpstrFile = filelist;
1260 of.nMaxFile = FILENAME_MAX;
1261 of.lpstrFileTitle = NULL;
1262 of.lpstrInitialDir = NULL;
1263 of.lpstrTitle = "Select Private Key File";
1264 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1265 if (GetOpenFileName(&of)) {
1266 if(strlen(filelist) > of.nFileOffset)
1267 /* Only one filename returned? */
1268 add_keyfile(filename_from_str(filelist));
1270 /* we are returned a bunch of strings, end to
1271 * end. first string is the directory, the
1272 * rest the filenames. terminated with an
1275 filewalker = filelist;
1276 dirlen = strlen(filewalker);
1277 if(dirlen > FILENAME_MAX - 8) return;
1278 memcpy(filename, filewalker, dirlen);
1280 filewalker += dirlen + 1;
1281 filename[dirlen++] = '\\';
1283 /* then go over names one by one */
1285 n = strlen(filewalker) + 1;
1286 /* end of the list */
1289 /* too big, shouldn't happen */
1290 if(n + dirlen > FILENAME_MAX)
1293 memcpy(filename + dirlen, filewalker, n);
1296 add_keyfile(filename_from_str(filename));
1301 forget_passphrases();
1307 * Dialog-box function for the key list box.
1309 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1310 WPARAM wParam, LPARAM lParam)
1312 struct RSAKey *rkey;
1313 struct ssh2_userkey *skey;
1318 * Centre the window.
1320 { /* centre the window */
1324 hw = GetDesktopWindow();
1325 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1327 (rs.right + rs.left + rd.left - rd.right) / 2,
1328 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1329 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1333 SetWindowLong(hwnd, GWL_EXSTYLE,
1334 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1336 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1338 DestroyWindow(item);
1340 requested_help = FALSE;
1344 static int tabs[] = { 35, 60, 210 };
1345 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1346 sizeof(tabs) / sizeof(*tabs),
1352 switch (LOWORD(wParam)) {
1356 DestroyWindow(hwnd);
1358 case 101: /* add key */
1359 if (HIWORD(wParam) == BN_CLICKED ||
1360 HIWORD(wParam) == BN_DOUBLECLICKED) {
1361 if (passphrase_box) {
1362 MessageBeep(MB_ICONERROR);
1363 SetForegroundWindow(passphrase_box);
1366 prompt_add_keyfile();
1369 case 102: /* remove key */
1370 if (HIWORD(wParam) == BN_CLICKED ||
1371 HIWORD(wParam) == BN_DOUBLECLICKED) {
1376 /* our counter within the array of selected items */
1379 /* get the number of items selected in the list */
1381 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1383 /* none selected? that was silly */
1384 if (numSelected == 0) {
1389 /* get item indices in an array */
1390 selectedArray = snewn(numSelected, int);
1391 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1392 numSelected, (WPARAM)selectedArray);
1394 itemNum = numSelected - 1;
1395 rCount = count234(rsakeys);
1396 sCount = count234(ssh2keys);
1398 /* go through the non-rsakeys until we've covered them all,
1399 * and/or we're out of selected items to check. note that
1400 * we go *backwards*, to avoid complications from deleting
1401 * things hence altering the offset of subsequent items
1403 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1404 skey = index234(ssh2keys, i);
1406 if (selectedArray[itemNum] == rCount + i) {
1407 del234(ssh2keys, skey);
1408 skey->alg->freekey(skey->data);
1414 /* do the same for the rsa keys */
1415 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1416 rkey = index234(rsakeys, i);
1418 if(selectedArray[itemNum] == i) {
1419 del234(rsakeys, rkey);
1426 sfree(selectedArray);
1430 case 103: /* help */
1431 if (HIWORD(wParam) == BN_CLICKED ||
1432 HIWORD(wParam) == BN_DOUBLECLICKED) {
1434 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1435 (DWORD)"JI(`',`pageant.general')");
1436 requested_help = TRUE;
1444 int id = ((LPHELPINFO)lParam)->iCtrlId;
1447 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1448 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1449 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1452 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1453 requested_help = TRUE;
1461 DestroyWindow(hwnd);
1467 /* Set up a system tray icon */
1468 static BOOL AddTrayIcon(HWND hwnd)
1471 NOTIFYICONDATA tnid;
1474 #ifdef NIM_SETVERSION
1476 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1479 tnid.cbSize = sizeof(NOTIFYICONDATA);
1481 tnid.uID = 1; /* unique within this systray use */
1482 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1483 tnid.uCallbackMessage = WM_SYSTRAY;
1484 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1485 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1487 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1489 if (hicon) DestroyIcon(hicon);
1494 /* Update the saved-sessions menu. */
1495 static void update_sessions(void)
1499 TCHAR buf[MAX_PATH + 1];
1502 int index_key, index_menu;
1507 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1510 for(num_entries = GetMenuItemCount(session_menu);
1511 num_entries > initial_menuitems_count;
1513 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1518 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1519 TCHAR session_name[MAX_PATH + 1];
1520 unmungestr(buf, session_name, MAX_PATH);
1521 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1522 memset(&mii, 0, sizeof(mii));
1523 mii.cbSize = sizeof(mii);
1524 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1525 mii.fType = MFT_STRING;
1526 mii.fState = MFS_ENABLED;
1527 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1528 mii.dwTypeData = session_name;
1529 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1537 if(index_menu == 0) {
1538 mii.cbSize = sizeof(mii);
1539 mii.fMask = MIIM_TYPE | MIIM_STATE;
1540 mii.fType = MFT_STRING;
1541 mii.fState = MFS_GRAYED;
1542 mii.dwTypeData = _T("(No sessions)");
1543 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1547 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1548 WPARAM wParam, LPARAM lParam)
1551 static int menuinprogress;
1552 static UINT msgTaskbarCreated = 0;
1556 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1559 if (message==msgTaskbarCreated) {
1561 * Explorer has been restarted, so the tray icon will
1569 if (lParam == WM_RBUTTONUP) {
1571 GetCursorPos(&cursorpos);
1572 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1573 } else if (lParam == WM_LBUTTONDBLCLK) {
1574 /* Equivalent to IDM_VIEWKEYS. */
1575 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1579 if (!menuinprogress) {
1582 SetForegroundWindow(hwnd);
1583 ret = TrackPopupMenu(systray_menu,
1584 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1586 wParam, lParam, 0, hwnd, NULL);
1592 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1594 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1596 MessageBox(NULL, "Unable to execute PuTTY!",
1597 "Error", MB_OK | MB_ICONERROR);
1602 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1603 SendMessage(hwnd, WM_CLOSE, 0, 0);
1607 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1609 ShowWindow(keylist, SW_SHOWNORMAL);
1612 * Sometimes the window comes up minimised / hidden for
1613 * no obvious reason. Prevent this. This also brings it
1614 * to the front if it's already present (the user
1615 * selected View Keys because they wanted to _see_ the
1618 SetForegroundWindow(keylist);
1619 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1620 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1623 if (passphrase_box) {
1624 MessageBeep(MB_ICONERROR);
1625 SetForegroundWindow(passphrase_box);
1628 prompt_add_keyfile();
1632 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1634 ShowWindow(aboutbox, SW_SHOWNORMAL);
1636 * Sometimes the window comes up minimised / hidden
1637 * for no obvious reason. Prevent this.
1639 SetForegroundWindow(aboutbox);
1640 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1641 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1646 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1647 (DWORD)"JI(`',`pageant.general')");
1648 requested_help = TRUE;
1653 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1655 TCHAR buf[MAX_PATH + 1];
1656 TCHAR param[MAX_PATH + 1];
1657 memset(&mii, 0, sizeof(mii));
1658 mii.cbSize = sizeof(mii);
1659 mii.fMask = MIIM_TYPE;
1661 mii.dwTypeData = buf;
1662 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1664 strcat(param, mii.dwTypeData);
1665 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1666 _T(""), SW_SHOW) <= 32) {
1667 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1668 MB_OK | MB_ICONERROR);
1676 if (requested_help) {
1677 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1678 requested_help = FALSE;
1684 COPYDATASTRUCT *cds;
1690 PSID mapowner, procowner;
1691 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1695 cds = (COPYDATASTRUCT *) lParam;
1696 if (cds->dwData != AGENT_COPYDATA_ID)
1697 return 0; /* not our message, mate */
1698 mapname = (char *) cds->lpData;
1699 if (mapname[cds->cbData - 1] != '\0')
1700 return 0; /* failure to be ASCIZ! */
1702 debug(("mapname is :%s:\n", mapname));
1704 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1706 debug(("filemap is %p\n", filemap));
1708 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1712 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1713 GetCurrentProcessId())) ==
1716 debug(("couldn't get handle for process\n"));
1720 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1721 OWNER_SECURITY_INFORMATION,
1722 &procowner, NULL, NULL, NULL,
1723 &psd2) != ERROR_SUCCESS) {
1725 debug(("couldn't get owner info for process\n"));
1728 return 0; /* unable to get security info */
1731 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1732 OWNER_SECURITY_INFORMATION,
1733 &mapowner, NULL, NULL, NULL,
1734 &psd1) != ERROR_SUCCESS)) {
1737 ("couldn't get owner info for filemap: %d\n",
1743 debug(("got security stuff\n"));
1745 if (!EqualSid(mapowner, procowner))
1746 return 0; /* security ID mismatch! */
1748 debug(("security stuff matched\n"));
1754 debug(("security APIs not present\n"));
1758 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1760 debug(("p is %p\n", p));
1763 for (i = 0; i < 5; i++)
1766 ((unsigned char *) p)[i]));}
1772 CloseHandle(filemap);
1777 return DefWindowProc(hwnd, message, wParam, lParam);
1781 * Fork and Exec the command in cmdline. [DBW]
1783 void spawn_cmd(char *cmdline, char * args, int show)
1785 if (ShellExecute(NULL, _T("open"), cmdline,
1786 args, NULL, show) <= (HINSTANCE) 32) {
1788 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1789 (int)GetLastError());
1790 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1796 * This is a can't-happen stub, since Pageant never makes
1797 * asynchronous agent requests.
1799 void agent_schedule_callback(void (*callback)(void *, void *, int),
1800 void *callback_ctx, void *data, int len)
1802 assert(!"We shouldn't get here");
1805 void cleanup_exit(int code) { exit(code); }
1807 int flags = FLAG_SYNCAGENT;
1809 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1815 char *command = NULL;
1818 char **argv, **argstart;
1821 * Determine whether we're an NT system (should have security
1822 * APIs) or a non-NT system (don't do security).
1824 memset(&osi, 0, sizeof(OSVERSIONINFO));
1825 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1826 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1827 has_security = TRUE;
1829 has_security = FALSE;
1834 * Attempt to get the security API we need.
1836 advapi = LoadLibrary("ADVAPI32.DLL");
1838 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1839 if (!getsecurityinfo) {
1841 "Unable to access security APIs. Pageant will\n"
1842 "not run, in case it causes a security breach.",
1843 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1848 "This program has been compiled for Win9X and will\n"
1849 "not run on NT, in case it causes a security breach.",
1850 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1859 * See if we can find our Help file.
1862 char b[2048], *p, *q, *r;
1864 GetModuleFileName(NULL, b, sizeof(b) - 1);
1866 p = strrchr(b, '\\');
1867 if (p && p >= r) r = p+1;
1868 q = strrchr(b, ':');
1869 if (q && q >= r) r = q+1;
1870 strcpy(r, "putty.hlp");
1871 if ( (fp = fopen(b, "r")) != NULL) {
1872 help_path = dupstr(b);
1879 * Look for the PuTTY binary (we will enable the saved session
1880 * submenu if we find it).
1883 char b[2048], *p, *q, *r;
1885 GetModuleFileName(NULL, b, sizeof(b) - 1);
1887 p = strrchr(b, '\\');
1888 if (p && p >= r) r = p+1;
1889 q = strrchr(b, ':');
1890 if (q && q >= r) r = q+1;
1891 strcpy(r, "putty.exe");
1892 if ( (fp = fopen(b, "r")) != NULL) {
1893 putty_path = dupstr(b);
1900 * Find out if Pageant is already running.
1902 already_running = FALSE;
1904 already_running = TRUE;
1909 wndclass.lpfnWndProc = WndProc;
1910 wndclass.cbClsExtra = 0;
1911 wndclass.cbWndExtra = 0;
1912 wndclass.hInstance = inst;
1913 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1914 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1915 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1916 wndclass.lpszMenuName = NULL;
1917 wndclass.lpszClassName = APPNAME;
1919 RegisterClass(&wndclass);
1922 main_hwnd = keylist = NULL;
1924 main_hwnd = CreateWindow(APPNAME, APPNAME,
1925 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1926 CW_USEDEFAULT, CW_USEDEFAULT,
1927 100, 100, NULL, NULL, inst, NULL);
1929 /* Set up a system tray icon */
1930 AddTrayIcon(main_hwnd);
1932 /* Accelerators used: nsvkxa */
1933 systray_menu = CreatePopupMenu();
1935 session_menu = CreateMenu();
1936 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1937 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1938 (UINT) session_menu, "&Saved Sessions");
1939 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1941 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1943 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1944 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1946 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1947 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1948 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1949 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1950 initial_menuitems_count = GetMenuItemCount(session_menu);
1952 ShowWindow(main_hwnd, SW_HIDE);
1955 * Initialise storage for RSA keys.
1957 rsakeys = newtree234(cmpkeys_rsa);
1958 ssh2keys = newtree234(cmpkeys_ssh2);
1963 * Initialise storage for short-term passphrase cache.
1965 passphrases = newtree234(NULL);
1968 * Process the command line and add keys as listed on it.
1970 split_into_argv(cmdline, &argc, &argv, &argstart);
1971 for (i = 0; i < argc; i++) {
1972 if (!strcmp(argv[i], "-c")) {
1974 * If we see `-c', then the rest of the
1975 * command line should be treated as a
1976 * command to be spawned.
1979 command = argstart[i+1];
1984 add_keyfile(filename_from_str(argv[i]));
1990 * Forget any passphrase that we retained while going over
1991 * command line keyfiles.
1993 forget_passphrases();
1997 if (command[0] == '"')
1998 args = strchr(++command, '"');
2000 args = strchr(command, ' ');
2003 while(*args && isspace(*args)) args++;
2005 spawn_cmd(command, args, show);
2009 * If Pageant was already running, we leave now. If we haven't
2010 * even taken any auxiliary action (spawned a command or added
2013 if (already_running) {
2014 if (!command && !added_keys) {
2015 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2016 MB_ICONERROR | MB_OK);
2019 FreeLibrary(advapi);
2024 * Main message loop.
2026 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2027 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2028 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2029 TranslateMessage(&msg);
2030 DispatchMessage(&msg);
2034 /* Clean up the system tray icon */
2036 NOTIFYICONDATA tnid;
2038 tnid.cbSize = sizeof(NOTIFYICONDATA);
2039 tnid.hWnd = main_hwnd;
2042 Shell_NotifyIcon(NIM_DELETE, &tnid);
2044 DestroyMenu(systray_menu);
2048 FreeLibrary(advapi);