wimlib: Automatically try to acquire needed privileges on Windows
authorEric Biggers <ebiggers3@gmail.com>
Wed, 14 Aug 2013 17:14:25 +0000 (12:14 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Wed, 14 Aug 2013 17:15:06 +0000 (12:15 -0500)
NEWS
include/wimlib.h
include/wimlib/win32.h
programs/imagex-win32.c
programs/imagex.c
src/header.c
src/wim.c
src/win32_common.c

diff --git a/NEWS b/NEWS
index a0d271b..6175eb3 100644 (file)
--- 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
index f23aa9c..af16742 100644 (file)
@@ -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.
  */
index f1e53fd..04fa4d1 100644 (file)
@@ -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);
 
index 146e07f..24f9910 100644 (file)
@@ -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)
index d5abe37..c97f2d7 100644 (file)
@@ -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 <DESCRIPTION> or <FLAGS> 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);
index 73e0bec..906d8e7 100644 (file)
@@ -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;
        }
 
index 9ddaddb..ee28a58 100644 (file)
--- 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;
index fc55709..a996378 100644 (file)
@@ -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;
        }