2 * Pageant: the PuTTY Authentication Agent.
17 #define IDI_MAINICON 200
18 #define IDI_TRAYICON 201
20 #define WM_XUSER (WM_USER + 0x2000)
21 #define WM_SYSTRAY (WM_XUSER + 6)
22 #define WM_SYSTRAY2 (WM_XUSER + 7)
24 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
27 * FIXME: maybe some day we can sort this out ...
29 #define AGENT_MAX_MSGLEN 8192
31 #define IDM_CLOSE 0x0010
32 #define IDM_VIEWKEYS 0x0020
33 #define IDM_ADDKEY 0x0030
34 #define IDM_ABOUT 0x0040
36 #define APPNAME "Pageant"
40 static HINSTANCE instance;
44 static HMENU systray_menu;
45 static int already_running;
47 static tree234 *rsakeys, *ssh2keys;
49 static int has_security;
51 typedef DWORD(WINAPI * gsi_fn_t)
52 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
53 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
54 static gsi_fn_t getsecurityinfo;
58 * Exports from pageantc.c
60 void agent_query(void *in, int inlen, void **out, int *outlen);
61 int agent_exists(void);
64 * We need this to link with the RSA code, because rsaencrypt()
65 * pads its data with random bytes. Since we only use rsadecrypt()
66 * and the signing functions, which are deterministic, this should
69 * If it _is_ called, there is a _serious_ problem, because it
70 * won't generate true random numbers. So we must scream, panic,
71 * and exit immediately if that should happen.
75 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
80 * Blob structure for passing to the asymmetric SSH2 key compare
81 * function, prototyped here.
87 static int cmpkeys_ssh2_asymm(void *av, void *bv);
90 * This function is needed to link with the DES code. We need not
91 * have it do anything at all.
93 void logevent(char *msg)
97 #define GET_32BIT(cp) \
98 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
99 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
100 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
101 ((unsigned long)(unsigned char)(cp)[3]))
103 #define PUT_32BIT(cp, value) { \
104 (cp)[0] = (unsigned char)((value) >> 24); \
105 (cp)[1] = (unsigned char)((value) >> 16); \
106 (cp)[2] = (unsigned char)((value) >> 8); \
107 (cp)[3] = (unsigned char)(value); }
109 #define PASSPHRASE_MAXLEN 512
111 struct PassphraseProcStruct {
117 * Dialog-box function for the Licence box.
119 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
120 WPARAM wParam, LPARAM lParam)
126 switch (LOWORD(wParam)) {
140 * Dialog-box function for the About box.
142 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
143 WPARAM wParam, LPARAM lParam)
147 SetDlgItemText(hwnd, 100, ver);
150 switch (LOWORD(wParam)) {
156 EnableWindow(hwnd, 0);
157 DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
158 EnableWindow(hwnd, 1);
159 SetActiveWindow(hwnd);
172 * Dialog-box function for the passphrase box.
174 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
175 WPARAM wParam, LPARAM lParam)
177 static char *passphrase = NULL;
178 struct PassphraseProcStruct *p;
185 { /* centre the window */
189 hw = GetDesktopWindow();
190 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
192 (rs.right + rs.left + rd.left - rd.right) / 2,
193 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
194 rd.right - rd.left, rd.bottom - rd.top, TRUE);
197 SetForegroundWindow(hwnd);
198 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
199 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
200 p = (struct PassphraseProcStruct *) lParam;
201 passphrase = p->passphrase;
203 SetDlgItemText(hwnd, 101, p->comment);
205 SetDlgItemText(hwnd, 102, passphrase);
208 switch (LOWORD(wParam)) {
218 case 102: /* edit box */
219 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
220 GetDlgItemText(hwnd, 102, passphrase,
221 PASSPHRASE_MAXLEN - 1);
222 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
235 * Update the visible key list.
237 static void keylist_update(void)
240 struct ssh2_userkey *skey;
244 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
245 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
246 char listentry[512], *p;
248 * Replace two spaces in the fingerprint with tabs, for
249 * nice alignment in the box.
251 strcpy(listentry, "ssh1\t");
252 p = listentry + strlen(listentry);
253 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
254 p = strchr(listentry, ' ');
257 p = strchr(listentry, ' ');
260 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
261 0, (LPARAM) listentry);
263 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
264 char listentry[512], *p;
267 * Replace two spaces in the fingerprint with tabs, for
268 * nice alignment in the box.
270 p = skey->alg->fingerprint(skey->data);
271 strncpy(listentry, p, sizeof(listentry));
272 p = strchr(listentry, ' ');
275 p = strchr(listentry, ' ');
278 len = strlen(listentry);
279 if (len < sizeof(listentry) - 2) {
280 listentry[len] = '\t';
281 strncpy(listentry + len + 1, skey->comment,
282 sizeof(listentry) - len - 1);
284 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
287 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
292 * This function loads a key from a file and adds it.
294 static void add_keyfile(char *filename)
296 char passphrase[PASSPHRASE_MAXLEN];
298 struct ssh2_userkey *skey;
303 struct PassphraseProcStruct pps;
306 ver = keyfile_version(filename);
308 MessageBox(NULL, "Couldn't load private key.", APPNAME,
309 MB_OK | MB_ICONERROR);
314 needs_pass = rsakey_encrypted(filename, &comment);
316 needs_pass = ssh2_userkey_encrypted(filename, &comment);
319 rkey = smalloc(sizeof(*rkey));
320 pps.passphrase = passphrase;
321 pps.comment = comment;
325 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
326 NULL, PassphraseProc, (LPARAM) & pps);
332 return; /* operation cancelled */
337 ret = loadrsakey(filename, rkey, passphrase);
339 skey = ssh2_load_userkey(filename, passphrase);
340 if (skey == SSH2_WRONG_PASSPHRASE)
352 MessageBox(NULL, "Couldn't load private key.", APPNAME,
353 MB_OK | MB_ICONERROR);
359 if (already_running) {
360 unsigned char *request, *response;
361 int reqlen, clen, resplen;
363 clen = strlen(rkey->comment);
365 reqlen = 4 + 1 + /* length, message type */
367 ssh1_bignum_length(rkey->modulus) +
368 ssh1_bignum_length(rkey->exponent) +
369 ssh1_bignum_length(rkey->private_exponent) +
370 ssh1_bignum_length(rkey->iqmp) +
371 ssh1_bignum_length(rkey->p) +
372 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
375 request = smalloc(reqlen);
377 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
379 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
381 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
382 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
384 ssh1_write_bignum(request + reqlen,
385 rkey->private_exponent);
386 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
387 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
388 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
389 PUT_32BIT(request + reqlen, clen);
390 memcpy(request + reqlen + 4, rkey->comment, clen);
392 PUT_32BIT(request, reqlen - 4);
394 agent_query(request, reqlen, &response, &resplen);
395 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
396 MessageBox(NULL, "The already running Pageant "
397 "refused to add the key.", APPNAME,
398 MB_OK | MB_ICONERROR);
400 if (add234(rsakeys, rkey) != rkey)
401 sfree(rkey); /* already present, don't waste RAM */
404 if (already_running) {
405 unsigned char *request, *response;
406 int reqlen, alglen, clen, keybloblen, resplen;
407 alglen = strlen(skey->alg->name);
408 clen = strlen(skey->comment);
410 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
412 reqlen = 4 + 1 + /* length, message type */
413 4 + alglen + /* algorithm name */
414 keybloblen + /* key data */
415 4 + clen /* comment */
418 request = smalloc(reqlen);
420 request[4] = SSH2_AGENTC_ADD_IDENTITY;
422 PUT_32BIT(request + reqlen, alglen);
424 memcpy(request + reqlen, skey->alg->name, alglen);
426 reqlen += skey->alg->openssh_fmtkey(skey->data,
429 PUT_32BIT(request + reqlen, clen);
430 memcpy(request + reqlen + 4, skey->comment, clen);
431 PUT_32BIT(request, reqlen - 4);
434 agent_query(request, reqlen, &response, &resplen);
435 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
436 MessageBox(NULL, "The already running Pageant"
437 "refused to add the key.", APPNAME,
438 MB_OK | MB_ICONERROR);
440 if (add234(ssh2keys, skey) != skey) {
441 skey->alg->freekey(skey->data);
442 sfree(skey); /* already present, don't waste RAM */
449 * This is the main agent function that answers messages.
451 static void answer_msg(void *msg)
453 unsigned char *p = msg;
454 unsigned char *ret = msg;
458 * Get the message type.
464 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
466 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
474 * Count up the number and length of keys we hold.
477 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
479 len += 4; /* length field */
480 len += ssh1_bignum_length(key->exponent);
481 len += ssh1_bignum_length(key->modulus);
482 len += 4 + strlen(key->comment);
486 * Packet header is the obvious five bytes, plus four
487 * bytes for the key count.
490 if (len > AGENT_MAX_MSGLEN)
491 goto failure; /* aaargh! too much stuff! */
492 PUT_32BIT(ret, len - 4);
493 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
494 PUT_32BIT(ret + 5, nkeys);
496 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
497 PUT_32BIT(p, bignum_bitcount(key->modulus));
499 p += ssh1_write_bignum(p, key->exponent);
500 p += ssh1_write_bignum(p, key->modulus);
501 PUT_32BIT(p, strlen(key->comment));
502 memcpy(p + 4, key->comment, strlen(key->comment));
503 p += 4 + strlen(key->comment);
507 case SSH2_AGENTC_REQUEST_IDENTITIES:
509 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
512 struct ssh2_userkey *key;
519 * Count up the number and length of keys we hold.
522 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
524 len += 4; /* length field */
525 blob = key->alg->public_blob(key->data, &bloblen);
528 len += 4 + strlen(key->comment);
532 * Packet header is the obvious five bytes, plus four
533 * bytes for the key count.
536 if (len > AGENT_MAX_MSGLEN)
537 goto failure; /* aaargh! too much stuff! */
538 PUT_32BIT(ret, len - 4);
539 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
540 PUT_32BIT(ret + 5, nkeys);
542 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
543 blob = key->alg->public_blob(key->data, &bloblen);
544 PUT_32BIT(p, bloblen);
546 memcpy(p, blob, bloblen);
549 PUT_32BIT(p, strlen(key->comment));
550 memcpy(p + 4, key->comment, strlen(key->comment));
551 p += 4 + strlen(key->comment);
555 case SSH1_AGENTC_RSA_CHALLENGE:
557 * Reply with either SSH1_AGENT_RSA_RESPONSE or
558 * SSH_AGENT_FAILURE, depending on whether we have that key
562 struct RSAKey reqkey, *key;
563 Bignum challenge, response;
564 unsigned char response_source[48], response_md5[16];
565 struct MD5Context md5c;
569 p += ssh1_read_bignum(p, &reqkey.exponent);
570 p += ssh1_read_bignum(p, &reqkey.modulus);
571 p += ssh1_read_bignum(p, &challenge);
572 memcpy(response_source + 32, p, 16);
574 if (GET_32BIT(p) != 1 ||
575 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
576 freebn(reqkey.exponent);
577 freebn(reqkey.modulus);
581 response = rsadecrypt(challenge, key);
582 for (i = 0; i < 32; i++)
583 response_source[i] = bignum_byte(response, 31 - i);
586 MD5Update(&md5c, response_source, 48);
587 MD5Final(response_md5, &md5c);
588 memset(response_source, 0, 48); /* burn the evidence */
589 freebn(response); /* and that evidence */
590 freebn(challenge); /* yes, and that evidence */
591 freebn(reqkey.exponent); /* and free some memory ... */
592 freebn(reqkey.modulus); /* ... while we're at it. */
595 * Packet is the obvious five byte header, plus sixteen
599 PUT_32BIT(ret, len - 4);
600 ret[4] = SSH1_AGENT_RSA_RESPONSE;
601 memcpy(ret + 5, response_md5, 16);
604 case SSH2_AGENTC_SIGN_REQUEST:
606 * Reply with either SSH2_AGENT_RSA_RESPONSE or
607 * SSH_AGENT_FAILURE, depending on whether we have that key
611 struct ssh2_userkey *key;
613 unsigned char *data, *signature;
614 int datalen, siglen, len;
616 b.len = GET_32BIT(p);
620 datalen = GET_32BIT(p);
623 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
626 signature = key->alg->sign(key->data, data, datalen, &siglen);
627 len = 5 + 4 + siglen;
628 PUT_32BIT(ret, len - 4);
629 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
630 PUT_32BIT(ret + 5, siglen);
631 memcpy(ret + 5 + 4, signature, siglen);
635 case SSH1_AGENTC_ADD_RSA_IDENTITY:
637 * Add to the list and return SSH_AGENT_SUCCESS, or
638 * SSH_AGENT_FAILURE if the key was malformed.
643 key = smalloc(sizeof(struct RSAKey));
644 memset(key, 0, sizeof(key));
645 p += makekey(p, key, NULL, 1);
646 p += makeprivate(p, key);
647 p += ssh1_read_bignum(p, key->iqmp); /* p^-1 mod q */
648 p += ssh1_read_bignum(p, key->p); /* p */
649 p += ssh1_read_bignum(p, key->q); /* q */
650 comment = smalloc(GET_32BIT(p));
652 memcpy(comment, p + 4, GET_32BIT(p));
653 key->comment = comment;
656 ret[4] = SSH_AGENT_FAILURE;
657 if (add234(rsakeys, key) == key) {
659 ret[4] = SSH_AGENT_SUCCESS;
666 case SSH2_AGENTC_ADD_IDENTITY:
668 * Add to the list and return SSH_AGENT_SUCCESS, or
669 * SSH_AGENT_FAILURE if the key was malformed.
672 struct ssh2_userkey *key;
677 key = smalloc(sizeof(struct ssh2_userkey));
679 alglen = GET_32BIT(p);
683 /* Add further algorithm names here. */
684 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
692 GET_32BIT((unsigned char *) msg) - (p -
693 (unsigned char *) msg -
695 key->data = key->alg->openssh_createkey(&p, &bloblen);
700 commlen = GET_32BIT(p);
703 comment = smalloc(commlen + 1);
705 memcpy(comment, p, commlen);
706 comment[commlen] = '\0';
708 key->comment = comment;
711 ret[4] = SSH_AGENT_FAILURE;
712 if (add234(ssh2keys, key) == key) {
714 ret[4] = SSH_AGENT_SUCCESS;
716 key->alg->freekey(key->data);
722 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
724 * Remove from the list and return SSH_AGENT_SUCCESS, or
725 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
729 struct RSAKey reqkey, *key;
731 p += makekey(p, &reqkey, NULL, 0);
732 key = find234(rsakeys, &reqkey, NULL);
733 freebn(reqkey.exponent);
734 freebn(reqkey.modulus);
736 ret[4] = SSH_AGENT_FAILURE;
738 del234(rsakeys, key);
742 ret[4] = SSH_AGENT_SUCCESS;
746 case SSH2_AGENTC_REMOVE_IDENTITY:
748 * Remove from the list and return SSH_AGENT_SUCCESS, or
749 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
753 struct ssh2_userkey *key;
756 b.len = GET_32BIT(p);
760 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
765 ret[4] = SSH_AGENT_FAILURE;
767 del234(ssh2keys, key);
769 key->alg->freekey(key->data);
771 ret[4] = SSH_AGENT_SUCCESS;
775 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
777 * Remove all SSH1 keys. Always returns success.
782 while ((rkey = index234(rsakeys, 0)) != NULL) {
783 del234(rsakeys, rkey);
790 ret[4] = SSH_AGENT_SUCCESS;
793 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
795 * Remove all SSH2 keys. Always returns success.
798 struct ssh2_userkey *skey;
800 while ((skey = index234(ssh2keys, 0)) != NULL) {
801 del234(ssh2keys, skey);
802 skey->alg->freekey(skey->data);
808 ret[4] = SSH_AGENT_SUCCESS;
814 * Unrecognised message. Return SSH_AGENT_FAILURE.
817 ret[4] = SSH_AGENT_FAILURE;
823 * Key comparison function for the 2-3-4 tree of RSA keys.
825 static int cmpkeys_rsa(void *av, void *bv)
827 struct RSAKey *a = (struct RSAKey *) av;
828 struct RSAKey *b = (struct RSAKey *) bv;
835 * Compare by length of moduli.
837 alen = bignum_bitcount(am);
838 blen = bignum_bitcount(bm);
841 else if (alen < blen)
844 * Now compare by moduli themselves.
846 alen = (alen + 7) / 8; /* byte count */
849 abyte = bignum_byte(am, alen);
850 bbyte = bignum_byte(bm, alen);
853 else if (abyte < bbyte)
863 * Key comparison function for the 2-3-4 tree of SSH2 keys.
865 static int cmpkeys_ssh2(void *av, void *bv)
867 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
868 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
871 unsigned char *ablob, *bblob;
875 * Compare purely by public blob.
877 ablob = a->alg->public_blob(a->data, &alen);
878 bblob = b->alg->public_blob(b->data, &blen);
881 for (i = 0; i < alen && i < blen; i++) {
882 if (ablob[i] < bblob[i]) {
885 } else if (ablob[i] > bblob[i]) {
890 if (c == 0 && i < alen)
891 c = +1; /* a is longer */
892 if (c == 0 && i < blen)
893 c = -1; /* a is longer */
902 * Key comparison function for looking up a blob in the 2-3-4 tree
905 static int cmpkeys_ssh2_asymm(void *av, void *bv)
907 struct blob *a = (struct blob *) av;
908 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
911 unsigned char *ablob, *bblob;
915 * Compare purely by public blob.
919 bblob = b->alg->public_blob(b->data, &blen);
922 for (i = 0; i < alen && i < blen; i++) {
923 if (ablob[i] < bblob[i]) {
926 } else if (ablob[i] > bblob[i]) {
931 if (c == 0 && i < alen)
932 c = +1; /* a is longer */
933 if (c == 0 && i < blen)
934 c = -1; /* a is longer */
941 static void error(char *s)
943 MessageBox(hwnd, s, APPNAME, MB_OK | MB_ICONERROR);
947 * Prompt for a key file to add, and add it.
949 static void prompt_add_keyfile(void)
952 char filename[FILENAME_MAX];
953 memset(&of, 0, sizeof(of));
954 #ifdef OPENFILENAME_SIZE_VERSION_400
955 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
957 of.lStructSize = sizeof(of);
960 of.lpstrFilter = "All Files\0*\0\0\0";
961 of.lpstrCustomFilter = NULL;
963 of.lpstrFile = filename;
965 of.nMaxFile = sizeof(filename);
966 of.lpstrFileTitle = NULL;
967 of.lpstrInitialDir = NULL;
968 of.lpstrTitle = "Select Private Key File";
970 if (GetOpenFileName(&of)) {
971 add_keyfile(filename);
977 * Dialog-box function for the key list box.
979 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
980 WPARAM wParam, LPARAM lParam)
983 struct ssh2_userkey *skey;
990 { /* centre the window */
994 hw = GetDesktopWindow();
995 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
997 (rs.right + rs.left + rd.left - rd.right) / 2,
998 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
999 rd.right - rd.left, rd.bottom - rd.top, TRUE);
1004 static int tabs[] = { 35, 60, 210 };
1005 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1006 sizeof(tabs) / sizeof(*tabs),
1012 switch (LOWORD(wParam)) {
1016 DestroyWindow(hwnd);
1018 case 101: /* add key */
1019 if (HIWORD(wParam) == BN_CLICKED ||
1020 HIWORD(wParam) == BN_DOUBLECLICKED) {
1021 prompt_add_keyfile();
1024 case 102: /* remove key */
1025 if (HIWORD(wParam) == BN_CLICKED ||
1026 HIWORD(wParam) == BN_DOUBLECLICKED) {
1027 int n = SendDlgItemMessage(hwnd, 100, LB_GETCURSEL, 0, 0);
1033 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++)
1037 del234(rsakeys, rkey);
1041 for (i = 0; NULL != (skey = index234(ssh2keys, i));
1045 del234(ssh2keys, skey);
1046 skey->alg->freekey(skey->data);
1057 DestroyWindow(hwnd);
1063 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1064 WPARAM wParam, LPARAM lParam)
1067 static int menuinprogress;
1071 if (lParam == WM_RBUTTONUP) {
1073 GetCursorPos(&cursorpos);
1074 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1075 } else if (lParam == WM_LBUTTONDBLCLK) {
1076 /* Equivalent to IDM_VIEWKEYS. */
1077 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1081 if (!menuinprogress) {
1083 SetForegroundWindow(hwnd);
1084 ret = TrackPopupMenu(systray_menu,
1085 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1087 wParam, lParam, 0, hwnd, NULL);
1093 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1095 SendMessage(hwnd, WM_CLOSE, 0, 0);
1099 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1101 ShowWindow(keylist, SW_SHOWNORMAL);
1103 * Sometimes the window comes up minimised / hidden
1104 * for no obvious reason. Prevent this.
1106 SetForegroundWindow(keylist);
1107 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1108 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1112 prompt_add_keyfile();
1116 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1118 ShowWindow(aboutbox, SW_SHOWNORMAL);
1120 * Sometimes the window comes up minimised / hidden
1121 * for no obvious reason. Prevent this.
1123 SetForegroundWindow(aboutbox);
1124 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1125 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1135 COPYDATASTRUCT *cds;
1138 HANDLE filemap, proc;
1139 PSID mapowner, procowner;
1140 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
1143 cds = (COPYDATASTRUCT *) lParam;
1144 if (cds->dwData != AGENT_COPYDATA_ID)
1145 return 0; /* not our message, mate */
1146 mapname = (char *) cds->lpData;
1147 if (mapname[cds->cbData - 1] != '\0')
1148 return 0; /* failure to be ASCIZ! */
1150 debug(("mapname is :%s:\n", mapname));
1152 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1154 debug(("filemap is %p\n", filemap));
1156 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1160 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1161 GetCurrentProcessId())) ==
1164 debug(("couldn't get handle for process\n"));
1168 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1169 OWNER_SECURITY_INFORMATION,
1170 &procowner, NULL, NULL, NULL,
1171 &psd2) != ERROR_SUCCESS) {
1173 debug(("couldn't get owner info for process\n"));
1176 return 0; /* unable to get security info */
1179 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1180 OWNER_SECURITY_INFORMATION,
1181 &mapowner, NULL, NULL, NULL,
1182 &psd1) != ERROR_SUCCESS)) {
1185 ("couldn't get owner info for filemap: %d\n",
1191 debug(("got security stuff\n"));
1193 if (!EqualSid(mapowner, procowner))
1194 return 0; /* security ID mismatch! */
1196 debug(("security stuff matched\n"));
1202 debug(("security APIs not present\n"));
1206 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1208 debug(("p is %p\n", p));
1211 for (i = 0; i < 5; i++)
1214 ((unsigned char *) p)[i]));}
1220 CloseHandle(filemap);
1225 return DefWindowProc(hwnd, message, wParam, lParam);
1229 * Fork and Exec the command in cmdline. [DBW]
1231 void spawn_cmd(char *cmdline, int show)
1233 if (ShellExecute(NULL, _T("open"), cmdline,
1234 NULL, NULL, show) <= (HINSTANCE) 32) {
1236 sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
1238 MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1242 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1248 char *command = NULL;
1252 * Determine whether we're an NT system (should have security
1253 * APIs) or a non-NT system (don't do security).
1255 memset(&osi, 0, sizeof(OSVERSIONINFO));
1256 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1257 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1258 has_security = TRUE;
1260 has_security = FALSE;
1265 * Attempt to get the security API we need.
1267 advapi = LoadLibrary("ADVAPI32.DLL");
1269 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1270 if (!getsecurityinfo) {
1272 "Unable to access security APIs. Pageant will\n"
1273 "not run, in case it causes a security breach.",
1274 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1279 "This program has been compiled for Win9X and will\n"
1280 "not run on NT, in case it causes a security breach.",
1281 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1290 * Find out if Pageant is already running.
1292 already_running = FALSE;
1294 already_running = TRUE;
1299 wndclass.lpfnWndProc = WndProc;
1300 wndclass.cbClsExtra = 0;
1301 wndclass.cbWndExtra = 0;
1302 wndclass.hInstance = inst;
1303 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1304 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1305 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1306 wndclass.lpszMenuName = NULL;
1307 wndclass.lpszClassName = APPNAME;
1309 RegisterClass(&wndclass);
1312 hwnd = keylist = NULL;
1314 hwnd = CreateWindow(APPNAME, APPNAME,
1315 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1316 CW_USEDEFAULT, CW_USEDEFAULT,
1317 100, 100, NULL, NULL, inst, NULL);
1319 /* Set up a system tray icon */
1322 NOTIFYICONDATA tnid;
1325 #ifdef NIM_SETVERSION
1327 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
1330 tnid.cbSize = sizeof(NOTIFYICONDATA);
1332 tnid.uID = 1; /* unique within this systray use */
1333 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1334 tnid.uCallbackMessage = WM_SYSTRAY;
1335 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
1336 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1338 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1343 systray_menu = CreatePopupMenu();
1344 /* accelerators used: vkxa */
1345 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1347 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1348 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1349 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1352 ShowWindow(hwnd, SW_HIDE);
1355 * Initialise storage for RSA keys.
1357 rsakeys = newtree234(cmpkeys_rsa);
1358 ssh2keys = newtree234(cmpkeys_ssh2);
1363 * Process the command line and add keys as listed on it.
1364 * If we already determined that we need to spawn a program from above we
1365 * need to ignore the first two arguments. [DBW]
1373 while (*p && isspace(*p))
1375 if (*p && !isspace(*p)) {
1376 char *q = p, *pp = p;
1377 while (*p && (inquotes || !isspace(*p))) {
1379 inquotes = !inquotes;
1390 if (!strcmp(q, "-c")) {
1392 * If we see `-c', then the rest of the
1393 * command line should be treated as a
1394 * command to be spawned.
1396 while (*p && isspace(*p))
1409 spawn_cmd(command, show);
1412 * If Pageant was already running, we leave now. If we haven't
1413 * even taken any auxiliary action (spawned a command or added
1416 if (already_running) {
1417 if (!command && !added_keys) {
1418 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1419 MB_ICONERROR | MB_OK);
1422 FreeLibrary(advapi);
1427 * Main message loop.
1429 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1430 TranslateMessage(&msg);
1431 DispatchMessage(&msg);
1434 /* Clean up the system tray icon */
1436 NOTIFYICONDATA tnid;
1438 tnid.cbSize = sizeof(NOTIFYICONDATA);
1442 Shell_NotifyIcon(NIM_DELETE, &tnid);
1444 DestroyMenu(systray_menu);
1448 FreeLibrary(advapi);