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@330: * @param [in] base64 encoded der certificates to write aheinecke@324: * @param [in] write_handle to write to aheinecke@330: * @param [in] remove weather the certificate should be installed or removed aheinecke@324: * aheinecke@324: * @returns true on success, false on failure aheinecke@324: */ aheinecke@324: static bool aheinecke@330: write_instructions(char **certificates, HANDLE write_handle, aheinecke@330: bool remove) aheinecke@324: { aheinecke@324: int i = 0; aheinecke@330: int cHandle = -1; aheinecke@330: FILE *write_stream = NULL; aheinecke@324: aheinecke@330: if (!certificates) aheinecke@324: { aheinecke@324: return true; aheinecke@324: } aheinecke@324: aheinecke@330: cHandle = _open_osfhandle ((intptr_t)write_handle, 0); aheinecke@330: aheinecke@330: if (cHandle == -1) aheinecke@324: { aheinecke@330: ERRORPRINTF ("Failed to open write handle.\n"); aheinecke@330: } aheinecke@330: aheinecke@330: write_stream = _fdopen(cHandle, "w"); aheinecke@330: for (i = 0; certificates[i]; i++) aheinecke@330: { aheinecke@330: int ret = 0; aheinecke@330: DEBUGPRINTF("Writing \n"); aheinecke@330: if (remove) aheinecke@330: ret = fprintf (write_stream, "R:%s\n", certificates[i]); aheinecke@330: else aheinecke@330: ret = fprintf (write_stream, "I:%s\n", certificates[i]); aheinecke@330: aheinecke@330: if (ret <= 0) aheinecke@324: { aheinecke@330: DEBUGPRINTF ("Failed to write everything.\n"); aheinecke@330: break; aheinecke@324: } aheinecke@324: } aheinecke@330: aheinecke@330: DEBUGPRINTF("Write done\n"); 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@330: siStartInfo.hStdInput = h_stdin_child_r; 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@330: CREATE_UNICODE_ENVIRONMENT, /* Creation flags. */ 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@330: if (!write_instructions (to_install, h_stdin_child_w, false)) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to write install instructions.\n"); aheinecke@324: goto closepipes; aheinecke@324: } aheinecke@330: if (!write_instructions (to_remove, h_stdin_child_w, true)) 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@330: CloseHandle (piProcInfo.hProcess); aheinecke@324: if (piProcInfo.hThread) aheinecke@330: CloseHandle (piProcInfo.hThread); aheinecke@324: aheinecke@324: closepipes: aheinecke@330: if (lpEnvironment) aheinecke@330: DestroyEnvironmentBlock (lpEnvironment); aheinecke@324: if (h_stdin_child_w) aheinecke@330: CloseHandle (h_stdin_child_w); aheinecke@324: if (h_stdin_child_r) aheinecke@330: CloseHandle (h_stdin_child_r); aheinecke@324: if (h_stdout_child_w) aheinecke@330: CloseHandle (h_stdout_child_w); aheinecke@324: if (h_stdout_child_r) aheinecke@330: 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