]> wimlib.net Git - wimlib/blob - src/win32_common.c
win32_common.c: include SeManageVolumePrivilege in apply privileges
[wimlib] / src / win32_common.c
1 /*
2  * win32_common.c - Windows code common to applying and capturing images.
3  */
4
5 /*
6  * Copyright (C) 2013, 2014, 2015 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifdef __WIN32__
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib/win32_common.h"
29
30 #include "wimlib/error.h"
31 #include "wimlib/util.h"
32
33 static bool
34 win32_modify_privilege(const wchar_t *privilege, bool enable)
35 {
36         HANDLE hToken;
37         LUID luid;
38         TOKEN_PRIVILEGES newState;
39         bool ret = FALSE;
40
41         if (!OpenProcessToken(GetCurrentProcess(),
42                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
43                               &hToken))
44                 goto out;
45
46         if (!LookupPrivilegeValue(NULL, privilege, &luid))
47                 goto out_close_handle;
48
49         newState.PrivilegeCount = 1;
50         newState.Privileges[0].Luid = luid;
51         newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
52         SetLastError(ERROR_SUCCESS);
53         ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
54         if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED)
55                 ret = FALSE;
56 out_close_handle:
57         CloseHandle(hToken);
58 out:
59         return ret;
60 }
61
62 static bool
63 win32_modify_capture_privileges(bool enable)
64 {
65         return win32_modify_privilege(SE_BACKUP_NAME, enable)
66             && win32_modify_privilege(SE_SECURITY_NAME, enable);
67 }
68
69 static bool
70 win32_modify_apply_privileges(bool enable)
71 {
72         return win32_modify_privilege(SE_RESTORE_NAME, enable)
73             && win32_modify_privilege(SE_SECURITY_NAME, enable)
74             && win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable)
75             && win32_modify_privilege(SE_MANAGE_VOLUME_NAME, enable);
76 }
77
78 static void
79 win32_release_capture_and_apply_privileges(void)
80 {
81         win32_modify_capture_privileges(false);
82         win32_modify_apply_privileges(false);
83 }
84
85 /* Pointers to dynamically loaded functions  */
86
87 /* ntdll.dll  */
88
89 NTSTATUS (WINAPI *func_NtCreateFile)(PHANDLE FileHandle,
90                                      ACCESS_MASK DesiredAccess,
91                                      POBJECT_ATTRIBUTES ObjectAttributes,
92                                      PIO_STATUS_BLOCK IoStatusBlock,
93                                      PLARGE_INTEGER AllocationSize,
94                                      ULONG FileAttributes,
95                                      ULONG ShareAccess,
96                                      ULONG CreateDisposition,
97                                      ULONG CreateOptions,
98                                      PVOID EaBuffer,
99                                      ULONG EaLength);
100
101 NTSTATUS (WINAPI *func_NtOpenFile) (PHANDLE FileHandle,
102                                     ACCESS_MASK DesiredAccess,
103                                     POBJECT_ATTRIBUTES ObjectAttributes,
104                                     PIO_STATUS_BLOCK IoStatusBlock,
105                                     ULONG ShareAccess,
106                                     ULONG OpenOptions);
107
108 NTSTATUS (WINAPI *func_NtReadFile) (HANDLE FileHandle,
109                                     HANDLE Event,
110                                     PIO_APC_ROUTINE ApcRoutine,
111                                     PVOID ApcContext,
112                                     PIO_STATUS_BLOCK IoStatusBlock,
113                                     PVOID Buffer,
114                                     ULONG Length,
115                                     PLARGE_INTEGER ByteOffset,
116                                     PULONG Key);
117
118 NTSTATUS (WINAPI *func_NtWriteFile) (HANDLE FileHandle,
119                                      HANDLE Event,
120                                      PIO_APC_ROUTINE ApcRoutine,
121                                      PVOID ApcContext,
122                                      PIO_STATUS_BLOCK IoStatusBlock,
123                                      PVOID Buffer,
124                                      ULONG Length,
125                                      PLARGE_INTEGER ByteOffset,
126                                      PULONG Key);
127
128 NTSTATUS (WINAPI *func_NtQueryInformationFile)(HANDLE FileHandle,
129                                                PIO_STATUS_BLOCK IoStatusBlock,
130                                                PVOID FileInformation,
131                                                ULONG Length,
132                                                FILE_INFORMATION_CLASS FileInformationClass);
133
134 NTSTATUS (WINAPI *func_NtQuerySecurityObject)(HANDLE handle,
135                                               SECURITY_INFORMATION SecurityInformation,
136                                               PSECURITY_DESCRIPTOR SecurityDescriptor,
137                                               ULONG Length,
138                                               PULONG LengthNeeded);
139
140 NTSTATUS (WINAPI *func_NtQueryDirectoryFile) (HANDLE FileHandle,
141                                               HANDLE Event,
142                                               PIO_APC_ROUTINE ApcRoutine,
143                                               PVOID ApcContext,
144                                               PIO_STATUS_BLOCK IoStatusBlock,
145                                               PVOID FileInformation,
146                                               ULONG Length,
147                                               FILE_INFORMATION_CLASS FileInformationClass,
148                                               BOOLEAN ReturnSingleEntry,
149                                               PUNICODE_STRING FileName,
150                                               BOOLEAN RestartScan);
151
152 NTSTATUS (WINAPI *func_NtQueryVolumeInformationFile) (HANDLE FileHandle,
153                                                       PIO_STATUS_BLOCK IoStatusBlock,
154                                                       PVOID FsInformation,
155                                                       ULONG Length,
156                                                       FS_INFORMATION_CLASS FsInformationClass);
157
158 NTSTATUS (WINAPI *func_NtSetInformationFile)(HANDLE FileHandle,
159                                              PIO_STATUS_BLOCK IoStatusBlock,
160                                              PVOID FileInformation,
161                                              ULONG Length,
162                                              FILE_INFORMATION_CLASS FileInformationClass);
163
164 NTSTATUS (WINAPI *func_NtSetSecurityObject)(HANDLE Handle,
165                                             SECURITY_INFORMATION SecurityInformation,
166                                             PSECURITY_DESCRIPTOR SecurityDescriptor);
167
168 NTSTATUS (WINAPI *func_NtFsControlFile) (HANDLE FileHandle,
169                                          HANDLE Event,
170                                          PIO_APC_ROUTINE ApcRoutine,
171                                          PVOID ApcContext,
172                                          PIO_STATUS_BLOCK IoStatusBlock,
173                                          ULONG FsControlCode,
174                                          PVOID InputBuffer,
175                                          ULONG InputBufferLength,
176                                          PVOID OutputBuffer,
177                                          ULONG OutputBufferLength);
178
179 NTSTATUS (WINAPI *func_NtClose) (HANDLE Handle);
180
181 DWORD (WINAPI *func_RtlNtStatusToDosError)(NTSTATUS status);
182
183 BOOLEAN (WINAPI *func_RtlDosPathNameToNtPathName_U)
184                   (IN PCWSTR DosName,
185                    OUT PUNICODE_STRING NtName,
186                    OUT PCWSTR *PartName,
187                    OUT PRTL_RELATIVE_NAME_U RelativeName);
188
189 NTSTATUS (WINAPI *func_RtlDosPathNameToNtPathName_U_WithStatus)
190                 (IN PCWSTR DosName,
191                  OUT PUNICODE_STRING NtName,
192                  OUT PCWSTR *PartName,
193                  OUT PRTL_RELATIVE_NAME_U RelativeName);
194
195 NTSTATUS (WINAPI *func_RtlCreateSystemVolumeInformationFolder)
196                 (PCUNICODE_STRING VolumeRootPath);
197
198 static bool acquired_privileges = false;
199
200 struct dll_sym {
201         void **func_ptr;
202         const char *name;
203         bool required;
204 };
205
206 #define DLL_SYM(name, required) { (void **)&func_##name, #name, required }
207
208 #define for_each_sym(sym, spec) \
209         for ((sym) = (spec)->syms; (sym)->name; (sym)++)
210
211 struct dll_spec {
212         const wchar_t *name;
213         HMODULE handle;
214         const struct dll_sym syms[];
215 };
216
217 struct dll_spec ntdll_spec = {
218         .name = L"ntdll.dll",
219         .syms = {
220                 DLL_SYM(NtCreateFile, true),
221                 DLL_SYM(NtOpenFile, true),
222                 DLL_SYM(NtReadFile, true),
223                 DLL_SYM(NtWriteFile, true),
224                 DLL_SYM(NtQueryInformationFile, true),
225                 DLL_SYM(NtQuerySecurityObject, true),
226                 DLL_SYM(NtQueryDirectoryFile, true),
227                 DLL_SYM(NtQueryVolumeInformationFile, true),
228                 DLL_SYM(NtSetInformationFile, true),
229                 DLL_SYM(NtSetSecurityObject, true),
230                 DLL_SYM(NtFsControlFile, true),
231                 DLL_SYM(NtClose, true),
232                 DLL_SYM(RtlNtStatusToDosError, true),
233                 DLL_SYM(RtlCreateSystemVolumeInformationFolder, false),
234                 DLL_SYM(RtlDosPathNameToNtPathName_U, true),
235                 DLL_SYM(RtlDosPathNameToNtPathName_U_WithStatus, false), /* Not present on XP  */
236                 {NULL, NULL},
237         },
238 };
239
240 static int
241 init_dll(struct dll_spec *spec)
242 {
243         const struct dll_sym *sym;
244         void *addr;
245
246         if (!spec->handle)
247                 spec->handle = LoadLibrary(spec->name);
248         if (!spec->handle) {
249                 for_each_sym(sym, spec) {
250                         if (sym->required) {
251                                 ERROR("%ls could not be loaded!", spec->name);
252                                 return WIMLIB_ERR_UNSUPPORTED;
253                         }
254                 }
255                 return 0;
256         }
257         for_each_sym(sym, spec) {
258                 addr = (void *)GetProcAddress(spec->handle, sym->name);
259                 if (addr) {
260                         *(sym->func_ptr) = addr;
261                 } else if (sym->required) {
262                         ERROR("Can't find %s in %ls", sym->name, spec->name);
263                         return WIMLIB_ERR_UNSUPPORTED;
264                 }
265         }
266         return 0;
267 }
268
269 static void
270 cleanup_dll(struct dll_spec *spec)
271 {
272         const struct dll_sym *sym;
273
274         if (spec->handle) {
275                 FreeLibrary(spec->handle);
276                 spec->handle = NULL;
277
278                 for_each_sym(sym, spec)
279                         *(sym->func_ptr) = NULL;
280         }
281 }
282
283 /* One-time initialization for Windows capture/apply code.  */
284 int
285 win32_global_init(int init_flags)
286 {
287         int ret;
288
289         /* Try to acquire useful privileges.  */
290         if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) {
291                 ret = WIMLIB_ERR_INSUFFICIENT_PRIVILEGES;
292                 if (!win32_modify_capture_privileges(true))
293                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES)
294                                 goto out_drop_privs;
295                 if (!win32_modify_apply_privileges(true))
296                         if (init_flags & WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES)
297                                 goto out_drop_privs;
298                 acquired_privileges = true;
299         }
300
301         ret = init_dll(&ntdll_spec);
302         if (ret)
303                 goto out_drop_privs;
304
305         return 0;
306
307 out_drop_privs:
308         win32_release_capture_and_apply_privileges();
309         return ret;
310 }
311
312 void
313 win32_global_cleanup(void)
314 {
315         if (acquired_privileges)
316                 win32_release_capture_and_apply_privileges();
317
318         cleanup_dll(&ntdll_spec);
319 }
320
321 /*
322  * Translates a Win32-namespace path into an NT-namespace path.
323  *
324  * On success, returns 0.  The NT-namespace path will be stored in the
325  * UNICODE_STRING structure pointed to by nt_path.  nt_path->Buffer will be set
326  * to a new buffer that must later be freed with HeapFree().  (Really
327  * RtlHeapFree(), but HeapFree() seems to be the same thing.)
328  *
329  * On failure, returns WIMLIB_ERR_NOMEM or WIMLIB_ERR_INVALID_PARAM.
330  */
331 int
332 win32_path_to_nt_path(const wchar_t *win32_path, UNICODE_STRING *nt_path)
333 {
334         NTSTATUS status;
335
336         if (func_RtlDosPathNameToNtPathName_U_WithStatus) {
337                 status = (*func_RtlDosPathNameToNtPathName_U_WithStatus)(win32_path,
338                                                                          nt_path,
339                                                                          NULL, NULL);
340         } else {
341                 if ((*func_RtlDosPathNameToNtPathName_U)(win32_path, nt_path,
342                                                          NULL, NULL))
343                         status = STATUS_SUCCESS;
344                 else
345                         status = STATUS_NO_MEMORY;
346         }
347
348         if (likely(NT_SUCCESS(status)))
349                 return 0;
350
351         if (status == STATUS_NO_MEMORY)
352                 return WIMLIB_ERR_NOMEM;
353
354         winnt_error(status, L"\"%ls\": invalid path name", win32_path);
355         return WIMLIB_ERR_INVALID_PARAM;
356 }
357
358 int
359 win32_get_drive_path(const wchar_t *file_path, wchar_t drive_path[7])
360 {
361         tchar *file_abspath;
362
363         file_abspath = realpath(file_path, NULL);
364         if (!file_abspath)
365                 return WIMLIB_ERR_NOMEM;
366
367         if (file_abspath[0] == L'\0' || file_abspath[1] != L':') {
368                 ERROR("\"%ls\": Path format not recognized", file_abspath);
369                 FREE(file_abspath);
370                 return WIMLIB_ERR_UNSUPPORTED;
371         }
372
373         wsprintf(drive_path, L"\\\\.\\%lc:", file_abspath[0]);
374         FREE(file_abspath);
375         return 0;
376 }
377
378 static void
379 windows_msg(u32 code, const wchar_t *format, va_list va,
380             bool is_ntstatus, bool is_error)
381 {
382         wchar_t _buf[STACK_MAX / 8];
383         wchar_t *buf = _buf;
384         size_t buflen = ARRAY_LEN(_buf);
385         size_t ret;
386         size_t n;
387
388 retry:
389         n = vsnwprintf(buf, buflen, format, va);
390
391         if (n >= buflen)
392                 goto realloc;
393
394         n += snwprintf(&buf[n], buflen - n,
395                        (is_ntstatus ?
396                         L" (status=%08"PRIx32"): " :
397                         L" (err=%"PRIu32"): "),
398                        code);
399
400         if (n >= buflen)
401                 goto realloc;
402
403         ret = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
404                             NULL,
405                             is_ntstatus ? (*func_RtlNtStatusToDosError)(code) : code,
406                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
407                             &buf[n],
408                             buflen - n,
409                             NULL);
410         n += ret;
411
412         if (n >= buflen || (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
413                 goto realloc;
414
415         if (buf[n - 1] == L'\n')
416                 buf[--n] = L'\0';
417         if (buf[n - 1] == L'\r')
418                 buf[--n] = L'\0';
419         if (buf[n - 1] == L'.')
420                 buf[--n] = L'\0';
421
422         if (is_error)
423                 ERROR("%ls", buf);
424         else
425                 WARNING("%ls", buf);
426         if (buf != _buf)
427                 FREE(buf);
428         return;
429
430 realloc:
431         if (buf != _buf)
432                 FREE(buf);
433         buflen *= 2;
434         buf = MALLOC(buflen * sizeof(buf[0]));
435         if (buf)
436                 goto retry;
437         ERROR("Ran out of memory while building error message!!!");
438 }
439
440 void
441 win32_warning(DWORD err, const wchar_t *format, ...)
442 {
443         va_list va;
444
445         va_start(va, format);
446         windows_msg(err, format, va, false, false);
447         va_end(va);
448 }
449
450 void
451 win32_error(DWORD err, const wchar_t *format, ...)
452 {
453         va_list va;
454
455         va_start(va, format);
456         windows_msg(err, format, va, false, true);
457         va_end(va);
458 }
459
460 void
461 winnt_warning(NTSTATUS status, const wchar_t *format, ...)
462 {
463         va_list va;
464
465         va_start(va, format);
466         windows_msg(status, format, va, true, false);
467         va_end(va);
468 }
469
470 void
471 winnt_error(NTSTATUS status, const wchar_t *format, ...)
472 {
473         va_list va;
474
475         va_start(va, format);
476         windows_msg(status, format, va, true, true);
477         va_end(va);
478 }
479
480 #endif /* __WIN32__ */