aheinecke@324: #ifdef WIN32 aheinecke@324: aheinecke@324: /* @file aheinecke@324: @brief Windows implementation of nssstore process control. aheinecke@324: */ aheinecke@324: aheinecke@324: #include aheinecke@324: #include aheinecke@324: #include aheinecke@324: #include aheinecke@324: #include aheinecke@329: #include aheinecke@324: aheinecke@324: #include "logging.h" aheinecke@324: #include "util.h" aheinecke@324: aheinecke@324: #define PROCESS_TIMEOUT 30000 /* In milliseconds */ aheinecke@324: aheinecke@324: #define BUFSIZE 4096 /* used for reading childs stdout */ aheinecke@324: aheinecke@324: #define PRINTLASTERROR(msg) \ aheinecke@324: char *my_error = getLastErrorMsg(); \ aheinecke@324: if (my_error) { \ aheinecke@329: DEBUGPRINTF(msg " : %s\n", my_error); \ aheinecke@324: ERRORPRINTF(msg" : %s\n", my_error); \ aheinecke@324: free (my_error); \ aheinecke@324: } \ aheinecke@329: DEBUGPRINTF ("Failed to get error information\n"); aheinecke@324: aheinecke@324: /**@brief Write strv of instructions to a handle aheinecke@324: * aheinecke@324: * Writes the null terminated list of instructions to aheinecke@324: * the handle. aheinecke@324: * aheinecke@324: * @param [in] instructions instructions to write aheinecke@324: * @param [in] write_handle to write to aheinecke@324: * aheinecke@324: * @returns true on success, false on failure aheinecke@324: */ aheinecke@324: static bool aheinecke@324: write_instructions(char **instructions, HANDLE write_handle) aheinecke@324: { aheinecke@324: bool retval = false; aheinecke@324: int i = 0; aheinecke@324: const char *line_end = "\n\0"; aheinecke@324: aheinecke@324: if (!instructions) aheinecke@324: { aheinecke@324: return true; aheinecke@324: } aheinecke@324: aheinecke@324: for (i = 0; instructions[i]; i++) aheinecke@324: { aheinecke@324: DWORD written = 0; aheinecke@324: DWORD inst_len = strlen (instructions[i]); aheinecke@324: retval = WriteFile (write_handle, (LPCVOID) instructions[i], inst_len, &written, NULL); aheinecke@324: if (!retval) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Failed to write\n"); aheinecke@324: return false; aheinecke@324: } aheinecke@324: if (inst_len != written) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to write everything\n"); aheinecke@324: retval = false; aheinecke@324: return false; aheinecke@324: } aheinecke@324: written = 0; aheinecke@324: retval = WriteFile (write_handle, (LPCVOID) line_end, 2, &written, NULL); aheinecke@324: if (!retval) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Failed to write line end\n"); aheinecke@324: return false; aheinecke@324: } aheinecke@324: if (inst_len != written) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to write full line end\n"); aheinecke@324: retval = false; aheinecke@324: return false; aheinecke@324: } aheinecke@324: } aheinecke@324: return true; aheinecke@324: } aheinecke@324: aheinecke@324: /**@brief Start the process to install / remove aheinecke@324: * aheinecke@324: * Creates a child process with the Security handle specified in hToken aheinecke@324: * sends the instructions and then waits for the process to finish. aheinecke@324: * aheinecke@324: * If the process is not done in PROCESS_TIMEOUT seconds this assumes an aheinecke@324: * unknown error happened. aheinecke@324: * aheinecke@324: * @param [in] to_install strv of DER encoded certificates to be added. aheinecke@324: * @param [in] to_remove strv of DER encoded certificates to be remvoed. aheinecke@324: * @param [in] hToken handle to the primary token that is used to install aheinecke@324: * certificates. aheinecke@324: * aheinecke@324: * @returns true on success, false on error. aheinecke@324: */ aheinecke@324: static bool aheinecke@324: start_procces_for_user (char **to_install, char **to_remove, aheinecke@324: HANDLE hToken) aheinecke@324: { aheinecke@324: HANDLE h_stdin_child_r = NULL, aheinecke@324: h_stdin_child_w = NULL, aheinecke@324: h_stdout_child_r = NULL, aheinecke@324: h_stdout_child_w = NULL; aheinecke@324: SECURITY_ATTRIBUTES saAttr = {0}; aheinecke@324: /* TODO get this as absolute path based on current module location */ aheinecke@324: LPWSTR lpApplicationName = L"mozilla.exe"; aheinecke@324: PROCESS_INFORMATION piProcInfo = {0}; aheinecke@324: STARTUPINFOW siStartInfo = {0}; aheinecke@324: LPVOID lpEnvironment = NULL; aheinecke@324: BOOL success = FALSE; aheinecke@324: int retval = -1; aheinecke@324: aheinecke@324: saAttr.nLength = sizeof (SECURITY_ATTRIBUTES); aheinecke@324: saAttr.bInheritHandle = TRUE; aheinecke@324: saAttr.lpSecurityDescriptor = NULL; /* Use default */ aheinecke@324: aheinecke@324: /* Create a pipe for the child process's STDIN. */ aheinecke@324: if (!CreatePipe (&h_stdin_child_r, &h_stdin_child_w, &saAttr, 0)) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Create pipe failed.\n"); aheinecke@324: return -1; aheinecke@324: } aheinecke@324: aheinecke@324: /* Create a pipe for the child process's STDOUT. */ aheinecke@324: if (!CreatePipe (&h_stdout_child_r, &h_stdout_child_w, &saAttr, 0)) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Create pipe failed.\n"); aheinecke@324: return -1; aheinecke@324: } aheinecke@324: aheinecke@324: /* Ensure that read/write is properly inherited */ aheinecke@324: if (!SetHandleInformation (h_stdin_child_w, HANDLE_FLAG_INHERIT, 0) || aheinecke@324: !SetHandleInformation (h_stdout_child_r, HANDLE_FLAG_INHERIT, 0)) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("SetHandleInformation failed.\n"); aheinecke@324: goto closepipes; aheinecke@324: } aheinecke@324: aheinecke@324: /* Create the environment for the user */ aheinecke@324: if (!CreateEnvironmentBlock (&lpEnvironment, hToken, FALSE)) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Failed to create the environment.\n"); aheinecke@324: goto closepipes; aheinecke@324: } aheinecke@324: aheinecke@324: /* set up handles. stdin and stdout go to the same stdout*/ aheinecke@324: siStartInfo.cb = sizeof (STARTUPINFO); aheinecke@324: siStartInfo.hStdError = h_stdout_child_w; aheinecke@324: siStartInfo.hStdOutput = h_stdout_child_w; aheinecke@324: siStartInfo.hStdInput = h_stdin_child_w; aheinecke@324: siStartInfo.dwFlags = STARTF_USESTDHANDLES; aheinecke@324: aheinecke@324: success = CreateProcessAsUserW (hToken, aheinecke@324: lpApplicationName, aheinecke@324: NULL, /* Commandline */ aheinecke@324: NULL, /* Process attributes. Take hToken */ aheinecke@324: NULL, /* Thread attribues. Take hToken */ aheinecke@324: TRUE, /* Inherit Handles */ aheinecke@324: 0, /* Creation flags. Use default */ aheinecke@324: lpEnvironment, aheinecke@324: NULL, /* Current working directory */ aheinecke@324: &siStartInfo, aheinecke@324: &piProcInfo); aheinecke@324: if (!success) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Failed to create process.\n"); aheinecke@324: goto closepipes; aheinecke@324: } aheinecke@324: aheinecke@324: if (!write_instructions (to_install, h_stdin_child_w)) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to write install instructions.\n"); aheinecke@324: goto closepipes; aheinecke@324: } aheinecke@324: if (!write_instructions (to_remove, h_stdin_child_w)) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to write remove instructions.\n"); aheinecke@324: goto closepipes; aheinecke@324: } aheinecke@324: aheinecke@324: /* Close the Handle so that the child knows we are finished aheinecke@324: telling it what to do */ aheinecke@324: CloseHandle (h_stdin_child_w); aheinecke@324: h_stdin_child_w = NULL; aheinecke@324: aheinecke@324: #ifndef RELEASE_BUILD aheinecke@324: /* print childs stdout / stderr to parents stdout */ aheinecke@324: { aheinecke@324: HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); aheinecke@324: DWORD read_bytes = 0; aheinecke@324: DWORD written = 0; aheinecke@324: bool success; aheinecke@324: char buf[BUFSIZE]; aheinecke@324: aheinecke@324: if (!stdout_handle || stdout_handle == INVALID_HANDLE_VALUE) aheinecke@324: { aheinecke@324: /* Should not happen */ aheinecke@324: ERRORPRINTF("Failed to get stdout handle.\n"); aheinecke@324: goto closeprocess; aheinecke@324: } aheinecke@324: aheinecke@324: for (;;) aheinecke@324: { aheinecke@324: success = ReadFile (h_stdout_child_r, buf, BUFSIZE, &read_bytes, NULL); aheinecke@324: if(!success || read_bytes == 0) aheinecke@324: break; aheinecke@324: aheinecke@324: success = WriteFile (stdout_handle, buf, aheinecke@324: read_bytes, &written, NULL); aheinecke@324: if (!success || written != read_bytes) aheinecke@324: break; aheinecke@324: } aheinecke@324: } aheinecke@324: #endif aheinecke@324: aheinecke@324: if (WaitForSingleObject (piProcInfo.hProcess, PROCESS_TIMEOUT) != WAIT_OBJECT_0) aheinecke@324: { aheinecke@324: /* Should not happen... */ aheinecke@324: ERRORPRINTF ("Failed to wait for process.\n"); aheinecke@324: } aheinecke@324: aheinecke@324: closeprocess: aheinecke@324: if (piProcInfo.hProcess) aheinecke@324: CloseHandle(piProcInfo.hProcess); aheinecke@324: if (piProcInfo.hThread) aheinecke@324: CloseHandle(piProcInfo.hThread); aheinecke@324: aheinecke@324: closepipes: aheinecke@324: if (h_stdin_child_w) aheinecke@324: CloseHandle(h_stdin_child_w); aheinecke@324: if (h_stdin_child_r) aheinecke@324: CloseHandle(h_stdin_child_r); aheinecke@324: if (h_stdout_child_w) aheinecke@324: CloseHandle(h_stdout_child_w); aheinecke@324: if (h_stdout_child_r) aheinecke@324: CloseHandle(h_stdout_child_r); aheinecke@324: aheinecke@324: return retval; aheinecke@324: } aheinecke@324: aheinecke@324: int aheinecke@324: write_stores_nss (char **to_install, char **to_remove) aheinecke@324: { aheinecke@324: HANDLE hToken = NULL; aheinecke@324: OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken); aheinecke@324: aheinecke@324: /* TODO loop over all users */ aheinecke@324: if (!start_procces_for_user (to_install, to_remove, hToken)) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to run NSS installation process.\n"); aheinecke@324: return -1; aheinecke@324: } aheinecke@324: return 0; aheinecke@324: } aheinecke@324: aheinecke@324: #endif