#include "wimlib/ntfs_3g.h"
#include "wimlib/paths.h"
#include "wimlib/resource.h"
+#include "wimlib/security_descriptor.h"
static ntfs_volume *
ntfs_3g_apply_ctx_get_volume(struct apply_ctx *ctx)
static int
ntfs_3g_set_file_attributes(const char *path, u32 attributes,
- struct apply_ctx *ctx)
+ struct apply_ctx *ctx, unsigned pass)
{
ntfs_inode *ni;
int ret = 0;
return ret;
}
+static size_t
+sid_size(const wimlib_SID *sid)
+{
+ return offsetof(wimlib_SID, sub_authority) +
+ sizeof(le32) * sid->sub_authority_count;
+}
+
+/*
+ * sd_fixup - Fix up a Windows NT security descriptor for libntfs-3g.
+ *
+ * libntfs-3g validates security descriptors before setting them, but old
+ * versions contain bugs causing it to reject unusual but valid security
+ * descriptors:
+ *
+ * - Versions before 2013.1.13 reject security descriptors ending with an empty
+ * SACL (System Access Control List). This bug can be worked around either by
+ * moving the empty SACL earlier in the security descriptor or by removing the
+ * SACL entirely. The latter work-around is valid because an empty SACL is
+ * equivalent to a "null", or non-existent, SACL.
+ * - Versions up to and including 2013.1.13 reject security descriptors ending
+ * with an empty DACL (Discretionary Access Control List). This is very
+ * similar to the SACL bug and should be fixed in the next release after
+ * 2013.1.13. However, removing the DACL is not a valid workaround because
+ * this changes the meaning of the security descriptor--- an empty DACL allows
+ * no access, whereas a "null" DACL allows all access.
+ *
+ * If the security descriptor was fixed, this function returns an allocated
+ * buffer containing the fixed security descriptor, and its size is updated.
+ * Otherwise (or if no memory is available) the original descriptor is returned.
+ */
+static u8 *
+sd_fixup(const u8 *_desc, size_t *size_p)
+{
+ u32 owner_offset, group_offset, dacl_offset, sacl_offset;
+ bool owner_valid, group_valid;
+ size_t size = *size_p;
+ const wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc =
+ (const wimlib_SECURITY_DESCRIPTOR_RELATIVE*)_desc;
+ wimlib_SECURITY_DESCRIPTOR_RELATIVE *desc_new;
+ const wimlib_SID *owner, *group, *sid;
+
+ /* Don't attempt to fix clearly invalid security descriptors. */
+ if (size < sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE))
+ return (u8*)_desc;
+
+ if (le16_to_cpu(desc->control) & wimlib_SE_DACL_PRESENT)
+ dacl_offset = le32_to_cpu(desc->dacl_offset);
+ else
+ dacl_offset = 0;
+
+ if (le16_to_cpu(desc->control) & wimlib_SE_SACL_PRESENT)
+ sacl_offset = le32_to_cpu(desc->sacl_offset);
+ else
+ sacl_offset = 0;
+
+ /* Check if the security descriptor will be affected by one of the bugs.
+ * If not, do nothing and return.
+ *
+ * Note: HAVE_NTFS_MNT_RDONLY is defined if libntfs-3g is
+ * version 2013.1.13 or later. */
+ if (!(
+ #if !defined(HAVE_NTFS_MNT_RDONLY)
+ (sacl_offset != 0 && sacl_offset == size - sizeof(wimlib_ACL)) ||
+ #endif
+ (dacl_offset != 0 && dacl_offset == size - sizeof(wimlib_ACL))))
+ return (u8*)_desc;
+
+ owner_offset = le32_to_cpu(desc->owner_offset);
+ group_offset = le32_to_cpu(desc->group_offset);
+ owner = (const wimlib_SID*)((const u8*)desc + owner_offset);
+ group = (const wimlib_SID*)((const u8*)desc + group_offset);
+
+ /* We'll try to move the owner or group SID to the end of the security
+ * descriptor to avoid the bug. This is only possible if at least one
+ * is valid. */
+ owner_valid = (owner_offset != 0) &&
+ (owner_offset % 4 == 0) &&
+ (owner_offset <= size - sizeof(SID)) &&
+ (owner_offset + sid_size(owner) <= size) &&
+ (owner_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE));
+ group_valid = (group_offset != 0) &&
+ (group_offset % 4 == 0) &&
+ (group_offset <= size - sizeof(SID)) &&
+ (group_offset + sid_size(group) <= size) &&
+ (group_offset >= sizeof(wimlib_SECURITY_DESCRIPTOR_RELATIVE));
+ if (owner_valid) {
+ sid = owner;
+ } else if (group_valid) {
+ sid = group;
+ } else {
+ return (u8*)_desc;
+ }
+
+ desc_new = MALLOC(size + sid_size(sid));
+ if (desc_new == NULL)
+ return (u8*)_desc;
+
+ memcpy(desc_new, desc, size);
+ if (owner_valid)
+ desc_new->owner_offset = cpu_to_le32(size);
+ else if (group_valid)
+ desc_new->group_offset = cpu_to_le32(size);
+ memcpy((u8*)desc_new + size, sid, sid_size(sid));
+ *size_p = size + sid_size(sid);
+ return (u8*)desc_new;
+}
+
static int
ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_size,
struct apply_ctx *ctx)
ntfs_volume *vol;
ntfs_inode *ni;
struct SECURITY_CONTEXT sec_ctx;
- int ret = 0;
+ u8 *desc_fixed;
+ int ret;
vol = ntfs_3g_apply_ctx_get_volume(ctx);
memset(&sec_ctx, 0, sizeof(sec_ctx));
sec_ctx.vol = vol;
- if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc, desc_size, 0))
+ desc_fixed = sd_fixup(desc, &desc_size);
+
+ ret = 0;
+
+ if (ntfs_set_ntfs_acl(&sec_ctx, ni, desc_fixed, desc_size, 0))
ret = WIMLIB_ERR_SET_SECURITY;
+
+ if (desc_fixed != desc)
+ FREE(desc_fixed);
+
if (ntfs_inode_close(ni))
ret = WIMLIB_ERR_WRITE;
+
return ret;
}
.path_separator = '/',
.path_max = 32768,
+ /* By default, NTFS-3g creates names in the NTFS POSIX namespace, which
+ * is case-sensitive. */
.supports_case_sensitive_filenames = 1,
+
+ /* The root directory of the NTFS volume should not be created
+ * explicitly. */
.root_directory_is_special = 1,
+
+ /* NTFS-3g can open files by MFT reference. */
.uses_cookies = 1,
+
+ /*
+ * With NTFS-3g, the extraction order of the names of a file that has a
+ * short name needs to be:
+ *
+ * 1. Create file using the long name that has an associated short name.
+ * This long name is temporarily placed in the POSIX namespace.
+ * 2. Set the short name on the file. This will either change the POSIX
+ * name to Win32 and create a new DOS name, or replace the POSIX name
+ * with a Win32+DOS name.
+ * 3. Create additional long names (links) of the file, which are placed
+ * in the POSIX namespace.
+ *
+ * The reason for this is that two issues can come up when the
+ * extraction is done otherwise:
+ *
+ * - If a DOS name is set on a file in a directory with several long
+ * names, it is ambiguous which long name to use (at least with the
+ * exported ntfs_set_ntfs_dos_name() function).
+ * - NTFS-3g 2013.1.13 will no longer allow even setting the DOS name on
+ * a file with multiple existing long names, even if those long names
+ * are in different directories and the ntfs_set_ntfs_dos_name() call
+ * is therefore unambiguous. (This was apparently changed with the
+ * FUSE interface in mind.)
+ */
+ .requires_short_name_reordering = 1,
};
#endif /* WITH_NTFS_3G */