2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
25 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
30 #define IDI_MAINICON 200
31 #define IDI_TRAYICON 201
33 #define WM_SYSTRAY (WM_APP + 6)
34 #define WM_SYSTRAY2 (WM_APP + 7)
36 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
38 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
39 * wParam are used by Windows, and should be masked off, so we shouldn't
40 * attempt to store information in them. Hence all these identifiers have
41 * the low 4 bits clear. Also, identifiers should < 0xF000. */
43 #define IDM_CLOSE 0x0010
44 #define IDM_VIEWKEYS 0x0020
45 #define IDM_ADDKEY 0x0030
46 #define IDM_HELP 0x0040
47 #define IDM_ABOUT 0x0050
49 #define APPNAME "Pageant"
55 static HMENU systray_menu, session_menu;
56 static int already_running;
58 static char *putty_path;
60 /* CWD for "add key" file requester. */
61 static filereq *keypath = NULL;
63 #define IDM_PUTTY 0x0060
64 #define IDM_SESSIONS_BASE 0x1000
65 #define IDM_SESSIONS_MAX 0x2000
66 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
67 #define PUTTY_DEFAULT "Default%20Settings"
68 static int initial_menuitems_count;
71 * Print a modal (Really Bad) message box and perform a fatal exit.
73 void modalfatalbox(char *fmt, ...)
79 buf = dupvprintf(fmt, ap);
81 MessageBox(hwnd, buf, "Pageant Fatal Error",
82 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
87 /* Un-munge session names out of the registry. */
88 static void unmungestr(char *in, char *out, int outlen)
91 if (*in == '%' && in[1] && in[2]) {
99 *out++ = (i << 4) + j;
113 static int has_security;
118 static void *get_keylist1(int *length);
119 static void *get_keylist2(int *length);
121 struct PassphraseProcStruct {
126 static tree234 *passphrases = NULL;
129 * After processing a list of filenames, we want to forget the
132 static void forget_passphrases(void)
134 while (count234(passphrases) > 0) {
135 char *pp = index234(passphrases, 0);
136 smemclr(pp, strlen(pp));
137 delpos234(passphrases, 0);
143 * Dialog-box function for the Licence box.
145 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
146 WPARAM wParam, LPARAM lParam)
152 switch (LOWORD(wParam)) {
167 * Dialog-box function for the About box.
169 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
170 WPARAM wParam, LPARAM lParam)
174 SetDlgItemText(hwnd, 100, ver);
177 switch (LOWORD(wParam)) {
184 EnableWindow(hwnd, 0);
185 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
186 EnableWindow(hwnd, 1);
187 SetActiveWindow(hwnd);
199 static HWND passphrase_box;
202 * Dialog-box function for the passphrase box.
204 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
205 WPARAM wParam, LPARAM lParam)
207 static char **passphrase = NULL;
208 struct PassphraseProcStruct *p;
212 passphrase_box = hwnd;
216 { /* centre the window */
220 hw = GetDesktopWindow();
221 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
223 (rs.right + rs.left + rd.left - rd.right) / 2,
224 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
225 rd.right - rd.left, rd.bottom - rd.top, TRUE);
228 SetForegroundWindow(hwnd);
229 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
230 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
231 p = (struct PassphraseProcStruct *) lParam;
232 passphrase = p->passphrase;
234 SetDlgItemText(hwnd, 101, p->comment);
235 burnstr(*passphrase);
236 *passphrase = dupstr("");
237 SetDlgItemText(hwnd, 102, *passphrase);
240 switch (LOWORD(wParam)) {
250 case 102: /* edit box */
251 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
252 burnstr(*passphrase);
253 *passphrase = GetDlgItemText_alloc(hwnd, 102);
266 * Warn about the obsolescent key file format.
268 void old_keyfile_warning(void)
270 static const char mbtitle[] = "PuTTY Key File Warning";
271 static const char message[] =
272 "You are loading an SSH-2 private key which has an\n"
273 "old version of the file format. This means your key\n"
274 "file is not fully tamperproof. Future versions of\n"
275 "PuTTY may stop supporting this private key format,\n"
276 "so we recommend you convert your key to the new\n"
279 "You can perform this conversion by loading the key\n"
280 "into PuTTYgen and then saving it again.";
282 MessageBox(NULL, message, mbtitle, MB_OK);
286 * Update the visible key list.
288 void keylist_update(void)
291 struct ssh2_userkey *skey;
295 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
296 for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
297 char listentry[512], *p;
299 * Replace two spaces in the fingerprint with tabs, for
300 * nice alignment in the box.
302 strcpy(listentry, "ssh1\t");
303 p = listentry + strlen(listentry);
304 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
305 p = strchr(listentry, ' ');
308 p = strchr(listentry, ' ');
311 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
312 0, (LPARAM) listentry);
314 for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
318 * Replace spaces with tabs in the fingerprint prefix, for
319 * nice alignment in the list box, until we encounter a :
320 * meaning we're into the fingerprint proper.
322 p = skey->alg->fingerprint(skey->data);
323 listentry = dupprintf("%s\t%s", p, skey->comment);
324 fp_len = strlen(listentry);
329 pos += strcspn(listentry + pos, " :");
330 if (listentry[pos] == ':')
332 listentry[pos++] = '\t';
335 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
339 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
344 * This function loads a key from a file and adds it.
346 static void add_keyfile(Filename *filename)
349 struct RSAKey *rkey = NULL;
350 struct ssh2_userkey *skey = NULL;
355 const char *error = NULL;
359 type = key_type(filename);
360 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
361 char *msg = dupprintf("Couldn't load this key (%s)",
362 key_type_to_str(type));
363 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
364 HELPCTXID(errors_cantloadkey));
370 * See if the key is already loaded (in the primary Pageant,
371 * which may or may not be us).
375 unsigned char *keylist, *p;
376 int i, nkeys, bloblen, keylistlen;
378 if (type == SSH_KEYTYPE_SSH1) {
379 if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
380 char *msg = dupprintf("Couldn't load private key (%s)", error);
381 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
382 HELPCTXID(errors_cantloadkey));
386 keylist = get_keylist1(&keylistlen);
388 unsigned char *blob2;
389 blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
392 char *msg = dupprintf("Couldn't load private key (%s)", error);
393 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
394 HELPCTXID(errors_cantloadkey));
398 /* For our purposes we want the blob prefixed with its length */
399 blob2 = snewn(bloblen+4, unsigned char);
400 PUT_32BIT(blob2, bloblen);
401 memcpy(blob2 + 4, blob, bloblen);
405 keylist = get_keylist2(&keylistlen);
408 if (keylistlen < 4) {
409 MessageBox(NULL, "Received broken key list?!", APPNAME,
410 MB_OK | MB_ICONERROR);
413 nkeys = toint(GET_32BIT(keylist));
415 MessageBox(NULL, "Received broken key list?!", APPNAME,
416 MB_OK | MB_ICONERROR);
422 for (i = 0; i < nkeys; i++) {
423 if (!memcmp(blob, p, bloblen)) {
424 /* Key is already present; we can now leave. */
429 /* Now skip over public blob */
430 if (type == SSH_KEYTYPE_SSH1) {
431 int n = rsa_public_blob_len(p, keylistlen);
433 MessageBox(NULL, "Received broken key list?!", APPNAME,
434 MB_OK | MB_ICONERROR);
441 if (keylistlen < 4) {
442 MessageBox(NULL, "Received broken key list?!", APPNAME,
443 MB_OK | MB_ICONERROR);
446 n = toint(4 + GET_32BIT(p));
447 if (n < 0 || keylistlen < n) {
448 MessageBox(NULL, "Received broken key list?!", APPNAME,
449 MB_OK | MB_ICONERROR);
455 /* Now skip over comment field */
458 if (keylistlen < 4) {
459 MessageBox(NULL, "Received broken key list?!", APPNAME,
460 MB_OK | MB_ICONERROR);
463 n = toint(4 + GET_32BIT(p));
464 if (n < 0 || keylistlen < n) {
465 MessageBox(NULL, "Received broken key list?!", APPNAME,
466 MB_OK | MB_ICONERROR);
481 if (type == SSH_KEYTYPE_SSH1)
482 needs_pass = rsakey_encrypted(filename, &comment);
484 needs_pass = ssh2_userkey_encrypted(filename, &comment);
486 if (type == SSH_KEYTYPE_SSH1)
487 rkey = snew(struct RSAKey);
495 /* try all the remembered passphrases first */
496 char *pp = index234(passphrases, attempts);
498 passphrase = dupstr(pp);
501 struct PassphraseProcStruct pps;
503 pps.passphrase = &passphrase;
504 pps.comment = comment;
507 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
508 NULL, PassphraseProc, (LPARAM) &pps);
509 passphrase_box = NULL;
513 if (type == SSH_KEYTYPE_SSH1)
515 return; /* operation cancelled */
518 assert(passphrase != NULL);
521 passphrase = dupstr("");
523 if (type == SSH_KEYTYPE_SSH1)
524 ret = loadrsakey(filename, rkey, passphrase, &error);
526 skey = ssh2_load_userkey(filename, passphrase, &error);
527 if (skey == SSH2_WRONG_PASSPHRASE)
537 if(original_pass && ret) {
538 /* If they typed in an ok passphrase, remember it */
539 addpos234(passphrases, passphrase, 0);
541 /* Otherwise, destroy it */
549 char *msg = dupprintf("Couldn't load private key (%s)", error);
550 message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
551 HELPCTXID(errors_cantloadkey));
553 if (type == SSH_KEYTYPE_SSH1)
557 if (type == SSH_KEYTYPE_SSH1) {
558 if (already_running) {
559 unsigned char *request, *response;
561 int reqlen, clen, resplen, ret;
563 clen = strlen(rkey->comment);
565 reqlen = 4 + 1 + /* length, message type */
567 ssh1_bignum_length(rkey->modulus) +
568 ssh1_bignum_length(rkey->exponent) +
569 ssh1_bignum_length(rkey->private_exponent) +
570 ssh1_bignum_length(rkey->iqmp) +
571 ssh1_bignum_length(rkey->p) +
572 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
575 request = snewn(reqlen, unsigned char);
577 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
579 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
581 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
582 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
584 ssh1_write_bignum(request + reqlen,
585 rkey->private_exponent);
586 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
587 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
588 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
589 PUT_32BIT(request + reqlen, clen);
590 memcpy(request + reqlen + 4, rkey->comment, clen);
592 PUT_32BIT(request, reqlen - 4);
594 ret = agent_query(request, reqlen, &vresponse, &resplen,
597 response = vresponse;
598 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
599 MessageBox(NULL, "The already running Pageant "
600 "refused to add the key.", APPNAME,
601 MB_OK | MB_ICONERROR);
606 if (!pageant_add_ssh1_key(rkey))
607 sfree(rkey); /* already present, don't waste RAM */
610 if (already_running) {
611 unsigned char *request, *response;
613 int reqlen, alglen, clen, keybloblen, resplen, ret;
614 alglen = strlen(skey->alg->name);
615 clen = strlen(skey->comment);
617 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
619 reqlen = 4 + 1 + /* length, message type */
620 4 + alglen + /* algorithm name */
621 keybloblen + /* key data */
622 4 + clen /* comment */
625 request = snewn(reqlen, unsigned char);
627 request[4] = SSH2_AGENTC_ADD_IDENTITY;
629 PUT_32BIT(request + reqlen, alglen);
631 memcpy(request + reqlen, skey->alg->name, alglen);
633 reqlen += skey->alg->openssh_fmtkey(skey->data,
636 PUT_32BIT(request + reqlen, clen);
637 memcpy(request + reqlen + 4, skey->comment, clen);
639 PUT_32BIT(request, reqlen - 4);
641 ret = agent_query(request, reqlen, &vresponse, &resplen,
644 response = vresponse;
645 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
646 MessageBox(NULL, "The already running Pageant "
647 "refused to add the key.", APPNAME,
648 MB_OK | MB_ICONERROR);
653 if (!pageant_add_ssh2_key(skey)) {
654 skey->alg->freekey(skey->data);
655 sfree(skey); /* already present, don't waste RAM */
662 * Acquire a keylist1 from the primary Pageant; this means either
663 * calling pageant_make_keylist1 (if that's us) or sending a message
664 * to the primary Pageant (if it's not).
666 static void *get_keylist1(int *length)
670 if (already_running) {
671 unsigned char request[5], *response;
674 request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
675 PUT_32BIT(request, 4);
677 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
679 response = vresponse;
680 if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
685 ret = snewn(resplen-5, unsigned char);
686 memcpy(ret, response+5, resplen-5);
692 ret = pageant_make_keylist1(length);
698 * Acquire a keylist2 from the primary Pageant; this means either
699 * calling pageant_make_keylist2 (if that's us) or sending a message
700 * to the primary Pageant (if it's not).
702 static void *get_keylist2(int *length)
706 if (already_running) {
707 unsigned char request[5], *response;
711 request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
712 PUT_32BIT(request, 4);
714 retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
716 response = vresponse;
717 if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
722 ret = snewn(resplen-5, unsigned char);
723 memcpy(ret, response+5, resplen-5);
729 ret = pageant_make_keylist2(length);
734 static void answer_msg(void *msgv)
736 unsigned char *msg = (unsigned char *)msgv;
741 msglen = GET_32BIT(msg);
742 if (msglen > AGENT_MAX_MSGLEN) {
743 reply = pageant_failure_msg(&replylen);
745 reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
746 if (replylen > AGENT_MAX_MSGLEN) {
747 smemclr(reply, replylen);
749 reply = pageant_failure_msg(&replylen);
754 * Windows Pageant answers messages in place, by overwriting the
755 * input message buffer.
757 memcpy(msg, reply, replylen);
758 smemclr(reply, replylen);
763 * Prompt for a key file to add, and add it.
765 static void prompt_add_keyfile(void)
768 char *filelist = snewn(8192, char);
770 if (!keypath) keypath = filereq_new();
771 memset(&of, 0, sizeof(of));
773 of.lpstrFilter = FILTER_KEY_FILES;
774 of.lpstrCustomFilter = NULL;
776 of.lpstrFile = filelist;
779 of.lpstrFileTitle = NULL;
780 of.lpstrTitle = "Select Private Key File";
781 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
782 if (request_file(keypath, &of, TRUE, FALSE)) {
783 if(strlen(filelist) > of.nFileOffset) {
784 /* Only one filename returned? */
785 Filename *fn = filename_from_str(filelist);
789 /* we are returned a bunch of strings, end to
790 * end. first string is the directory, the
791 * rest the filenames. terminated with an
794 char *dir = filelist;
795 char *filewalker = filelist + strlen(dir) + 1;
796 while (*filewalker != '\0') {
797 char *filename = dupcat(dir, "\\", filewalker, NULL);
798 Filename *fn = filename_from_str(filename);
802 filewalker += strlen(filewalker) + 1;
807 forget_passphrases();
813 * Dialog-box function for the key list box.
815 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
816 WPARAM wParam, LPARAM lParam)
819 struct ssh2_userkey *skey;
826 { /* centre the window */
830 hw = GetDesktopWindow();
831 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
833 (rs.right + rs.left + rd.left - rd.right) / 2,
834 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
835 rd.right - rd.left, rd.bottom - rd.top, TRUE);
839 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
840 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
843 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
850 static int tabs[] = { 35, 75, 250 };
851 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
852 sizeof(tabs) / sizeof(*tabs),
858 switch (LOWORD(wParam)) {
864 case 101: /* add key */
865 if (HIWORD(wParam) == BN_CLICKED ||
866 HIWORD(wParam) == BN_DOUBLECLICKED) {
867 if (passphrase_box) {
868 MessageBeep(MB_ICONERROR);
869 SetForegroundWindow(passphrase_box);
872 prompt_add_keyfile();
875 case 102: /* remove key */
876 if (HIWORD(wParam) == BN_CLICKED ||
877 HIWORD(wParam) == BN_DOUBLECLICKED) {
882 /* our counter within the array of selected items */
885 /* get the number of items selected in the list */
887 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
889 /* none selected? that was silly */
890 if (numSelected == 0) {
895 /* get item indices in an array */
896 selectedArray = snewn(numSelected, int);
897 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
898 numSelected, (WPARAM)selectedArray);
900 itemNum = numSelected - 1;
901 rCount = pageant_count_ssh1_keys();
902 sCount = pageant_count_ssh2_keys();
904 /* go through the non-rsakeys until we've covered them all,
905 * and/or we're out of selected items to check. note that
906 * we go *backwards*, to avoid complications from deleting
907 * things hence altering the offset of subsequent items
909 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
910 skey = pageant_nth_ssh2_key(i);
912 if (selectedArray[itemNum] == rCount + i) {
913 pageant_delete_ssh2_key(skey);
914 skey->alg->freekey(skey->data);
920 /* do the same for the rsa keys */
921 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
922 rkey = pageant_nth_ssh1_key(i);
924 if(selectedArray[itemNum] == i) {
925 pageant_delete_ssh1_key(rkey);
932 sfree(selectedArray);
937 if (HIWORD(wParam) == BN_CLICKED ||
938 HIWORD(wParam) == BN_DOUBLECLICKED) {
939 launch_help(hwnd, WINHELP_CTX_pageant_general);
946 int id = ((LPHELPINFO)lParam)->iCtrlId;
949 case 100: topic = WINHELP_CTX_pageant_keylist; break;
950 case 101: topic = WINHELP_CTX_pageant_addkey; break;
951 case 102: topic = WINHELP_CTX_pageant_remkey; break;
954 launch_help(hwnd, topic);
968 /* Set up a system tray icon */
969 static BOOL AddTrayIcon(HWND hwnd)
975 #ifdef NIM_SETVERSION
977 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
980 tnid.cbSize = sizeof(NOTIFYICONDATA);
982 tnid.uID = 1; /* unique within this systray use */
983 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
984 tnid.uCallbackMessage = WM_SYSTRAY;
985 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
986 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
988 res = Shell_NotifyIcon(NIM_ADD, &tnid);
990 if (hicon) DestroyIcon(hicon);
995 /* Update the saved-sessions menu. */
996 static void update_sessions(void)
1000 TCHAR buf[MAX_PATH + 1];
1003 int index_key, index_menu;
1008 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
1011 for(num_entries = GetMenuItemCount(session_menu);
1012 num_entries > initial_menuitems_count;
1014 RemoveMenu(session_menu, 0, MF_BYPOSITION);
1019 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
1020 TCHAR session_name[MAX_PATH + 1];
1021 unmungestr(buf, session_name, MAX_PATH);
1022 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
1023 memset(&mii, 0, sizeof(mii));
1024 mii.cbSize = sizeof(mii);
1025 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
1026 mii.fType = MFT_STRING;
1027 mii.fState = MFS_ENABLED;
1028 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
1029 mii.dwTypeData = session_name;
1030 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1038 if(index_menu == 0) {
1039 mii.cbSize = sizeof(mii);
1040 mii.fMask = MIIM_TYPE | MIIM_STATE;
1041 mii.fType = MFT_STRING;
1042 mii.fState = MFS_GRAYED;
1043 mii.dwTypeData = _T("(No sessions)");
1044 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
1050 * Versions of Pageant prior to 0.61 expected this SID on incoming
1051 * communications. For backwards compatibility, and more particularly
1052 * for compatibility with derived works of PuTTY still using the old
1053 * Pageant client code, we accept it as an alternative to the one
1054 * returned from get_user_sid() in winpgntc.c.
1056 PSID get_default_sid(void)
1060 PSECURITY_DESCRIPTOR psd = NULL;
1061 PSID sid = NULL, copy = NULL, ret = NULL;
1063 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1064 GetCurrentProcessId())) == NULL)
1067 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
1068 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
1071 sidlen = GetLengthSid(sid);
1073 copy = (PSID)smalloc(sidlen);
1075 if (!CopySid(sidlen, copy, sid))
1078 /* Success. Move sid into the return value slot, and null it out
1079 * to stop the cleanup code freeing it. */
1095 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1096 WPARAM wParam, LPARAM lParam)
1099 static int menuinprogress;
1100 static UINT msgTaskbarCreated = 0;
1104 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
1107 if (message==msgTaskbarCreated) {
1109 * Explorer has been restarted, so the tray icon will
1117 if (lParam == WM_RBUTTONUP) {
1119 GetCursorPos(&cursorpos);
1120 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1121 } else if (lParam == WM_LBUTTONDBLCLK) {
1122 /* Run the default menu item. */
1123 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
1125 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
1129 if (!menuinprogress) {
1132 SetForegroundWindow(hwnd);
1133 ret = TrackPopupMenu(systray_menu,
1134 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1136 wParam, lParam, 0, hwnd, NULL);
1142 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1144 if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
1146 MessageBox(NULL, "Unable to execute PuTTY!",
1147 "Error", MB_OK | MB_ICONERROR);
1152 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
1153 SendMessage(hwnd, WM_CLOSE, 0, 0);
1157 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
1159 ShowWindow(keylist, SW_SHOWNORMAL);
1162 * Sometimes the window comes up minimised / hidden for
1163 * no obvious reason. Prevent this. This also brings it
1164 * to the front if it's already present (the user
1165 * selected View Keys because they wanted to _see_ the
1168 SetForegroundWindow(keylist);
1169 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1170 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1173 if (passphrase_box) {
1174 MessageBeep(MB_ICONERROR);
1175 SetForegroundWindow(passphrase_box);
1178 prompt_add_keyfile();
1182 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
1184 ShowWindow(aboutbox, SW_SHOWNORMAL);
1186 * Sometimes the window comes up minimised / hidden
1187 * for no obvious reason. Prevent this.
1189 SetForegroundWindow(aboutbox);
1190 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1191 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1195 launch_help(hwnd, WINHELP_CTX_pageant_general);
1199 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
1201 TCHAR buf[MAX_PATH + 1];
1202 TCHAR param[MAX_PATH + 1];
1203 memset(&mii, 0, sizeof(mii));
1204 mii.cbSize = sizeof(mii);
1205 mii.fMask = MIIM_TYPE;
1207 mii.dwTypeData = buf;
1208 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
1210 strcat(param, mii.dwTypeData);
1211 if((int)ShellExecute(hwnd, NULL, putty_path, param,
1212 _T(""), SW_SHOW) <= 32) {
1213 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
1214 MB_OK | MB_ICONERROR);
1227 COPYDATASTRUCT *cds;
1232 PSID mapowner, ourself, ourself2;
1234 PSECURITY_DESCRIPTOR psd = NULL;
1237 cds = (COPYDATASTRUCT *) lParam;
1238 if (cds->dwData != AGENT_COPYDATA_ID)
1239 return 0; /* not our message, mate */
1240 mapname = (char *) cds->lpData;
1241 if (mapname[cds->cbData - 1] != '\0')
1242 return 0; /* failure to be ASCIZ! */
1244 debug(("mapname is :%s:\n", mapname));
1246 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
1248 debug(("filemap is %p\n", filemap));
1250 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
1254 if ((ourself = get_user_sid()) == NULL) {
1256 debug(("couldn't get user SID\n"));
1258 CloseHandle(filemap);
1262 if ((ourself2 = get_default_sid()) == NULL) {
1264 debug(("couldn't get default SID\n"));
1266 CloseHandle(filemap);
1271 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
1272 OWNER_SECURITY_INFORMATION,
1273 &mapowner, NULL, NULL, NULL,
1274 &psd) != ERROR_SUCCESS)) {
1276 debug(("couldn't get owner info for filemap: %d\n",
1279 CloseHandle(filemap);
1286 LPTSTR ours, ours2, theirs;
1287 ConvertSidToStringSid(mapowner, &theirs);
1288 ConvertSidToStringSid(ourself, &ours);
1289 ConvertSidToStringSid(ourself2, &ours2);
1290 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1291 " theirs=%s\n", ours, ours2, theirs));
1297 if (!EqualSid(mapowner, ourself) &&
1298 !EqualSid(mapowner, ourself2)) {
1299 CloseHandle(filemap);
1303 return 0; /* security ID mismatch! */
1306 debug(("security stuff matched\n"));
1313 debug(("security APIs not present\n"));
1317 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1319 debug(("p is %p\n", p));
1322 for (i = 0; i < 5; i++)
1323 debug(("p[%d]=%02x\n", i,
1324 ((unsigned char *) p)[i]));
1331 CloseHandle(filemap);
1336 return DefWindowProc(hwnd, message, wParam, lParam);
1340 * Fork and Exec the command in cmdline. [DBW]
1342 void spawn_cmd(char *cmdline, char * args, int show)
1344 if (ShellExecute(NULL, _T("open"), cmdline,
1345 args, NULL, show) <= (HINSTANCE) 32) {
1347 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1348 (int)GetLastError());
1349 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1355 * This is a can't-happen stub, since Pageant never makes
1356 * asynchronous agent requests.
1358 void agent_schedule_callback(void (*callback)(void *, void *, int),
1359 void *callback_ctx, void *data, int len)
1361 assert(!"We shouldn't get here");
1364 void cleanup_exit(int code)
1370 int flags = FLAG_SYNCAGENT;
1372 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1376 char *command = NULL;
1379 char **argv, **argstart;
1385 * Determine whether we're an NT system (should have security
1386 * APIs) or a non-NT system (don't do security).
1390 modalfatalbox("Windows refuses to report a version");
1392 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1393 has_security = TRUE;
1395 has_security = FALSE;
1400 * Attempt to get the security API we need.
1402 if (!got_advapi()) {
1404 "Unable to access security APIs. Pageant will\n"
1405 "not run, in case it causes a security breach.",
1406 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1411 "This program has been compiled for Win9X and will\n"
1412 "not run on NT, in case it causes a security breach.",
1413 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1419 * See if we can find our Help file.
1424 * Look for the PuTTY binary (we will enable the saved session
1425 * submenu if we find it).
1428 char b[2048], *p, *q, *r;
1430 GetModuleFileName(NULL, b, sizeof(b) - 16);
1432 p = strrchr(b, '\\');
1433 if (p && p >= r) r = p+1;
1434 q = strrchr(b, ':');
1435 if (q && q >= r) r = q+1;
1436 strcpy(r, "putty.exe");
1437 if ( (fp = fopen(b, "r")) != NULL) {
1438 putty_path = dupstr(b);
1445 * Find out if Pageant is already running.
1447 already_running = agent_exists();
1450 * Initialise the cross-platform Pageant code.
1452 if (!already_running) {
1457 * Initialise storage for short-term passphrase cache.
1459 passphrases = newtree234(NULL);
1462 * Process the command line and add keys as listed on it.
1464 split_into_argv(cmdline, &argc, &argv, &argstart);
1465 for (i = 0; i < argc; i++) {
1466 if (!strcmp(argv[i], "-pgpfp")) {
1469 } else if (!strcmp(argv[i], "-c")) {
1471 * If we see `-c', then the rest of the
1472 * command line should be treated as a
1473 * command to be spawned.
1476 command = argstart[i+1];
1481 Filename *fn = filename_from_str(argv[i]);
1489 * Forget any passphrase that we retained while going over
1490 * command line keyfiles.
1492 forget_passphrases();
1496 if (command[0] == '"')
1497 args = strchr(++command, '"');
1499 args = strchr(command, ' ');
1502 while(*args && isspace(*args)) args++;
1504 spawn_cmd(command, args, show);
1508 * If Pageant was already running, we leave now. If we haven't
1509 * even taken any auxiliary action (spawned a command or added
1512 if (already_running) {
1513 if (!command && !added_keys) {
1514 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1515 MB_ICONERROR | MB_OK);
1522 wndclass.lpfnWndProc = WndProc;
1523 wndclass.cbClsExtra = 0;
1524 wndclass.cbWndExtra = 0;
1525 wndclass.hInstance = inst;
1526 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1527 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1528 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1529 wndclass.lpszMenuName = NULL;
1530 wndclass.lpszClassName = APPNAME;
1532 RegisterClass(&wndclass);
1537 hwnd = CreateWindow(APPNAME, APPNAME,
1538 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1539 CW_USEDEFAULT, CW_USEDEFAULT,
1540 100, 100, NULL, NULL, inst, NULL);
1542 /* Set up a system tray icon */
1545 /* Accelerators used: nsvkxa */
1546 systray_menu = CreatePopupMenu();
1548 session_menu = CreateMenu();
1549 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1550 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1551 (UINT) session_menu, "&Saved Sessions");
1552 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1554 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1556 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1557 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1559 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1560 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1561 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1562 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1563 initial_menuitems_count = GetMenuItemCount(session_menu);
1565 /* Set the default menu item. */
1566 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1568 ShowWindow(hwnd, SW_HIDE);
1571 * Main message loop.
1573 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1574 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1575 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1576 TranslateMessage(&msg);
1577 DispatchMessage(&msg);
1581 /* Clean up the system tray icon */
1583 NOTIFYICONDATA tnid;
1585 tnid.cbSize = sizeof(NOTIFYICONDATA);
1589 Shell_NotifyIcon(NIM_DELETE, &tnid);
1591 DestroyMenu(systray_menu);
1594 if (keypath) filereq_free(keypath);
1596 cleanup_exit(msg.wParam);
1597 return msg.wParam; /* just in case optimiser complains */