2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
26 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
31 #define IDI_MAINICON 200
32 #define IDI_TRAYICON 201
34 #define WM_SYSTRAY (WM_APP + 6)
35 #define WM_SYSTRAY2 (WM_APP + 7)
37 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
39 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
40 * wParam are used by Windows, and should be masked off, so we shouldn't
41 * attempt to store information in them. Hence all these identifiers have
42 * the low 4 bits clear. Also, identifiers should < 0xF000. */
44 #define IDM_CLOSE 0x0010
45 #define IDM_VIEWKEYS 0x0020
46 #define IDM_ADDKEY 0x0030
47 #define IDM_HELP 0x0040
48 #define IDM_ABOUT 0x0050
50 #define APPNAME "Pageant"
52 extern const char ver[];
56 static HMENU systray_menu, session_menu;
57 static int already_running;
59 static char *putty_path;
61 /* CWD for "add key" file requester. */
62 static filereq *keypath = NULL;
64 #define IDM_PUTTY 0x0060
65 #define IDM_SESSIONS_BASE 0x1000
66 #define IDM_SESSIONS_MAX 0x2000
67 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
68 #define PUTTY_DEFAULT "Default%20Settings"
69 static int initial_menuitems_count;
72 * Print a modal (Really Bad) message box and perform a fatal exit.
74 void modalfatalbox(const char *fmt, ...)
80 buf = dupvprintf(fmt, ap);
82 MessageBox(hwnd, buf, "Pageant Fatal Error",
83 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
88 /* Un-munge session names out of the registry. */
89 static void unmungestr(char *in, char *out, int outlen)
92 if (*in == '%' && in[1] && in[2]) {
100 *out++ = (i << 4) + j;
114 static int has_security;
116 struct PassphraseProcStruct {
122 * Dialog-box function for the Licence box.
124 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
125 WPARAM wParam, LPARAM lParam)
129 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
132 switch (LOWORD(wParam)) {
147 * Dialog-box function for the About box.
149 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
150 WPARAM wParam, LPARAM lParam)
155 char *buildinfo_text = buildinfo("\r\n");
156 char *text = dupprintf
157 ("Pageant\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
159 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
160 sfree(buildinfo_text);
161 SetDlgItemText(hwnd, 1000, text);
166 switch (LOWORD(wParam)) {
173 EnableWindow(hwnd, 0);
174 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
175 EnableWindow(hwnd, 1);
176 SetActiveWindow(hwnd);
188 static HWND passphrase_box;
191 * Dialog-box function for the passphrase box.
193 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
194 WPARAM wParam, LPARAM lParam)
196 static char **passphrase = NULL;
197 struct PassphraseProcStruct *p;
201 passphrase_box = hwnd;
205 { /* centre the window */
209 hw = GetDesktopWindow();
210 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
212 (rs.right + rs.left + rd.left - rd.right) / 2,
213 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
214 rd.right - rd.left, rd.bottom - rd.top, TRUE);
217 SetForegroundWindow(hwnd);
218 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
219 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
220 p = (struct PassphraseProcStruct *) lParam;
221 passphrase = p->passphrase;
223 SetDlgItemText(hwnd, 101, p->comment);
224 burnstr(*passphrase);
225 *passphrase = dupstr("");
226 SetDlgItemText(hwnd, 102, *passphrase);
229 switch (LOWORD(wParam)) {
239 case 102: /* edit box */
240 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
241 burnstr(*passphrase);
242 *passphrase = GetDlgItemText_alloc(hwnd, 102);
255 * Warn about the obsolescent key file format.
257 void old_keyfile_warning(void)
259 static const char mbtitle[] = "PuTTY Key File Warning";
260 static const char message[] =
261 "You are loading an SSH-2 private key which has an\n"
262 "old version of the file format. This means your key\n"
263 "file is not fully tamperproof. Future versions of\n"
264 "PuTTY may stop supporting this private key format,\n"
265 "so we recommend you convert your key to the new\n"
268 "You can perform this conversion by loading the key\n"
269 "into PuTTYgen and then saving it again.";
271 MessageBox(NULL, message, mbtitle, MB_OK);
275 * Update the visible key list.
277 void keylist_update(void)
280 struct ssh2_userkey *skey;
284 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
285 for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
286 char listentry[512], *p;
288 * Replace two spaces in the fingerprint with tabs, for
289 * nice alignment in the box.
291 strcpy(listentry, "ssh1\t");
292 p = listentry + strlen(listentry);
293 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
294 p = strchr(listentry, ' ');
297 p = strchr(listentry, ' ');
300 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
301 0, (LPARAM) listentry);
303 for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
308 * For nice alignment in the list box, we would ideally
309 * want every entry to align to the tab stop settings, and
310 * have a column for algorithm name, one for bit count,
311 * one for hex fingerprint, and one for key comment.
313 * Unfortunately, some of the algorithm names are so long
314 * that they overflow into the bit-count field.
315 * Fortunately, at the moment, those are _precisely_ the
316 * algorithm names that don't need a bit count displayed
317 * anyway (because for NIST-style ECDSA the bit count is
318 * mentioned in the algorithm name, and for ssh-ed25519
319 * there is only one possible value anyway). So we fudge
320 * this by simply omitting the bit count field in that
323 * This is fragile not only in the face of further key
324 * types that don't follow this pattern, but also in the
325 * face of font metrics changes - the Windows semantics
326 * for list box tab stops is that \t aligns to the next
327 * one you haven't already exceeded, so I have to guess
328 * when the key type will overflow past the bit-count tab
329 * stop and leave out a tab character. Urgh.
332 p = ssh2_fingerprint(skey->alg, skey->data);
333 listentry = dupprintf("%s\t%s", p, skey->comment);
338 pos += strcspn(listentry + pos, " :");
339 if (listentry[pos] == ':' || !listentry[pos])
341 listentry[pos++] = '\t';
343 if (skey->alg != &ssh_dss && skey->alg != &ssh_rsa) {
345 * Remove the bit-count field, which is between the
346 * first and second \t.
350 while (listentry[pos] && listentry[pos] != '\t')
354 while (listentry[pos] && listentry[pos] != '\t')
357 if ((listentry[outpos] = listentry[pos]) == '\0')
364 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
368 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
372 static void answer_msg(void *msgv)
374 unsigned char *msg = (unsigned char *)msgv;
379 msglen = GET_32BIT(msg);
380 if (msglen > AGENT_MAX_MSGLEN) {
381 reply = pageant_failure_msg(&replylen);
383 reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
384 if (replylen > AGENT_MAX_MSGLEN) {
385 smemclr(reply, replylen);
387 reply = pageant_failure_msg(&replylen);
392 * Windows Pageant answers messages in place, by overwriting the
393 * input message buffer.
395 memcpy(msg, reply, replylen);
396 smemclr(reply, replylen);
400 static void win_add_keyfile(Filename *filename)
404 char *passphrase = NULL;
407 * Try loading the key without a passphrase. (Or rather, without a
408 * _new_ passphrase; pageant_add_keyfile will take care of trying
409 * all the passphrases we've already stored.)
411 ret = pageant_add_keyfile(filename, NULL, &err);
412 if (ret == PAGEANT_ACTION_OK) {
414 } else if (ret == PAGEANT_ACTION_FAILURE) {
419 * OK, a passphrase is needed, and we've been given the key
420 * comment to use in the passphrase prompt.
424 struct PassphraseProcStruct pps;
426 pps.passphrase = &passphrase;
428 dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
429 NULL, PassphraseProc, (LPARAM) &pps);
430 passphrase_box = NULL;
433 goto done; /* operation cancelled */
437 assert(passphrase != NULL);
439 ret = pageant_add_keyfile(filename, passphrase, &err);
440 if (ret == PAGEANT_ACTION_OK) {
442 } else if (ret == PAGEANT_ACTION_FAILURE) {
446 smemclr(passphrase, strlen(passphrase));
452 message_box(err, APPNAME, MB_OK | MB_ICONERROR,
453 HELPCTXID(errors_cantloadkey));
456 smemclr(passphrase, strlen(passphrase));
464 * Prompt for a key file to add, and add it.
466 static void prompt_add_keyfile(void)
469 char *filelist = snewn(8192, char);
471 if (!keypath) keypath = filereq_new();
472 memset(&of, 0, sizeof(of));
474 of.lpstrFilter = FILTER_KEY_FILES;
475 of.lpstrCustomFilter = NULL;
477 of.lpstrFile = filelist;
480 of.lpstrFileTitle = NULL;
481 of.lpstrTitle = "Select Private Key File";
482 of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
483 if (request_file(keypath, &of, TRUE, FALSE)) {
484 if(strlen(filelist) > of.nFileOffset) {
485 /* Only one filename returned? */
486 Filename *fn = filename_from_str(filelist);
490 /* we are returned a bunch of strings, end to
491 * end. first string is the directory, the
492 * rest the filenames. terminated with an
495 char *dir = filelist;
496 char *filewalker = filelist + strlen(dir) + 1;
497 while (*filewalker != '\0') {
498 char *filename = dupcat(dir, "\\", filewalker, NULL);
499 Filename *fn = filename_from_str(filename);
503 filewalker += strlen(filewalker) + 1;
508 pageant_forget_passphrases();
514 * Dialog-box function for the key list box.
516 static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
517 WPARAM wParam, LPARAM lParam)
520 struct ssh2_userkey *skey;
527 { /* centre the window */
531 hw = GetDesktopWindow();
532 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
534 (rs.right + rs.left + rd.left - rd.right) / 2,
535 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
536 rd.right - rd.left, rd.bottom - rd.top, TRUE);
540 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
541 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
544 HWND item = GetDlgItem(hwnd, 103); /* the Help button */
551 static int tabs[] = { 35, 75, 250 };
552 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
553 sizeof(tabs) / sizeof(*tabs),
559 switch (LOWORD(wParam)) {
565 case 101: /* add key */
566 if (HIWORD(wParam) == BN_CLICKED ||
567 HIWORD(wParam) == BN_DOUBLECLICKED) {
568 if (passphrase_box) {
569 MessageBeep(MB_ICONERROR);
570 SetForegroundWindow(passphrase_box);
573 prompt_add_keyfile();
576 case 102: /* remove key */
577 if (HIWORD(wParam) == BN_CLICKED ||
578 HIWORD(wParam) == BN_DOUBLECLICKED) {
583 /* our counter within the array of selected items */
586 /* get the number of items selected in the list */
588 SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
590 /* none selected? that was silly */
591 if (numSelected == 0) {
596 /* get item indices in an array */
597 selectedArray = snewn(numSelected, int);
598 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
599 numSelected, (WPARAM)selectedArray);
601 itemNum = numSelected - 1;
602 rCount = pageant_count_ssh1_keys();
603 sCount = pageant_count_ssh2_keys();
605 /* go through the non-rsakeys until we've covered them all,
606 * and/or we're out of selected items to check. note that
607 * we go *backwards*, to avoid complications from deleting
608 * things hence altering the offset of subsequent items
610 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
611 skey = pageant_nth_ssh2_key(i);
613 if (selectedArray[itemNum] == rCount + i) {
614 pageant_delete_ssh2_key(skey);
615 skey->alg->freekey(skey->data);
621 /* do the same for the rsa keys */
622 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
623 rkey = pageant_nth_ssh1_key(i);
625 if(selectedArray[itemNum] == i) {
626 pageant_delete_ssh1_key(rkey);
633 sfree(selectedArray);
638 if (HIWORD(wParam) == BN_CLICKED ||
639 HIWORD(wParam) == BN_DOUBLECLICKED) {
640 launch_help(hwnd, WINHELP_CTX_pageant_general);
647 int id = ((LPHELPINFO)lParam)->iCtrlId;
648 const char *topic = NULL;
650 case 100: topic = WINHELP_CTX_pageant_keylist; break;
651 case 101: topic = WINHELP_CTX_pageant_addkey; break;
652 case 102: topic = WINHELP_CTX_pageant_remkey; break;
655 launch_help(hwnd, topic);
669 /* Set up a system tray icon */
670 static BOOL AddTrayIcon(HWND hwnd)
676 #ifdef NIM_SETVERSION
678 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
681 tnid.cbSize = sizeof(NOTIFYICONDATA);
683 tnid.uID = 1; /* unique within this systray use */
684 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
685 tnid.uCallbackMessage = WM_SYSTRAY;
686 tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
687 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
689 res = Shell_NotifyIcon(NIM_ADD, &tnid);
691 if (hicon) DestroyIcon(hicon);
696 /* Update the saved-sessions menu. */
697 static void update_sessions(void)
701 TCHAR buf[MAX_PATH + 1];
704 int index_key, index_menu;
709 if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
712 for(num_entries = GetMenuItemCount(session_menu);
713 num_entries > initial_menuitems_count;
715 RemoveMenu(session_menu, 0, MF_BYPOSITION);
720 while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
721 TCHAR session_name[MAX_PATH + 1];
722 unmungestr(buf, session_name, MAX_PATH);
723 if(strcmp(buf, PUTTY_DEFAULT) != 0) {
724 memset(&mii, 0, sizeof(mii));
725 mii.cbSize = sizeof(mii);
726 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
727 mii.fType = MFT_STRING;
728 mii.fState = MFS_ENABLED;
729 mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
730 mii.dwTypeData = session_name;
731 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
739 if(index_menu == 0) {
740 mii.cbSize = sizeof(mii);
741 mii.fMask = MIIM_TYPE | MIIM_STATE;
742 mii.fType = MFT_STRING;
743 mii.fState = MFS_GRAYED;
744 mii.dwTypeData = _T("(No sessions)");
745 InsertMenuItem(session_menu, index_menu, TRUE, &mii);
751 * Versions of Pageant prior to 0.61 expected this SID on incoming
752 * communications. For backwards compatibility, and more particularly
753 * for compatibility with derived works of PuTTY still using the old
754 * Pageant client code, we accept it as an alternative to the one
755 * returned from get_user_sid() in winpgntc.c.
757 PSID get_default_sid(void)
761 PSECURITY_DESCRIPTOR psd = NULL;
762 PSID sid = NULL, copy = NULL, ret = NULL;
764 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
765 GetCurrentProcessId())) == NULL)
768 if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
769 &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
772 sidlen = GetLengthSid(sid);
774 copy = (PSID)smalloc(sidlen);
776 if (!CopySid(sidlen, copy, sid))
779 /* Success. Move sid into the return value slot, and null it out
780 * to stop the cleanup code freeing it. */
796 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
797 WPARAM wParam, LPARAM lParam)
799 static int menuinprogress;
800 static UINT msgTaskbarCreated = 0;
804 msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
807 if (message==msgTaskbarCreated) {
809 * Explorer has been restarted, so the tray icon will
817 if (lParam == WM_RBUTTONUP) {
819 GetCursorPos(&cursorpos);
820 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
821 } else if (lParam == WM_LBUTTONDBLCLK) {
822 /* Run the default menu item. */
823 UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
825 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
829 if (!menuinprogress) {
832 SetForegroundWindow(hwnd);
833 TrackPopupMenu(systray_menu,
834 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
836 wParam, lParam, 0, hwnd, NULL);
842 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
844 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
846 MessageBox(NULL, "Unable to execute PuTTY!",
847 "Error", MB_OK | MB_ICONERROR);
852 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
853 SendMessage(hwnd, WM_CLOSE, 0, 0);
857 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
859 ShowWindow(keylist, SW_SHOWNORMAL);
862 * Sometimes the window comes up minimised / hidden for
863 * no obvious reason. Prevent this. This also brings it
864 * to the front if it's already present (the user
865 * selected View Keys because they wanted to _see_ the
868 SetForegroundWindow(keylist);
869 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
870 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
873 if (passphrase_box) {
874 MessageBeep(MB_ICONERROR);
875 SetForegroundWindow(passphrase_box);
878 prompt_add_keyfile();
882 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
884 ShowWindow(aboutbox, SW_SHOWNORMAL);
886 * Sometimes the window comes up minimised / hidden
887 * for no obvious reason. Prevent this.
889 SetForegroundWindow(aboutbox);
890 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
891 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
895 launch_help(hwnd, WINHELP_CTX_pageant_general);
899 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
901 TCHAR buf[MAX_PATH + 1];
902 TCHAR param[MAX_PATH + 1];
903 memset(&mii, 0, sizeof(mii));
904 mii.cbSize = sizeof(mii);
905 mii.fMask = MIIM_TYPE;
907 mii.dwTypeData = buf;
908 GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
910 strcat(param, mii.dwTypeData);
911 if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param,
912 _T(""), SW_SHOW) <= 32) {
913 MessageBox(NULL, "Unable to execute PuTTY!", "Error",
914 MB_OK | MB_ICONERROR);
932 PSID mapowner, ourself, ourself2;
934 PSECURITY_DESCRIPTOR psd = NULL;
937 cds = (COPYDATASTRUCT *) lParam;
938 if (cds->dwData != AGENT_COPYDATA_ID)
939 return 0; /* not our message, mate */
940 mapname = (char *) cds->lpData;
941 if (mapname[cds->cbData - 1] != '\0')
942 return 0; /* failure to be ASCIZ! */
944 debug(("mapname is :%s:\n", mapname));
946 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
948 debug(("filemap is %p\n", filemap));
950 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
954 if ((ourself = get_user_sid()) == NULL) {
956 debug(("couldn't get user SID\n"));
958 CloseHandle(filemap);
962 if ((ourself2 = get_default_sid()) == NULL) {
964 debug(("couldn't get default SID\n"));
966 CloseHandle(filemap);
970 if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
971 OWNER_SECURITY_INFORMATION,
972 &mapowner, NULL, NULL, NULL,
973 &psd) != ERROR_SUCCESS)) {
975 debug(("couldn't get owner info for filemap: %d\n",
978 CloseHandle(filemap);
984 LPTSTR ours, ours2, theirs;
985 ConvertSidToStringSid(mapowner, &theirs);
986 ConvertSidToStringSid(ourself, &ours);
987 ConvertSidToStringSid(ourself2, &ours2);
988 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
989 " theirs=%s\n", ours, ours2, theirs));
995 if (!EqualSid(mapowner, ourself) &&
996 !EqualSid(mapowner, ourself2)) {
997 CloseHandle(filemap);
1000 return 0; /* security ID mismatch! */
1003 debug(("security stuff matched\n"));
1009 debug(("security APIs not present\n"));
1013 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
1015 debug(("p is %p\n", p));
1018 for (i = 0; i < 5; i++)
1019 debug(("p[%d]=%02x\n", i,
1020 ((unsigned char *) p)[i]));
1027 CloseHandle(filemap);
1032 return DefWindowProc(hwnd, message, wParam, lParam);
1036 * Fork and Exec the command in cmdline. [DBW]
1038 void spawn_cmd(const char *cmdline, const char *args, int show)
1040 if (ShellExecute(NULL, _T("open"), cmdline,
1041 args, NULL, show) <= (HINSTANCE) 32) {
1043 msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
1044 (int)GetLastError());
1045 MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
1051 * This is a can't-happen stub, since Pageant never makes
1052 * asynchronous agent requests.
1054 void agent_schedule_callback(void (*callback)(void *, void *, int),
1055 void *callback_ctx, void *data, int len)
1057 assert(!"We shouldn't get here");
1060 void cleanup_exit(int code)
1066 int flags = FLAG_SYNCAGENT;
1068 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1072 const char *command = NULL;
1075 char **argv, **argstart;
1077 dll_hijacking_protection();
1083 * Determine whether we're an NT system (should have security
1084 * APIs) or a non-NT system (don't do security).
1088 modalfatalbox("Windows refuses to report a version");
1090 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1091 has_security = TRUE;
1093 has_security = FALSE;
1098 * Attempt to get the security API we need.
1100 if (!got_advapi()) {
1102 "Unable to access security APIs. Pageant will\n"
1103 "not run, in case it causes a security breach.",
1104 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1109 "This program has been compiled for Win9X and will\n"
1110 "not run on NT, in case it causes a security breach.",
1111 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1117 * See if we can find our Help file.
1122 * Look for the PuTTY binary (we will enable the saved session
1123 * submenu if we find it).
1126 char b[2048], *p, *q, *r;
1128 GetModuleFileName(NULL, b, sizeof(b) - 16);
1130 p = strrchr(b, '\\');
1131 if (p && p >= r) r = p+1;
1132 q = strrchr(b, ':');
1133 if (q && q >= r) r = q+1;
1134 strcpy(r, "putty.exe");
1135 if ( (fp = fopen(b, "r")) != NULL) {
1136 putty_path = dupstr(b);
1143 * Find out if Pageant is already running.
1145 already_running = agent_exists();
1148 * Initialise the cross-platform Pageant code.
1150 if (!already_running) {
1155 * Process the command line and add keys as listed on it.
1157 split_into_argv(cmdline, &argc, &argv, &argstart);
1158 for (i = 0; i < argc; i++) {
1159 if (!strcmp(argv[i], "-pgpfp")) {
1162 } else if (!strcmp(argv[i], "-restrict-acl") ||
1163 !strcmp(argv[i], "-restrict_acl") ||
1164 !strcmp(argv[i], "-restrictacl")) {
1165 restrict_process_acl();
1166 } else if (!strcmp(argv[i], "-c")) {
1168 * If we see `-c', then the rest of the
1169 * command line should be treated as a
1170 * command to be spawned.
1173 command = argstart[i+1];
1178 Filename *fn = filename_from_str(argv[i]);
1179 win_add_keyfile(fn);
1186 * Forget any passphrase that we retained while going over
1187 * command line keyfiles.
1189 pageant_forget_passphrases();
1193 if (command[0] == '"')
1194 args = strchr(++command, '"');
1196 args = strchr(command, ' ');
1199 while(*args && isspace(*args)) args++;
1201 spawn_cmd(command, args, show);
1205 * If Pageant was already running, we leave now. If we haven't
1206 * even taken any auxiliary action (spawned a command or added
1209 if (already_running) {
1210 if (!command && !added_keys) {
1211 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1212 MB_ICONERROR | MB_OK);
1219 wndclass.lpfnWndProc = WndProc;
1220 wndclass.cbClsExtra = 0;
1221 wndclass.cbWndExtra = 0;
1222 wndclass.hInstance = inst;
1223 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1224 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1225 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1226 wndclass.lpszMenuName = NULL;
1227 wndclass.lpszClassName = APPNAME;
1229 RegisterClass(&wndclass);
1234 hwnd = CreateWindow(APPNAME, APPNAME,
1235 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1236 CW_USEDEFAULT, CW_USEDEFAULT,
1237 100, 100, NULL, NULL, inst, NULL);
1239 /* Set up a system tray icon */
1242 /* Accelerators used: nsvkxa */
1243 systray_menu = CreatePopupMenu();
1245 session_menu = CreateMenu();
1246 AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1247 AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1248 (UINT_PTR) session_menu, "&Saved Sessions");
1249 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1251 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1253 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1254 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1256 AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1257 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1258 AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1259 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1260 initial_menuitems_count = GetMenuItemCount(session_menu);
1262 /* Set the default menu item. */
1263 SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1265 ShowWindow(hwnd, SW_HIDE);
1268 * Main message loop.
1270 while (GetMessage(&msg, NULL, 0, 0) == 1) {
1271 if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1272 !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1273 TranslateMessage(&msg);
1274 DispatchMessage(&msg);
1278 /* Clean up the system tray icon */
1280 NOTIFYICONDATA tnid;
1282 tnid.cbSize = sizeof(NOTIFYICONDATA);
1286 Shell_NotifyIcon(NIM_DELETE, &tnid);
1288 DestroyMenu(systray_menu);
1291 if (keypath) filereq_free(keypath);
1293 cleanup_exit(msg.wParam);
1294 return msg.wParam; /* just in case optimiser complains */