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 vsprintf(stuff, fmt, ap);
73 MessageBox(main_hwnd, stuff, "Pageant Fatal Error",
74 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
78 /* Un-munge session names out of the registry. */
79 static void unmungestr(char *in, char *out, int outlen)
82 if (*in == '%' && in[1] && in[2]) {
90 *out++ = (i << 4) + j;
104 static tree234 *rsakeys, *ssh2keys;
106 static int has_security;
108 typedef DWORD(WINAPI * gsi_fn_t)
109 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
110 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
111 static gsi_fn_t getsecurityinfo;
115 * Exports from pageantc.c
117 void agent_query(void *in, int inlen, void **out, int *outlen);
118 int agent_exists(void);
123 static void *make_keylist1(int *length);
124 static void *make_keylist2(int *length);
125 static void *get_keylist1(void);
126 static void *get_keylist2(void);
129 * We need this to link with the RSA code, because rsaencrypt()
130 * pads its data with random bytes. Since we only use rsadecrypt()
131 * and the signing functions, which are deterministic, this should
134 * If it _is_ called, there is a _serious_ problem, because it
135 * won't generate true random numbers. So we must scream, panic,
136 * and exit immediately if that should happen.
138 int random_byte(void)
140 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
142 /* this line can't be reached but it placates MSVC's warnings :-) */
147 * Blob structure for passing to the asymmetric SSH2 key compare
148 * function, prototyped here.
154 static int cmpkeys_ssh2_asymm(void *av, void *bv);
157 * This function is needed to link with the DES code. We need not
158 * have it do anything at all.
160 void logevent(char *msg)
164 #define GET_32BIT(cp) \
165 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
166 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
167 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
168 ((unsigned long)(unsigned char)(cp)[3]))
170 #define PUT_32BIT(cp, value) { \
171 (cp)[0] = (unsigned char)((value) >> 24); \
172 (cp)[1] = (unsigned char)((value) >> 16); \
173 (cp)[2] = (unsigned char)((value) >> 8); \
174 (cp)[3] = (unsigned char)(value); }
176 #define PASSPHRASE_MAXLEN 512
178 struct PassphraseProcStruct {
183 static tree234 *passphrases = NULL;
186 * After processing a list of filenames, we want to forget the
189 static void forget_passphrases(void)
191 while (count234(passphrases) > 0) {
192 char *pp = index234(passphrases, 0);
193 memset(pp, 0, strlen(pp));
194 delpos234(passphrases, 0);
200 * Dialog-box function for the Licence box.
202 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
203 WPARAM wParam, LPARAM lParam)
209 switch (LOWORD(wParam)) {
223 * Dialog-box function for the About box.
225 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
226 WPARAM wParam, LPARAM lParam)
230 SetDlgItemText(hwnd, 100, ver);
233 switch (LOWORD(wParam)) {
239 EnableWindow(hwnd, 0);
240 DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
241 EnableWindow(hwnd, 1);
242 SetActiveWindow(hwnd);
254 static HWND passphrase_box;
257 * Dialog-box function for the passphrase box.
259 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
260 WPARAM wParam, LPARAM lParam)
262 static char *passphrase = NULL;
263 struct PassphraseProcStruct *p;
267 passphrase_box = hwnd;
271 { /* centre the window */
275 hw = GetDesktopWindow();
276 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
278 (rs.right + rs.left + rd.left - rd.right) / 2,
279 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
280 rd.right - rd.left, rd.bottom - rd.top, TRUE);
283 SetForegroundWindow(hwnd);
284 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
285 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
286 p = (struct PassphraseProcStruct *) lParam;
287 passphrase = p->passphrase;
289 SetDlgItemText(hwnd, 101, p->comment);
291 SetDlgItemText(hwnd, 102, passphrase);
294 switch (LOWORD(wParam)) {
304 case 102: /* edit box */
305 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
306 GetDlgItemText(hwnd, 102, passphrase,
307 PASSPHRASE_MAXLEN - 1);
308 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
321 * Warn about the obsolescent key file format.
323 void old_keyfile_warning(void)
325 static const char mbtitle[] = "PuTTY Key File Warning";
326 static const char message[] =
327 "You are loading an SSH 2 private key which has an\n"
328 "old version of the file format. This means your key\n"
329 "file is not fully tamperproof. Future versions of\n"
330 "PuTTY may stop supporting this private key format,\n"
331 "so we recommend you convert your key to the new\n"
334 "You can perform this conversion by loading the key\n"
335 "into PuTTYgen and then saving it again.";
337 MessageBox(NULL, message, mbtitle, MB_OK);
341 * Update the visible key list.
343 static void keylist_update(void)
346 struct ssh2_userkey *skey;
350 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
351 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
352 char listentry[512], *p;
354 * Replace two spaces in the fingerprint with tabs, for
355 * nice alignment in the box.
357 strcpy(listentry, "ssh1\t");
358 p = listentry + strlen(listentry);
359 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
360 p = strchr(listentry, ' ');
363 p = strchr(listentry, ' ');
366 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
367 0, (LPARAM) listentry);
369 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
370 char listentry[512], *p;
373 * Replace two spaces in the fingerprint with tabs, for
374 * nice alignment in the box.
376 p = skey->alg->fingerprint(skey->data);
377 strncpy(listentry, p, sizeof(listentry));
378 p = strchr(listentry, ' ');
381 p = strchr(listentry, ' ');
384 len = strlen(listentry);
385 if (len < sizeof(listentry) - 2) {
386 listentry[len] = '\t';
387 strncpy(listentry + len + 1, skey->comment,
388 sizeof(listentry) - len - 1);
390 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
393 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
398 * This function loads a key from a file and adds it.
400 static void add_keyfile(char *filename)
402 char passphrase[PASSPHRASE_MAXLEN];
403 struct RSAKey *rkey = NULL;
404 struct ssh2_userkey *skey = NULL;
409 struct PassphraseProcStruct pps;
413 type = key_type(filename);
414 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
416 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
417 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
422 * See if the key is already loaded (in the primary Pageant,
423 * which may or may not be us).
427 unsigned char *keylist, *p;
428 int i, nkeys, bloblen;
430 if (type == SSH_KEYTYPE_SSH1) {
431 if (!rsakey_pubblob(filename, &blob, &bloblen)) {
432 MessageBox(NULL, "Couldn't load private key.", APPNAME,
433 MB_OK | MB_ICONERROR);
436 keylist = get_keylist1();
438 unsigned char *blob2;
439 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen);
441 MessageBox(NULL, "Couldn't load private key.", APPNAME,
442 MB_OK | MB_ICONERROR);
445 /* For our purposes we want the blob prefixed with its length */
446 blob2 = smalloc(bloblen+4);
447 PUT_32BIT(blob2, bloblen);
448 memcpy(blob2 + 4, blob, bloblen);
452 keylist = get_keylist2();
455 nkeys = GET_32BIT(keylist);
458 for (i = 0; i < nkeys; i++) {
459 if (!memcmp(blob, p, bloblen)) {
460 /* Key is already present; we can now leave. */
465 /* Now skip over public blob */
466 if (type == SSH_KEYTYPE_SSH1)
467 p += rsa_public_blob_len(p);
469 p += 4 + GET_32BIT(p);
470 /* Now skip over comment field */
471 p += 4 + GET_32BIT(p);
480 if (type == SSH_KEYTYPE_SSH1)
481 needs_pass = rsakey_encrypted(filename, &comment);
483 needs_pass = ssh2_userkey_encrypted(filename, &comment);
485 if (type == SSH_KEYTYPE_SSH1)
486 rkey = smalloc(sizeof(*rkey));
487 pps.passphrase = passphrase;
488 pps.comment = comment;
492 /* try all the remembered passphrases first */
493 char *pp = index234(passphrases, attempts);
495 strcpy(passphrase, pp);
499 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
500 NULL, PassphraseProc, (LPARAM) & pps);
501 passphrase_box = NULL;
505 if (type == SSH_KEYTYPE_SSH1)
507 return; /* operation cancelled */
512 if (type == SSH_KEYTYPE_SSH1)
513 ret = loadrsakey(filename, rkey, passphrase);
515 skey = ssh2_load_userkey(filename, passphrase);
516 if (skey == SSH2_WRONG_PASSPHRASE)
526 /* if they typed in an ok passphrase, remember it */
527 if(original_pass && ret) {
528 char *pp = dupstr(passphrase);
529 addpos234(passphrases, pp, 0);
535 MessageBox(NULL, "Couldn't load private key.", APPNAME,
536 MB_OK | MB_ICONERROR);
537 if (type == SSH_KEYTYPE_SSH1)
541 if (type == SSH_KEYTYPE_SSH1) {
542 if (already_running) {
543 unsigned char *request, *response;
545 int reqlen, clen, resplen;
547 clen = strlen(rkey->comment);
549 reqlen = 4 + 1 + /* length, message type */
551 ssh1_bignum_length(rkey->modulus) +
552 ssh1_bignum_length(rkey->exponent) +
553 ssh1_bignum_length(rkey->private_exponent) +
554 ssh1_bignum_length(rkey->iqmp) +
555 ssh1_bignum_length(rkey->p) +
556 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
559 request = smalloc(reqlen);
561 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
563 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
565 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
566 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
568 ssh1_write_bignum(request + reqlen,
569 rkey->private_exponent);
570 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
571 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
572 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
573 PUT_32BIT(request + reqlen, clen);
574 memcpy(request + reqlen + 4, rkey->comment, clen);
576 PUT_32BIT(request, reqlen - 4);
578 agent_query(request, reqlen, &vresponse, &resplen);
579 response = vresponse;
580 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
581 MessageBox(NULL, "The already running Pageant "
582 "refused to add the key.", APPNAME,
583 MB_OK | MB_ICONERROR);
588 if (add234(rsakeys, rkey) != rkey)
589 sfree(rkey); /* already present, don't waste RAM */
592 if (already_running) {
593 unsigned char *request, *response;
595 int reqlen, alglen, clen, keybloblen, resplen;
596 alglen = strlen(skey->alg->name);
597 clen = strlen(skey->comment);
599 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
601 reqlen = 4 + 1 + /* length, message type */
602 4 + alglen + /* algorithm name */
603 keybloblen + /* key data */
604 4 + clen /* comment */
607 request = smalloc(reqlen);
609 request[4] = SSH2_AGENTC_ADD_IDENTITY;
611 PUT_32BIT(request + reqlen, alglen);
613 memcpy(request + reqlen, skey->alg->name, alglen);
615 reqlen += skey->alg->openssh_fmtkey(skey->data,
618 PUT_32BIT(request + reqlen, clen);
619 memcpy(request + reqlen + 4, skey->comment, clen);
620 PUT_32BIT(request, reqlen - 4);
623 agent_query(request, reqlen, &vresponse, &resplen);
624 response = vresponse;
625 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
626 MessageBox(NULL, "The already running Pageant "
627 "refused to add the key.", APPNAME,
628 MB_OK | MB_ICONERROR);
633 if (add234(ssh2keys, skey) != skey) {
634 skey->alg->freekey(skey->data);
635 sfree(skey); /* already present, don't waste RAM */
642 * Create an SSH1 key list in a malloc'ed buffer; return its
645 static void *make_keylist1(int *length)
649 unsigned char *blob, *p, *ret;
653 * Count up the number and length of keys we hold.
657 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
659 blob = rsa_public_blob(key, &bloblen);
662 len += 4 + strlen(key->comment);
665 /* Allocate the buffer. */
666 p = ret = smalloc(len);
667 if (length) *length = len;
671 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
672 blob = rsa_public_blob(key, &bloblen);
673 memcpy(p, blob, bloblen);
676 PUT_32BIT(p, strlen(key->comment));
677 memcpy(p + 4, key->comment, strlen(key->comment));
678 p += 4 + strlen(key->comment);
681 assert(p - ret == len);
686 * Create an SSH2 key list in a malloc'ed buffer; return its
689 static void *make_keylist2(int *length)
691 struct ssh2_userkey *key;
693 unsigned char *blob, *p, *ret;
697 * Count up the number and length of keys we hold.
701 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
703 len += 4; /* length field */
704 blob = key->alg->public_blob(key->data, &bloblen);
707 len += 4 + strlen(key->comment);
710 /* Allocate the buffer. */
711 p = ret = smalloc(len);
712 if (length) *length = len;
715 * Packet header is the obvious five bytes, plus four
716 * bytes for the key count.
720 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
721 blob = key->alg->public_blob(key->data, &bloblen);
722 PUT_32BIT(p, bloblen);
724 memcpy(p, blob, bloblen);
727 PUT_32BIT(p, strlen(key->comment));
728 memcpy(p + 4, key->comment, strlen(key->comment));
729 p += 4 + strlen(key->comment);
732 assert(p - ret == len);
737 * Acquire a keylist1 from the primary Pageant; this means either
738 * calling make_keylist1 (if that's us) or sending a message to the
739 * primary Pageant (if it's not).
741 static void *get_keylist1(void)
745 if (already_running) {
746 unsigned char request[5], *response;
749 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
750 PUT_32BIT(request, 4);
752 agent_query(request, 5, &vresponse, &resplen);
753 response = vresponse;
754 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
757 ret = smalloc(resplen-5);
758 memcpy(ret, response+5, resplen-5);
761 ret = make_keylist1(NULL);
767 * Acquire a keylist2 from the primary Pageant; this means either
768 * calling make_keylist2 (if that's us) or sending a message to the
769 * primary Pageant (if it's not).
771 static void *get_keylist2(void)
775 if (already_running) {
776 unsigned char request[5], *response;
780 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
781 PUT_32BIT(request, 4);
783 agent_query(request, 5, &vresponse, &resplen);
784 response = vresponse;
785 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
788 ret = smalloc(resplen-5);
789 memcpy(ret, response+5, resplen-5);
792 ret = make_keylist2(NULL);
798 * This is the main agent function that answers messages.
800 static void answer_msg(void *msg)
802 unsigned char *p = msg;
803 unsigned char *ret = msg;
807 * Get the message type.
813 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
815 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
821 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
822 keylist = make_keylist1(&len);
823 if (len + 5 > AGENT_MAX_MSGLEN) {
827 PUT_32BIT(ret, len + 1);
828 memcpy(ret + 5, keylist, len);
832 case SSH2_AGENTC_REQUEST_IDENTITIES:
834 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
840 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
841 keylist = make_keylist2(&len);
842 if (len + 5 > AGENT_MAX_MSGLEN) {
846 PUT_32BIT(ret, len + 1);
847 memcpy(ret + 5, keylist, len);
851 case SSH1_AGENTC_RSA_CHALLENGE:
853 * Reply with either SSH1_AGENT_RSA_RESPONSE or
854 * SSH_AGENT_FAILURE, depending on whether we have that key
858 struct RSAKey reqkey, *key;
859 Bignum challenge, response;
860 unsigned char response_source[48], response_md5[16];
861 struct MD5Context md5c;
865 p += ssh1_read_bignum(p, &reqkey.exponent);
866 p += ssh1_read_bignum(p, &reqkey.modulus);
867 p += ssh1_read_bignum(p, &challenge);
868 memcpy(response_source + 32, p, 16);
870 if (GET_32BIT(p) != 1 ||
871 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
872 freebn(reqkey.exponent);
873 freebn(reqkey.modulus);
877 response = rsadecrypt(challenge, key);
878 for (i = 0; i < 32; i++)
879 response_source[i] = bignum_byte(response, 31 - i);
882 MD5Update(&md5c, response_source, 48);
883 MD5Final(response_md5, &md5c);
884 memset(response_source, 0, 48); /* burn the evidence */
885 freebn(response); /* and that evidence */
886 freebn(challenge); /* yes, and that evidence */
887 freebn(reqkey.exponent); /* and free some memory ... */
888 freebn(reqkey.modulus); /* ... while we're at it. */
891 * Packet is the obvious five byte header, plus sixteen
895 PUT_32BIT(ret, len - 4);
896 ret[4] = SSH1_AGENT_RSA_RESPONSE;
897 memcpy(ret + 5, response_md5, 16);
900 case SSH2_AGENTC_SIGN_REQUEST:
902 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
903 * SSH_AGENT_FAILURE, depending on whether we have that key
907 struct ssh2_userkey *key;
909 unsigned char *data, *signature;
910 int datalen, siglen, len;
912 b.len = GET_32BIT(p);
916 datalen = GET_32BIT(p);
919 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
922 signature = key->alg->sign(key->data, data, datalen, &siglen);
923 len = 5 + 4 + siglen;
924 PUT_32BIT(ret, len - 4);
925 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
926 PUT_32BIT(ret + 5, siglen);
927 memcpy(ret + 5 + 4, signature, siglen);
931 case SSH1_AGENTC_ADD_RSA_IDENTITY:
933 * Add to the list and return SSH_AGENT_SUCCESS, or
934 * SSH_AGENT_FAILURE if the key was malformed.
940 key = smalloc(sizeof(struct RSAKey));
941 memset(key, 0, sizeof(struct RSAKey));
942 p += makekey(p, key, NULL, 1);
943 p += makeprivate(p, key);
944 p += ssh1_read_bignum(p, &key->iqmp); /* p^-1 mod q */
945 p += ssh1_read_bignum(p, &key->p); /* p */
946 p += ssh1_read_bignum(p, &key->q); /* q */
947 commentlen = GET_32BIT(p);
948 comment = smalloc(commentlen+1);
950 memcpy(comment, p + 4, commentlen);
951 comment[commentlen] = '\0';
952 key->comment = comment;
955 ret[4] = SSH_AGENT_FAILURE;
956 if (add234(rsakeys, key) == key) {
958 ret[4] = SSH_AGENT_SUCCESS;
965 case SSH2_AGENTC_ADD_IDENTITY:
967 * Add to the list and return SSH_AGENT_SUCCESS, or
968 * SSH_AGENT_FAILURE if the key was malformed.
971 struct ssh2_userkey *key;
976 key = smalloc(sizeof(struct ssh2_userkey));
978 alglen = GET_32BIT(p);
982 /* Add further algorithm names here. */
983 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
985 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
993 GET_32BIT((unsigned char *) msg) - (p -
994 (unsigned char *) msg -
996 key->data = key->alg->openssh_createkey(&p, &bloblen);
1001 commlen = GET_32BIT(p);
1004 comment = smalloc(commlen + 1);
1006 memcpy(comment, p, commlen);
1007 comment[commlen] = '\0';
1009 key->comment = comment;
1012 ret[4] = SSH_AGENT_FAILURE;
1013 if (add234(ssh2keys, key) == key) {
1015 ret[4] = SSH_AGENT_SUCCESS;
1017 key->alg->freekey(key->data);
1018 sfree(key->comment);
1023 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1025 * Remove from the list and return SSH_AGENT_SUCCESS, or
1026 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1030 struct RSAKey reqkey, *key;
1032 p += makekey(p, &reqkey, NULL, 0);
1033 key = find234(rsakeys, &reqkey, NULL);
1034 freebn(reqkey.exponent);
1035 freebn(reqkey.modulus);
1037 ret[4] = SSH_AGENT_FAILURE;
1039 del234(rsakeys, key);
1043 ret[4] = SSH_AGENT_SUCCESS;
1047 case SSH2_AGENTC_REMOVE_IDENTITY:
1049 * Remove from the list and return SSH_AGENT_SUCCESS, or
1050 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1054 struct ssh2_userkey *key;
1057 b.len = GET_32BIT(p);
1061 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1066 ret[4] = SSH_AGENT_FAILURE;
1068 del234(ssh2keys, key);
1070 key->alg->freekey(key->data);
1072 ret[4] = SSH_AGENT_SUCCESS;
1076 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1078 * Remove all SSH1 keys. Always returns success.
1081 struct RSAKey *rkey;
1083 while ((rkey = index234(rsakeys, 0)) != NULL) {
1084 del234(rsakeys, rkey);
1091 ret[4] = SSH_AGENT_SUCCESS;
1094 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1096 * Remove all SSH2 keys. Always returns success.
1099 struct ssh2_userkey *skey;
1101 while ((skey = index234(ssh2keys, 0)) != NULL) {
1102 del234(ssh2keys, skey);
1103 skey->alg->freekey(skey->data);
1109 ret[4] = SSH_AGENT_SUCCESS;
1115 * Unrecognised message. Return SSH_AGENT_FAILURE.
1118 ret[4] = SSH_AGENT_FAILURE;
1124 * Key comparison function for the 2-3-4 tree of RSA keys.
1126 static int cmpkeys_rsa(void *av, void *bv)
1128 struct RSAKey *a = (struct RSAKey *) av;
1129 struct RSAKey *b = (struct RSAKey *) bv;
1136 * Compare by length of moduli.
1138 alen = bignum_bitcount(am);
1139 blen = bignum_bitcount(bm);
1142 else if (alen < blen)
1145 * Now compare by moduli themselves.
1147 alen = (alen + 7) / 8; /* byte count */
1148 while (alen-- > 0) {
1150 abyte = bignum_byte(am, alen);
1151 bbyte = bignum_byte(bm, alen);
1154 else if (abyte < bbyte)
1164 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1166 static int cmpkeys_ssh2(void *av, void *bv)
1168 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1169 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1172 unsigned char *ablob, *bblob;
1176 * Compare purely by public blob.
1178 ablob = a->alg->public_blob(a->data, &alen);
1179 bblob = b->alg->public_blob(b->data, &blen);
1182 for (i = 0; i < alen && i < blen; i++) {
1183 if (ablob[i] < bblob[i]) {
1186 } else if (ablob[i] > bblob[i]) {
1191 if (c == 0 && i < alen)
1192 c = +1; /* a is longer */
1193 if (c == 0 && i < blen)
1194 c = -1; /* a is longer */
1203 * Key comparison function for looking up a blob in the 2-3-4 tree
1206 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1208 struct blob *a = (struct blob *) av;
1209 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1212 unsigned char *ablob, *bblob;
1216 * Compare purely by public blob.
1220 bblob = b->alg->public_blob(b->data, &blen);
1223 for (i = 0; i < alen && i < blen; i++) {
1224 if (ablob[i] < bblob[i]) {
1227 } else if (ablob[i] > bblob[i]) {
1232 if (c == 0 && i < alen)
1233 c = +1; /* a is longer */
1234 if (c == 0 && i < blen)
1235 c = -1; /* a is longer */
1243 * Prompt for a key file to add, and add it.
1245 static void prompt_add_keyfile(void)
1248 char filename[FILENAME_MAX];
1249 char *filelist = smalloc(8192);
1253 memset(&of, 0, sizeof(of));
1254 #ifdef OPENFILENAME_SIZE_VERSION_400
1255 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1257 of.lStructSize = sizeof(of);
1259 of.hwndOwner = main_hwnd;
1260 of.lpstrFilter = "PuTTY Private Key Files\0*.PPK\0AllFiles\0*\0\0\0";
1261 of.lpstrCustomFilter = NULL;
1262 of.nFilterIndex = 1;
1263 of.lpstrFile = filelist;
1265 of.nMaxFile = FILENAME_MAX;
1266 of.lpstrFileTitle = NULL;
1267 of.lpstrInitialDir = NULL;
1268 of.lpstrTitle = "Select Private Key File";
1269 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1270 if (GetOpenFileName(&of)) {
1271 if(strlen(filelist) > of.nFileOffset)
1272 /* Only one filename returned? */
1273 add_keyfile(filelist);
1275 /* we are returned a bunch of strings, end to
1276 * end. first string is the directory, the
1277 * rest the filenames. terminated with an
1280 filewalker = filelist;
1281 dirlen = strlen(filewalker);
1282 if(dirlen > FILENAME_MAX - 8) return;
1283 memcpy(filename, filewalker, dirlen);
1285 filewalker += dirlen + 1;
1286 filename[dirlen++] = '\\';
1288 /* then go over names one by one */
1290 n = strlen(filewalker) + 1;
1291 /* end of the list */
1294 /* too big, shouldn't happen */
1295 if(n + dirlen > FILENAME_MAX)
1298 memcpy(filename + dirlen, filewalker, n);
1301 add_keyfile(filename);
1306 forget_passphrases();
1312 * Dialog-box function for the key list box.
1314 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1315 WPARAM wParam, LPARAM lParam)
1317 struct RSAKey *rkey;
1318 struct ssh2_userkey *skey;
1323 * Centre the window.
1325 { /* centre the window */
1329 hw = GetDesktopWindow();
1330 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1332 (rs.right + rs.left + rd.left - rd.right) / 2,
1333 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1334 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1338 SetWindowLong(hwnd, GWL_EXSTYLE,
1339 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1341 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1343 DestroyWindow(item);
1345 requested_help = FALSE;
1349 static int tabs[] = { 35, 60, 210 };
1350 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1351 sizeof(tabs) / sizeof(*tabs),
1357 switch (LOWORD(wParam)) {
1361 DestroyWindow(hwnd);
1363 case 101: /* add key */
1364 if (HIWORD(wParam) == BN_CLICKED ||
1365 HIWORD(wParam) == BN_DOUBLECLICKED) {
1366 if (passphrase_box) {
1367 MessageBeep(MB_ICONERROR);
1368 SetForegroundWindow(passphrase_box);
1371 prompt_add_keyfile();
1374 case 102: /* remove key */
1375 if (HIWORD(wParam) == BN_CLICKED ||
1376 HIWORD(wParam) == BN_DOUBLECLICKED) {
1381 /* our counter within the array of selected items */
1384 /* get the number of items selected in the list */
1386 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1388 /* none selected? that was silly */
1389 if (numSelected == 0) {
1394 /* get item indices in an array */
1395 selectedArray = smalloc(numSelected * sizeof(int));
1396 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1397 numSelected, (WPARAM)selectedArray);
1399 itemNum = numSelected - 1;
1400 rCount = count234(rsakeys);
1401 sCount = count234(ssh2keys);
1403 /* go through the non-rsakeys until we've covered them all,
1404 * and/or we're out of selected items to check. note that
1405 * we go *backwards*, to avoid complications from deleting
1406 * things hence altering the offset of subsequent items
1408 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1409 skey = index234(ssh2keys, i);
1411 if (selectedArray[itemNum] == rCount + i) {
1412 del234(ssh2keys, skey);
1413 skey->alg->freekey(skey->data);
1419 /* do the same for the rsa keys */
1420 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1421 rkey = index234(rsakeys, i);
1423 if(selectedArray[itemNum] == i) {
1424 del234(rsakeys, rkey);
1431 sfree(selectedArray);
1435 case 103: /* help */
1436 if (HIWORD(wParam) == BN_CLICKED ||
1437 HIWORD(wParam) == BN_DOUBLECLICKED) {
1439 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1440 (DWORD)"JI(`',`pageant.general')");
1441 requested_help = TRUE;
1449 int id = ((LPHELPINFO)lParam)->iCtrlId;
1452 case 100: cmd = "JI(`',`pageant.keylist')"; break;
1453 case 101: cmd = "JI(`',`pageant.addkey')"; break;
1454 case 102: cmd = "JI(`',`pageant.remkey')"; break;
1457 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1458 requested_help = TRUE;
1466 DestroyWindow(hwnd);
1472 /* Set up a system tray icon */
1473 static BOOL AddTrayIcon(HWND hwnd)
1476 NOTIFYICONDATA tnid;
1479 #ifdef NIM_SETVERSION
1481 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1484 tnid.cbSize = sizeof(NOTIFYICONDATA);
1486 tnid.uID = 1; /* unique within this systray use */
1487 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1488 tnid.uCallbackMessage = WM_SYSTRAY;
1489 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1490 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1492 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1494 if (hicon) DestroyIcon(hicon);
1499 /* Update the saved-sessions menu. */
1500 static void update_sessions(void)
1504 TCHAR buf[MAX_PATH + 1];
1507 int index_key, index_menu;
1512 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1515 for(num_entries = GetMenuItemCount(session_menu);
1516 num_entries > initial_menuitems_count;
1518 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1523 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1524 TCHAR session_name[MAX_PATH + 1];
1525 unmungestr(buf, session_name, MAX_PATH);
1526 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1527 memset(&mii, 0, sizeof(mii));
1528 mii.cbSize = sizeof(mii);
1529 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1530 mii.fType = MFT_STRING;
1531 mii.fState = MFS_ENABLED;
1532 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1533 mii.dwTypeData = session_name;
1534 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1542 if(index_menu == 0) {
1543 mii.cbSize = sizeof(mii);
1544 mii.fMask = MIIM_TYPE | MIIM_STATE;
1545 mii.fType = MFT_STRING;
1546 mii.fState = MFS_GRAYED;
1547 mii.dwTypeData = _T("(No sessions)");
1548 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1552 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1553 WPARAM wParam, LPARAM lParam)
1556 static int menuinprogress;
1557 static UINT msgTaskbarCreated = 0;
1561 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1564 if (message==msgTaskbarCreated) {
1566 * Explorer has been restarted, so the tray icon will
1574 if (lParam == WM_RBUTTONUP) {
1576 GetCursorPos(&cursorpos);
1577 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1578 } else if (lParam == WM_LBUTTONDBLCLK) {
1579 /* Equivalent to IDM_VIEWKEYS. */
1580 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1584 if (!menuinprogress) {
1587 SetForegroundWindow(hwnd);
1588 ret = TrackPopupMenu(systray_menu,
1589 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1591 wParam, lParam, 0, hwnd, NULL);
1597 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1599 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1601 MessageBox(NULL, "Unable to execute PuTTY!",
1602 "Error", MB_OK | MB_ICONERROR);
1607 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1608 SendMessage(hwnd, WM_CLOSE, 0, 0);
1612 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1614 ShowWindow(keylist, SW_SHOWNORMAL);
1617 * Sometimes the window comes up minimised / hidden for
1618 * no obvious reason. Prevent this. This also brings it
1619 * to the front if it's already present (the user
1620 * selected View Keys because they wanted to _see_ the
1623 SetForegroundWindow(keylist);
1624 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1625 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1628 if (passphrase_box) {
1629 MessageBeep(MB_ICONERROR);
1630 SetForegroundWindow(passphrase_box);
1633 prompt_add_keyfile();
1637 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1639 ShowWindow(aboutbox, SW_SHOWNORMAL);
1641 * Sometimes the window comes up minimised / hidden
1642 * for no obvious reason. Prevent this.
1644 SetForegroundWindow(aboutbox);
1645 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1646 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1651 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1652 (DWORD)"JI(`',`pageant.general')");
1653 requested_help = TRUE;
1658 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1660 TCHAR buf[MAX_PATH + 1];
1661 TCHAR param[MAX_PATH + 1];
1662 memset(&mii, 0, sizeof(mii));
1663 mii.cbSize = sizeof(mii);
1664 mii.fMask = MIIM_TYPE;
1666 mii.dwTypeData = buf;
1667 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1669 strcat(param, mii.dwTypeData);
1670 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1671 _T(""), SW_SHOW) <= 32) {
1672 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1673 MB_OK | MB_ICONERROR);
1681 if (requested_help) {
1682 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1683 requested_help = FALSE;
1689 COPYDATASTRUCT *cds;
1695 PSID mapowner, procowner;
1696 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1700 cds = (COPYDATASTRUCT *) lParam;
1701 if (cds->dwData != AGENT_COPYDATA_ID)
1702 return 0; /* not our message, mate */
1703 mapname = (char *) cds->lpData;
1704 if (mapname[cds->cbData - 1] != '\0')
1705 return 0; /* failure to be ASCIZ! */
1707 debug(("mapname is :%s:\n", mapname));
1709 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1711 debug(("filemap is %p\n", filemap));
1713 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1717 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1718 GetCurrentProcessId())) ==
1721 debug(("couldn't get handle for process\n"));
1725 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1726 OWNER_SECURITY_INFORMATION,
1727 &procowner, NULL, NULL, NULL,
1728 &psd2) != ERROR_SUCCESS) {
1730 debug(("couldn't get owner info for process\n"));
1733 return 0; /* unable to get security info */
1736 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1737 OWNER_SECURITY_INFORMATION,
1738 &mapowner, NULL, NULL, NULL,
1739 &psd1) != ERROR_SUCCESS)) {
1742 ("couldn't get owner info for filemap: %d\n",
1748 debug(("got security stuff\n"));
1750 if (!EqualSid(mapowner, procowner))
1751 return 0; /* security ID mismatch! */
1753 debug(("security stuff matched\n"));
1759 debug(("security APIs not present\n"));
1763 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1765 debug(("p is %p\n", p));
1768 for (i = 0; i < 5; i++)
1771 ((unsigned char *) p)[i]));}
1777 CloseHandle(filemap);
1782 return DefWindowProc(hwnd, message, wParam, lParam);
1786 * Fork and Exec the command in cmdline. [DBW]
1788 void spawn_cmd(char *cmdline, char * args, int show)
1790 if (ShellExecute(NULL, _T("open"), cmdline,
1791 args, NULL, show) <= (HINSTANCE) 32) {
1793 sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
1794 (int)GetLastError());
1795 MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1799 void cleanup_exit(int code) { exit(code); }
1801 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1807 char *command = NULL;
1810 char **argv, **argstart;
1813 * Determine whether we're an NT system (should have security
1814 * APIs) or a non-NT system (don't do security).
1816 memset(&osi, 0, sizeof(OSVERSIONINFO));
1817 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1818 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1819 has_security = TRUE;
1821 has_security = FALSE;
1826 * Attempt to get the security API we need.
1828 advapi = LoadLibrary("ADVAPI32.DLL");
1830 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1831 if (!getsecurityinfo) {
1833 "Unable to access security APIs. Pageant will\n"
1834 "not run, in case it causes a security breach.",
1835 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1840 "This program has been compiled for Win9X and will\n"
1841 "not run on NT, in case it causes a security breach.",
1842 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1851 * See if we can find our Help file.
1854 char b[2048], *p, *q, *r;
1856 GetModuleFileName(NULL, b, sizeof(b) - 1);
1858 p = strrchr(b, '\\');
1859 if (p && p >= r) r = p+1;
1860 q = strrchr(b, ':');
1861 if (q && q >= r) r = q+1;
1862 strcpy(r, "putty.hlp");
1863 if ( (fp = fopen(b, "r")) != NULL) {
1864 help_path = dupstr(b);
1871 * Look for the PuTTY binary (we will enable the saved session
1872 * submenu if we find it).
1875 char b[2048], *p, *q, *r;
1877 GetModuleFileName(NULL, b, sizeof(b) - 1);
1879 p = strrchr(b, '\\');
1880 if (p && p >= r) r = p+1;
1881 q = strrchr(b, ':');
1882 if (q && q >= r) r = q+1;
1883 strcpy(r, "putty.exe");
1884 if ( (fp = fopen(b, "r")) != NULL) {
1885 putty_path = dupstr(b);
1892 * Find out if Pageant is already running.
1894 already_running = FALSE;
1896 already_running = TRUE;
1901 wndclass.lpfnWndProc = WndProc;
1902 wndclass.cbClsExtra = 0;
1903 wndclass.cbWndExtra = 0;
1904 wndclass.hInstance = inst;
1905 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1906 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1907 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1908 wndclass.lpszMenuName = NULL;
1909 wndclass.lpszClassName = APPNAME;
1911 RegisterClass(&wndclass);
1914 main_hwnd = keylist = NULL;
1916 main_hwnd = CreateWindow(APPNAME, APPNAME,
1917 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1918 CW_USEDEFAULT, CW_USEDEFAULT,
1919 100, 100, NULL, NULL, inst, NULL);
1921 /* Set up a system tray icon */
1922 AddTrayIcon(main_hwnd);
1924 /* Accelerators used: nsvkxa */
1925 systray_menu = CreatePopupMenu();
1927 session_menu = CreateMenu();
1928 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1929 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1930 (UINT) session_menu, "&Saved Sessions");
1931 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1933 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1935 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1936 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1938 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1939 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1940 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1941 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1942 initial_menuitems_count = GetMenuItemCount(session_menu);
1944 ShowWindow(main_hwnd, SW_HIDE);
1947 * Initialise storage for RSA keys.
1949 rsakeys = newtree234(cmpkeys_rsa);
1950 ssh2keys = newtree234(cmpkeys_ssh2);
1955 * Initialise storage for short-term passphrase cache.
1957 passphrases = newtree234(NULL);
1960 * Process the command line and add keys as listed on it.
1962 split_into_argv(cmdline, &argc, &argv, &argstart);
1963 for (i = 0; i < argc; i++) {
1964 if (!strcmp(argv[i], "-c")) {
1966 * If we see `-c', then the rest of the
1967 * command line should be treated as a
1968 * command to be spawned.
1971 command = argstart[i+1];
1976 add_keyfile(argv[i]);
1982 * Forget any passphrase that we retained while going over
1983 * command line keyfiles.
1985 forget_passphrases();
1989 if (command[0] == '"')
1990 args = strchr(++command, '"');
1992 args = strchr(command, ' ');
1995 while(*args && isspace(*args)) args++;
1997 spawn_cmd(command, args, show);
2001 * If Pageant was already running, we leave now. If we haven't
2002 * even taken any auxiliary action (spawned a command or added
2005 if (already_running) {
2006 if (!command && !added_keys) {
2007 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2008 MB_ICONERROR | MB_OK);
2011 FreeLibrary(advapi);
2016 * Main message loop.
2018 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2019 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2020 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2021 TranslateMessage(&msg);
2022 DispatchMessage(&msg);
2026 /* Clean up the system tray icon */
2028 NOTIFYICONDATA tnid;
2030 tnid.cbSize = sizeof(NOTIFYICONDATA);
2031 tnid.hWnd = main_hwnd;
2034 Shell_NotifyIcon(NIM_DELETE, &tnid);
2036 DestroyMenu(systray_menu);
2040 FreeLibrary(advapi);