X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/2faac2e1a455012cbde5fa291ec20b07ca93018a..e35b295164f7ef50373fbb25ddd80a94a2ab0fae:/pageant.c diff --git a/pageant.c b/pageant.c index f8d7ad2d..888dd430 100644 --- a/pageant.c +++ b/pageant.c @@ -3,9 +3,12 @@ */ #include +#ifndef NO_SECURITY #include -#include /* FIXME */ -#include "putty.h" /* FIXME */ +#endif +#include +#include +#include #include "ssh.h" #include "tree234.h" @@ -25,6 +28,8 @@ #define IDM_CLOSE 0x0010 #define IDM_VIEWKEYS 0x0020 +#define IDM_ADDKEY 0x0030 +#define IDM_ABOUT 0x0040 #define APPNAME "Pageant" @@ -37,12 +42,24 @@ #define SSH_AGENTC_ADD_RSA_IDENTITY 7 #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 -HINSTANCE instance; -HWND hwnd; -HWND keylist; -HMENU systray_menu; +extern char ver[]; + +static HINSTANCE instance; +static HWND hwnd; +static HWND keylist; +static HWND aboutbox; +static HMENU systray_menu; -tree234 *rsakeys; +static tree234 *rsakeys; + +static int has_security; +#ifndef NO_SECURITY +typedef DWORD (WINAPI *gsi_fn_t) + (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, + PSID *, PSID *, PACL *, PACL *, + PSECURITY_DESCRIPTOR *); +static gsi_fn_t getsecurityinfo; +#endif /* * We need this to link with the RSA code, because rsaencrypt() @@ -79,16 +96,81 @@ void logevent(char *msg) { #define PASSPHRASE_MAXLEN 512 +struct PassphraseProcStruct { + char *passphrase; + char *comment; +}; + +/* + * Dialog-box function for the Licence box. + */ +static int CALLBACK LicenceProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: + return 1; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 1); + return 0; + } + return 0; + case WM_CLOSE: + EndDialog(hwnd, 1); + return 0; + } + return 0; +} + +/* + * Dialog-box function for the About box. + */ +static int CALLBACK AboutProc (HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: + SetDlgItemText (hwnd, 100, ver); + return 1; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + aboutbox = NULL; + DestroyWindow (hwnd); + return 0; + case 101: + EnableWindow(hwnd, 0); + DialogBox (instance, MAKEINTRESOURCE(214), NULL, LicenceProc); + EnableWindow(hwnd, 1); + SetActiveWindow(hwnd); + return 0; + } + return 0; + case WM_CLOSE: + aboutbox = NULL; + DestroyWindow (hwnd); + return 0; + } + return 0; +} + /* * Dialog-box function for the passphrase box. */ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static char *passphrase; + struct PassphraseProcStruct *p; switch (msg) { case WM_INITDIALOG: - passphrase = (char *)lParam; + SetForegroundWindow(hwnd); + SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + p = (struct PassphraseProcStruct *)lParam; + passphrase = p->passphrase; + if (p->comment) + SetDlgItemText(hwnd, 101, p->comment); *passphrase = 0; return 0; case WM_COMMAND: @@ -118,48 +200,78 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg, } /* + * Update the visible key list. + */ +static void keylist_update(void) { + struct RSAKey *key; + enum234 e; + + if (keylist) { + SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0); + for (key = first234(rsakeys, &e); key; key = next234(&e)) { + char listentry[512], *p; + /* + * Replace two spaces in the fingerprint with tabs, for + * nice alignment in the box. + */ + rsa_fingerprint(listentry, sizeof(listentry), key); + p = strchr(listentry, ' '); if (p) *p = '\t'; + p = strchr(listentry, ' '); if (p) *p = '\t'; + SendDlgItemMessage (keylist, 100, LB_ADDSTRING, + 0, (LPARAM)listentry); + } + SendDlgItemMessage (keylist, 100, LB_SETCURSEL, (WPARAM) -1, 0); + } +} + +/* * This function loads a key from a file and adds it. */ -void add_keyfile(char *filename) { +static void add_keyfile(char *filename) { char passphrase[PASSPHRASE_MAXLEN]; struct RSAKey *key; int needs_pass; int ret; int attempts; + char *comment; + struct PassphraseProcStruct pps; - /* FIXME: we can acquire comment here and use it in dialog */ - needs_pass = rsakey_encrypted(filename, NULL); + needs_pass = rsakey_encrypted(filename, &comment); attempts = 0; - key = malloc(sizeof(*key)); + key = smalloc(sizeof(*key)); + pps.passphrase = passphrase; + pps.comment = comment; do { if (needs_pass) { int dlgret; dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210), NULL, PassphraseProc, - (LPARAM)passphrase); + (LPARAM)&pps); if (!dlgret) { - free(key); + if (comment) sfree(comment); + sfree(key); return; /* operation cancelled */ } } else *passphrase = '\0'; - ret = loadrsakey(filename, key, passphrase); + ret = loadrsakey(filename, key, NULL, passphrase); attempts++; } while (ret == -1); + if (comment) sfree(comment); if (ret == 0) { - MessageBox(NULL, "Couldn't load public key.", APPNAME, + MessageBox(NULL, "Couldn't load private key.", APPNAME, MB_OK | MB_ICONERROR); - free(key); + sfree(key); return; } if (add234(rsakeys, key) != key) - free(key); /* already present, don't waste RAM */ + sfree(key); /* already present, don't waste RAM */ } /* * This is the main agent function that answers messages. */ -void answer_msg(void *msg) { +static void answer_msg(void *msg) { unsigned char *p = msg; unsigned char *ret = msg; int type; @@ -262,12 +374,36 @@ void answer_msg(void *msg) { memcpy(ret+5, response_md5, 16); } break; -#if 0 /* FIXME: implement these */ case SSH_AGENTC_ADD_RSA_IDENTITY: /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ + { + struct RSAKey *key; + char *comment; + key = smalloc(sizeof(struct RSAKey)); + memset(key, 0, sizeof(key)); + p += makekey(p, key, NULL, 1); + p += makeprivate(p, key); + p += ssh1_read_bignum(p, NULL); /* p^-1 mod q */ + p += ssh1_read_bignum(p, NULL); /* p */ + p += ssh1_read_bignum(p, NULL); /* q */ + comment = smalloc(GET_32BIT(p)); + if (comment) { + memcpy(comment, p+4, GET_32BIT(p)); + key->comment = comment; + } + PUT_32BIT(ret, 1); + ret[4] = SSH_AGENT_FAILURE; + if (add234(rsakeys, key) == key) { + keylist_update(); + ret[4] = SSH_AGENT_SUCCESS; + } else { + freersakey(key); + sfree(key); + } + } break; case SSH_AGENTC_REMOVE_RSA_IDENTITY: /* @@ -275,8 +411,23 @@ void answer_msg(void *msg) { * perhaps SSH_AGENT_FAILURE if it wasn't in the list to * start with. */ + { + struct RSAKey reqkey, *key; + + p += makekey(p, &reqkey, NULL, 0); + key = find234(rsakeys, &reqkey, NULL); + freebn(reqkey.exponent); + freebn(reqkey.modulus); + PUT_32BIT(ret, 1); + ret[4] = SSH_AGENT_FAILURE; + if (key) { + del234(rsakeys, key); + keylist_update(); + freersakey(key); + ret[4] = SSH_AGENT_SUCCESS; + } + } break; -#endif default: failure: /* @@ -291,7 +442,7 @@ void answer_msg(void *msg) { /* * Key comparison function for the 2-3-4 tree of RSA keys. */ -int cmpkeys(void *av, void *bv) { +static int cmpkeys(void *av, void *bv) { struct RSAKey *a = (struct RSAKey *)av; struct RSAKey *b = (struct RSAKey *)bv; Bignum am, bm; @@ -326,21 +477,50 @@ static void error(char *s) { } /* + * Prompt for a key file to add, and add it. + */ +static void prompt_add_keyfile(void) { + OPENFILENAME of; + char filename[FILENAME_MAX]; + memset(&of, 0, sizeof(of)); +#ifdef OPENFILENAME_SIZE_VERSION_400 + of.lStructSize = OPENFILENAME_SIZE_VERSION_400; +#else + of.lStructSize = sizeof(of); +#endif + of.hwndOwner = hwnd; + of.lpstrFilter = "All Files\0*\0\0\0"; + of.lpstrCustomFilter = NULL; + of.nFilterIndex = 1; + of.lpstrFile = filename; *filename = '\0'; + of.nMaxFile = sizeof(filename); + of.lpstrFileTitle = NULL; + of.lpstrInitialDir = NULL; + of.lpstrTitle = "Select Private Key File"; + of.Flags = 0; + if (GetOpenFileName(&of)) { + add_keyfile(filename); + keylist_update(); + } +} + +/* * Dialog-box function for the key list box. */ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { enum234 e; struct RSAKey *key; - OPENFILENAME of; - char filename[FILENAME_MAX]; switch (msg) { case WM_INITDIALOG: - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - SendDlgItemMessage (hwnd, 100, LB_ADDSTRING, - 0, (LPARAM) key->comment); - } + keylist = hwnd; + { + static int tabs[2] = {25, 175}; + SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS, 2, + (LPARAM) tabs); + } + keylist_update(); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -352,38 +532,14 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, case 101: /* add key */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { - memset(&of, 0, sizeof(of)); -#ifdef OPENFILENAME_SIZE_VERSION_400 - of.lStructSize = OPENFILENAME_SIZE_VERSION_400; -#else - of.lStructSize = sizeof(of); -#endif - of.hwndOwner = hwnd; - of.lpstrFilter = "All Files\0*\0\0\0"; - of.lpstrCustomFilter = NULL; - of.nFilterIndex = 1; - of.lpstrFile = filename; *filename = '\0'; - of.nMaxFile = sizeof(filename); - of.lpstrFileTitle = NULL; - of.lpstrInitialDir = NULL; - of.lpstrTitle = "Select Public Key File"; - of.Flags = 0; - if (GetOpenFileName(&of)) { - add_keyfile(filename); - } - SendDlgItemMessage(hwnd, 100, LB_RESETCONTENT, 0, 0); - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - SendDlgItemMessage (hwnd, 100, LB_ADDSTRING, - 0, (LPARAM) key->comment); - } - SendDlgItemMessage (hwnd, 100, LB_SETCURSEL, (WPARAM) -1, 0); + prompt_add_keyfile(); } return 0; case 102: /* remove key */ if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0); - if (n == LB_ERR || n == 0) { + if (n == LB_ERR) { MessageBeep(0); break; } @@ -392,12 +548,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, break; del234(rsakeys, key); freersakey(key); free(key); - SendDlgItemMessage(hwnd, 100, LB_RESETCONTENT, 0, 0); - for (key = first234(rsakeys, &e); key; key = next234(&e)) { - SendDlgItemMessage (hwnd, 100, LB_ADDSTRING, - 0, (LPARAM) key->comment); - } - SendDlgItemMessage (hwnd, 100, LB_SETCURSEL, (WPARAM) -1, 0); + keylist_update(); } return 0; } @@ -457,6 +608,23 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } break; + case IDM_ADDKEY: + prompt_add_keyfile(); + break; + case IDM_ABOUT: + if (!aboutbox) { + aboutbox = CreateDialog (instance, MAKEINTRESOURCE(213), + NULL, AboutProc); + ShowWindow (aboutbox, SW_SHOWNORMAL); + /* + * Sometimes the window comes up minimised / hidden + * for no obvious reason. Prevent this. + */ + SetForegroundWindow(aboutbox); + SetWindowPos (aboutbox, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + } + break; } break; case WM_DESTROY: @@ -487,43 +655,51 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, #endif if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) { int rc; - if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE, - GetCurrentProcessId())) == NULL) { +#ifndef NO_SECURITY + if (has_security) { + if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE, + GetCurrentProcessId())) == NULL) { #ifdef DEBUG_IPC - debug(("couldn't get handle for process\r\n")); + debug(("couldn't get handle for process\r\n")); #endif - return 0; - } - if (GetSecurityInfo(proc, SE_KERNEL_OBJECT, - OWNER_SECURITY_INFORMATION, - &procowner, NULL, NULL, NULL, - &psd2) != ERROR_SUCCESS) { + return 0; + } + if (getsecurityinfo(proc, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION, + &procowner, NULL, NULL, NULL, + &psd2) != ERROR_SUCCESS) { #ifdef DEBUG_IPC - debug(("couldn't get owner info for process\r\n")); + debug(("couldn't get owner info for process\r\n")); #endif + CloseHandle(proc); + return 0; /* unable to get security info */ + } CloseHandle(proc); - return 0; /* unable to get security info */ - } - CloseHandle(proc); - if ((rc = GetSecurityInfo(filemap, SE_KERNEL_OBJECT, - OWNER_SECURITY_INFORMATION, - &mapowner, NULL, NULL, NULL, - &psd1) != ERROR_SUCCESS)) { + if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION, + &mapowner, NULL, NULL, NULL, + &psd1) != ERROR_SUCCESS)) { #ifdef DEBUG_IPC - debug(("couldn't get owner info for filemap: %d\r\n", rc)); + debug(("couldn't get owner info for filemap: %d\r\n", rc)); #endif - return 0; - } + return 0; + } +#ifdef DEBUG_IPC + debug(("got security stuff\r\n")); +#endif + if (!EqualSid(mapowner, procowner)) + return 0; /* security ID mismatch! */ #ifdef DEBUG_IPC - debug(("got security stuff\r\n")); + debug(("security stuff matched\r\n")); #endif - if (!EqualSid(mapowner, procowner)) - return 0; /* security ID mismatch! */ + LocalFree(psd1); + LocalFree(psd2); + } else { #ifdef DEBUG_IPC - debug(("security stuff matched\r\n")); + debug(("security APIs not present\r\n")); +#endif + } #endif - LocalFree(psd1); - LocalFree(psd2); p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); #ifdef DEBUG_IPC debug(("p is %p\r\n", p)); @@ -544,6 +720,43 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; + OSVERSIONINFO osi; + HMODULE advapi; + + /* + * Determine whether we're an NT system (should have security + * APIs) or a non-NT system (don't do security). + */ + memset(&osi, 0, sizeof(OSVERSIONINFO)); + osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (GetVersionEx(&osi) && osi.dwPlatformId==VER_PLATFORM_WIN32_NT) { + has_security = TRUE; + } else + has_security = FALSE; + + if (has_security) { +#ifndef NO_SECURITY + /* + * Attempt to ge the security API we need. + */ + advapi = LoadLibrary("ADVAPI32.DLL"); + getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo"); + if (!getsecurityinfo) { + MessageBox(NULL, + "Unable to access security APIs. Pageant will\n" + "not run, in case it causes a security breach.", + "Pageant Fatal Error", MB_ICONERROR | MB_OK); + return 1; + } +#else + MessageBox(NULL, + "This program has been compiled for Win9X and will\n" + "not run on NT, in case it causes a security breach.", + "Pageant Fatal Error", MB_ICONERROR | MB_OK); + return 1; +#endif + } else + advapi = NULL; /* * First bomb out totally if we are already running. @@ -551,6 +764,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { if (FindWindow("Pageant", "Pageant")) { MessageBox(NULL, "Pageant is already running", "Pageant Error", MB_ICONERROR | MB_OK); + if (advapi) FreeLibrary(advapi); return 0; } @@ -604,8 +818,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { DestroyIcon(hicon); systray_menu = CreatePopupMenu(); - AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "View Keys"); - AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "Terminate"); + /* accelerators used: vkxa */ + AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys"); + AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key"); + AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About"); + AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit"); } ShowWindow (hwnd, SW_HIDE); @@ -665,5 +882,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { DestroyMenu(systray_menu); } + if (advapi) FreeLibrary(advapi); exit(msg.wParam); }