Mercurial > trustbridge
comparison cinst/nssstore_win.c @ 360:c0eac5c8c245
Keep working on nssstore_win and clarify its specification
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Thu, 10 Apr 2014 20:20:02 +0200 |
parents | 1e6d1eab8395 |
children | d10d9bc2e84f |
comparison
equal
deleted
inserted
replaced
359:f6ce186cebc2 | 360:c0eac5c8c245 |
---|---|
1 #ifdef WIN32 | 1 #ifdef WIN32 |
2 | 2 |
3 /* @file | 3 /* @file |
4 @brief Windows implementation of nssstore process control. | 4 @brief Windows implementation of nssstore process control. |
5 | |
6 The windows process will write an instructions file for | |
7 the mozilla process into the current users temp directory | |
8 (%APPDATA%/Local/Temp/) and start the NSS installation process to | |
9 exectute those instructions. If the current process is elevated | |
10 the NSS process is run with a restricted token. | |
11 The execution of the mozilla process is not monitored. | |
12 You have to refer to the system log to check which certificates were | |
13 installed / removed by it. | |
14 | |
15 If the installation process is running elevated it | |
16 will create the file in the ProgramData directory in | |
17 a subdirectory with the defined application name. | |
18 %PROGRAMDATA%/$APPLICATION_NAME | |
19 with the file name: | |
20 current_selection.txt | |
21 The folder will have restricted permissions so | |
22 that only Administrators are allowed to access it. | |
23 | |
24 Additionally if this process is Elevated it also starts the | |
25 NSS installation process in default profile mode once to change | |
26 the default NSS certificate databases for new profiles. | |
27 | |
28 The process then adds a new RunOnce registry key | |
29 for each user on the system that executes the NSS installation | |
30 process on login to make sure it is launched once in the | |
31 security context of that user. | |
5 */ | 32 */ |
6 | 33 |
7 #include <windows.h> | 34 #include <windows.h> |
8 #include <stdio.h> | 35 #include <stdio.h> |
9 #include <strsafe.h> | |
10 #include <stdbool.h> | 36 #include <stdbool.h> |
11 #include <userenv.h> | 37 #include <userenv.h> |
12 #include <io.h> | 38 #include <io.h> |
39 #include <accctrl.h> | |
40 #include <aclapi.h> | |
41 #include <shlobj.h> | |
13 | 42 |
14 #include "logging.h" | 43 #include "logging.h" |
15 #include "util.h" | 44 #include "util.h" |
16 | 45 #include "strhelp.h" |
17 #define PROCESS_TIMEOUT 30000 /* In milliseconds */ | 46 |
18 | 47 #ifndef APPNAME |
19 #define BUFSIZE 4096 /* used for reading childs stdout */ | 48 #define APPNAME L"cinst" |
49 #endif | |
50 | |
51 #ifndef SELECTION_FILE_NAME | |
52 #define SELECTION_FILE_NAME L"currently_selected.txt" | |
53 #endif | |
54 | |
55 #define PROCESS_TIMEOUT 30000 | |
20 | 56 |
21 #define PRINTLASTERROR(msg) \ | 57 #define PRINTLASTERROR(msg) \ |
22 char *my_error = getLastErrorMsg(); \ | 58 char *my_error = getLastErrorMsg(); \ |
23 if (my_error) { \ | 59 if (my_error) { \ |
24 DEBUGPRINTF(msg " : %s\n", my_error); \ | 60 DEBUGPRINTF(msg " : %s\n", my_error); \ |
79 return true; | 115 return true; |
80 } | 116 } |
81 | 117 |
82 /**@brief Start the process to install / remove | 118 /**@brief Start the process to install / remove |
83 * | 119 * |
84 * Creates a child process with the Security handle specified in hToken | 120 * Starts the NSS installation process for the current user |
85 * sends the instructions and then waits for the process to finish. | |
86 * | 121 * |
87 * If the process is not done in PROCESS_TIMEOUT seconds this assumes an | 122 * @param [in] selection_file filename of the file containing |
88 * unknown error happened. | 123 * the users installall / remove selection. |
89 * | |
90 * @param [in] to_install strv of DER encoded certificates to be added. | |
91 * @param [in] to_remove strv of DER encoded certificates to be remvoed. | |
92 * @param [in] hToken handle to the primary token that is used to install | |
93 * certificates. | |
94 * | 124 * |
95 * @returns true on success, false on error. | 125 * @returns true on success, false on error. |
96 */ | 126 */ |
97 static bool | 127 static bool |
98 start_procces_for_user (char **to_install, char **to_remove, | 128 start_procces_for_user (wchar_t *selection_file) |
99 HANDLE hToken) | |
100 { | 129 { |
101 HANDLE h_stdin_child_r = NULL, | 130 HANDLE hToken = NULL;/*, |
102 h_stdin_child_w = NULL, | 131 hChildToken = NULL;*/ |
103 h_stdout_child_r = NULL, | |
104 h_stdout_child_w = NULL; | |
105 SECURITY_ATTRIBUTES saAttr = {0}; | |
106 /* TODO get this as absolute path based on current module location */ | 132 /* TODO get this as absolute path based on current module location */ |
107 LPWSTR lpApplicationName = L"mozilla.exe"; | 133 LPWSTR lpApplicationName = L"mozilla.exe", |
134 lpCommandLine; | |
108 PROCESS_INFORMATION piProcInfo = {0}; | 135 PROCESS_INFORMATION piProcInfo = {0}; |
109 STARTUPINFOW siStartInfo = {0}; | 136 STARTUPINFOW siStartInfo = {0}; |
110 LPVOID lpEnvironment = NULL; | |
111 BOOL success = FALSE; | 137 BOOL success = FALSE; |
112 int retval = -1; | 138 size_t cmd_line_len = 0; |
113 | 139 |
114 saAttr.nLength = sizeof (SECURITY_ATTRIBUTES); | 140 if (!selection_file) |
115 saAttr.bInheritHandle = TRUE; | 141 { |
116 saAttr.lpSecurityDescriptor = NULL; /* Use default */ | 142 ERRORPRINTF ("Invalid call\n"); |
117 | 143 return false; |
118 /* Create a pipe for the child process's STDIN. */ | |
119 if (!CreatePipe (&h_stdin_child_r, &h_stdin_child_w, &saAttr, 0)) | |
120 { | |
121 PRINTLASTERROR ("Create pipe failed.\n"); | |
122 return -1; | |
123 } | |
124 | |
125 /* Create a pipe for the child process's STDOUT. */ | |
126 if (!CreatePipe (&h_stdout_child_r, &h_stdout_child_w, &saAttr, 0)) | |
127 { | |
128 PRINTLASTERROR ("Create pipe failed.\n"); | |
129 return -1; | |
130 } | |
131 | |
132 /* Ensure that read/write is properly inherited */ | |
133 if (!SetHandleInformation (h_stdin_child_w, HANDLE_FLAG_INHERIT, 0) || | |
134 !SetHandleInformation (h_stdout_child_r, HANDLE_FLAG_INHERIT, 0)) | |
135 { | |
136 PRINTLASTERROR ("SetHandleInformation failed.\n"); | |
137 goto closepipes; | |
138 } | |
139 | |
140 /* Create the environment for the user */ | |
141 if (!CreateEnvironmentBlock (&lpEnvironment, hToken, FALSE)) | |
142 { | |
143 PRINTLASTERROR ("Failed to create the environment.\n"); | |
144 goto closepipes; | |
145 } | 144 } |
146 | 145 |
147 /* set up handles. stdin and stdout go to the same stdout*/ | 146 /* set up handles. stdin and stdout go to the same stdout*/ |
148 siStartInfo.cb = sizeof (STARTUPINFO); | 147 siStartInfo.cb = sizeof (STARTUPINFO); |
149 siStartInfo.hStdError = h_stdout_child_w; | 148 |
150 siStartInfo.hStdOutput = h_stdout_child_w; | 149 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) |
151 siStartInfo.hStdInput = h_stdin_child_r; | 150 { |
152 siStartInfo.dwFlags = STARTF_USESTDHANDLES; | 151 PRINTLASTERROR("Failed to get current handle."); |
152 return false; | |
153 } | |
154 /* TODO! if (is_elevated()) | |
155 restrict token -> hChildToken | |
156 */ | |
157 | |
158 cmd_line_len = wcslen (lpApplicationName) + wcslen(selection_file) + 1; | |
159 lpCommandLine = xmalloc (cmd_line_len * sizeof(wchar_t)); | |
160 | |
161 wcscpy (lpCommandLine, lpApplicationName); | |
162 wcscat (lpCommandLine, selection_file); | |
153 | 163 |
154 success = CreateProcessAsUserW (hToken, | 164 success = CreateProcessAsUserW (hToken, |
155 lpApplicationName, | 165 lpApplicationName, |
156 NULL, /* Commandline */ | 166 lpCommandLine, /* Commandline */ |
157 NULL, /* Process attributes. Take hToken */ | 167 NULL, /* Process attributes. Take hToken */ |
158 NULL, /* Thread attribues. Take hToken */ | 168 NULL, /* Thread attribues. Take hToken */ |
159 TRUE, /* Inherit Handles */ | 169 FALSE, /* Inherit Handles */ |
160 CREATE_UNICODE_ENVIRONMENT, /* Creation flags. */ | 170 0, /* Creation flags. */ |
161 lpEnvironment, | 171 NULL, /* Inherit environment */ |
162 NULL, /* Current working directory */ | 172 NULL, /* Current working directory */ |
163 &siStartInfo, | 173 &siStartInfo, |
164 &piProcInfo); | 174 &piProcInfo); |
175 xfree (lpCommandLine); | |
165 if (!success) | 176 if (!success) |
166 { | 177 { |
167 PRINTLASTERROR ("Failed to create process.\n"); | 178 PRINTLASTERROR ("Failed to create process.\n"); |
168 goto closepipes; | 179 return false; |
169 } | 180 } |
170 | |
171 if (!write_instructions (to_install, h_stdin_child_w, false)) | |
172 { | |
173 ERRORPRINTF ("Failed to write install instructions.\n"); | |
174 goto closepipes; | |
175 } | |
176 if (!write_instructions (to_remove, h_stdin_child_w, true)) | |
177 { | |
178 ERRORPRINTF ("Failed to write remove instructions.\n"); | |
179 goto closepipes; | |
180 } | |
181 | |
182 /* Close the Handle so that the child knows we are finished | |
183 telling it what to do */ | |
184 CloseHandle (h_stdin_child_w); | |
185 h_stdin_child_w = NULL; | |
186 | |
187 #ifndef RELEASE_BUILD | |
188 /* print childs stdout / stderr to parents stdout */ | |
189 { | |
190 HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); | |
191 DWORD read_bytes = 0; | |
192 DWORD written = 0; | |
193 bool success; | |
194 char buf[BUFSIZE]; | |
195 | |
196 if (!stdout_handle || stdout_handle == INVALID_HANDLE_VALUE) | |
197 { | |
198 /* Should not happen */ | |
199 ERRORPRINTF("Failed to get stdout handle.\n"); | |
200 goto closeprocess; | |
201 } | |
202 | |
203 for (;;) | |
204 { | |
205 success = ReadFile (h_stdout_child_r, buf, BUFSIZE, &read_bytes, NULL); | |
206 if(!success || read_bytes == 0) | |
207 break; | |
208 | |
209 success = WriteFile (stdout_handle, buf, | |
210 read_bytes, &written, NULL); | |
211 if (!success || written != read_bytes) | |
212 break; | |
213 } | |
214 } | |
215 #endif | |
216 | 181 |
217 if (WaitForSingleObject (piProcInfo.hProcess, PROCESS_TIMEOUT) != WAIT_OBJECT_0) | 182 if (WaitForSingleObject (piProcInfo.hProcess, PROCESS_TIMEOUT) != WAIT_OBJECT_0) |
218 { | 183 { |
219 /* Should not happen... */ | 184 /* Should not happen... */ |
220 ERRORPRINTF ("Failed to wait for process.\n"); | 185 ERRORPRINTF ("Failed to wait for process.\n"); |
221 } | 186 if (piProcInfo.hProcess) |
222 | 187 CloseHandle (piProcInfo.hProcess); |
223 closeprocess: | 188 if (piProcInfo.hThread) |
189 CloseHandle (piProcInfo.hThread); | |
190 return false; | |
191 } | |
224 if (piProcInfo.hProcess) | 192 if (piProcInfo.hProcess) |
225 CloseHandle (piProcInfo.hProcess); | 193 CloseHandle (piProcInfo.hProcess); |
226 if (piProcInfo.hThread) | 194 if (piProcInfo.hThread) |
227 CloseHandle (piProcInfo.hThread); | 195 CloseHandle (piProcInfo.hThread); |
228 | 196 return true; |
229 closepipes: | 197 } |
230 if (lpEnvironment) | 198 |
231 DestroyEnvironmentBlock (lpEnvironment); | 199 /**@brief Create a directory with restricted access rights |
232 if (h_stdin_child_w) | 200 * |
233 CloseHandle (h_stdin_child_w); | 201 * This creates a security attributes structure that restricts |
234 if (h_stdin_child_r) | 202 * write access to the Administrators group but allows everyone to read files |
235 CloseHandle (h_stdin_child_r); | 203 * in that directory. |
236 if (h_stdout_child_w) | 204 * Basically a very complicated vetrsion of mkdir path -m 604 |
237 CloseHandle (h_stdout_child_w); | 205 * |
238 if (h_stdout_child_r) | 206 * If the directory exists the permissions of that directory are checked if |
239 CloseHandle (h_stdout_child_r); | 207 * they are acceptable and true or false is returned accordingly. |
208 * | |
209 * Code based on msdn example: | |
210 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa446595%28v=vs.85%29.aspx | |
211 * | |
212 * @param[in] path Path of the directory to create | |
213 * | |
214 * @returns true on success of if the directory exists, false on error | |
215 */ | |
216 bool | |
217 create_restricted_directory (LPWSTR path) | |
218 { | |
219 bool retval = false; | |
220 PSID everyone_SID = NULL, | |
221 admin_SID = NULL; | |
222 PACL access_control_list = NULL; | |
223 PSECURITY_DESCRIPTOR descriptor = NULL; | |
224 EXPLICIT_ACCESS explicit_access[2]; | |
225 SID_IDENTIFIER_AUTHORITY world_identifier = {SECURITY_WORLD_SID_AUTHORITY}, | |
226 admin_identifier = {SECURITY_NT_AUTHORITY}; | |
227 SECURITY_ATTRIBUTES security_attributes; | |
228 | |
229 ZeroMemory(&security_attributes, sizeof(security_attributes)); | |
230 ZeroMemory(&explicit_access, 2 * sizeof(EXPLICIT_ACCESS)); | |
231 | |
232 /* Create a well-known SID for the Everyone group. */ | |
233 if(!AllocateAndInitializeSid(&world_identifier, /* top-level identifier */ | |
234 1, /* subauthorties count */ | |
235 SECURITY_WORLD_RID, /* Only one authority */ | |
236 0, 0, 0, 0, 0, 0, 0, /* No other authorities*/ | |
237 &everyone_SID)) | |
238 { | |
239 PRINTLASTERROR ("Failed to allocate world sid.\n"); | |
240 return false; | |
241 } | |
242 | |
243 /* Initialize the first EXPLICIT_ACCESS structure for an ACE. | |
244 to allow everyone read access */ | |
245 explicit_access[0].grfAccessPermissions = GENERIC_READ; /* Give read access */ | |
246 explicit_access[0].grfAccessMode = SET_ACCESS; /* Overwrite other access for all users */ | |
247 explicit_access[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; /* make it stick */ | |
248 explicit_access[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; | |
249 explicit_access[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; | |
250 explicit_access[0].Trustee.ptstrName = (LPTSTR) everyone_SID; | |
251 | |
252 /* Create the SID for the BUILTIN\Administrators group. */ | |
253 if(!AllocateAndInitializeSid(&admin_identifier, | |
254 2, | |
255 SECURITY_BUILTIN_DOMAIN_RID, /*BUILTIN\ */ | |
256 DOMAIN_ALIAS_RID_ADMINS, /*\Administrators */ | |
257 0, 0, 0, 0, 0, 0, /* No other */ | |
258 &admin_SID)) | |
259 { | |
260 PRINTLASTERROR ("Failed to allocate admin sid."); | |
261 goto done; | |
262 } | |
263 | |
264 /* explicit_access[1] grants admins full rights for this object and inherits | |
265 it to the children */ | |
266 explicit_access[1].grfAccessPermissions = GENERIC_ALL; | |
267 explicit_access[1].grfAccessMode = SET_ACCESS; | |
268 explicit_access[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; | |
269 explicit_access[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; | |
270 explicit_access[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; | |
271 explicit_access[1].Trustee.ptstrName = (LPTSTR) admin_SID; | |
272 | |
273 /* Set up the ACL structure. */ | |
274 if (ERROR_SUCCESS != SetEntriesInAcl(2, explicit_access, NULL, &access_control_list)) | |
275 { | |
276 PRINTLASTERROR ("Failed to set up Acl."); | |
277 goto done; | |
278 } | |
279 | |
280 /* Initialize a security descriptor */ | |
281 descriptor = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, | |
282 SECURITY_DESCRIPTOR_MIN_LENGTH); | |
283 if (descriptor == NULL) | |
284 { | |
285 PRINTLASTERROR("Failed to allocate descriptor."); | |
286 goto done; | |
287 } | |
288 | |
289 if (!InitializeSecurityDescriptor(descriptor, | |
290 SECURITY_DESCRIPTOR_REVISION)) | |
291 { | |
292 PRINTLASTERROR("Failed to initialize descriptor."); | |
293 goto done; | |
294 } | |
295 | |
296 /* Now we add the ACL to the the descriptor */ | |
297 if (!SetSecurityDescriptorDacl(descriptor, | |
298 TRUE, /* bDaclPresent flag */ | |
299 access_control_list, | |
300 FALSE)) /* not a default DACL */ | |
301 { | |
302 PRINTLASTERROR("Failed to set security descriptor."); | |
303 goto done; | |
304 } | |
305 | |
306 /* Finally set up the security attributes structure */ | |
307 security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES); | |
308 security_attributes.lpSecurityDescriptor = descriptor; | |
309 security_attributes.bInheritHandle = FALSE; | |
310 | |
311 /* Use the security attributes to create the directory */ | |
312 if (!CreateDirectoryW(path, &security_attributes)) | |
313 { | |
314 DWORD err = GetLastError(); | |
315 if (err == ERROR_ALREADY_EXISTS) | |
316 { | |
317 /* Verify that the directory has the correct rights */ | |
318 // TODO | |
319 retval = true; | |
320 goto done; | |
321 } | |
322 ERRORPRINTF ("Failed to create directory. Err: %lu", err); | |
323 } | |
324 retval = true; | |
325 | |
326 done: | |
327 | |
328 if (everyone_SID) | |
329 FreeSid(everyone_SID); | |
330 if (admin_SID) | |
331 FreeSid(admin_SID); | |
332 if (access_control_list) | |
333 LocalFree(access_control_list); | |
334 if (descriptor) | |
335 LocalFree(descriptor); | |
240 | 336 |
241 return retval; | 337 return retval; |
338 } | |
339 | |
340 /**@brief Writes the selection file containing the instructions | |
341 * | |
342 * If the process is running elevated the instructions are | |
343 * written to the global ProgramData directory otherwise | |
344 * they are written in the temporary directory of the current user. | |
345 * | |
346 * If the return value is not NULL it needs to be freed by the caller. | |
347 * The returned path will contain backslashes as directory seperators. | |
348 * | |
349 * @param[in] to_install Certificates that should be installed | |
350 * @param[in] to_remove Certificates that should be removed | |
351 * @returns pointer to the absolute filename of the selection file or NULL | |
352 */ | |
353 wchar_t * | |
354 write_selection_file (char **to_install, char **to_remove) | |
355 { | |
356 wchar_t *folder_name = NULL, | |
357 *path = NULL; | |
358 bool elevated = is_elevated(); | |
359 HRESULT result = E_FAIL; | |
360 HANDLE hFile = NULL; | |
361 size_t path_len; | |
362 | |
363 if (!elevated) | |
364 { | |
365 /* TODO */ | |
366 } | |
367 | |
368 #if 0 | |
369 result = SHGetKnownFolderPath (FOLDERID_ProgramData, /* Get program data dir */ | |
370 KF_FLAG_CREATE | /* Create if it does not exist */ | |
371 KF_FLAG_INIT, /* Initialize it if created */ | |
372 -1, /* Get it for the default user */ | |
373 &folder_name); | |
374 #endif | |
375 /* TODO figure out how to do this with mingw */ | |
376 result = S_OK; | |
377 folder_name = L"C:\\ProgramData"; | |
378 | |
379 if (result != S_OK) | |
380 { | |
381 PRINTLASTERROR ("Failed to get folder path"); | |
382 return NULL; | |
383 } | |
384 | |
385 path_len = wcslen (folder_name) + wcslen (APPNAME) + 2; /* path + dirsep + \0 */ | |
386 path_len += wcslen (SELECTION_FILE_NAME) + 1; /* filename + dirsep */ | |
387 | |
388 if (path_len >= MAX_PATH) | |
389 { | |
390 /* We could go and use the full 32,767 characters but this | |
391 should be a very weird setup if this is neccessary. */ | |
392 ERRORPRINTF ("Path too long.\n"); | |
393 return NULL; | |
394 } | |
395 | |
396 path = xmalloc (path_len * sizeof (wchar_t)); | |
397 if (wcscpy (path, folder_name) != 0) | |
398 { | |
399 ERRORPRINTF ("Failed to copy folder name.\n"); | |
400 #if 0 | |
401 CoTaskMemFree (folder_name); | |
402 #endif | |
403 return NULL; | |
404 } | |
405 | |
406 #if 0 | |
407 CoTaskMemFree (folder_name); | |
408 #endif | |
409 | |
410 if (wcscat (path, L"\\") != 0) | |
411 { | |
412 ERRORPRINTF ("Failed to cat dirsep.\n"); | |
413 xfree(path); | |
414 return NULL; | |
415 } | |
416 | |
417 if (wcscat (path, APPNAME) != 0) | |
418 { | |
419 ERRORPRINTF ("Failed to cat appname.\n"); | |
420 xfree(path); | |
421 return NULL; | |
422 } | |
423 | |
424 /* Security: if someone has created this directory before | |
425 it might be a symlink to another place that a users | |
426 wants us to grant read access to or makes us overwrite | |
427 something */ | |
428 if(!create_restricted_directory (path)) | |
429 { | |
430 ERRORPRINTF ("Failed to create directory\n"); | |
431 xfree(path); | |
432 return NULL; | |
433 } | |
434 | |
435 if (wcscat (path, L"\\") != 0) | |
436 { | |
437 ERRORPRINTF ("Failed to cat dirsep.\n"); | |
438 xfree(path); | |
439 return NULL; | |
440 } | |
441 | |
442 if (wcscat (path, SELECTION_FILE_NAME) != 0) | |
443 { | |
444 ERRORPRINTF ("Failed to cat filename.\n"); | |
445 xfree(path); | |
446 return NULL; | |
447 } | |
448 | |
449 hFile = CreateFileW(path, | |
450 GENERIC_WRITE, | |
451 0, /* don't share */ | |
452 NULL, /* use the security attributes from the folder */ | |
453 OPEN_ALWAYS, | |
454 0, | |
455 NULL); | |
456 | |
457 if (hFile == INVALID_HANDLE_VALUE) | |
458 { | |
459 ERRORPRINTF ("Failed to create file\n"); | |
460 xfree(path); | |
461 return NULL; | |
462 } | |
463 if (!write_instructions (to_install, hFile, false)) | |
464 { | |
465 ERRORPRINTF ("Failed to write install instructions.\n"); | |
466 CloseHandle(hFile); | |
467 xfree(path); | |
468 return NULL; | |
469 } | |
470 if (!write_instructions (to_remove, hFile, true)) | |
471 { | |
472 ERRORPRINTF ("Failed to write remove instructions.\n"); | |
473 CloseHandle(hFile); | |
474 xfree(path); | |
475 return NULL; | |
476 } | |
477 CloseHandle(hFile); | |
478 | |
479 return path; | |
242 } | 480 } |
243 | 481 |
244 int | 482 int |
245 write_stores_nss (char **to_install, char **to_remove) | 483 write_stores_nss (char **to_install, char **to_remove) |
246 { | 484 { |
247 HANDLE hToken = NULL; | 485 wchar_t *selection_file_name = NULL; |
248 OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken); | 486 |
249 | 487 selection_file_name = write_selection_file (to_install, to_remove); |
250 /* TODO loop over all users */ | 488 if (!selection_file_name) |
251 if (!start_procces_for_user (to_install, to_remove, hToken)) | 489 { |
490 ERRORPRINTF ("Failed to write instructions.\n"); | |
491 return -1; | |
492 } | |
493 | |
494 DEBUGPRINTF ("Wrote selection file. Loc: %S\n", selection_file_name); | |
495 | |
496 /* TODO loop over all users create startup entries for them*/ | |
497 | |
498 if (!start_procces_for_user (selection_file_name)) | |
252 { | 499 { |
253 ERRORPRINTF ("Failed to run NSS installation process.\n"); | 500 ERRORPRINTF ("Failed to run NSS installation process.\n"); |
501 xfree(selection_file_name); | |
254 return -1; | 502 return -1; |
255 } | 503 } |
504 xfree(selection_file_name); | |
256 return 0; | 505 return 0; |
257 } | 506 } |
258 | 507 |
259 #endif | 508 #endif |