# HG changeset patch # User Andre Heinecke # Date 1396877690 0 # Node ID eff8e7ce4daea43183259acaa8fe7b49c9788fe8 # Parent 31ba7ed4d50fc14420db11cb7c4353c71d68cf5d Add first compiling implementation of nssstore_win.c diff -r 31ba7ed4d50f -r eff8e7ce4dae cinst/CMakeLists.txt --- a/cinst/CMakeLists.txt Mon Apr 07 13:20:34 2014 +0200 +++ b/cinst/CMakeLists.txt Mon Apr 07 13:34:50 2014 +0000 @@ -7,12 +7,13 @@ set(CINST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/windowsstore.c ${CMAKE_CURRENT_SOURCE_DIR}/nssstore_linux.c + ${CMAKE_CURRENT_SOURCE_DIR}/nssstore_win.c ${CMAKE_CURRENT_SOURCE_DIR}/main.c ) add_executable(cinst ${CINST_SOURCES}) if (WIN32) - set(WIN_EXTRA_LIBS -lcrypt32) + set(WIN_EXTRA_LIBS -lcrypt32 -luserenv) endif(WIN32) target_link_libraries(cinst diff -r 31ba7ed4d50f -r eff8e7ce4dae cinst/main.c --- a/cinst/main.c Mon Apr 07 13:20:34 2014 +0200 +++ b/cinst/main.c Mon Apr 07 13:34:50 2014 +0000 @@ -307,9 +307,7 @@ ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); } #endif -#ifndef WIN32 /* TODO Remove ifdef once it is implemented */ ret = write_stores_nss (to_install, to_remove); -#endif if (ret != 0) { ERRORPRINTF ("Failed to write nss stores"); diff -r 31ba7ed4d50f -r eff8e7ce4dae cinst/nssstore_win.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cinst/nssstore_win.c Mon Apr 07 13:34:50 2014 +0000 @@ -0,0 +1,260 @@ +#ifdef WIN32 + +/* @file + @brief Windows implementation of nssstore process control. +*/ + +#include +#include +#include +#include +#include + +#include "logging.h" +#include "util.h" + +#define PROCESS_TIMEOUT 30000 /* In milliseconds */ + +#define BUFSIZE 4096 /* used for reading childs stdout */ + +#define PRINTLASTERROR(msg) \ + char *my_error = getLastErrorMsg(); \ + if (my_error) { \ + DEBUGMSG(msg " :"); \ + DEBUGMSG(my_error); \ + ERRORPRINTF(msg" : %s\n", my_error); \ + free (my_error); \ + } \ + DEBUGMSG ("Failed to get error information\n"); + +/**@brief Write strv of instructions to a handle +* +* Writes the null terminated list of instructions to +* the handle. +* +* @param [in] instructions instructions to write +* @param [in] write_handle to write to +* +* @returns true on success, false on failure +*/ +static bool +write_instructions(char **instructions, HANDLE write_handle) +{ + bool retval = false; + int i = 0; + const char *line_end = "\n\0"; + + if (!instructions) + { + return true; + } + + for (i = 0; instructions[i]; i++) + { + DWORD written = 0; + DWORD inst_len = strlen (instructions[i]); + retval = WriteFile (write_handle, (LPCVOID) instructions[i], inst_len, &written, NULL); + if (!retval) + { + PRINTLASTERROR ("Failed to write\n"); + return false; + } + if (inst_len != written) + { + ERRORPRINTF ("Failed to write everything\n"); + retval = false; + return false; + } + written = 0; + retval = WriteFile (write_handle, (LPCVOID) line_end, 2, &written, NULL); + if (!retval) + { + PRINTLASTERROR ("Failed to write line end\n"); + return false; + } + if (inst_len != written) + { + ERRORPRINTF ("Failed to write full line end\n"); + retval = false; + return false; + } + } + return true; +} + +/**@brief Start the process to install / remove +* +* Creates a child process with the Security handle specified in hToken +* sends the instructions and then waits for the process to finish. +* +* If the process is not done in PROCESS_TIMEOUT seconds this assumes an +* unknown error happened. +* +* @param [in] to_install strv of DER encoded certificates to be added. +* @param [in] to_remove strv of DER encoded certificates to be remvoed. +* @param [in] hToken handle to the primary token that is used to install +* certificates. +* +* @returns true on success, false on error. +*/ +static bool +start_procces_for_user (char **to_install, char **to_remove, + HANDLE hToken) +{ + HANDLE h_stdin_child_r = NULL, + h_stdin_child_w = NULL, + h_stdout_child_r = NULL, + h_stdout_child_w = NULL; + SECURITY_ATTRIBUTES saAttr = {0}; + /* TODO get this as absolute path based on current module location */ + LPWSTR lpApplicationName = L"mozilla.exe"; + PROCESS_INFORMATION piProcInfo = {0}; + STARTUPINFOW siStartInfo = {0}; + LPVOID lpEnvironment = NULL; + BOOL success = FALSE; + int retval = -1; + + saAttr.nLength = sizeof (SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; /* Use default */ + + /* Create a pipe for the child process's STDIN. */ + if (!CreatePipe (&h_stdin_child_r, &h_stdin_child_w, &saAttr, 0)) + { + PRINTLASTERROR ("Create pipe failed.\n"); + return -1; + } + + /* Create a pipe for the child process's STDOUT. */ + if (!CreatePipe (&h_stdout_child_r, &h_stdout_child_w, &saAttr, 0)) + { + PRINTLASTERROR ("Create pipe failed.\n"); + return -1; + } + + /* Ensure that read/write is properly inherited */ + if (!SetHandleInformation (h_stdin_child_w, HANDLE_FLAG_INHERIT, 0) || + !SetHandleInformation (h_stdout_child_r, HANDLE_FLAG_INHERIT, 0)) + { + PRINTLASTERROR ("SetHandleInformation failed.\n"); + goto closepipes; + } + + /* Create the environment for the user */ + if (!CreateEnvironmentBlock (&lpEnvironment, hToken, FALSE)) + { + PRINTLASTERROR ("Failed to create the environment.\n"); + goto closepipes; + } + + /* set up handles. stdin and stdout go to the same stdout*/ + siStartInfo.cb = sizeof (STARTUPINFO); + siStartInfo.hStdError = h_stdout_child_w; + siStartInfo.hStdOutput = h_stdout_child_w; + siStartInfo.hStdInput = h_stdin_child_w; + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + + success = CreateProcessAsUserW (hToken, + lpApplicationName, + NULL, /* Commandline */ + NULL, /* Process attributes. Take hToken */ + NULL, /* Thread attribues. Take hToken */ + TRUE, /* Inherit Handles */ + 0, /* Creation flags. Use default */ + lpEnvironment, + NULL, /* Current working directory */ + &siStartInfo, + &piProcInfo); + if (!success) + { + PRINTLASTERROR ("Failed to create process.\n"); + goto closepipes; + } + + if (!write_instructions (to_install, h_stdin_child_w)) + { + ERRORPRINTF ("Failed to write install instructions.\n"); + goto closepipes; + } + if (!write_instructions (to_remove, h_stdin_child_w)) + { + ERRORPRINTF ("Failed to write remove instructions.\n"); + goto closepipes; + } + + /* Close the Handle so that the child knows we are finished + telling it what to do */ + CloseHandle (h_stdin_child_w); + h_stdin_child_w = NULL; + +#ifndef RELEASE_BUILD + /* print childs stdout / stderr to parents stdout */ + { + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD read_bytes = 0; + DWORD written = 0; + bool success; + char buf[BUFSIZE]; + + if (!stdout_handle || stdout_handle == INVALID_HANDLE_VALUE) + { + /* Should not happen */ + ERRORPRINTF("Failed to get stdout handle.\n"); + goto closeprocess; + } + + for (;;) + { + success = ReadFile (h_stdout_child_r, buf, BUFSIZE, &read_bytes, NULL); + if(!success || read_bytes == 0) + break; + + success = WriteFile (stdout_handle, buf, + read_bytes, &written, NULL); + if (!success || written != read_bytes) + break; + } + } +#endif + + if (WaitForSingleObject (piProcInfo.hProcess, PROCESS_TIMEOUT) != WAIT_OBJECT_0) + { + /* Should not happen... */ + ERRORPRINTF ("Failed to wait for process.\n"); + } + +closeprocess: + if (piProcInfo.hProcess) + CloseHandle(piProcInfo.hProcess); + if (piProcInfo.hThread) + CloseHandle(piProcInfo.hThread); + +closepipes: + if (h_stdin_child_w) + CloseHandle(h_stdin_child_w); + if (h_stdin_child_r) + CloseHandle(h_stdin_child_r); + if (h_stdout_child_w) + CloseHandle(h_stdout_child_w); + if (h_stdout_child_r) + CloseHandle(h_stdout_child_r); + + return retval; +} + +int +write_stores_nss (char **to_install, char **to_remove) +{ + HANDLE hToken = NULL; + OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken); + + /* TODO loop over all users */ + if (!start_procces_for_user (to_install, to_remove, hToken)) + { + ERRORPRINTF ("Failed to run NSS installation process.\n"); + return -1; + } + return 0; +} + +#endif