X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fwin32_apply.c;h=7800e5994a5adf0aecda4532d32fc4a455c99359;hb=4c73e29d8d74a4e969782d2d40e209337414034c;hp=9fac1561a32a76d8cb01ca1047ded7c2bb6a4def;hpb=5ad50d41e2ba1e89d4f930a654f7d0ff2ac3fb0b;p=wimlib diff --git a/src/win32_apply.c b/src/win32_apply.c index 9fac1561..7800e599 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -5,20 +5,18 @@ /* * Copyright (C) 2013, 2014 Eric Biggers * - * This file is part of wimlib, a library for working with WIM files. + * This file is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. * - * wimlib is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. - * - * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * - * You should have received a copy of the GNU General Public License - * along with wimlib; if not, see http://www.gnu.org/licenses/. + * You should have received a copy of the GNU Lesser General Public License + * along with this file; if not, see http://www.gnu.org/licenses/. */ #ifdef __WIN32__ @@ -2009,33 +2007,100 @@ end_extract_stream(struct wim_lookup_table_entry *stream, int status, void *_ctx /* Set the security descriptor @desc, of @desc_size bytes, on the file with open * handle @h. */ static NTSTATUS -set_security_descriptor(HANDLE h, const void *desc, +set_security_descriptor(HANDLE h, const void *_desc, size_t desc_size, struct win32_apply_ctx *ctx) { SECURITY_INFORMATION info; NTSTATUS status; + SECURITY_DESCRIPTOR_RELATIVE *desc; + + /* + * Ideally, we would just pass in the security descriptor buffer as-is. + * But it turns out that Windows can mess up the security descriptor + * even when using the low-level NtSetSecurityObject() function: + * + * - Windows will clear SE_DACL_AUTO_INHERITED if it is set in the + * passed buffer. To actually get Windows to set + * SE_DACL_AUTO_INHERITED, the application must set the non-persistent + * flag SE_DACL_AUTO_INHERIT_REQ. As usual, Microsoft didn't bother + * to properly document either of these flags. It's unclear how + * important SE_DACL_AUTO_INHERITED actually is, but to be safe we use + * the SE_DACL_AUTO_INHERIT_REQ workaround to set it if needed. + * + * - The above also applies to the equivalent SACL flags, + * SE_SACL_AUTO_INHERITED and SE_SACL_AUTO_INHERIT_REQ. + * + * - If the application says that it's setting + * DACL_SECURITY_INFORMATION, then Windows sets SE_DACL_PRESENT in the + * resulting security descriptor, even if the security descriptor the + * application provided did not have a DACL. This seems to be + * unavoidable, since omitting DACL_SECURITY_INFORMATION would cause a + * default DACL to remain. Fortunately, this behavior seems harmless, + * since the resulting DACL will still be "null" --- but it will be + * "the other representation of null". + * + * - The above also applies to SACL_SECURITY_INFORMATION and + * SE_SACL_PRESENT. Again, it's seemingly unavoidable but "harmless" + * that Windows changes the representation of a "null SACL". + */ + if (likely(desc_size <= STACK_MAX)) { + desc = alloca(desc_size); + } else { + desc = MALLOC(desc_size); + if (!desc) + return STATUS_NO_MEMORY; + } + + memcpy(desc, _desc, desc_size); + + if (likely(desc_size >= 4)) { + + if (desc->Control & SE_DACL_AUTO_INHERITED) + desc->Control |= SE_DACL_AUTO_INHERIT_REQ; + + if (desc->Control & SE_SACL_AUTO_INHERITED) + desc->Control |= SE_SACL_AUTO_INHERIT_REQ; + } + + /* + * More API insanity. We want to set the entire security descriptor + * as-is. But all available APIs require specifying the specific parts + * of the security descriptor being set. Especially annoying is that + * mandatory integrity labels are part of the SACL, but they aren't set + * with SACL_SECURITY_INFORMATION. Instead, applications must also + * specify LABEL_SECURITY_INFORMATION (Windows Vista, Windows 7) or + * BACKUP_SECURITY_INFORMATION (Windows 8). But at least older versions + * of Windows don't error out if you provide these newer flags... + * + * Also, if the process isn't running as Administrator, then it probably + * doesn't have SE_RESTORE_PRIVILEGE. In this case, it will always get + * the STATUS_PRIVILEGE_NOT_HELD error by trying to set the SACL, even + * if the security descriptor it provided did not have a SACL. By + * default, in this case we try to recover and set as much of the + * security descriptor as possible --- potentially excluding the DACL, and + * even the owner, as well as the SACL. + */ - /* We really just want to set entire the security descriptor as-is, but - * all available APIs require specifying the specific parts of the - * descriptor being set. Start out by requesting all parts be set. If - * permissions problems are encountered, fall back to omitting some - * parts (first the SACL, then the DACL, then the owner), unless the - * WIMLIB_EXTRACT_FLAG_STRICT_ACLS flag has been enabled. */ info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; - - /* Prefer NtSetSecurityObject() to SetFileSecurity(). SetFileSecurity() - * itself necessarily uses NtSetSecurityObject() as the latter is the - * underlying system call for setting security information, but - * SetFileSecurity() opens the handle with NtCreateFile() without - * FILE_OPEN_FILE_BACKUP_INTENT. Hence, access checks are done and due - * to the Windows security model, even a process running as the - * Administrator can have access denied. (Of course, this not mentioned - * in the MS "documentation".) */ + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | + LABEL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION; + + + /* + * It's also worth noting that SetFileSecurity() is unusable because it + * doesn't request "backup semantics" when it opens the file internally. + * NtSetSecurityObject() seems to be the best function to use in backup + * applications. (SetSecurityInfo() should also work, but it's harder + * to use and must call NtSetSecurityObject() internally anyway. + * BackupWrite() is theoretically usable as well, but it's inflexible + * and poorly documented.) + */ + retry: - status = (*func_NtSetSecurityObject)(h, info, (PSECURITY_DESCRIPTOR)desc); + status = (*func_NtSetSecurityObject)(h, info, desc); if (NT_SUCCESS(status)) - return status; + goto out_maybe_free_desc; + /* Failed to set the requested parts of the security descriptor. If the * error was permissions-related, try to set fewer parts of the security * descriptor, unless WIMLIB_EXTRACT_FLAG_STRICT_ACLS is enabled. */ @@ -2044,7 +2109,9 @@ retry: !(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)) { if (info & SACL_SECURITY_INFORMATION) { - info &= ~SACL_SECURITY_INFORMATION; + info &= ~(SACL_SECURITY_INFORMATION | + LABEL_SECURITY_INFORMATION | + BACKUP_SECURITY_INFORMATION); ctx->partial_security_descriptors++; goto retry; } @@ -2066,6 +2133,10 @@ retry: if (!(info & SACL_SECURITY_INFORMATION)) ctx->partial_security_descriptors--; ctx->no_security_descriptors++; + +out_maybe_free_desc: + if (unlikely(desc_size > STACK_MAX)) + FREE(desc); return status; }