X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fntfs-3g_apply.c;h=86a28c10e2aa31e2be7182e5ff0a521901cfe6cc;hp=a1c6775136b3de0faee2383e273ce40bbbc50b68;hb=e10a0fd1f906c342b0b45bc393ec10043c6a8936;hpb=1a4696b013a95bf9af1580a93dc2e25c97d10b43 diff --git a/src/ntfs-3g_apply.c b/src/ntfs-3g_apply.c index a1c67751..86a28c10 100644 --- a/src/ntfs-3g_apply.c +++ b/src/ntfs-3g_apply.c @@ -50,6 +50,7 @@ #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) @@ -260,14 +261,14 @@ ntfs_3g_extract_stream(file_spec_t file, const utf16lechar *raw_stream_name, * to skip even more useless work (for example it fills resized * attributes with 0's, then we just override it.) */ ret = WIMLIB_ERR_WRITE; - if (ntfs_attr_truncate_solid(na, wim_resource_size(lte))) + if (ntfs_attr_truncate_solid(na, lte->size)) goto out_attr_close; /* Extract stream data to the NTFS attribute. */ extract_ctx.na = na; extract_ctx.offset = 0; - ret = extract_wim_resource(lte, wim_resource_size(lte), - ntfs_3g_extract_wim_chunk, &extract_ctx); + ret = extract_stream(lte, lte->size, + ntfs_3g_extract_wim_chunk, &extract_ctx); /* Clean up and return. */ out_attr_close: ntfs_attr_close(na); @@ -379,6 +380,113 @@ out: 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) @@ -386,7 +494,8 @@ ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_si 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); @@ -397,10 +506,19 @@ ntfs_3g_set_security_descriptor(const char *path, const u8 *desc, size_t desc_si 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; } @@ -508,9 +626,42 @@ const struct apply_operations ntfs_3g_apply_ops = { .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 */