2 * Pageant: the PuTTY Authentication Agent.
22 #define IDI_MAINICON 200
23 #define IDI_TRAYICON 201
25 #define WM_XUSER (WM_USER + 0x2000)
26 #define WM_SYSTRAY (WM_XUSER + 6)
27 #define WM_SYSTRAY2 (WM_XUSER + 7)
29 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
32 * FIXME: maybe some day we can sort this out ...
34 #define AGENT_MAX_MSGLEN 8192
36 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
37 * wParam are used by Windows, and should be masked off, so we shouldn't
38 * attempt to store information in them. Hence all these identifiers have
39 * the low 4 bits clear. Also, identifiers should < 0xF000. */
41 #define IDM_CLOSE 0x0010
42 #define IDM_VIEWKEYS 0x0020
43 #define IDM_ADDKEY 0x0030
44 #define IDM_HELP 0x0040
45 #define IDM_ABOUT 0x0050
47 #define APPNAME "Pageant"
51 static HINSTANCE instance;
52 static HWND main_hwnd;
55 static HMENU systray_menu, session_menu;
56 static int already_running;
57 static int requested_help;
60 static char *putty_path;
62 #define IDM_PUTTY 0x0060
63 #define IDM_SESSIONS_BASE 0x1000
64 #define IDM_SESSIONS_MAX 0x2000
65 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
66 #define PUTTY_DEFAULT "Default%20Settings"
67 static int initial_menuitems_count;
70 * Print a modal (Really Bad) message box and perform a fatal exit.
72 void modalfatalbox(char *fmt, ...)
78 buf = dupvprintf(fmt, ap);
80 MessageBox(main_hwnd, buf, "Pageant Fatal Error",
81 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
86 /* Un-munge session names out of the registry. */
87 static void unmungestr(char *in, char *out, int outlen)
90 if (*in == '%' && in[1] && in[2]) {
98 *out++ = (i << 4) + j;
112 static tree234 *rsakeys, *ssh2keys;
114 static int has_security;
116 typedef DWORD(WINAPI * gsi_fn_t)
117 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
118 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
119 static gsi_fn_t getsecurityinfo;
125 static void *make_keylist1(int *length);
126 static void *make_keylist2(int *length);
127 static void *get_keylist1(int *length);
128 static void *get_keylist2(int *length);
131 * We need this to link with the RSA code, because rsaencrypt()
132 * pads its data with random bytes. Since we only use rsadecrypt()
133 * and the signing functions, which are deterministic, this should
136 * If it _is_ called, there is a _serious_ problem, because it
137 * won't generate true random numbers. So we must scream, panic,
138 * and exit immediately if that should happen.
140 int random_byte(void)
142 MessageBox(main_hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
144 /* this line can't be reached but it placates MSVC's warnings :-) */
149 * Blob structure for passing to the asymmetric SSH2 key compare
150 * function, prototyped here.
156 static int cmpkeys_ssh2_asymm(void *av, void *bv);
158 #define GET_32BIT(cp) \
159 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
160 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
161 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
162 ((unsigned long)(unsigned char)(cp)[3]))
164 #define PUT_32BIT(cp, value) { \
165 (cp)[0] = (unsigned char)((value) >> 24); \
166 (cp)[1] = (unsigned char)((value) >> 16); \
167 (cp)[2] = (unsigned char)((value) >> 8); \
168 (cp)[3] = (unsigned char)(value); }
170 #define PASSPHRASE_MAXLEN 512
172 struct PassphraseProcStruct {
177 static tree234 *passphrases = NULL;
180 * After processing a list of filenames, we want to forget the
183 static void forget_passphrases(void)
185 while (count234(passphrases) > 0) {
186 char *pp = index234(passphrases, 0);
187 memset(pp, 0, strlen(pp));
188 delpos234(passphrases, 0);
194 * Dialog-box function for the Licence box.
196 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
197 WPARAM wParam, LPARAM lParam)
203 switch (LOWORD(wParam)) {
218 * Dialog-box function for the About box.
220 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
221 WPARAM wParam, LPARAM lParam)
225 SetDlgItemText(hwnd, 100, ver);
228 switch (LOWORD(wParam)) {
235 EnableWindow(hwnd, 0);
236 DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
237 EnableWindow(hwnd, 1);
238 SetActiveWindow(hwnd);
250 static HWND passphrase_box;
253 * Dialog-box function for the passphrase box.
255 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
256 WPARAM wParam, LPARAM lParam)
258 static char *passphrase = NULL;
259 struct PassphraseProcStruct *p;
263 passphrase_box = hwnd;
267 { /* centre the window */
271 hw = GetDesktopWindow();
272 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
274 (rs.right + rs.left + rd.left - rd.right) / 2,
275 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
276 rd.right - rd.left, rd.bottom - rd.top, TRUE);
279 SetForegroundWindow(hwnd);
280 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
281 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
282 p = (struct PassphraseProcStruct *) lParam;
283 passphrase = p->passphrase;
285 SetDlgItemText(hwnd, 101, p->comment);
287 SetDlgItemText(hwnd, 102, passphrase);
290 switch (LOWORD(wParam)) {
300 case 102: /* edit box */
301 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
302 GetDlgItemText(hwnd, 102, passphrase,
303 PASSPHRASE_MAXLEN - 1);
304 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
317 * Warn about the obsolescent key file format.
319 void old_keyfile_warning(void)
321 static const char mbtitle[] = "PuTTY Key File Warning";
322 static const char message[] =
323 "You are loading an SSH 2 private key which has an\n"
324 "old version of the file format. This means your key\n"
325 "file is not fully tamperproof. Future versions of\n"
326 "PuTTY may stop supporting this private key format,\n"
327 "so we recommend you convert your key to the new\n"
330 "You can perform this conversion by loading the key\n"
331 "into PuTTYgen and then saving it again.";
333 MessageBox(NULL, message, mbtitle, MB_OK);
337 * Update the visible key list.
339 static void keylist_update(void)
342 struct ssh2_userkey *skey;
346 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
347 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
348 char listentry[512], *p;
350 * Replace two spaces in the fingerprint with tabs, for
351 * nice alignment in the box.
353 strcpy(listentry, "ssh1\t");
354 p = listentry + strlen(listentry);
355 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
356 p = strchr(listentry, ' ');
359 p = strchr(listentry, ' ');
362 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
363 0, (LPARAM) listentry);
365 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
366 char listentry[512], *p;
369 * Replace two spaces in the fingerprint with tabs, for
370 * nice alignment in the box.
372 p = skey->alg->fingerprint(skey->data);
373 strncpy(listentry, p, sizeof(listentry));
374 p = strchr(listentry, ' ');
377 p = strchr(listentry, ' ');
380 len = strlen(listentry);
381 if (len < sizeof(listentry) - 2) {
382 listentry[len] = '\t';
383 strncpy(listentry + len + 1, skey->comment,
384 sizeof(listentry) - len - 1);
386 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
389 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
394 * This function loads a key from a file and adds it.
396 static void add_keyfile(Filename filename)
398 char passphrase[PASSPHRASE_MAXLEN];
399 struct RSAKey *rkey = NULL;
400 struct ssh2_userkey *skey = NULL;
405 struct PassphraseProcStruct pps;
409 type = key_type(&filename);
410 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
412 sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
413 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONERROR);
418 * See if the key is already loaded (in the primary Pageant,
419 * which may or may not be us).
423 unsigned char *keylist, *p;
424 int i, nkeys, bloblen, keylistlen;
426 if (type == SSH_KEYTYPE_SSH1) {
427 if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
428 MessageBox(NULL, "Couldn't load private key.", APPNAME,
429 MB_OK | MB_ICONERROR);
432 keylist = get_keylist1(&keylistlen);
434 unsigned char *blob2;
435 blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
437 MessageBox(NULL, "Couldn't load private key.", APPNAME,
438 MB_OK | MB_ICONERROR);
441 /* For our purposes we want the blob prefixed with its length */
442 blob2 = snewn(bloblen+4, unsigned char);
443 PUT_32BIT(blob2, bloblen);
444 memcpy(blob2 + 4, blob, bloblen);
448 keylist = get_keylist2(&keylistlen);
451 if (keylistlen < 4) {
452 MessageBox(NULL, "Received broken key list?!", APPNAME,
453 MB_OK | MB_ICONERROR);
456 nkeys = GET_32BIT(keylist);
460 for (i = 0; i < nkeys; i++) {
461 if (!memcmp(blob, p, bloblen)) {
462 /* Key is already present; we can now leave. */
467 /* Now skip over public blob */
468 if (type == SSH_KEYTYPE_SSH1) {
469 int n = rsa_public_blob_len(p, keylistlen);
471 MessageBox(NULL, "Received broken key list?!", APPNAME,
472 MB_OK | MB_ICONERROR);
479 if (keylistlen < 4) {
480 MessageBox(NULL, "Received broken key list?!", APPNAME,
481 MB_OK | MB_ICONERROR);
484 n = 4 + GET_32BIT(p);
485 if (keylistlen < n) {
486 MessageBox(NULL, "Received broken key list?!", APPNAME,
487 MB_OK | MB_ICONERROR);
493 /* Now skip over comment field */
496 if (keylistlen < 4) {
497 MessageBox(NULL, "Received broken key list?!", APPNAME,
498 MB_OK | MB_ICONERROR);
501 n = 4 + GET_32BIT(p);
502 if (keylistlen < n) {
503 MessageBox(NULL, "Received broken key list?!", APPNAME,
504 MB_OK | MB_ICONERROR);
518 if (type == SSH_KEYTYPE_SSH1)
519 needs_pass = rsakey_encrypted(&filename, &comment);
521 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
523 if (type == SSH_KEYTYPE_SSH1)
524 rkey = snew(struct RSAKey);
525 pps.passphrase = passphrase;
526 pps.comment = comment;
530 /* try all the remembered passphrases first */
531 char *pp = index234(passphrases, attempts);
533 strcpy(passphrase, pp);
537 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
538 NULL, PassphraseProc, (LPARAM) & pps);
539 passphrase_box = NULL;
543 if (type == SSH_KEYTYPE_SSH1)
545 return; /* operation cancelled */
550 if (type == SSH_KEYTYPE_SSH1)
551 ret = loadrsakey(&filename, rkey, passphrase, NULL);
553 skey = ssh2_load_userkey(&filename, passphrase, NULL);
554 if (skey == SSH2_WRONG_PASSPHRASE)
564 /* if they typed in an ok passphrase, remember it */
565 if(original_pass && ret) {
566 char *pp = dupstr(passphrase);
567 addpos234(passphrases, pp, 0);
573 MessageBox(NULL, "Couldn't load private key.", APPNAME,
574 MB_OK | MB_ICONERROR);
575 if (type == SSH_KEYTYPE_SSH1)
579 if (type == SSH_KEYTYPE_SSH1) {
580 if (already_running) {
581 unsigned char *request, *response;
583 int reqlen, clen, resplen, ret;
585 clen = strlen(rkey->comment);
587 reqlen = 4 + 1 + /* length, message type */
589 ssh1_bignum_length(rkey->modulus) +
590 ssh1_bignum_length(rkey->exponent) +
591 ssh1_bignum_length(rkey->private_exponent) +
592 ssh1_bignum_length(rkey->iqmp) +
593 ssh1_bignum_length(rkey->p) +
594 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
597 request = snewn(reqlen, unsigned char);
599 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
601 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
603 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
604 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
606 ssh1_write_bignum(request + reqlen,
607 rkey->private_exponent);
608 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
609 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
610 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
611 PUT_32BIT(request + reqlen, clen);
612 memcpy(request + reqlen + 4, rkey->comment, clen);
614 PUT_32BIT(request, reqlen - 4);
616 ret = agent_query(request, reqlen, &vresponse, &resplen,
619 response = vresponse;
620 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
621 MessageBox(NULL, "The already running Pageant "
622 "refused to add the key.", APPNAME,
623 MB_OK | MB_ICONERROR);
628 if (add234(rsakeys, rkey) != rkey)
629 sfree(rkey); /* already present, don't waste RAM */
632 if (already_running) {
633 unsigned char *request, *response;
635 int reqlen, alglen, clen, keybloblen, resplen, ret;
636 alglen = strlen(skey->alg->name);
637 clen = strlen(skey->comment);
639 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
641 reqlen = 4 + 1 + /* length, message type */
642 4 + alglen + /* algorithm name */
643 keybloblen + /* key data */
644 4 + clen /* comment */
647 request = snewn(reqlen, unsigned char);
649 request[4] = SSH2_AGENTC_ADD_IDENTITY;
651 PUT_32BIT(request + reqlen, alglen);
653 memcpy(request + reqlen, skey->alg->name, alglen);
655 reqlen += skey->alg->openssh_fmtkey(skey->data,
658 PUT_32BIT(request + reqlen, clen);
659 memcpy(request + reqlen + 4, skey->comment, clen);
661 PUT_32BIT(request, reqlen - 4);
663 ret = agent_query(request, reqlen, &vresponse, &resplen,
666 response = vresponse;
667 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
668 MessageBox(NULL, "The already running Pageant "
669 "refused to add the key.", APPNAME,
670 MB_OK | MB_ICONERROR);
675 if (add234(ssh2keys, skey) != skey) {
676 skey->alg->freekey(skey->data);
677 sfree(skey); /* already present, don't waste RAM */
684 * Create an SSH1 key list in a malloc'ed buffer; return its
687 static void *make_keylist1(int *length)
691 unsigned char *blob, *p, *ret;
695 * Count up the number and length of keys we hold.
699 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
701 blob = rsa_public_blob(key, &bloblen);
704 len += 4 + strlen(key->comment);
707 /* Allocate the buffer. */
708 p = ret = snewn(len, unsigned char);
709 if (length) *length = len;
713 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
714 blob = rsa_public_blob(key, &bloblen);
715 memcpy(p, blob, bloblen);
718 PUT_32BIT(p, strlen(key->comment));
719 memcpy(p + 4, key->comment, strlen(key->comment));
720 p += 4 + strlen(key->comment);
723 assert(p - ret == len);
728 * Create an SSH2 key list in a malloc'ed buffer; return its
731 static void *make_keylist2(int *length)
733 struct ssh2_userkey *key;
735 unsigned char *blob, *p, *ret;
739 * Count up the number and length of keys we hold.
743 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
745 len += 4; /* length field */
746 blob = key->alg->public_blob(key->data, &bloblen);
749 len += 4 + strlen(key->comment);
752 /* Allocate the buffer. */
753 p = ret = snewn(len, unsigned char);
754 if (length) *length = len;
757 * Packet header is the obvious five bytes, plus four
758 * bytes for the key count.
762 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
763 blob = key->alg->public_blob(key->data, &bloblen);
764 PUT_32BIT(p, bloblen);
766 memcpy(p, blob, bloblen);
769 PUT_32BIT(p, strlen(key->comment));
770 memcpy(p + 4, key->comment, strlen(key->comment));
771 p += 4 + strlen(key->comment);
774 assert(p - ret == len);
779 * Acquire a keylist1 from the primary Pageant; this means either
780 * calling make_keylist1 (if that's us) or sending a message to the
781 * primary Pageant (if it's not).
783 static void *get_keylist1(int *length)
787 if (already_running) {
788 unsigned char request[5], *response;
791 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
792 PUT_32BIT(request, 4);
794 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
796 response = vresponse;
797 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
800 ret = snewn(resplen-5, unsigned char);
801 memcpy(ret, response+5, resplen-5);
807 ret = make_keylist1(length);
813 * Acquire a keylist2 from the primary Pageant; this means either
814 * calling make_keylist2 (if that's us) or sending a message to the
815 * primary Pageant (if it's not).
817 static void *get_keylist2(int *length)
821 if (already_running) {
822 unsigned char request[5], *response;
826 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
827 PUT_32BIT(request, 4);
829 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
831 response = vresponse;
832 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
835 ret = snewn(resplen-5, unsigned char);
836 memcpy(ret, response+5, resplen-5);
842 ret = make_keylist2(length);
848 * This is the main agent function that answers messages.
850 static void answer_msg(void *msg)
852 unsigned char *p = msg;
853 unsigned char *ret = msg;
854 unsigned char *msgend;
858 * Get the message length.
860 msgend = p + 4 + GET_32BIT(p);
863 * Get the message type.
871 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
873 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
879 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
880 keylist = make_keylist1(&len);
881 if (len + 5 > AGENT_MAX_MSGLEN) {
885 PUT_32BIT(ret, len + 1);
886 memcpy(ret + 5, keylist, len);
890 case SSH2_AGENTC_REQUEST_IDENTITIES:
892 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
898 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
899 keylist = make_keylist2(&len);
900 if (len + 5 > AGENT_MAX_MSGLEN) {
904 PUT_32BIT(ret, len + 1);
905 memcpy(ret + 5, keylist, len);
909 case SSH1_AGENTC_RSA_CHALLENGE:
911 * Reply with either SSH1_AGENT_RSA_RESPONSE or
912 * SSH_AGENT_FAILURE, depending on whether we have that key
916 struct RSAKey reqkey, *key;
917 Bignum challenge, response;
918 unsigned char response_source[48], response_md5[16];
919 struct MD5Context md5c;
923 i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
927 i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
931 i = ssh1_read_bignum(p, msgend - p, &challenge);
936 freebn(reqkey.exponent);
937 freebn(reqkey.modulus);
941 memcpy(response_source + 32, p, 16);
945 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
946 freebn(reqkey.exponent);
947 freebn(reqkey.modulus);
951 response = rsadecrypt(challenge, key);
952 for (i = 0; i < 32; i++)
953 response_source[i] = bignum_byte(response, 31 - i);
956 MD5Update(&md5c, response_source, 48);
957 MD5Final(response_md5, &md5c);
958 memset(response_source, 0, 48); /* burn the evidence */
959 freebn(response); /* and that evidence */
960 freebn(challenge); /* yes, and that evidence */
961 freebn(reqkey.exponent); /* and free some memory ... */
962 freebn(reqkey.modulus); /* ... while we're at it. */
965 * Packet is the obvious five byte header, plus sixteen
969 PUT_32BIT(ret, len - 4);
970 ret[4] = SSH1_AGENT_RSA_RESPONSE;
971 memcpy(ret + 5, response_md5, 16);
974 case SSH2_AGENTC_SIGN_REQUEST:
976 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
977 * SSH_AGENT_FAILURE, depending on whether we have that key
981 struct ssh2_userkey *key;
983 unsigned char *data, *signature;
984 int datalen, siglen, len;
988 b.len = GET_32BIT(p);
990 if (msgend < p+b.len)
996 datalen = GET_32BIT(p);
998 if (msgend < p+datalen)
1001 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1004 signature = key->alg->sign(key->data, data, datalen, &siglen);
1005 len = 5 + 4 + siglen;
1006 PUT_32BIT(ret, len - 4);
1007 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
1008 PUT_32BIT(ret + 5, siglen);
1009 memcpy(ret + 5 + 4, signature, siglen);
1013 case SSH1_AGENTC_ADD_RSA_IDENTITY:
1015 * Add to the list and return SSH_AGENT_SUCCESS, or
1016 * SSH_AGENT_FAILURE if the key was malformed.
1023 key = snew(struct RSAKey);
1024 memset(key, 0, sizeof(struct RSAKey));
1026 n = makekey(p, msgend - p, key, NULL, 1);
1034 n = makeprivate(p, msgend - p, key);
1042 n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
1050 n = ssh1_read_bignum(p, msgend - p, &key->p); /* p */
1058 n = ssh1_read_bignum(p, msgend - p, &key->q); /* q */
1071 commentlen = GET_32BIT(p);
1073 if (msgend < p+commentlen) {
1079 comment = snewn(commentlen+1, char);
1081 memcpy(comment, p + 4, commentlen);
1082 comment[commentlen] = '\0';
1083 key->comment = comment;
1086 ret[4] = SSH_AGENT_FAILURE;
1087 if (add234(rsakeys, key) == key) {
1089 ret[4] = SSH_AGENT_SUCCESS;
1096 case SSH2_AGENTC_ADD_IDENTITY:
1098 * Add to the list and return SSH_AGENT_SUCCESS, or
1099 * SSH_AGENT_FAILURE if the key was malformed.
1102 struct ssh2_userkey *key;
1103 char *comment, *alg;
1104 int alglen, commlen;
1110 alglen = GET_32BIT(p);
1112 if (msgend < p+alglen)
1117 key = snew(struct ssh2_userkey);
1118 /* Add further algorithm names here. */
1119 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
1120 key->alg = &ssh_rsa;
1121 else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
1122 key->alg = &ssh_dss;
1128 bloblen = msgend - p;
1129 key->data = key->alg->openssh_createkey(&p, &bloblen);
1136 * p has been advanced by openssh_createkey, but
1137 * certainly not _beyond_ the end of the buffer.
1139 assert(p <= msgend);
1142 key->alg->freekey(key->data);
1146 commlen = GET_32BIT(p);
1149 if (msgend < p+commlen) {
1150 key->alg->freekey(key->data);
1154 comment = snewn(commlen + 1, char);
1156 memcpy(comment, p, commlen);
1157 comment[commlen] = '\0';
1159 key->comment = comment;
1162 ret[4] = SSH_AGENT_FAILURE;
1163 if (add234(ssh2keys, key) == key) {
1165 ret[4] = SSH_AGENT_SUCCESS;
1167 key->alg->freekey(key->data);
1168 sfree(key->comment);
1173 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
1175 * Remove from the list and return SSH_AGENT_SUCCESS, or
1176 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1180 struct RSAKey reqkey, *key;
1183 n = makekey(p, msgend - p, &reqkey, NULL, 0);
1187 key = find234(rsakeys, &reqkey, NULL);
1188 freebn(reqkey.exponent);
1189 freebn(reqkey.modulus);
1191 ret[4] = SSH_AGENT_FAILURE;
1193 del234(rsakeys, key);
1197 ret[4] = SSH_AGENT_SUCCESS;
1201 case SSH2_AGENTC_REMOVE_IDENTITY:
1203 * Remove from the list and return SSH_AGENT_SUCCESS, or
1204 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1208 struct ssh2_userkey *key;
1213 b.len = GET_32BIT(p);
1216 if (msgend < p+b.len)
1221 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
1226 ret[4] = SSH_AGENT_FAILURE;
1228 del234(ssh2keys, key);
1230 key->alg->freekey(key->data);
1232 ret[4] = SSH_AGENT_SUCCESS;
1236 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
1238 * Remove all SSH1 keys. Always returns success.
1241 struct RSAKey *rkey;
1243 while ((rkey = index234(rsakeys, 0)) != NULL) {
1244 del234(rsakeys, rkey);
1251 ret[4] = SSH_AGENT_SUCCESS;
1254 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
1256 * Remove all SSH2 keys. Always returns success.
1259 struct ssh2_userkey *skey;
1261 while ((skey = index234(ssh2keys, 0)) != NULL) {
1262 del234(ssh2keys, skey);
1263 skey->alg->freekey(skey->data);
1269 ret[4] = SSH_AGENT_SUCCESS;
1275 * Unrecognised message. Return SSH_AGENT_FAILURE.
1278 ret[4] = SSH_AGENT_FAILURE;
1284 * Key comparison function for the 2-3-4 tree of RSA keys.
1286 static int cmpkeys_rsa(void *av, void *bv)
1288 struct RSAKey *a = (struct RSAKey *) av;
1289 struct RSAKey *b = (struct RSAKey *) bv;
1296 * Compare by length of moduli.
1298 alen = bignum_bitcount(am);
1299 blen = bignum_bitcount(bm);
1302 else if (alen < blen)
1305 * Now compare by moduli themselves.
1307 alen = (alen + 7) / 8; /* byte count */
1308 while (alen-- > 0) {
1310 abyte = bignum_byte(am, alen);
1311 bbyte = bignum_byte(bm, alen);
1314 else if (abyte < bbyte)
1324 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1326 static int cmpkeys_ssh2(void *av, void *bv)
1328 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
1329 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1332 unsigned char *ablob, *bblob;
1336 * Compare purely by public blob.
1338 ablob = a->alg->public_blob(a->data, &alen);
1339 bblob = b->alg->public_blob(b->data, &blen);
1342 for (i = 0; i < alen && i < blen; i++) {
1343 if (ablob[i] < bblob[i]) {
1346 } else if (ablob[i] > bblob[i]) {
1351 if (c == 0 && i < alen)
1352 c = +1; /* a is longer */
1353 if (c == 0 && i < blen)
1354 c = -1; /* a is longer */
1363 * Key comparison function for looking up a blob in the 2-3-4 tree
1366 static int cmpkeys_ssh2_asymm(void *av, void *bv)
1368 struct blob *a = (struct blob *) av;
1369 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
1372 unsigned char *ablob, *bblob;
1376 * Compare purely by public blob.
1380 bblob = b->alg->public_blob(b->data, &blen);
1383 for (i = 0; i < alen && i < blen; i++) {
1384 if (ablob[i] < bblob[i]) {
1387 } else if (ablob[i] > bblob[i]) {
1392 if (c == 0 && i < alen)
1393 c = +1; /* a is longer */
1394 if (c == 0 && i < blen)
1395 c = -1; /* a is longer */
1403 * Prompt for a key file to add, and add it.
1405 static void prompt_add_keyfile(void)
1408 char filename[FILENAME_MAX];
1409 char *filelist = snewn(8192, char);
1413 memset(&of, 0, sizeof(of));
1414 #ifdef OPENFILENAME_SIZE_VERSION_400
1415 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1417 of.lStructSize = sizeof(of);
1419 of.hwndOwner = main_hwnd;
1420 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1421 "All Files (*.*)\0*\0\0\0";
1422 of.lpstrCustomFilter = NULL;
1423 of.nFilterIndex = 1;
1424 of.lpstrFile = filelist;
1426 of.nMaxFile = FILENAME_MAX;
1427 of.lpstrFileTitle = NULL;
1428 of.lpstrInitialDir = NULL;
1429 of.lpstrTitle = "Select Private Key File";
1430 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
1431 if (GetOpenFileName(&of)) {
1432 if(strlen(filelist) > of.nFileOffset)
1433 /* Only one filename returned? */
1434 add_keyfile(filename_from_str(filelist));
1436 /* we are returned a bunch of strings, end to
1437 * end. first string is the directory, the
1438 * rest the filenames. terminated with an
1441 filewalker = filelist;
1442 dirlen = strlen(filewalker);
1443 if(dirlen > FILENAME_MAX - 8) return;
1444 memcpy(filename, filewalker, dirlen);
1446 filewalker += dirlen + 1;
1447 filename[dirlen++] = '\\';
1449 /* then go over names one by one */
1451 n = strlen(filewalker) + 1;
1452 /* end of the list */
1455 /* too big, shouldn't happen */
1456 if(n + dirlen > FILENAME_MAX)
1459 memcpy(filename + dirlen, filewalker, n);
1462 add_keyfile(filename_from_str(filename));
1467 forget_passphrases();
1473 * Dialog-box function for the key list box.
1475 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
1476 WPARAM wParam, LPARAM lParam)
1478 struct RSAKey *rkey;
1479 struct ssh2_userkey *skey;
1484 * Centre the window.
1486 { /* centre the window */
1490 hw = GetDesktopWindow();
1491 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
1493 (rs.right + rs.left + rd.left - rd.right) / 2,
1494 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
1495 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1499 SetWindowLong(hwnd, GWL_EXSTYLE,
1500 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
1502 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
1504 DestroyWindow(item);
1506 requested_help = FALSE;
1510 static int tabs[] = { 35, 60, 210 };
1511 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1512 sizeof(tabs) / sizeof(*tabs),
1518 switch (LOWORD(wParam)) {
1522 DestroyWindow(hwnd);
1524 case 101: /* add key */
1525 if (HIWORD(wParam) == BN_CLICKED ||
1526 HIWORD(wParam) == BN_DOUBLECLICKED) {
1527 if (passphrase_box) {
1528 MessageBeep(MB_ICONERROR);
1529 SetForegroundWindow(passphrase_box);
1532 prompt_add_keyfile();
1535 case 102: /* remove key */
1536 if (HIWORD(wParam) == BN_CLICKED ||
1537 HIWORD(wParam) == BN_DOUBLECLICKED) {
1542 /* our counter within the array of selected items */
1545 /* get the number of items selected in the list */
1547 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
1549 /* none selected? that was silly */
1550 if (numSelected == 0) {
1555 /* get item indices in an array */
1556 selectedArray = snewn(numSelected, int);
1557 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
1558 numSelected, (WPARAM)selectedArray);
1560 itemNum = numSelected - 1;
1561 rCount = count234(rsakeys);
1562 sCount = count234(ssh2keys);
1564 /* go through the non-rsakeys until we've covered them all,
1565 * and/or we're out of selected items to check. note that
1566 * we go *backwards*, to avoid complications from deleting
1567 * things hence altering the offset of subsequent items
1569 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1570 skey = index234(ssh2keys, i);
1572 if (selectedArray[itemNum] == rCount + i) {
1573 del234(ssh2keys, skey);
1574 skey->alg->freekey(skey->data);
1580 /* do the same for the rsa keys */
1581 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
1582 rkey = index234(rsakeys, i);
1584 if(selectedArray[itemNum] == i) {
1585 del234(rsakeys, rkey);
1592 sfree(selectedArray);
1596 case 103: /* help */
1597 if (HIWORD(wParam) == BN_CLICKED ||
1598 HIWORD(wParam) == BN_DOUBLECLICKED) {
1600 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1601 (DWORD)"JI(`',`pageant.general')");
1602 requested_help = TRUE;
1610 int id = ((LPHELPINFO)lParam)->iCtrlId;
1613 case 100: topic = "pageant.keylist"; break;
1614 case 101: topic = "pageant.addkey"; break;
1615 case 102: topic = "pageant.remkey"; break;
1618 char *cmd = dupprintf("JI(`',`%s')", topic);
1619 WinHelp(main_hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1621 requested_help = TRUE;
1629 DestroyWindow(hwnd);
1635 /* Set up a system tray icon */
1636 static BOOL AddTrayIcon(HWND hwnd)
1639 NOTIFYICONDATA tnid;
1642 #ifdef NIM_SETVERSION
1644 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1647 tnid.cbSize = sizeof(NOTIFYICONDATA);
1649 tnid.uID = 1; /* unique within this systray use */
1650 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1651 tnid.uCallbackMessage = WM_SYSTRAY;
1652 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1653 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1655 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1657 if (hicon) DestroyIcon(hicon);
1662 /* Update the saved-sessions menu. */
1663 static void update_sessions(void)
1667 TCHAR buf[MAX_PATH + 1];
1670 int index_key, index_menu;
1675 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1678 for(num_entries = GetMenuItemCount(session_menu);
1679 num_entries > initial_menuitems_count;
1681 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1686 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1687 TCHAR session_name[MAX_PATH + 1];
1688 unmungestr(buf, session_name, MAX_PATH);
1689 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1690 memset(&mii, 0, sizeof(mii));
1691 mii.cbSize = sizeof(mii);
1692 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1693 mii.fType = MFT_STRING;
1694 mii.fState = MFS_ENABLED;
1695 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1696 mii.dwTypeData = session_name;
1697 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1705 if(index_menu == 0) {
1706 mii.cbSize = sizeof(mii);
1707 mii.fMask = MIIM_TYPE | MIIM_STATE;
1708 mii.fType = MFT_STRING;
1709 mii.fState = MFS_GRAYED;
1710 mii.dwTypeData = _T("(No sessions)");
1711 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1715 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1716 WPARAM wParam, LPARAM lParam)
1719 static int menuinprogress;
1720 static UINT msgTaskbarCreated = 0;
1724 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1727 if (message==msgTaskbarCreated) {
1729 * Explorer has been restarted, so the tray icon will
1737 if (lParam == WM_RBUTTONUP) {
1739 GetCursorPos(&cursorpos);
1740 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1741 } else if (lParam == WM_LBUTTONDBLCLK) {
1742 /* Run the default menu item. */
1743 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1745 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1749 if (!menuinprogress) {
1752 SetForegroundWindow(hwnd);
1753 ret = TrackPopupMenu(systray_menu,
1754 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1756 wParam, lParam, 0, hwnd, NULL);
1762 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1764 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1766 MessageBox(NULL, "Unable to execute PuTTY!",
1767 "Error", MB_OK | MB_ICONERROR);
1772 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1773 SendMessage(hwnd, WM_CLOSE, 0, 0);
1777 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1779 ShowWindow(keylist, SW_SHOWNORMAL);
1782 * Sometimes the window comes up minimised / hidden for
1783 * no obvious reason. Prevent this. This also brings it
1784 * to the front if it's already present (the user
1785 * selected View Keys because they wanted to _see_ the
1788 SetForegroundWindow(keylist);
1789 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1790 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1793 if (passphrase_box) {
1794 MessageBeep(MB_ICONERROR);
1795 SetForegroundWindow(passphrase_box);
1798 prompt_add_keyfile();
1802 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1804 ShowWindow(aboutbox, SW_SHOWNORMAL);
1806 * Sometimes the window comes up minimised / hidden
1807 * for no obvious reason. Prevent this.
1809 SetForegroundWindow(aboutbox);
1810 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1811 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1816 WinHelp(main_hwnd, help_path, HELP_COMMAND,
1817 (DWORD)"JI(`',`pageant.general')");
1818 requested_help = TRUE;
1823 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1825 TCHAR buf[MAX_PATH + 1];
1826 TCHAR param[MAX_PATH + 1];
1827 memset(&mii, 0, sizeof(mii));
1828 mii.cbSize = sizeof(mii);
1829 mii.fMask = MIIM_TYPE;
1831 mii.dwTypeData = buf;
1832 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1834 strcat(param, mii.dwTypeData);
1835 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1836 _T(""), SW_SHOW) <= 32) {
1837 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1838 MB_OK | MB_ICONERROR);
1846 if (requested_help) {
1847 WinHelp(main_hwnd, help_path, HELP_QUIT, 0);
1848 requested_help = FALSE;
1854 COPYDATASTRUCT *cds;
1860 PSID mapowner, procowner;
1861 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1865 cds = (COPYDATASTRUCT *) lParam;
1866 if (cds->dwData != AGENT_COPYDATA_ID)
1867 return 0; /* not our message, mate */
1868 mapname = (char *) cds->lpData;
1869 if (mapname[cds->cbData - 1] != '\0')
1870 return 0; /* failure to be ASCIZ! */
1872 debug(("mapname is :%s:\n", mapname));
1874 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1876 debug(("filemap is %p\n", filemap));
1878 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1882 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1883 GetCurrentProcessId())) ==
1886 debug(("couldn't get handle for process\n"));
1890 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1891 OWNER_SECURITY_INFORMATION,
1892 &procowner, NULL, NULL, NULL,
1893 &psd2) != ERROR_SUCCESS) {
1895 debug(("couldn't get owner info for process\n"));
1898 return 0; /* unable to get security info */
1901 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1902 OWNER_SECURITY_INFORMATION,
1903 &mapowner, NULL, NULL, NULL,
1904 &psd1) != ERROR_SUCCESS)) {
1907 ("couldn't get owner info for filemap: %d\n",
1913 debug(("got security stuff\n"));
1915 if (!EqualSid(mapowner, procowner))
1916 return 0; /* security ID mismatch! */
1918 debug(("security stuff matched\n"));
1924 debug(("security APIs not present\n"));
1928 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1930 debug(("p is %p\n", p));
1933 for (i = 0; i < 5; i++)
1936 ((unsigned char *) p)[i]));}
1942 CloseHandle(filemap);
1947 return DefWindowProc(hwnd, message, wParam, lParam);
1951 * Fork and Exec the command in cmdline. [DBW]
1953 void spawn_cmd(char *cmdline, char * args, int show)
1955 if (ShellExecute(NULL, _T("open"), cmdline,
1956 args, NULL, show) <= (HINSTANCE) 32) {
1958 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1959 (int)GetLastError());
1960 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1966 * This is a can't-happen stub, since Pageant never makes
1967 * asynchronous agent requests.
1969 void agent_schedule_callback(void (*callback)(void *, void *, int),
1970 void *callback_ctx, void *data, int len)
1972 assert(!"We shouldn't get here");
1975 void cleanup_exit(int code) { exit(code); }
1977 int flags = FLAG_SYNCAGENT;
1979 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1984 char *command = NULL;
1987 char **argv, **argstart;
1990 * Determine whether we're an NT system (should have security
1991 * APIs) or a non-NT system (don't do security).
1995 modalfatalbox("Windows refuses to report a version");
1997 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1998 has_security = TRUE;
2000 has_security = FALSE;
2005 * Attempt to get the security API we need.
2007 advapi = LoadLibrary("ADVAPI32.DLL");
2009 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
2010 if (!getsecurityinfo) {
2012 "Unable to access security APIs. Pageant will\n"
2013 "not run, in case it causes a security breach.",
2014 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2019 "This program has been compiled for Win9X and will\n"
2020 "not run on NT, in case it causes a security breach.",
2021 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
2030 * See if we can find our Help file.
2033 char b[2048], *p, *q, *r;
2035 GetModuleFileName(NULL, b, sizeof(b) - 1);
2037 p = strrchr(b, '\\');
2038 if (p && p >= r) r = p+1;
2039 q = strrchr(b, ':');
2040 if (q && q >= r) r = q+1;
2041 strcpy(r, PUTTY_HELP_FILE);
2042 if ( (fp = fopen(b, "r")) != NULL) {
2043 help_path = dupstr(b);
2050 * Look for the PuTTY binary (we will enable the saved session
2051 * submenu if we find it).
2054 char b[2048], *p, *q, *r;
2056 GetModuleFileName(NULL, b, sizeof(b) - 1);
2058 p = strrchr(b, '\\');
2059 if (p && p >= r) r = p+1;
2060 q = strrchr(b, ':');
2061 if (q && q >= r) r = q+1;
2062 strcpy(r, "putty.exe");
2063 if ( (fp = fopen(b, "r")) != NULL) {
2064 putty_path = dupstr(b);
2071 * Find out if Pageant is already running.
2073 already_running = FALSE;
2075 already_running = TRUE;
2080 wndclass.lpfnWndProc = WndProc;
2081 wndclass.cbClsExtra = 0;
2082 wndclass.cbWndExtra = 0;
2083 wndclass.hInstance = inst;
2084 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
2085 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
2086 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
2087 wndclass.lpszMenuName = NULL;
2088 wndclass.lpszClassName = APPNAME;
2090 RegisterClass(&wndclass);
2093 main_hwnd = keylist = NULL;
2095 main_hwnd = CreateWindow(APPNAME, APPNAME,
2096 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
2097 CW_USEDEFAULT, CW_USEDEFAULT,
2098 100, 100, NULL, NULL, inst, NULL);
2100 /* Set up a system tray icon */
2101 AddTrayIcon(main_hwnd);
2103 /* Accelerators used: nsvkxa */
2104 systray_menu = CreatePopupMenu();
2106 session_menu = CreateMenu();
2107 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
2108 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
2109 (UINT) session_menu, "&Saved Sessions");
2110 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2112 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
2114 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
2115 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2117 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
2118 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
2119 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
2120 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
2121 initial_menuitems_count = GetMenuItemCount(session_menu);
2123 /* Set the default menu item. */
2124 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
2126 ShowWindow(main_hwnd, SW_HIDE);
2129 * Initialise storage for RSA keys.
2131 rsakeys = newtree234(cmpkeys_rsa);
2132 ssh2keys = newtree234(cmpkeys_ssh2);
2137 * Initialise storage for short-term passphrase cache.
2139 passphrases = newtree234(NULL);
2142 * Process the command line and add keys as listed on it.
2144 split_into_argv(cmdline, &argc, &argv, &argstart);
2145 for (i = 0; i < argc; i++) {
2146 if (!strcmp(argv[i], "-c")) {
2148 * If we see `-c', then the rest of the
2149 * command line should be treated as a
2150 * command to be spawned.
2153 command = argstart[i+1];
2158 add_keyfile(filename_from_str(argv[i]));
2164 * Forget any passphrase that we retained while going over
2165 * command line keyfiles.
2167 forget_passphrases();
2171 if (command[0] == '"')
2172 args = strchr(++command, '"');
2174 args = strchr(command, ' ');
2177 while(*args && isspace(*args)) args++;
2179 spawn_cmd(command, args, show);
2183 * If Pageant was already running, we leave now. If we haven't
2184 * even taken any auxiliary action (spawned a command or added
2187 if (already_running) {
2188 if (!command && !added_keys) {
2189 MessageBox(NULL, "Pageant is already running", "Pageant Error",
2190 MB_ICONERROR | MB_OK);
2193 FreeLibrary(advapi);
2198 * Main message loop.
2200 while (GetMessage(&msg, NULL, 0, 0) == 1) {
2201 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
2202 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
2203 TranslateMessage(&msg);
2204 DispatchMessage(&msg);
2208 /* Clean up the system tray icon */
2210 NOTIFYICONDATA tnid;
2212 tnid.cbSize = sizeof(NOTIFYICONDATA);
2213 tnid.hWnd = main_hwnd;
2216 Shell_NotifyIcon(NIM_DELETE, &tnid);
2218 DestroyMenu(systray_menu);
2222 FreeLibrary(advapi);