From b79609d7660ef30bdfba091f839e61b256059a80 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 14 Aug 2013 12:14:25 -0500 Subject: [PATCH] wimlib: Automatically try to acquire needed privileges on Windows --- NEWS | 7 +++- include/wimlib.h | 18 +++++--- include/wimlib/win32.h | 3 +- programs/imagex-win32.c | 77 ---------------------------------- programs/imagex.c | 40 +++--------------- src/header.c | 2 +- src/wim.c | 2 +- src/win32_common.c | 92 +++++++++++++++++++++++++++++++++++------ 8 files changed, 106 insertions(+), 135 deletions(-) diff --git a/NEWS b/NEWS index a0d271b6..6175eb36 100644 --- a/NEWS +++ b/NEWS @@ -46,8 +46,13 @@ Version 1.5.0: A few changes were made to the error codes returned by library routines. + To make wimlib easier to use on Windows, wimlib_global_init() now + automatically attempts to acquire additional privileges on Windows, so + library clients need not do this (although they can provide a flag to + get the old behavior and manage privileges themselves). + This update bumps the shared library version number up to 9, since it - doesn't quite not maintain binary compatibility with previous releases. + doesn't quite maintain binary compatibility with previous releases. Version 1.4.2: Fixed bug in `wimlib-imagex export' that made it impossible to export an diff --git a/include/wimlib.h b/include/wimlib.h index f23aa9c9..af167428 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -1207,9 +1207,18 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour ******************************/ /** Assume that strings are represented in UTF-8, even if this is not the - * locale's character encoding. */ + * locale's character encoding. Not used on Windows. */ #define WIMLIB_INIT_FLAG_ASSUME_UTF8 0x00000001 +/** Windows-only: do not attempt to acquire additional privileges (currently + * SeBackupPrivilege, SeRestorePrivilege, SeSecurityPrivilege, and + * SeTakeOwnershipPrivilege) when initializing the library. This is intended + * for the case where the calling program manages these privileges itself. + * Note: no error is issued if privileges cannot be acquired, although related + * errors may be reported later, depending on if the operations performed + * actually require additional privileges or not. */ +#define WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES 0x00000002 + /** Specification of an update to perform on a WIM image. */ struct wimlib_update_command { @@ -2067,11 +2076,8 @@ wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info); * but you should not rely on this behavior.) * * @param init_flags - * On UNIX, specify ::WIMLIB_INIT_FLAG_ASSUME_UTF8 if wimlib should assume - * that all input data, including filenames, are in UTF-8 rather than the - * locale-dependent character encoding which may or may not be UTF-8, and - * that UTF-8 data can be directly printed to the console. On Windows, use - * 0 for this parameter. + * Bitwise OR of ::WIMLIB_INIT_FLAG_ASSUME_UTF8 and/or + * ::WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES. * * @return 0; other error codes may be returned in future releases. */ diff --git a/include/wimlib/win32.h b/include/wimlib/win32.h index f1e53fd1..04fa4d12 100644 --- a/include/wimlib/win32.h +++ b/include/wimlib/win32.h @@ -27,7 +27,7 @@ read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte, extern void -win32_global_init(void); +win32_global_init(int init_flags); extern void win32_global_cleanup(void); @@ -63,7 +63,6 @@ win32_strerror_r_replacement(int errnum, tchar *buf, size_t buflen); extern int win32_get_file_and_vol_ids(const wchar_t *path, u64 *ino_ret, u64 *dev_ret); - extern ssize_t pread(int fd, void *buf, size_t count, off_t offset); diff --git a/programs/imagex-win32.c b/programs/imagex-win32.c index 146e07f0..24f99108 100644 --- a/programs/imagex-win32.c +++ b/programs/imagex-win32.c @@ -121,83 +121,6 @@ globfree(glob_t *pglob) free(pglob->gl_pathv); } -static bool -win32_modify_privilege(const wchar_t *privilege, bool enable) -{ - HANDLE hToken; - LUID luid; - TOKEN_PRIVILEGES newState; - bool ret = FALSE; - - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &hToken)) - goto out; - - if (!LookupPrivilegeValue(NULL, privilege, &luid)) - goto out_close_handle; - - newState.PrivilegeCount = 1; - newState.Privileges[0].Luid = luid; - newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0); - SetLastError(ERROR_SUCCESS); - ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL); - if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED) - ret = FALSE; -out_close_handle: - CloseHandle(hToken); -out: - if (!ret && enable) { - fwprintf(stderr, - L"WARNING: Failed to enable %ls!\n" - " The program will continue, but if " - "permission issues are\n" - " encountered, you may need to run " - "this program as the Administrator.\n", - privilege); - } - return ret; -} - -static void -win32_modify_capture_privileges(bool enable) -{ - win32_modify_privilege(SE_BACKUP_NAME, enable); - win32_modify_privilege(SE_SECURITY_NAME, enable); -} - -static void -win32_modify_restore_privileges(bool enable) -{ - win32_modify_privilege(SE_RESTORE_NAME, enable); - win32_modify_privilege(SE_SECURITY_NAME, enable); - win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable); -} - -void -win32_acquire_capture_privileges(void) -{ - win32_modify_capture_privileges(true); -} - -void -win32_release_capture_privileges(void) -{ - win32_modify_capture_privileges(false); -} - -void -win32_acquire_restore_privileges(void) -{ - win32_modify_restore_privileges(true); -} - -void -win32_release_restore_privileges(void) -{ - win32_modify_restore_privileges(false); -} - /* Convert a string from the "current Windows codepage" to UTF-16LE. */ wchar_t * win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret) diff --git a/programs/imagex.c b/programs/imagex.c index d5abe37e..c97f2d73 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -1587,9 +1587,6 @@ imagex_apply(int argc, tchar **argv, int cmd) } #endif -#ifdef __WIN32__ - win32_acquire_restore_privileges(); -#endif if (wim) { ret = wimlib_extract_image(wim, image, target, extract_flags, additional_swms, num_additional_swms, @@ -1603,9 +1600,6 @@ imagex_apply(int argc, tchar **argv, int cmd) } if (ret == 0) imagex_printf(T("Done applying WIM image.\n")); -#ifdef __WIN32__ - win32_release_restore_privileges(); -#endif out_free_swms: for (unsigned i = 0; i < num_additional_swms; i++) wimlib_free(additional_swms[i]); @@ -1864,10 +1858,6 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) tsprintf(name_end, T(" (%lu)"), conflict_idx); } } -#ifdef __WIN32__ - win32_acquire_capture_privileges(); -#endif - ret = wimlib_add_image_multisource(wim, capture_sources, num_sources, @@ -1876,7 +1866,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) add_image_flags, imagex_progress_func); if (ret) - goto out_release_privs; + goto out_wimlib_free; if (desc || flags_element) { /* User provided or element. Get the @@ -1891,14 +1881,14 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) info.image_count, desc); if (ret) - goto out_release_privs; + goto out_wimlib_free; } if (flags_element) { ret = wimlib_set_image_flags(wim, info.image_count, flags_element); if (ret) - goto out_release_privs; + goto out_wimlib_free; } } @@ -1916,10 +1906,6 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) write_flags, num_threads, imagex_progress_func); } -out_release_privs: -#ifdef __WIN32__ - win32_release_capture_privileges(); -#endif out_wimlib_free: wimlib_free(wim); out_free_config: @@ -2467,10 +2453,6 @@ imagex_extract(int argc, tchar **argv, int cmd) num_additional_swms = 0; } -#ifdef __WIN32__ - win32_acquire_restore_privileges(); -#endif - ret = wimlib_extract_files(wim, image, cmds, num_cmds, 0, additional_swms, num_additional_swms, imagex_progress_func); @@ -2483,9 +2465,6 @@ imagex_extract(int argc, tchar **argv, int cmd) " are in the WIM image.\n"), get_cmd_string(CMD_INFO, false)); } -#ifdef __WIN32__ - win32_release_restore_privileges(); -#endif for (unsigned i = 0; i < num_additional_swms; i++) wimlib_free(additional_swms[i]); free(additional_swms); @@ -3397,25 +3376,16 @@ imagex_update(int argc, tchar **argv, int cmd) } } -#ifdef __WIN32__ - if (have_add_command) - win32_acquire_capture_privileges(); -#endif - /* Execute the update commands */ ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags, imagex_progress_func); if (ret) - goto out_release_privs; + goto out_free_cmds; /* Overwrite the updated WIM */ ret = wimlib_overwrite(wim, write_flags, num_threads, imagex_progress_func); -out_release_privs: -#ifdef __WIN32__ - if (have_add_command) - win32_release_capture_privileges(); -#endif +out_free_cmds: free(cmds); out_free_cmd_file_contents: free(cmd_file_contents); diff --git a/src/header.c b/src/header.c index 73e0beca..906d8e75 100644 --- a/src/header.c +++ b/src/header.c @@ -141,7 +141,7 @@ read_wim_header(const tchar *filename, struct filedes *in_fd, if (!filename) { pipe_str = alloca(40); - tsprintf(pipe_str, "[fd %d]", in_fd->fd); + tsprintf(pipe_str, T("[fd %d]"), in_fd->fd); filename = pipe_str; } diff --git a/src/wim.c b/src/wim.c index 9ddaddbe..ee28a581 100644 --- a/src/wim.c +++ b/src/wim.c @@ -919,7 +919,7 @@ wimlib_global_init(int init_flags) #endif } #ifdef __WIN32__ - win32_global_init(); + win32_global_init(init_flags); #endif already_inited = true; return 0; diff --git a/src/win32_common.c b/src/win32_common.c index fc55709a..a996378a 100644 --- a/src/win32_common.c +++ b/src/win32_common.c @@ -450,6 +450,69 @@ out: return 0; } +static bool +win32_modify_privilege(const wchar_t *privilege, bool enable) +{ + HANDLE hToken; + LUID luid; + TOKEN_PRIVILEGES newState; + bool ret = FALSE; + + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &hToken)) + goto out; + + if (!LookupPrivilegeValue(NULL, privilege, &luid)) + goto out_close_handle; + + newState.PrivilegeCount = 1; + newState.Privileges[0].Luid = luid; + newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0); + SetLastError(ERROR_SUCCESS); + ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL); + if (ret && GetLastError() == ERROR_NOT_ALL_ASSIGNED) + ret = FALSE; +out_close_handle: + CloseHandle(hToken); +out: + return ret; +} + +static void +win32_modify_capture_privileges(bool enable) +{ + win32_modify_privilege(SE_BACKUP_NAME, enable); + win32_modify_privilege(SE_SECURITY_NAME, enable); +} + +static void +win32_modify_apply_privileges(bool enable) +{ + win32_modify_privilege(SE_RESTORE_NAME, enable); + win32_modify_privilege(SE_SECURITY_NAME, enable); + win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable); +} + +static void +win32_modify_capture_and_apply_privileges(bool enable) +{ + win32_modify_capture_privileges(enable); + win32_modify_apply_privileges(enable); +} + +static void +win32_acquire_capture_and_apply_privileges(void) +{ + win32_modify_capture_privileges(true); +} + +static void +win32_release_capture_and_apply_privileges(void) +{ + win32_modify_capture_privileges(false); +} + HANDLE win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess) { @@ -489,6 +552,8 @@ static OSVERSIONINFO windows_version_info = { static HMODULE hKernel32 = NULL; +static bool acquired_privileges = false; + bool windows_version_is_at_least(unsigned major, unsigned minor) { @@ -497,22 +562,25 @@ windows_version_is_at_least(unsigned major, unsigned minor) windows_version_info.dwMinorVersion >= minor); } -/* Try to dynamically load some functions */ +/* One-time initialization for Windows capture/apply code. */ void -win32_global_init(void) +win32_global_init(int init_flags) { DWORD err; - if (hKernel32 == NULL) { - DEBUG("Loading Kernel32.dll"); - hKernel32 = LoadLibraryW(L"Kernel32.dll"); - if (hKernel32 == NULL) { - err = GetLastError(); - WARNING("Can't load Kernel32.dll"); - win32_error(err); - } + /* Try to acquire useful privileges. */ + if (!(init_flags & WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES)) { + win32_acquire_capture_and_apply_privileges(); + acquired_privileges = true; } + /* Get Windows version information. */ + GetVersionEx(&windows_version_info); + + /* Try to dynamically load some functions. */ + if (hKernel32 == NULL) + hKernel32 = LoadLibrary(L"Kernel32.dll"); + if (hKernel32) { win32func_FindFirstStreamW = (void*)GetProcAddress(hKernel32, "FindFirstStreamW"); @@ -524,14 +592,14 @@ win32_global_init(void) } } - GetVersionEx(&windows_version_info); } void win32_global_cleanup(void) { + if (acquired_privileges) + win32_release_capture_and_apply_privileges(); if (hKernel32 != NULL) { - DEBUG("Closing Kernel32.dll"); FreeLibrary(hKernel32); hKernel32 = NULL; } -- 2.43.0