aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ aheinecke@324: #ifdef WIN32 aheinecke@324: aheinecke@324: /* @file aheinecke@324: @brief Windows implementation of nssstore process control. andre@360: andre@360: The windows process will write an instructions file for andre@360: the mozilla process into the current users temp directory andre@360: (%APPDATA%/Local/Temp/) and start the NSS installation process to andre@360: exectute those instructions. If the current process is elevated andre@360: the NSS process is run with a restricted token. andre@360: The execution of the mozilla process is not monitored. andre@360: You have to refer to the system log to check which certificates were andre@360: installed / removed by it. andre@360: andre@360: If the installation process is running elevated it andre@360: will create the file in the ProgramData directory in andre@360: a subdirectory with the defined application name. andre@360: %PROGRAMDATA%/$APPLICATION_NAME andre@360: with the file name: andre@360: current_selection.txt andre@360: The folder will have restricted permissions so andre@360: that only Administrators are allowed to access it. andre@360: andre@360: Additionally if this process is Elevated it also starts the andre@360: NSS installation process in default profile mode once to change andre@360: the default NSS certificate databases for new profiles. andre@360: andre@360: The process then adds a new RunOnce registry key andre@360: for each user on the system that executes the NSS installation andre@360: process on login to make sure it is launched once in the andre@360: security context of that user. aheinecke@324: */ aheinecke@324: aheinecke@324: #include andre@670: #include aheinecke@324: #include aheinecke@324: #include aheinecke@324: #include aheinecke@329: #include andre@360: #include andre@360: #include andre@360: #include aheinecke@324: aheinecke@324: #include "logging.h" aheinecke@324: #include "util.h" andre@360: #include "strhelp.h" aheinecke@324: andre@360: #ifndef APPNAME andre@360: #define APPNAME L"cinst" andre@360: #endif aheinecke@324: andre@360: #ifndef SELECTION_FILE_NAME andre@360: #define SELECTION_FILE_NAME L"currently_selected.txt" andre@360: #endif andre@360: andre@668: /**@def The maximum time to wait for the NSS Process */ andre@360: #define PROCESS_TIMEOUT 30000 aheinecke@324: andre@668: /**@def The registry key to look for user profile directories */ andre@668: #define PROFILE_LIST L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList" andre@668: 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@489: * @param [in] certificates base64 encoded der certificate to write aheinecke@489: * @param [in] write_handle 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@489: bool retval = false; aheinecke@324: int i = 0; aheinecke@489: const char *line_end = "\r\n"; aheinecke@489: char *line_start = NULL; aheinecke@324: aheinecke@330: if (!certificates) aheinecke@324: { aheinecke@324: return true; aheinecke@324: } aheinecke@324: aheinecke@489: line_start = remove ? "R:" : "I:"; aheinecke@330: aheinecke@330: for (i = 0; certificates[i]; i++) aheinecke@330: { aheinecke@489: DWORD written = 0; aheinecke@489: DWORD inst_len = strlen (certificates[i]); aheinecke@489: retval = WriteFile (write_handle, (LPCVOID) line_start, 2, &written, NULL); aheinecke@489: if (!retval) aheinecke@324: { aheinecke@489: PRINTLASTERROR ("Failed to write line start\n"); aheinecke@489: return false; aheinecke@489: } aheinecke@489: if (written != 2) aheinecke@489: { aheinecke@489: ERRORPRINTF ("Failed to write line start\n"); aheinecke@489: retval = false; aheinecke@489: return false; aheinecke@489: } aheinecke@489: written = 0; aheinecke@489: retval = WriteFile (write_handle, (LPCVOID) certificates[i], inst_len, &written, NULL); aheinecke@489: if (!retval) aheinecke@489: { aheinecke@489: PRINTLASTERROR ("Failed to write certificate\n"); aheinecke@489: return false; aheinecke@489: } aheinecke@489: if (inst_len != written) aheinecke@489: { aheinecke@489: ERRORPRINTF ("Failed to write everything\n"); aheinecke@489: retval = false; aheinecke@489: return false; aheinecke@489: } aheinecke@489: written = 0; aheinecke@489: retval = WriteFile (write_handle, (LPCVOID) line_end, 2, &written, NULL); aheinecke@489: if (!retval) aheinecke@489: { aheinecke@489: PRINTLASTERROR ("Failed to write line end\n"); aheinecke@489: return false; aheinecke@489: } aheinecke@489: if (written != 2) aheinecke@489: { aheinecke@489: ERRORPRINTF ("Failed to write full line end\n"); aheinecke@489: retval = false; aheinecke@489: return false; aheinecke@324: } aheinecke@324: } aheinecke@324: return true; aheinecke@324: } aheinecke@324: andre@668: /**@brief Get the path to all users default registry hive andre@668: * andre@668: * Enumerates the keys in #PROFILE_LIST and retuns a andre@668: * strv array with the utf-8 encoded paths to their suggested andre@668: * registry hive location. andre@668: * andre@668: * Users with an SID not starting with S-1-5-21- are ignored andre@668: * as is the current user. andre@668: * andre@668: * Use strv_free to free that array. andre@668: * andre@668: * @returns a newly allocated strv of the paths to the registry hives or NULL andre@668: */ andre@668: andre@668: static char** andre@668: locate_other_hives() andre@668: { andre@668: HKEY profile_list = NULL; andre@668: int ret = 0; andre@668: DWORD index = 0, andre@668: key_len = 257; andre@668: /* According to andre@668: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724872%28v=vs.85%29.aspx andre@668: a registry key is limited to 255 characters. But according to andre@668: http://www.sepago.de/e/holger/2010/07/20/how-long-can-a-registry-key-name-really-be andre@668: the actual limit is 256 + \0 thus we create a buffer for 257 wchar_t's*/ andre@670: wchar_t key_name[257], andre@670: *current_user_sid = NULL; andre@668: char **retval = NULL; andre@668: bool error = true; andre@670: PSID current_user = NULL; andre@668: andre@668: ret = RegOpenKeyExW (HKEY_LOCAL_MACHINE, PROFILE_LIST, 0, andre@668: KEY_READ, &profile_list); andre@668: if (ret != ERROR_SUCCESS) andre@668: { andre@668: ERRORPRINTF ("Failed to open profile list. Error: %i", ret); andre@668: return NULL; andre@668: } andre@668: andre@670: /* Obtain the current user sid to prevent it from being returned. */ andre@670: current_user = get_process_owner (GetCurrentProcess()); andre@670: andre@670: if (!current_user) andre@670: { andre@670: ERRORPRINTF ("Failed to get the current user."); andre@670: goto done; andre@670: } andre@670: andre@670: if (!ConvertSidToStringSidW (current_user, ¤t_user_sid)) andre@670: { andre@670: PRINTLASTERROR ("Failed to convert sid to string."); andre@670: goto done; andre@670: } andre@670: andre@668: while ((ret = RegEnumKeyExW (profile_list, index++, andre@668: key_name, &key_len, andre@668: NULL, NULL, NULL, NULL)) == ERROR_SUCCESS) andre@668: { andre@668: if (key_len == 257) andre@668: { andre@668: ERRORPRINTF ("Registry key too long."); andre@668: goto done; andre@668: } andre@668: andre@668: /* Reset key_len to buffer size */ andre@668: key_len = 257; andre@668: andre@670: if (wcsncmp (L"S-1-5-21-", key_name, 9) != 0 || andre@670: wcscmp (current_user_sid, key_name) == 0) andre@668: { andre@670: /* S-1-5-21 is the well known prefix for local users. Skip all andre@670: others and the current user*/ andre@668: continue; andre@668: } andre@670: andre@670: DEBUGPRINTF ("Key : %S", key_name); andre@668: } andre@668: andre@668: if (ret != ERROR_NO_MORE_ITEMS) andre@668: { andre@668: ERRORPRINTF ("Failed to enumeratre profile list. Error: %i", ret); andre@668: goto done; andre@668: } andre@668: andre@668: done: andre@670: xfree (current_user); andre@670: andre@668: RegCloseKey (profile_list); andre@668: andre@670: if (current_user_sid) andre@670: { andre@670: LocalFree (current_user_sid); andre@670: } andre@670: andre@668: if (error) andre@668: { andre@668: strv_free (retval); andre@668: retval = NULL; andre@668: } andre@668: andre@668: return retval; andre@668: } andre@668: andre@668: /**@brief Register NSS process as runOnce for other users andre@668: * andre@668: * Loads the registry hives of other users on the system and andre@668: * adds a RunOnce registry key to start the NSS process to andre@668: * install the current selection on their next login. andre@668: * andre@668: * This should avoid conflicts with their firefox / thunderbird andre@668: * while making the certificates available for their applications. andre@668: * andre@668: * This function needs SE_BACKUP_NAME and SE_RESTORE_NAME andre@668: * privileges. andre@668: * andre@668: * @param [in] selection_file filename of the file containing andre@668: * the users install / remove selection. andre@668: */ andre@668: void andre@668: register_proccesses_for_others (wchar_t *selection_file) andre@668: { andre@668: char **hives = locate_other_hives(); andre@668: andre@668: strv_free (hives); andre@668: printf("Selection file %S", selection_file); andre@668: } andre@668: aheinecke@324: /**@brief Start the process to install / remove aheinecke@324: * andre@360: * Starts the NSS installation process for the current user aheinecke@324: * andre@360: * @param [in] selection_file filename of the file containing andre@668: * the users install / remove selection. aheinecke@324: * aheinecke@324: * @returns true on success, false on error. aheinecke@324: */ aheinecke@324: static bool andre@360: start_procces_for_user (wchar_t *selection_file) aheinecke@324: { andre@360: HANDLE hToken = NULL;/*, andre@360: hChildToken = NULL;*/ aheinecke@324: /* TODO get this as absolute path based on current module location */ andre@360: LPWSTR lpApplicationName = L"mozilla.exe", andre@360: lpCommandLine; aheinecke@324: PROCESS_INFORMATION piProcInfo = {0}; aheinecke@324: STARTUPINFOW siStartInfo = {0}; aheinecke@324: BOOL success = FALSE; andre@360: size_t cmd_line_len = 0; aheinecke@324: andre@360: if (!selection_file) aheinecke@324: { andre@360: ERRORPRINTF ("Invalid call\n"); andre@360: return false; aheinecke@324: } aheinecke@324: aheinecke@324: /* set up handles. stdin and stdout go to the same stdout*/ aheinecke@324: siStartInfo.cb = sizeof (STARTUPINFO); andre@360: andre@360: if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) andre@360: { andre@360: PRINTLASTERROR("Failed to get current handle."); andre@360: return false; andre@360: } andre@360: /* TODO! if (is_elevated()) andre@360: restrict token -> hChildToken andre@360: */ andre@360: andre@392: cmd_line_len = wcslen (lpApplicationName) + wcslen(selection_file) + 2; andre@360: lpCommandLine = xmalloc (cmd_line_len * sizeof(wchar_t)); andre@360: aheinecke@363: wcscpy_s (lpCommandLine, cmd_line_len, lpApplicationName); andre@392: wcscpy_s (lpCommandLine, cmd_line_len, L" "); aheinecke@363: wcscat_s (lpCommandLine, cmd_line_len, selection_file); aheinecke@324: andre@392: DEBUGPRINTF ("Starting %S with command line %S\n", lpApplicationName, lpCommandLine); andre@392: aheinecke@324: success = CreateProcessAsUserW (hToken, aheinecke@324: lpApplicationName, andre@360: lpCommandLine, /* Commandline */ aheinecke@324: NULL, /* Process attributes. Take hToken */ aheinecke@324: NULL, /* Thread attribues. Take hToken */ andre@360: FALSE, /* Inherit Handles */ andre@360: 0, /* Creation flags. */ andre@360: NULL, /* Inherit environment */ aheinecke@324: NULL, /* Current working directory */ aheinecke@324: &siStartInfo, aheinecke@324: &piProcInfo); andre@360: xfree (lpCommandLine); aheinecke@324: if (!success) aheinecke@324: { aheinecke@324: PRINTLASTERROR ("Failed to create process.\n"); andre@360: return false; aheinecke@324: } 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"); andre@360: if (piProcInfo.hProcess) andre@360: CloseHandle (piProcInfo.hProcess); andre@360: if (piProcInfo.hThread) andre@360: CloseHandle (piProcInfo.hThread); andre@360: return false; aheinecke@324: } aheinecke@324: if (piProcInfo.hProcess) aheinecke@330: CloseHandle (piProcInfo.hProcess); aheinecke@324: if (piProcInfo.hThread) aheinecke@330: CloseHandle (piProcInfo.hThread); andre@360: return true; andre@360: } aheinecke@324: andre@360: /**@brief Create a directory with restricted access rights andre@360: * andre@360: * This creates a security attributes structure that restricts andre@360: * write access to the Administrators group but allows everyone to read files andre@360: * in that directory. aheinecke@363: * Basically a very complicated version of mkdir path -m 644 andre@360: * andre@360: * If the directory exists the permissions of that directory are checked if andre@360: * they are acceptable and true or false is returned accordingly. andre@360: * andre@360: * Code based on msdn example: andre@360: * http://msdn.microsoft.com/en-us/library/windows/desktop/aa446595%28v=vs.85%29.aspx andre@360: * andre@360: * @param[in] path Path of the directory to create andre@360: * andre@360: * @returns true on success of if the directory exists, false on error andre@360: */ andre@360: bool andre@360: create_restricted_directory (LPWSTR path) andre@360: { andre@360: bool retval = false; andre@360: PSID everyone_SID = NULL, andre@360: admin_SID = NULL; andre@360: PACL access_control_list = NULL; andre@360: PSECURITY_DESCRIPTOR descriptor = NULL; andre@360: EXPLICIT_ACCESS explicit_access[2]; andre@360: SID_IDENTIFIER_AUTHORITY world_identifier = {SECURITY_WORLD_SID_AUTHORITY}, andre@360: admin_identifier = {SECURITY_NT_AUTHORITY}; andre@360: SECURITY_ATTRIBUTES security_attributes; andre@360: andre@360: ZeroMemory(&security_attributes, sizeof(security_attributes)); andre@360: ZeroMemory(&explicit_access, 2 * sizeof(EXPLICIT_ACCESS)); andre@360: andre@360: /* Create a well-known SID for the Everyone group. */ andre@360: if(!AllocateAndInitializeSid(&world_identifier, /* top-level identifier */ andre@360: 1, /* subauthorties count */ andre@360: SECURITY_WORLD_RID, /* Only one authority */ andre@360: 0, 0, 0, 0, 0, 0, 0, /* No other authorities*/ andre@360: &everyone_SID)) andre@360: { andre@360: PRINTLASTERROR ("Failed to allocate world sid.\n"); andre@360: return false; andre@360: } andre@360: andre@360: /* Initialize the first EXPLICIT_ACCESS structure for an ACE. andre@360: to allow everyone read access */ andre@360: explicit_access[0].grfAccessPermissions = GENERIC_READ; /* Give read access */ andre@360: explicit_access[0].grfAccessMode = SET_ACCESS; /* Overwrite other access for all users */ andre@360: explicit_access[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; /* make it stick */ andre@360: explicit_access[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; andre@360: explicit_access[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; andre@360: explicit_access[0].Trustee.ptstrName = (LPTSTR) everyone_SID; andre@360: andre@360: /* Create the SID for the BUILTIN\Administrators group. */ andre@360: if(!AllocateAndInitializeSid(&admin_identifier, andre@360: 2, andre@360: SECURITY_BUILTIN_DOMAIN_RID, /*BUILTIN\ */ andre@360: DOMAIN_ALIAS_RID_ADMINS, /*\Administrators */ andre@360: 0, 0, 0, 0, 0, 0, /* No other */ andre@360: &admin_SID)) andre@360: { andre@360: PRINTLASTERROR ("Failed to allocate admin sid."); andre@360: goto done; andre@360: } andre@360: andre@360: /* explicit_access[1] grants admins full rights for this object and inherits andre@360: it to the children */ andre@360: explicit_access[1].grfAccessPermissions = GENERIC_ALL; andre@360: explicit_access[1].grfAccessMode = SET_ACCESS; andre@360: explicit_access[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; andre@360: explicit_access[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; andre@360: explicit_access[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; andre@360: explicit_access[1].Trustee.ptstrName = (LPTSTR) admin_SID; andre@360: andre@360: /* Set up the ACL structure. */ andre@360: if (ERROR_SUCCESS != SetEntriesInAcl(2, explicit_access, NULL, &access_control_list)) andre@360: { andre@360: PRINTLASTERROR ("Failed to set up Acl."); andre@360: goto done; andre@360: } andre@360: andre@360: /* Initialize a security descriptor */ andre@360: descriptor = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, andre@360: SECURITY_DESCRIPTOR_MIN_LENGTH); andre@360: if (descriptor == NULL) andre@360: { andre@360: PRINTLASTERROR("Failed to allocate descriptor."); andre@360: goto done; andre@360: } andre@360: andre@360: if (!InitializeSecurityDescriptor(descriptor, andre@360: SECURITY_DESCRIPTOR_REVISION)) andre@360: { andre@360: PRINTLASTERROR("Failed to initialize descriptor."); andre@360: goto done; andre@360: } andre@360: andre@360: /* Now we add the ACL to the the descriptor */ andre@360: if (!SetSecurityDescriptorDacl(descriptor, andre@360: TRUE, /* bDaclPresent flag */ andre@360: access_control_list, andre@360: FALSE)) /* not a default DACL */ andre@360: { andre@360: PRINTLASTERROR("Failed to set security descriptor."); andre@360: goto done; andre@360: } andre@360: andre@360: /* Finally set up the security attributes structure */ andre@360: security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES); andre@360: security_attributes.lpSecurityDescriptor = descriptor; andre@360: security_attributes.bInheritHandle = FALSE; andre@360: andre@360: /* Use the security attributes to create the directory */ andre@360: if (!CreateDirectoryW(path, &security_attributes)) andre@360: { andre@360: DWORD err = GetLastError(); andre@360: if (err == ERROR_ALREADY_EXISTS) andre@360: { andre@360: /* Verify that the directory has the correct rights */ andre@360: // TODO andre@360: retval = true; andre@360: goto done; andre@360: } andre@360: ERRORPRINTF ("Failed to create directory. Err: %lu", err); andre@360: } andre@360: retval = true; andre@360: andre@360: done: andre@360: andre@360: if (everyone_SID) andre@360: FreeSid(everyone_SID); andre@360: if (admin_SID) andre@360: FreeSid(admin_SID); andre@360: if (access_control_list) andre@360: LocalFree(access_control_list); andre@360: if (descriptor) andre@360: LocalFree(descriptor); aheinecke@324: aheinecke@324: return retval; aheinecke@324: } aheinecke@324: andre@360: /**@brief Writes the selection file containing the instructions andre@360: * andre@360: * If the process is running elevated the instructions are andre@360: * written to the global ProgramData directory otherwise andre@360: * they are written in the temporary directory of the current user. andre@360: * andre@360: * If the return value is not NULL it needs to be freed by the caller. andre@360: * The returned path will contain backslashes as directory seperators. andre@360: * andre@360: * @param[in] to_install Certificates that should be installed andre@360: * @param[in] to_remove Certificates that should be removed andre@360: * @returns pointer to the absolute filename of the selection file or NULL andre@360: */ andre@360: wchar_t * andre@360: write_selection_file (char **to_install, char **to_remove) andre@360: { andre@360: wchar_t *folder_name = NULL, andre@360: *path = NULL; andre@360: bool elevated = is_elevated(); andre@360: HRESULT result = E_FAIL; andre@360: HANDLE hFile = NULL; andre@360: size_t path_len; andre@360: andre@360: if (!elevated) andre@360: { andre@360: /* TODO */ andre@360: } andre@360: aheinecke@363: result = SHGetKnownFolderPath (&FOLDERID_ProgramData, /* Get program data dir */ andre@360: KF_FLAG_CREATE | /* Create if it does not exist */ andre@360: KF_FLAG_INIT, /* Initialize it if created */ aheinecke@363: INVALID_HANDLE_VALUE, /* Get it for the default user */ andre@360: &folder_name); andre@360: andre@360: if (result != S_OK) andre@360: { andre@360: PRINTLASTERROR ("Failed to get folder path"); andre@360: return NULL; andre@360: } andre@360: andre@360: path_len = wcslen (folder_name) + wcslen (APPNAME) + 2; /* path + dirsep + \0 */ andre@360: path_len += wcslen (SELECTION_FILE_NAME) + 1; /* filename + dirsep */ andre@360: andre@360: if (path_len >= MAX_PATH) andre@360: { andre@360: /* We could go and use the full 32,767 characters but this andre@360: should be a very weird setup if this is neccessary. */ andre@360: ERRORPRINTF ("Path too long.\n"); andre@360: return NULL; andre@360: } andre@360: andre@360: path = xmalloc (path_len * sizeof (wchar_t)); aheinecke@363: if (wcscpy_s (path, path_len, folder_name) != 0) andre@360: { andre@360: ERRORPRINTF ("Failed to copy folder name.\n"); aheinecke@363: andre@360: CoTaskMemFree (folder_name); aheinecke@363: andre@360: return NULL; andre@360: } andre@360: andre@360: CoTaskMemFree (folder_name); andre@360: aheinecke@363: if (wcscat_s (path, path_len, L"\\") != 0) andre@360: { andre@360: ERRORPRINTF ("Failed to cat dirsep.\n"); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: aheinecke@363: if (wcscat_s (path, path_len, APPNAME) != 0) andre@360: { andre@360: ERRORPRINTF ("Failed to cat appname.\n"); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: andre@360: /* Security: if someone has created this directory before andre@360: it might be a symlink to another place that a users andre@360: wants us to grant read access to or makes us overwrite andre@360: something */ andre@360: if(!create_restricted_directory (path)) andre@360: { andre@360: ERRORPRINTF ("Failed to create directory\n"); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: aheinecke@363: if (wcscat_s (path, path_len, L"\\") != 0) andre@360: { andre@360: ERRORPRINTF ("Failed to cat dirsep.\n"); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: aheinecke@363: if (wcscat_s (path, path_len, SELECTION_FILE_NAME) != 0) andre@360: { andre@360: ERRORPRINTF ("Failed to cat filename.\n"); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: andre@360: hFile = CreateFileW(path, andre@360: GENERIC_WRITE, andre@360: 0, /* don't share */ andre@360: NULL, /* use the security attributes from the folder */ aheinecke@489: OPEN_ALWAYS | TRUNCATE_EXISTING, andre@360: 0, andre@360: NULL); andre@360: aheinecke@502: if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND) aheinecke@502: { aheinecke@502: hFile = CreateFileW(path, aheinecke@502: GENERIC_WRITE, aheinecke@502: 0, /* don't share */ aheinecke@502: NULL, /* use the security attributes from the folder */ aheinecke@502: CREATE_NEW, aheinecke@502: 0, aheinecke@502: NULL); aheinecke@502: } andre@360: if (hFile == INVALID_HANDLE_VALUE) andre@360: { aheinecke@502: PRINTLASTERROR ("Failed to create file\n"); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: if (!write_instructions (to_install, hFile, false)) andre@360: { andre@360: ERRORPRINTF ("Failed to write install instructions.\n"); andre@360: CloseHandle(hFile); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: if (!write_instructions (to_remove, hFile, true)) andre@360: { andre@360: ERRORPRINTF ("Failed to write remove instructions.\n"); andre@360: CloseHandle(hFile); andre@360: xfree(path); andre@360: return NULL; andre@360: } andre@360: CloseHandle(hFile); andre@360: andre@360: return path; andre@360: } andre@360: aheinecke@324: int aheinecke@324: write_stores_nss (char **to_install, char **to_remove) aheinecke@324: { andre@360: wchar_t *selection_file_name = NULL; aheinecke@324: andre@360: selection_file_name = write_selection_file (to_install, to_remove); andre@360: if (!selection_file_name) andre@360: { andre@360: ERRORPRINTF ("Failed to write instructions.\n"); andre@360: return -1; andre@360: } andre@360: andre@360: DEBUGPRINTF ("Wrote selection file. Loc: %S\n", selection_file_name); andre@360: andre@668: if (is_elevated()) andre@668: { andre@668: register_proccesses_for_others (selection_file_name); andre@668: } andre@360: andre@360: if (!start_procces_for_user (selection_file_name)) aheinecke@324: { aheinecke@324: ERRORPRINTF ("Failed to run NSS installation process.\n"); andre@360: xfree(selection_file_name); aheinecke@324: return -1; aheinecke@324: } andre@360: xfree(selection_file_name); aheinecke@324: return 0; aheinecke@324: } aheinecke@324: aheinecke@324: #endif