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\0*.PPK\0AllFiles\0*\0\0\0";
1254 of.lpstrCustomFilter = NULL;
1255 of.nFilterIndex = 1;
1256 of.lpstrFile = filelist;
1258 of.nMaxFile = FILENAME_MAX;
1259 of.lpstrFileTitle = NULL;
1260 of.lpstrInitialDir = NULL;
1261 of.lpstrTitle = "Select Private Key File";
1262 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1263 if (GetOpenFileName(&of)) {
1264 if(strlen(filelist) > of.nFileOffset)
1265 /* Only one filename returned? */
1266 add_keyfile(filelist);
1268 /* we are returned a bunch of strings, end to
1269 * end. first string is the directory, the
1270 * rest the filenames. terminated with an
1273 filewalker = filelist;
1274 dirlen = strlen(filewalker);
1275 if(dirlen > FILENAME_MAX - 8) return;
1276 memcpy(filename, filewalker, dirlen);
1278 filewalker += dirlen + 1;
1279 filename[dirlen++] = '\\';
1281 /* then go over names one by one */
1283 n = strlen(filewalker) + 1;
1284 /* end of the list */
1287 /* too big, shouldn't happen */
1288 if(n + dirlen > FILENAME_MAX)
1291 memcpy(filename + dirlen, filewalker, n);
1294 add_keyfile(filename);
1299 forget_passphrases();
1305 * Dialog-box function for the key list box.
1307 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1308 WPARAM wParam, LPARAM lParam)
1310 struct RSAKey *rkey;
1311 struct ssh2_userkey *skey;
1316 * Centre the window.
1318 { /* centre the window */
1322 hw = GetDesktopWindow();
1323 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1325 (rs.right + rs.left + rd.left - rd.right) / 2,
1326 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1327 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1331 SetWindowLong(hwnd, GWL_EXSTYLE,
1332 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1334 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1336 DestroyWindow(item);
1338 requested_help = FALSE;
1342 static int tabs[] = { 35, 60, 210 };
1343 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1344 sizeof(tabs) / sizeof(*tabs),
1350 switch (LOWORD(wParam)) {
1354 DestroyWindow(hwnd);
1356 case 101: /* add key */
1357 if (HIWORD(wParam) == BN_CLICKED ||
1358 HIWORD(wParam) == BN_DOUBLECLICKED) {
1359 if (passphrase_box) {
1360 MessageBeep(MB_ICONERROR);
1361 SetForegroundWindow(passphrase_box);
1364 prompt_add_keyfile();
1367 case 102: /* remove key */
1368 if (HIWORD(wParam) == BN_CLICKED ||
1369 HIWORD(wParam) == BN_DOUBLECLICKED) {
1374 /* our counter within the array of selected items */
1377 /* get the number of items selected in the list */
1379 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1381 /* none selected? that was silly */
1382 if (numSelected == 0) {
1387 /* get item indices in an array */
1388 selectedArray = smalloc(numSelected * sizeof(int));
1389 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1390 numSelected, (WPARAM)selectedArray);
1392 itemNum = numSelected - 1;
1393 rCount = count234(rsakeys);
1394 sCount = count234(ssh2keys);
1396 /* go through the non-rsakeys until we've covered them all,
1397 * and/or we're out of selected items to check. note that
1398 * we go *backwards*, to avoid complications from deleting
1399 * things hence altering the offset of subsequent items
1401 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1402 skey = index234(ssh2keys, i);
1404 if (selectedArray[itemNum] == rCount + i) {
1405 del234(ssh2keys, skey);
1406 skey->alg->freekey(skey->data);
1412 /* do the same for the rsa keys */
1413 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1414 rkey = index234(rsakeys, i);
1416 if(selectedArray[itemNum] == i) {
1417 del234(rsakeys, rkey);
1424 sfree(selectedArray);
1428 case 103: /* help */
1429 if (HIWORD(wParam) == BN_CLICKED ||
1430 HIWORD(wParam) == BN_DOUBLECLICKED) {
1432 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1433 (DWORD)"JI(`',`pageant.general')");
1434 requested_help = TRUE;
1442 int id = ((LPHELPINFO)lParam)->iCtrlId;
1445 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1446 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1447 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1450 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1451 requested_help = TRUE;
1459 DestroyWindow(hwnd);
1465 /* Set up a system tray icon */
1466 static BOOL AddTrayIcon(HWND hwnd)
1469 NOTIFYICONDATA tnid;
1472 #ifdef NIM_SETVERSION
1474 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1477 tnid.cbSize = sizeof(NOTIFYICONDATA);
1479 tnid.uID = 1; /* unique within this systray use */
1480 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1481 tnid.uCallbackMessage = WM_SYSTRAY;
1482 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1483 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1485 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1487 if (hicon) DestroyIcon(hicon);
1492 /* Update the saved-sessions menu. */
1493 static void update_sessions(void)
1497 TCHAR buf[MAX_PATH + 1];
1500 int index_key, index_menu;
1505 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1508 for(num_entries = GetMenuItemCount(session_menu);
1509 num_entries > initial_menuitems_count;
1511 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1516 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1517 TCHAR session_name[MAX_PATH + 1];
1518 unmungestr(buf, session_name, MAX_PATH);
1519 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1520 memset(&mii, 0, sizeof(mii));
1521 mii.cbSize = sizeof(mii);
1522 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1523 mii.fType = MFT_STRING;
1524 mii.fState = MFS_ENABLED;
1525 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1526 mii.dwTypeData = session_name;
1527 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1535 if(index_menu == 0) {
1536 mii.cbSize = sizeof(mii);
1537 mii.fMask = MIIM_TYPE | MIIM_STATE;
1538 mii.fType = MFT_STRING;
1539 mii.fState = MFS_GRAYED;
1540 mii.dwTypeData = _T("(No sessions)");
1541 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1545 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1546 WPARAM wParam, LPARAM lParam)
1549 static int menuinprogress;
1550 static UINT msgTaskbarCreated = 0;
1554 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1557 if (message==msgTaskbarCreated) {
1559 * Explorer has been restarted, so the tray icon will
1567 if (lParam == WM_RBUTTONUP) {
1569 GetCursorPos(&cursorpos);
1570 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1571 } else if (lParam == WM_LBUTTONDBLCLK) {
1572 /* Equivalent to IDM_VIEWKEYS. */
1573 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1577 if (!menuinprogress) {
1580 SetForegroundWindow(hwnd);
1581 ret = TrackPopupMenu(systray_menu,
1582 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1584 wParam, lParam, 0, hwnd, NULL);
1590 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1592 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1594 MessageBox(NULL, "Unable to execute PuTTY!",
1595 "Error", MB_OK | MB_ICONERROR);
1600 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1601 SendMessage(hwnd, WM_CLOSE, 0, 0);
1605 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1607 ShowWindow(keylist, SW_SHOWNORMAL);
1610 * Sometimes the window comes up minimised / hidden for
1611 * no obvious reason. Prevent this. This also brings it
1612 * to the front if it's already present (the user
1613 * selected View Keys because they wanted to _see_ the
1616 SetForegroundWindow(keylist);
1617 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1618 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1621 if (passphrase_box) {
1622 MessageBeep(MB_ICONERROR);
1623 SetForegroundWindow(passphrase_box);
1626 prompt_add_keyfile();
1630 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1632 ShowWindow(aboutbox, SW_SHOWNORMAL);
1634 * Sometimes the window comes up minimised / hidden
1635 * for no obvious reason. Prevent this.
1637 SetForegroundWindow(aboutbox);
1638 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1639 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1644 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1645 (DWORD)"JI(`',`pageant.general')");
1646 requested_help = TRUE;
1651 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1653 TCHAR buf[MAX_PATH + 1];
1654 TCHAR param[MAX_PATH + 1];
1655 memset(&mii, 0, sizeof(mii));
1656 mii.cbSize = sizeof(mii);
1657 mii.fMask = MIIM_TYPE;
1659 mii.dwTypeData = buf;
1660 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1662 strcat(param, mii.dwTypeData);
1663 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1664 _T(""), SW_SHOW) <= 32) {
1665 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1666 MB_OK | MB_ICONERROR);
1674 if (requested_help) {
1675 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1676 requested_help = FALSE;
1682 COPYDATASTRUCT *cds;
1688 PSID mapowner, procowner;
1689 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1693 cds = (COPYDATASTRUCT *) lParam;
1694 if (cds->dwData != AGENT_COPYDATA_ID)
1695 return 0; /* not our message, mate */
1696 mapname = (char *) cds->lpData;
1697 if (mapname[cds->cbData - 1] != '\0')
1698 return 0; /* failure to be ASCIZ! */
1700 debug(("mapname is :%s:\n", mapname));
1702 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1704 debug(("filemap is %p\n", filemap));
1706 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1710 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1711 GetCurrentProcessId())) ==
1714 debug(("couldn't get handle for process\n"));
1718 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1719 OWNER_SECURITY_INFORMATION,
1720 &procowner, NULL, NULL, NULL,
1721 &psd2) != ERROR_SUCCESS) {
1723 debug(("couldn't get owner info for process\n"));
1726 return 0; /* unable to get security info */
1729 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1730 OWNER_SECURITY_INFORMATION,
1731 &mapowner, NULL, NULL, NULL,
1732 &psd1) != ERROR_SUCCESS)) {
1735 ("couldn't get owner info for filemap: %d\n",
1741 debug(("got security stuff\n"));
1743 if (!EqualSid(mapowner, procowner))
1744 return 0; /* security ID mismatch! */
1746 debug(("security stuff matched\n"));
1752 debug(("security APIs not present\n"));
1756 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1758 debug(("p is %p\n", p));
1761 for (i = 0; i < 5; i++)
1764 ((unsigned char *) p)[i]));}
1770 CloseHandle(filemap);
1775 return DefWindowProc(hwnd, message, wParam, lParam);
1779 * Fork and Exec the command in cmdline. [DBW]
1781 void spawn_cmd(char *cmdline, char * args, int show)
1783 if (ShellExecute(NULL, _T("open"), cmdline,
1784 args, NULL, show) <= (HINSTANCE) 32) {
1786 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1787 (int)GetLastError());
1788 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1793 void cleanup_exit(int code) { exit(code); }
1795 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1801 char *command = NULL;
1804 char **argv, **argstart;
1807 * Determine whether we're an NT system (should have security
1808 * APIs) or a non-NT system (don't do security).
1810 memset(&osi, 0, sizeof(OSVERSIONINFO));
1811 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1812 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1813 has_security = TRUE;
1815 has_security = FALSE;
1820 * Attempt to get the security API we need.
1822 advapi = LoadLibrary("ADVAPI32.DLL");
1824 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1825 if (!getsecurityinfo) {
1827 "Unable to access security APIs. Pageant will\n"
1828 "not run, in case it causes a security breach.",
1829 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1834 "This program has been compiled for Win9X and will\n"
1835 "not run on NT, in case it causes a security breach.",
1836 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1845 * See if we can find our Help file.
1848 char b[2048], *p, *q, *r;
1850 GetModuleFileName(NULL, b, sizeof(b) - 1);
1852 p = strrchr(b, '\\');
1853 if (p && p >= r) r = p+1;
1854 q = strrchr(b, ':');
1855 if (q && q >= r) r = q+1;
1856 strcpy(r, "putty.hlp");
1857 if ( (fp = fopen(b, "r")) != NULL) {
1858 help_path = dupstr(b);
1865 * Look for the PuTTY binary (we will enable the saved session
1866 * submenu if we find it).
1869 char b[2048], *p, *q, *r;
1871 GetModuleFileName(NULL, b, sizeof(b) - 1);
1873 p = strrchr(b, '\\');
1874 if (p && p >= r) r = p+1;
1875 q = strrchr(b, ':');
1876 if (q && q >= r) r = q+1;
1877 strcpy(r, "putty.exe");
1878 if ( (fp = fopen(b, "r")) != NULL) {
1879 putty_path = dupstr(b);
1886 * Find out if Pageant is already running.
1888 already_running = FALSE;
1890 already_running = TRUE;
1895 wndclass.lpfnWndProc = WndProc;
1896 wndclass.cbClsExtra = 0;
1897 wndclass.cbWndExtra = 0;
1898 wndclass.hInstance = inst;
1899 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1900 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1901 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1902 wndclass.lpszMenuName = NULL;
1903 wndclass.lpszClassName = APPNAME;
1905 RegisterClass(&wndclass);
1908 main_hwnd = keylist = NULL;
1910 main_hwnd = CreateWindow(APPNAME, APPNAME,
1911 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1912 CW_USEDEFAULT, CW_USEDEFAULT,
1913 100, 100, NULL, NULL, inst, NULL);
1915 /* Set up a system tray icon */
1916 AddTrayIcon(main_hwnd);
1918 /* Accelerators used: nsvkxa */
1919 systray_menu = CreatePopupMenu();
1921 session_menu = CreateMenu();
1922 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1923 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1924 (UINT) session_menu, "&Saved Sessions");
1925 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1927 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1929 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1930 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1932 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1933 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1934 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1935 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1936 initial_menuitems_count = GetMenuItemCount(session_menu);
1938 ShowWindow(main_hwnd, SW_HIDE);
1941 * Initialise storage for RSA keys.
1943 rsakeys = newtree234(cmpkeys_rsa);
1944 ssh2keys = newtree234(cmpkeys_ssh2);
1949 * Initialise storage for short-term passphrase cache.
1951 passphrases = newtree234(NULL);
1954 * Process the command line and add keys as listed on it.
1956 split_into_argv(cmdline, &argc, &argv, &argstart);
1957 for (i = 0; i < argc; i++) {
1958 if (!strcmp(argv[i], "-c")) {
1960 * If we see `-c', then the rest of the
1961 * command line should be treated as a
1962 * command to be spawned.
1965 command = argstart[i+1];
1970 add_keyfile(argv[i]);
1976 * Forget any passphrase that we retained while going over
1977 * command line keyfiles.
1979 forget_passphrases();
1983 if (command[0] == '"')
1984 args = strchr(++command, '"');
1986 args = strchr(command, ' ');
1989 while(*args && isspace(*args)) args++;
1991 spawn_cmd(command, args, show);
1995 * If Pageant was already running, we leave now. If we haven't
1996 * even taken any auxiliary action (spawned a command or added
1999 if (already_running) {
2000 if (!command && !added_keys) {
2001 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2002 MB_ICONERROR | MB_OK);
2005 FreeLibrary(advapi);
2010 * Main message loop.
2012 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2013 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2014 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2015 TranslateMessage(&msg);
2016 DispatchMessage(&msg);
2020 /* Clean up the system tray icon */
2022 NOTIFYICONDATA tnid;
2024 tnid.cbSize = sizeof(NOTIFYICONDATA);
2025 tnid.hWnd = main_hwnd;
2028 Shell_NotifyIcon(NIM_DELETE, &tnid);
2030 DestroyMenu(systray_menu);
2034 FreeLibrary(advapi);