+/*
+ * win32_query_security_descriptor() - Query a file's security descriptor
+ *
+ * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE
+ * format, and we currently have a handle opened with as many relevant
+ * permissions as possible. At this point, on Windows there are a number of
+ * options for reading a file's security descriptor:
+ *
+ * GetFileSecurity(): This takes in a path and returns the
+ * SECURITY_DESCRIPTOR_RELATIVE. Problem: this uses an internal handle, not
+ * ours, and the handle created internally doesn't specify
+ * FILE_FLAG_BACKUP_SEMANTICS. Therefore there can be access denied errors on
+ * some files and directories, even when running as the Administrator.
+ *
+ * GetSecurityInfo(): This takes in a handle and returns the security
+ * descriptor split into a bunch of different parts. This should work, but it's
+ * dumb because we have to put the security descriptor back together again.
+ *
+ * BackupRead(): This can read the security descriptor, but this is a
+ * difficult-to-use API, probably only works as the Administrator, and the
+ * format of the returned data is not well documented.
+ *
+ * NtQuerySecurityObject(): This is exactly what we need, as it takes in a
+ * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE
+ * format. Only problem is that it's a ntdll function and therefore not
+ * officially part of the Win32 API. Oh well.
+ */
+static DWORD
+win32_query_security_descriptor(HANDLE hFile, const wchar_t *path,
+ SECURITY_INFORMATION requestedInformation,
+ SECURITY_DESCRIPTOR *buf,
+ DWORD bufsize, DWORD *lengthNeeded)
+{
+#ifdef WITH_NTDLL
+ if (func_NtQuerySecurityObject) {
+ NTSTATUS status;
+
+ status = (*func_NtQuerySecurityObject)(hFile,
+ requestedInformation, buf,
+ bufsize, lengthNeeded);
+ /* Since it queries an already-open handle, NtQuerySecurityObject()
+ * apparently returns STATUS_ACCESS_DENIED rather than
+ * STATUS_PRIVILEGE_NOT_HELD. */
+ if (status == STATUS_ACCESS_DENIED)
+ return ERROR_PRIVILEGE_NOT_HELD;
+ else
+ return (*func_RtlNtStatusToDosError)(status);
+ }
+#endif
+ if (GetFileSecurity(path, requestedInformation, buf,
+ bufsize, lengthNeeded))
+ return ERROR_SUCCESS;
+ else
+ return GetLastError();
+}
+