2 * Pageant: the PuTTY Authentication Agent.
20 #define IDI_MAINICON 200
21 #define IDI_TRAYICON 201
23 #define WM_XUSER (WM_USER + 0x2000)
24 #define WM_SYSTRAY (WM_XUSER + 6)
25 #define WM_SYSTRAY2 (WM_XUSER + 7)
27 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
30 * FIXME: maybe some day we can sort this out ...
32 #define AGENT_MAX_MSGLEN 8192
34 #define IDM_CLOSE 0x0010
35 #define IDM_VIEWKEYS 0x0020
36 #define IDM_ADDKEY 0x0030
37 #define IDM_HELP 0x0040
38 #define IDM_ABOUT 0x0050
40 #define APPNAME "Pageant"
44 static HINSTANCE instance;
45 static HWND main_hwnd;
48 static HMENU systray_menu, session_menu;
49 static int already_running;
50 static int requested_help;
52 static char *help_path;
53 static char *putty_path;
55 #define IDM_PUTTY 0x0060
56 #define IDM_SESSIONS_BASE 0x1000
57 #define IDM_SESSIONS_MAX 0x2000
58 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
59 #define PUTTY_DEFAULT "Default%20Settings"
60 static int initial_menuitems_count;
63 * Print a modal (Really Bad) message box and perform a fatal exit.
65 void modalfatalbox(char *fmt, ...)
71 buf = dupvprintf(fmt, ap);
73 MessageBox(main_hwnd, buf, "Pageant Fatal Error",
74 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
79 /* Un-munge session names out of the registry. */
80 static void unmungestr(char *in, char *out, int outlen)
83 if (*in == '%' && in[1] && in[2]) {
91 *out++ = (i << 4) + j;
105 static tree234 *rsakeys, *ssh2keys;
107 static int has_security;
109 typedef DWORD(WINAPI * gsi_fn_t)
110 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
111 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
112 static gsi_fn_t getsecurityinfo;
116 * Exports from pageantc.c
118 void agent_query(void *in, int inlen, void **out, int *outlen);
119 int agent_exists(void);
124 static void *make_keylist1(int *length);
125 static void *make_keylist2(int *length);
126 static void *get_keylist1(void);
127 static void *get_keylist2(void);
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(main_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 SSH2 key compare
149 * function, prototyped here.
155 static int cmpkeys_ssh2_asymm(void *av, void *bv);
157 #define GET_32BIT(cp) \
158 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
159 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
160 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
161 ((unsigned long)(unsigned char)(cp)[3]))
163 #define PUT_32BIT(cp, value) { \
164 (cp)[0] = (unsigned char)((value) >> 24); \
165 (cp)[1] = (unsigned char)((value) >> 16); \
166 (cp)[2] = (unsigned char)((value) >> 8); \
167 (cp)[3] = (unsigned char)(value); }
169 #define PASSPHRASE_MAXLEN 512
171 struct PassphraseProcStruct {
176 static tree234 *passphrases = NULL;
179 * After processing a list of filenames, we want to forget the
182 static void forget_passphrases(void)
184 while (count234(passphrases) > 0) {
185 char *pp = index234(passphrases, 0);
186 memset(pp, 0, strlen(pp));
187 delpos234(passphrases, 0);
193 * Dialog-box function for the Licence box.
195 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
196 WPARAM wParam, LPARAM lParam)
202 switch (LOWORD(wParam)) {
216 * Dialog-box function for the About box.
218 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
219 WPARAM wParam, LPARAM lParam)
223 SetDlgItemText(hwnd, 100, ver);
226 switch (LOWORD(wParam)) {
232 EnableWindow(hwnd, 0);
233 DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
234 EnableWindow(hwnd, 1);
235 SetActiveWindow(hwnd);
247 static HWND passphrase_box;
250 * Dialog-box function for the passphrase box.
252 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
253 WPARAM wParam, LPARAM lParam)
255 static char *passphrase = NULL;
256 struct PassphraseProcStruct *p;
260 passphrase_box = hwnd;
264 { /* centre the window */
268 hw = GetDesktopWindow();
269 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
271 (rs.right + rs.left + rd.left - rd.right) / 2,
272 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
273 rd.right - rd.left, rd.bottom - rd.top, TRUE);
276 SetForegroundWindow(hwnd);
277 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
278 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
279 p = (struct PassphraseProcStruct *) lParam;
280 passphrase = p->passphrase;
282 SetDlgItemText(hwnd, 101, p->comment);
284 SetDlgItemText(hwnd, 102, passphrase);
287 switch (LOWORD(wParam)) {
297 case 102: /* edit box */
298 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
299 GetDlgItemText(hwnd, 102, passphrase,
300 PASSPHRASE_MAXLEN - 1);
301 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
314 * Warn about the obsolescent key file format.
316 void old_keyfile_warning(void)
318 static const char mbtitle[] = "PuTTY Key File Warning";
319 static const char message[] =
320 "You are loading an SSH 2 private key which has an\n"
321 "old version of the file format. This means your key\n"
322 "file is not fully tamperproof. Future versions of\n"
323 "PuTTY may stop supporting this private key format,\n"
324 "so we recommend you convert your key to the new\n"
327 "You can perform this conversion by loading the key\n"
328 "into PuTTYgen and then saving it again.";
330 MessageBox(NULL, message, mbtitle, MB_OK);
334 * Update the visible key list.
336 static void keylist_update(void)
339 struct ssh2_userkey *skey;
343 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
344 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
345 char listentry[512], *p;
347 * Replace two spaces in the fingerprint with tabs, for
348 * nice alignment in the box.
350 strcpy(listentry, "ssh1\t");
351 p = listentry + strlen(listentry);
352 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
353 p = strchr(listentry, ' ');
356 p = strchr(listentry, ' ');
359 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
360 0, (LPARAM) listentry);
362 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
363 char listentry[512], *p;
366 * Replace two spaces in the fingerprint with tabs, for
367 * nice alignment in the box.
369 p = skey->alg->fingerprint(skey->data);
370 strncpy(listentry, p, sizeof(listentry));
371 p = strchr(listentry, ' ');
374 p = strchr(listentry, ' ');
377 len = strlen(listentry);
378 if (len < sizeof(listentry) - 2) {
379 listentry[len] = '\t';
380 strncpy(listentry + len + 1, skey->comment,
381 sizeof(listentry) - len - 1);
383 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
386 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
391 * This function loads a key from a file and adds it.
393 static void add_keyfile(char *filename)
395 char passphrase[PASSPHRASE_MAXLEN];
396 struct RSAKey *rkey = NULL;
397 struct ssh2_userkey *skey = NULL;
402 struct PassphraseProcStruct pps;
406 type = key_type(filename);
407 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
409 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
410 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
415 * See if the key is already loaded (in the primary Pageant,
416 * which may or may not be us).
420 unsigned char *keylist, *p;
421 int i, nkeys, bloblen;
423 if (type == SSH_KEYTYPE_SSH1) {
424 if (!rsakey_pubblob(filename, &blob, &bloblen)) {
425 MessageBox(NULL, "Couldn't load private key.", APPNAME,
426 MB_OK | MB_ICONERROR);
429 keylist = get_keylist1();
431 unsigned char *blob2;
432 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen);
434 MessageBox(NULL, "Couldn't load private key.", APPNAME,
435 MB_OK | MB_ICONERROR);
438 /* For our purposes we want the blob prefixed with its length */
439 blob2 = smalloc(bloblen+4);
440 PUT_32BIT(blob2, bloblen);
441 memcpy(blob2 + 4, blob, bloblen);
445 keylist = get_keylist2();
448 nkeys = GET_32BIT(keylist);
451 for (i = 0; i < nkeys; i++) {
452 if (!memcmp(blob, p, bloblen)) {
453 /* Key is already present; we can now leave. */
458 /* Now skip over public blob */
459 if (type == SSH_KEYTYPE_SSH1)
460 p += rsa_public_blob_len(p);
462 p += 4 + GET_32BIT(p);
463 /* Now skip over comment field */
464 p += 4 + GET_32BIT(p);
473 if (type == SSH_KEYTYPE_SSH1)
474 needs_pass = rsakey_encrypted(filename, &comment);
476 needs_pass = ssh2_userkey_encrypted(filename, &comment);
478 if (type == SSH_KEYTYPE_SSH1)
479 rkey = smalloc(sizeof(*rkey));
480 pps.passphrase = passphrase;
481 pps.comment = comment;
485 /* try all the remembered passphrases first */
486 char *pp = index234(passphrases, attempts);
488 strcpy(passphrase, pp);
492 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
493 NULL, PassphraseProc, (LPARAM) & pps);
494 passphrase_box = NULL;
498 if (type == SSH_KEYTYPE_SSH1)
500 return; /* operation cancelled */
505 if (type == SSH_KEYTYPE_SSH1)
506 ret = loadrsakey(filename, rkey, passphrase);
508 skey = ssh2_load_userkey(filename, passphrase);
509 if (skey == SSH2_WRONG_PASSPHRASE)
519 /* if they typed in an ok passphrase, remember it */
520 if(original_pass && ret) {
521 char *pp = dupstr(passphrase);
522 addpos234(passphrases, pp, 0);
528 MessageBox(NULL, "Couldn't load private key.", APPNAME,
529 MB_OK | MB_ICONERROR);
530 if (type == SSH_KEYTYPE_SSH1)
534 if (type == SSH_KEYTYPE_SSH1) {
535 if (already_running) {
536 unsigned char *request, *response;
538 int reqlen, clen, resplen;
540 clen = strlen(rkey->comment);
542 reqlen = 4 + 1 + /* length, message type */
544 ssh1_bignum_length(rkey->modulus) +
545 ssh1_bignum_length(rkey->exponent) +
546 ssh1_bignum_length(rkey->private_exponent) +
547 ssh1_bignum_length(rkey->iqmp) +
548 ssh1_bignum_length(rkey->p) +
549 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
552 request = smalloc(reqlen);
554 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
556 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
558 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
559 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
561 ssh1_write_bignum(request + reqlen,
562 rkey->private_exponent);
563 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
564 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
565 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
566 PUT_32BIT(request + reqlen, clen);
567 memcpy(request + reqlen + 4, rkey->comment, clen);
569 PUT_32BIT(request, reqlen - 4);
571 agent_query(request, reqlen, &vresponse, &resplen);
572 response = vresponse;
573 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
574 MessageBox(NULL, "The already running Pageant "
575 "refused to add the key.", APPNAME,
576 MB_OK | MB_ICONERROR);
581 if (add234(rsakeys, rkey) != rkey)
582 sfree(rkey); /* already present, don't waste RAM */
585 if (already_running) {
586 unsigned char *request, *response;
588 int reqlen, alglen, clen, keybloblen, resplen;
589 alglen = strlen(skey->alg->name);
590 clen = strlen(skey->comment);
592 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
594 reqlen = 4 + 1 + /* length, message type */
595 4 + alglen + /* algorithm name */
596 keybloblen + /* key data */
597 4 + clen /* comment */
600 request = smalloc(reqlen);
602 request[4] = SSH2_AGENTC_ADD_IDENTITY;
604 PUT_32BIT(request + reqlen, alglen);
606 memcpy(request + reqlen, skey->alg->name, alglen);
608 reqlen += skey->alg->openssh_fmtkey(skey->data,
611 PUT_32BIT(request + reqlen, clen);
612 memcpy(request + reqlen + 4, skey->comment, clen);
613 PUT_32BIT(request, reqlen - 4);
616 agent_query(request, reqlen, &vresponse, &resplen);
617 response = vresponse;
618 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
619 MessageBox(NULL, "The already running Pageant "
620 "refused to add the key.", APPNAME,
621 MB_OK | MB_ICONERROR);
626 if (add234(ssh2keys, skey) != skey) {
627 skey->alg->freekey(skey->data);
628 sfree(skey); /* already present, don't waste RAM */
635 * Create an SSH1 key list in a malloc'ed buffer; return its
638 static void *make_keylist1(int *length)
642 unsigned char *blob, *p, *ret;
646 * Count up the number and length of keys we hold.
650 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
652 blob = rsa_public_blob(key, &bloblen);
655 len += 4 + strlen(key->comment);
658 /* Allocate the buffer. */
659 p = ret = smalloc(len);
660 if (length) *length = len;
664 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
665 blob = rsa_public_blob(key, &bloblen);
666 memcpy(p, blob, bloblen);
669 PUT_32BIT(p, strlen(key->comment));
670 memcpy(p + 4, key->comment, strlen(key->comment));
671 p += 4 + strlen(key->comment);
674 assert(p - ret == len);
679 * Create an SSH2 key list in a malloc'ed buffer; return its
682 static void *make_keylist2(int *length)
684 struct ssh2_userkey *key;
686 unsigned char *blob, *p, *ret;
690 * Count up the number and length of keys we hold.
694 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
696 len += 4; /* length field */
697 blob = key->alg->public_blob(key->data, &bloblen);
700 len += 4 + strlen(key->comment);
703 /* Allocate the buffer. */
704 p = ret = smalloc(len);
705 if (length) *length = len;
708 * Packet header is the obvious five bytes, plus four
709 * bytes for the key count.
713 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
714 blob = key->alg->public_blob(key->data, &bloblen);
715 PUT_32BIT(p, bloblen);
717 memcpy(p, blob, bloblen);
720 PUT_32BIT(p, strlen(key->comment));
721 memcpy(p + 4, key->comment, strlen(key->comment));
722 p += 4 + strlen(key->comment);
725 assert(p - ret == len);
730 * Acquire a keylist1 from the primary Pageant; this means either
731 * calling make_keylist1 (if that's us) or sending a message to the
732 * primary Pageant (if it's not).
734 static void *get_keylist1(void)
738 if (already_running) {
739 unsigned char request[5], *response;
742 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
743 PUT_32BIT(request, 4);
745 agent_query(request, 5, &vresponse, &resplen);
746 response = vresponse;
747 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
750 ret = smalloc(resplen-5);
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 agent_query(request, 5, &vresponse, &resplen);
777 response = vresponse;
778 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
781 ret = smalloc(resplen-5);
782 memcpy(ret, response+5, resplen-5);
785 ret = make_keylist2(NULL);
791 * This is the main agent function that answers messages.
793 static void answer_msg(void *msg)
795 unsigned char *p = msg;
796 unsigned char *ret = msg;
800 * Get the message type.
806 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
808 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
814 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
815 keylist = make_keylist1(&len);
816 if (len + 5 > AGENT_MAX_MSGLEN) {
820 PUT_32BIT(ret, len + 1);
821 memcpy(ret + 5, keylist, len);
825 case SSH2_AGENTC_REQUEST_IDENTITIES:
827 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
833 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
834 keylist = make_keylist2(&len);
835 if (len + 5 > AGENT_MAX_MSGLEN) {
839 PUT_32BIT(ret, len + 1);
840 memcpy(ret + 5, keylist, len);
844 case SSH1_AGENTC_RSA_CHALLENGE:
846 * Reply with either SSH1_AGENT_RSA_RESPONSE or
847 * SSH_AGENT_FAILURE, depending on whether we have that key
851 struct RSAKey reqkey, *key;
852 Bignum challenge, response;
853 unsigned char response_source[48], response_md5[16];
854 struct MD5Context md5c;
858 p += ssh1_read_bignum(p, &reqkey.exponent);
859 p += ssh1_read_bignum(p, &reqkey.modulus);
860 p += ssh1_read_bignum(p, &challenge);
861 memcpy(response_source + 32, p, 16);
863 if (GET_32BIT(p) != 1 ||
864 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
865 freebn(reqkey.exponent);
866 freebn(reqkey.modulus);
870 response = rsadecrypt(challenge, key);
871 for (i = 0; i < 32; i++)
872 response_source[i] = bignum_byte(response, 31 - i);
875 MD5Update(&md5c, response_source, 48);
876 MD5Final(response_md5, &md5c);
877 memset(response_source, 0, 48); /* burn the evidence */
878 freebn(response); /* and that evidence */
879 freebn(challenge); /* yes, and that evidence */
880 freebn(reqkey.exponent); /* and free some memory ... */
881 freebn(reqkey.modulus); /* ... while we're at it. */
884 * Packet is the obvious five byte header, plus sixteen
888 PUT_32BIT(ret, len - 4);
889 ret[4] = SSH1_AGENT_RSA_RESPONSE;
890 memcpy(ret + 5, response_md5, 16);
893 case SSH2_AGENTC_SIGN_REQUEST:
895 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
896 * SSH_AGENT_FAILURE, depending on whether we have that key
900 struct ssh2_userkey *key;
902 unsigned char *data, *signature;
903 int datalen, siglen, len;
905 b.len = GET_32BIT(p);
909 datalen = GET_32BIT(p);
912 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
915 signature = key->alg->sign(key->data, data, datalen, &siglen);
916 len = 5 + 4 + siglen;
917 PUT_32BIT(ret, len - 4);
918 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
919 PUT_32BIT(ret + 5, siglen);
920 memcpy(ret + 5 + 4, signature, siglen);
924 case SSH1_AGENTC_ADD_RSA_IDENTITY:
926 * Add to the list and return SSH_AGENT_SUCCESS, or
927 * SSH_AGENT_FAILURE if the key was malformed.
933 key = smalloc(sizeof(struct RSAKey));
934 memset(key, 0, sizeof(struct RSAKey));
935 p += makekey(p, key, NULL, 1);
936 p += makeprivate(p, key);
937 p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
938 p += ssh1_read_bignum(p, &key->p); /* p */
939 p += ssh1_read_bignum(p, &key->q); /* q */
940 commentlen = GET_32BIT(p);
941 comment = smalloc(commentlen+1);
943 memcpy(comment, p + 4, commentlen);
944 comment[commentlen] = '\0';
945 key->comment = comment;
948 ret[4] = SSH_AGENT_FAILURE;
949 if (add234(rsakeys, key) == key) {
951 ret[4] = SSH_AGENT_SUCCESS;
958 case SSH2_AGENTC_ADD_IDENTITY:
960 * Add to the list and return SSH_AGENT_SUCCESS, or
961 * SSH_AGENT_FAILURE if the key was malformed.
964 struct ssh2_userkey *key;
969 key = smalloc(sizeof(struct ssh2_userkey));
971 alglen = GET_32BIT(p);
975 /* Add further algorithm names here. */
976 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
978 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
986 GET_32BIT((unsigned char *) msg) - (p -
987 (unsigned char *) msg -
989 key->data = key->alg->openssh_createkey(&p, &bloblen);
994 commlen = GET_32BIT(p);
997 comment = smalloc(commlen + 1);
999 memcpy(comment, p, commlen);
1000 comment[commlen] = '\0';
1002 key->comment = comment;
1005 ret[4] = SSH_AGENT_FAILURE;
1006 if (add234(ssh2keys, key) == key) {
1008 ret[4] = SSH_AGENT_SUCCESS;
1010 key->alg->freekey(key->data);
1011 sfree(key->comment);
1016 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1018 * Remove from the list and return SSH_AGENT_SUCCESS, or
1019 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1023 struct RSAKey reqkey, *key;
1025 p += makekey(p, &reqkey, NULL, 0);
1026 key = find234(rsakeys, &reqkey, NULL);
1027 freebn(reqkey.exponent);
1028 freebn(reqkey.modulus);
1030 ret[4] = SSH_AGENT_FAILURE;
1032 del234(rsakeys, key);
1036 ret[4] = SSH_AGENT_SUCCESS;
1040 case SSH2_AGENTC_REMOVE_IDENTITY:
1042 * Remove from the list and return SSH_AGENT_SUCCESS, or
1043 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1047 struct ssh2_userkey *key;
1050 b.len = GET_32BIT(p);
1054 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1059 ret[4] = SSH_AGENT_FAILURE;
1061 del234(ssh2keys, key);
1063 key->alg->freekey(key->data);
1065 ret[4] = SSH_AGENT_SUCCESS;
1069 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1071 * Remove all SSH1 keys. Always returns success.
1074 struct RSAKey *rkey;
1076 while ((rkey = index234(rsakeys, 0)) != NULL) {
1077 del234(rsakeys, rkey);
1084 ret[4] = SSH_AGENT_SUCCESS;
1087 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1089 * Remove all SSH2 keys. Always returns success.
1092 struct ssh2_userkey *skey;
1094 while ((skey = index234(ssh2keys, 0)) != NULL) {
1095 del234(ssh2keys, skey);
1096 skey->alg->freekey(skey->data);
1102 ret[4] = SSH_AGENT_SUCCESS;
1108 * Unrecognised message. Return SSH_AGENT_FAILURE.
1111 ret[4] = SSH_AGENT_FAILURE;
1117 * Key comparison function for the 2-3-4 tree of RSA keys.
1119 static int cmpkeys_rsa(void *av, void *bv)
1121 struct RSAKey *a = (struct RSAKey *) av;
1122 struct RSAKey *b = (struct RSAKey *) bv;
1129 * Compare by length of moduli.
1131 alen = bignum_bitcount(am);
1132 blen = bignum_bitcount(bm);
1135 else if (alen < blen)
1138 * Now compare by moduli themselves.
1140 alen = (alen + 7) / 8; /* byte count */
1141 while (alen-- > 0) {
1143 abyte = bignum_byte(am, alen);
1144 bbyte = bignum_byte(bm, alen);
1147 else if (abyte < bbyte)
1157 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1159 static int cmpkeys_ssh2(void *av, void *bv)
1161 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1162 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1165 unsigned char *ablob, *bblob;
1169 * Compare purely by public blob.
1171 ablob = a->alg->public_blob(a->data, &alen);
1172 bblob = b->alg->public_blob(b->data, &blen);
1175 for (i = 0; i < alen && i < blen; i++) {
1176 if (ablob[i] < bblob[i]) {
1179 } else if (ablob[i] > bblob[i]) {
1184 if (c == 0 && i < alen)
1185 c = +1; /* a is longer */
1186 if (c == 0 && i < blen)
1187 c = -1; /* a is longer */
1196 * Key comparison function for looking up a blob in the 2-3-4 tree
1199 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1201 struct blob *a = (struct blob *) av;
1202 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1205 unsigned char *ablob, *bblob;
1209 * Compare purely by public blob.
1213 bblob = b->alg->public_blob(b->data, &blen);
1216 for (i = 0; i < alen && i < blen; i++) {
1217 if (ablob[i] < bblob[i]) {
1220 } else if (ablob[i] > bblob[i]) {
1225 if (c == 0 && i < alen)
1226 c = +1; /* a is longer */
1227 if (c == 0 && i < blen)
1228 c = -1; /* a is longer */
1236 * Prompt for a key file to add, and add it.
1238 static void prompt_add_keyfile(void)
1241 char filename[FILENAME_MAX];
1242 char *filelist = smalloc(8192);
1246 memset(&of, 0, sizeof(of));
1247 #ifdef OPENFILENAME_SIZE_VERSION_400
1248 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1250 of.lStructSize = sizeof(of);
1252 of.hwndOwner = main_hwnd;
1253 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.PPK\0"
1254 "AllFiles\0*\0\0\0";
1255 of.lpstrCustomFilter = NULL;
1256 of.nFilterIndex = 1;
1257 of.lpstrFile = filelist;
1259 of.nMaxFile = FILENAME_MAX;
1260 of.lpstrFileTitle = NULL;
1261 of.lpstrInitialDir = NULL;
1262 of.lpstrTitle = "Select Private Key File";
1263 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1264 if (GetOpenFileName(&of)) {
1265 if(strlen(filelist) > of.nFileOffset)
1266 /* Only one filename returned? */
1267 add_keyfile(filelist);
1269 /* we are returned a bunch of strings, end to
1270 * end. first string is the directory, the
1271 * rest the filenames. terminated with an
1274 filewalker = filelist;
1275 dirlen = strlen(filewalker);
1276 if(dirlen > FILENAME_MAX - 8) return;
1277 memcpy(filename, filewalker, dirlen);
1279 filewalker += dirlen + 1;
1280 filename[dirlen++] = '\\';
1282 /* then go over names one by one */
1284 n = strlen(filewalker) + 1;
1285 /* end of the list */
1288 /* too big, shouldn't happen */
1289 if(n + dirlen > FILENAME_MAX)
1292 memcpy(filename + dirlen, filewalker, n);
1295 add_keyfile(filename);
1300 forget_passphrases();
1306 * Dialog-box function for the key list box.
1308 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1309 WPARAM wParam, LPARAM lParam)
1311 struct RSAKey *rkey;
1312 struct ssh2_userkey *skey;
1317 * Centre the window.
1319 { /* centre the window */
1323 hw = GetDesktopWindow();
1324 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1326 (rs.right + rs.left + rd.left - rd.right) / 2,
1327 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1328 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1332 SetWindowLong(hwnd, GWL_EXSTYLE,
1333 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1335 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1337 DestroyWindow(item);
1339 requested_help = FALSE;
1343 static int tabs[] = { 35, 60, 210 };
1344 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1345 sizeof(tabs) / sizeof(*tabs),
1351 switch (LOWORD(wParam)) {
1355 DestroyWindow(hwnd);
1357 case 101: /* add key */
1358 if (HIWORD(wParam) == BN_CLICKED ||
1359 HIWORD(wParam) == BN_DOUBLECLICKED) {
1360 if (passphrase_box) {
1361 MessageBeep(MB_ICONERROR);
1362 SetForegroundWindow(passphrase_box);
1365 prompt_add_keyfile();
1368 case 102: /* remove key */
1369 if (HIWORD(wParam) == BN_CLICKED ||
1370 HIWORD(wParam) == BN_DOUBLECLICKED) {
1375 /* our counter within the array of selected items */
1378 /* get the number of items selected in the list */
1380 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1382 /* none selected? that was silly */
1383 if (numSelected == 0) {
1388 /* get item indices in an array */
1389 selectedArray = smalloc(numSelected * sizeof(int));
1390 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1391 numSelected, (WPARAM)selectedArray);
1393 itemNum = numSelected - 1;
1394 rCount = count234(rsakeys);
1395 sCount = count234(ssh2keys);
1397 /* go through the non-rsakeys until we've covered them all,
1398 * and/or we're out of selected items to check. note that
1399 * we go *backwards*, to avoid complications from deleting
1400 * things hence altering the offset of subsequent items
1402 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1403 skey = index234(ssh2keys, i);
1405 if (selectedArray[itemNum] == rCount + i) {
1406 del234(ssh2keys, skey);
1407 skey->alg->freekey(skey->data);
1413 /* do the same for the rsa keys */
1414 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1415 rkey = index234(rsakeys, i);
1417 if(selectedArray[itemNum] == i) {
1418 del234(rsakeys, rkey);
1425 sfree(selectedArray);
1429 case 103: /* help */
1430 if (HIWORD(wParam) == BN_CLICKED ||
1431 HIWORD(wParam) == BN_DOUBLECLICKED) {
1433 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1434 (DWORD)"JI(`',`pageant.general')");
1435 requested_help = TRUE;
1443 int id = ((LPHELPINFO)lParam)->iCtrlId;
1446 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1447 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1448 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1451 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1452 requested_help = TRUE;
1460 DestroyWindow(hwnd);
1466 /* Set up a system tray icon */
1467 static BOOL AddTrayIcon(HWND hwnd)
1470 NOTIFYICONDATA tnid;
1473 #ifdef NIM_SETVERSION
1475 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1478 tnid.cbSize = sizeof(NOTIFYICONDATA);
1480 tnid.uID = 1; /* unique within this systray use */
1481 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1482 tnid.uCallbackMessage = WM_SYSTRAY;
1483 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1484 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1486 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1488 if (hicon) DestroyIcon(hicon);
1493 /* Update the saved-sessions menu. */
1494 static void update_sessions(void)
1498 TCHAR buf[MAX_PATH + 1];
1501 int index_key, index_menu;
1506 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1509 for(num_entries = GetMenuItemCount(session_menu);
1510 num_entries > initial_menuitems_count;
1512 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1517 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1518 TCHAR session_name[MAX_PATH + 1];
1519 unmungestr(buf, session_name, MAX_PATH);
1520 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1521 memset(&mii, 0, sizeof(mii));
1522 mii.cbSize = sizeof(mii);
1523 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1524 mii.fType = MFT_STRING;
1525 mii.fState = MFS_ENABLED;
1526 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1527 mii.dwTypeData = session_name;
1528 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1536 if(index_menu == 0) {
1537 mii.cbSize = sizeof(mii);
1538 mii.fMask = MIIM_TYPE | MIIM_STATE;
1539 mii.fType = MFT_STRING;
1540 mii.fState = MFS_GRAYED;
1541 mii.dwTypeData = _T("(No sessions)");
1542 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1546 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1547 WPARAM wParam, LPARAM lParam)
1550 static int menuinprogress;
1551 static UINT msgTaskbarCreated = 0;
1555 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1558 if (message==msgTaskbarCreated) {
1560 * Explorer has been restarted, so the tray icon will
1568 if (lParam == WM_RBUTTONUP) {
1570 GetCursorPos(&cursorpos);
1571 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1572 } else if (lParam == WM_LBUTTONDBLCLK) {
1573 /* Equivalent to IDM_VIEWKEYS. */
1574 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1578 if (!menuinprogress) {
1581 SetForegroundWindow(hwnd);
1582 ret = TrackPopupMenu(systray_menu,
1583 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1585 wParam, lParam, 0, hwnd, NULL);
1591 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1593 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1595 MessageBox(NULL, "Unable to execute PuTTY!",
1596 "Error", MB_OK | MB_ICONERROR);
1601 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1602 SendMessage(hwnd, WM_CLOSE, 0, 0);
1606 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1608 ShowWindow(keylist, SW_SHOWNORMAL);
1611 * Sometimes the window comes up minimised / hidden for
1612 * no obvious reason. Prevent this. This also brings it
1613 * to the front if it's already present (the user
1614 * selected View Keys because they wanted to _see_ the
1617 SetForegroundWindow(keylist);
1618 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1619 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1622 if (passphrase_box) {
1623 MessageBeep(MB_ICONERROR);
1624 SetForegroundWindow(passphrase_box);
1627 prompt_add_keyfile();
1631 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1633 ShowWindow(aboutbox, SW_SHOWNORMAL);
1635 * Sometimes the window comes up minimised / hidden
1636 * for no obvious reason. Prevent this.
1638 SetForegroundWindow(aboutbox);
1639 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1640 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1645 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1646 (DWORD)"JI(`',`pageant.general')");
1647 requested_help = TRUE;
1652 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1654 TCHAR buf[MAX_PATH + 1];
1655 TCHAR param[MAX_PATH + 1];
1656 memset(&mii, 0, sizeof(mii));
1657 mii.cbSize = sizeof(mii);
1658 mii.fMask = MIIM_TYPE;
1660 mii.dwTypeData = buf;
1661 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1663 strcat(param, mii.dwTypeData);
1664 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1665 _T(""), SW_SHOW) <= 32) {
1666 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1667 MB_OK | MB_ICONERROR);
1675 if (requested_help) {
1676 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1677 requested_help = FALSE;
1683 COPYDATASTRUCT *cds;
1689 PSID mapowner, procowner;
1690 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1694 cds = (COPYDATASTRUCT *) lParam;
1695 if (cds->dwData != AGENT_COPYDATA_ID)
1696 return 0; /* not our message, mate */
1697 mapname = (char *) cds->lpData;
1698 if (mapname[cds->cbData - 1] != '\0')
1699 return 0; /* failure to be ASCIZ! */
1701 debug(("mapname is :%s:\n", mapname));
1703 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1705 debug(("filemap is %p\n", filemap));
1707 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1711 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1712 GetCurrentProcessId())) ==
1715 debug(("couldn't get handle for process\n"));
1719 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1720 OWNER_SECURITY_INFORMATION,
1721 &procowner, NULL, NULL, NULL,
1722 &psd2) != ERROR_SUCCESS) {
1724 debug(("couldn't get owner info for process\n"));
1727 return 0; /* unable to get security info */
1730 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1731 OWNER_SECURITY_INFORMATION,
1732 &mapowner, NULL, NULL, NULL,
1733 &psd1) != ERROR_SUCCESS)) {
1736 ("couldn't get owner info for filemap: %d\n",
1742 debug(("got security stuff\n"));
1744 if (!EqualSid(mapowner, procowner))
1745 return 0; /* security ID mismatch! */
1747 debug(("security stuff matched\n"));
1753 debug(("security APIs not present\n"));
1757 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1759 debug(("p is %p\n", p));
1762 for (i = 0; i < 5; i++)
1765 ((unsigned char *) p)[i]));}
1771 CloseHandle(filemap);
1776 return DefWindowProc(hwnd, message, wParam, lParam);
1780 * Fork and Exec the command in cmdline. [DBW]
1782 void spawn_cmd(char *cmdline, char * args, int show)
1784 if (ShellExecute(NULL, _T("open"), cmdline,
1785 args, NULL, show) <= (HINSTANCE) 32) {
1787 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1788 (int)GetLastError());
1789 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1794 void cleanup_exit(int code) { exit(code); }
1796 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1802 char *command = NULL;
1805 char **argv, **argstart;
1808 * Determine whether we're an NT system (should have security
1809 * APIs) or a non-NT system (don't do security).
1811 memset(&osi, 0, sizeof(OSVERSIONINFO));
1812 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1813 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1814 has_security = TRUE;
1816 has_security = FALSE;
1821 * Attempt to get the security API we need.
1823 advapi = LoadLibrary("ADVAPI32.DLL");
1825 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1826 if (!getsecurityinfo) {
1828 "Unable to access security APIs. Pageant will\n"
1829 "not run, in case it causes a security breach.",
1830 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1835 "This program has been compiled for Win9X and will\n"
1836 "not run on NT, in case it causes a security breach.",
1837 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1846 * See if we can find our Help file.
1849 char b[2048], *p, *q, *r;
1851 GetModuleFileName(NULL, b, sizeof(b) - 1);
1853 p = strrchr(b, '\\');
1854 if (p && p >= r) r = p+1;
1855 q = strrchr(b, ':');
1856 if (q && q >= r) r = q+1;
1857 strcpy(r, "putty.hlp");
1858 if ( (fp = fopen(b, "r")) != NULL) {
1859 help_path = dupstr(b);
1866 * Look for the PuTTY binary (we will enable the saved session
1867 * submenu if we find it).
1870 char b[2048], *p, *q, *r;
1872 GetModuleFileName(NULL, b, sizeof(b) - 1);
1874 p = strrchr(b, '\\');
1875 if (p && p >= r) r = p+1;
1876 q = strrchr(b, ':');
1877 if (q && q >= r) r = q+1;
1878 strcpy(r, "putty.exe");
1879 if ( (fp = fopen(b, "r")) != NULL) {
1880 putty_path = dupstr(b);
1887 * Find out if Pageant is already running.
1889 already_running = FALSE;
1891 already_running = TRUE;
1896 wndclass.lpfnWndProc = WndProc;
1897 wndclass.cbClsExtra = 0;
1898 wndclass.cbWndExtra = 0;
1899 wndclass.hInstance = inst;
1900 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1901 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1902 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1903 wndclass.lpszMenuName = NULL;
1904 wndclass.lpszClassName = APPNAME;
1906 RegisterClass(&wndclass);
1909 main_hwnd = keylist = NULL;
1911 main_hwnd = CreateWindow(APPNAME, APPNAME,
1912 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1913 CW_USEDEFAULT, CW_USEDEFAULT,
1914 100, 100, NULL, NULL, inst, NULL);
1916 /* Set up a system tray icon */
1917 AddTrayIcon(main_hwnd);
1919 /* Accelerators used: nsvkxa */
1920 systray_menu = CreatePopupMenu();
1922 session_menu = CreateMenu();
1923 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1924 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1925 (UINT) session_menu, "&Saved Sessions");
1926 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1928 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1930 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1931 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1933 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1934 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1935 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1936 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1937 initial_menuitems_count = GetMenuItemCount(session_menu);
1939 ShowWindow(main_hwnd, SW_HIDE);
1942 * Initialise storage for RSA keys.
1944 rsakeys = newtree234(cmpkeys_rsa);
1945 ssh2keys = newtree234(cmpkeys_ssh2);
1950 * Initialise storage for short-term passphrase cache.
1952 passphrases = newtree234(NULL);
1955 * Process the command line and add keys as listed on it.
1957 split_into_argv(cmdline, &argc, &argv, &argstart);
1958 for (i = 0; i < argc; i++) {
1959 if (!strcmp(argv[i], "-c")) {
1961 * If we see `-c', then the rest of the
1962 * command line should be treated as a
1963 * command to be spawned.
1966 command = argstart[i+1];
1971 add_keyfile(argv[i]);
1977 * Forget any passphrase that we retained while going over
1978 * command line keyfiles.
1980 forget_passphrases();
1984 if (command[0] == '"')
1985 args = strchr(++command, '"');
1987 args = strchr(command, ' ');
1990 while(*args && isspace(*args)) args++;
1992 spawn_cmd(command, args, show);
1996 * If Pageant was already running, we leave now. If we haven't
1997 * even taken any auxiliary action (spawned a command or added
2000 if (already_running) {
2001 if (!command && !added_keys) {
2002 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2003 MB_ICONERROR | MB_OK);
2006 FreeLibrary(advapi);
2011 * Main message loop.
2013 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2014 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2015 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2016 TranslateMessage(&msg);
2017 DispatchMessage(&msg);
2021 /* Clean up the system tray icon */
2023 NOTIFYICONDATA tnid;
2025 tnid.cbSize = sizeof(NOTIFYICONDATA);
2026 tnid.hWnd = main_hwnd;
2029 Shell_NotifyIcon(NIM_DELETE, &tnid);
2031 DestroyMenu(systray_menu);
2035 FreeLibrary(advapi);