4 * Read and write the per-WIM-image table of security descriptors.
8 * Copyright (C) 2012, 2013 Eric Biggers
10 * This file is part of wimlib, a library for working with WIM files.
12 * wimlib is free software; you can redistribute it and/or modify it under the
13 * terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your option)
17 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19 * A PARTICULAR PURPOSE. See the GNU General Public License for more
22 * You should have received a copy of the GNU General Public License
23 * along with wimlib; if not, see http://www.gnu.org/licenses/.
30 #include "wimlib/assert.h"
31 #include "wimlib/endianness.h"
32 #include "wimlib/error.h"
33 #include "wimlib/security.h"
34 #include "wimlib/sha1.h"
35 #include "wimlib/util.h"
37 struct wim_security_data_disk {
43 struct wim_security_data *
44 new_wim_security_data(void)
46 return CALLOC(1, sizeof(struct wim_security_data));
50 * Reads the security data from the metadata resource of a WIM image.
52 * @metadata_resource: An array that contains the uncompressed metadata
53 * resource for the WIM image.
54 * @metadata_resource_len: The length of @metadata_resource.
55 * @sd_ret: A pointer to a pointer to a wim_security_data structure that
56 * will be filled in with a pointer to a new wim_security_data
57 * structure containing the security data on success.
59 * Note: There is no `offset' argument because the security data is located at
60 * the beginning of the metadata resource.
63 * WIMLIB_ERR_SUCCESS (0)
64 * WIMLIB_ERR_INVALID_METADATA_RESOURCE
68 read_wim_security_data(const u8 metadata_resource[], size_t metadata_resource_len,
69 struct wim_security_data **sd_ret)
71 struct wim_security_data *sd;
75 u64 size_no_descriptors;
76 const struct wim_security_data_disk *sd_disk;
79 if (metadata_resource_len < 8)
80 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
82 sd = new_wim_security_data();
86 sd_disk = (const struct wim_security_data_disk*)metadata_resource;
87 sd->total_length = le32_to_cpu(sd_disk->total_length);
88 sd->num_entries = le32_to_cpu(sd_disk->num_entries);
90 DEBUG("Reading security data: num_entries=%u, total_length=%u",
91 sd->num_entries, sd->total_length);
93 /* Length field of 0 is a special case that really means length
95 if (sd->total_length == 0)
98 /* The security_id field of each dentry is a signed 32-bit integer, so
99 * the possible indices into the security descriptors table are 0
100 * through 0x7fffffff. Which means 0x80000000 security descriptors
101 * maximum. Not like you should ever have anywhere close to that many
102 * security descriptors! */
103 if (sd->num_entries > 0x80000000)
106 /* Verify the listed total length of the security data is big enough to
107 * include the sizes array, verify that the file data is big enough to
108 * include it as well, then allocate the array of sizes.
110 * Note: The total length of the security data must fit in a 32-bit
111 * integer, even though each security descriptor size is a 64-bit
112 * integer. This is stupid, and we need to be careful not to actually
113 * let the security descriptor sizes be over 0xffffffff. */
114 if (sd->total_length > metadata_resource_len)
117 sizes_size = (u64)sd->num_entries * sizeof(u64);
118 size_no_descriptors = 8 + sizes_size;
119 if (size_no_descriptors > sd->total_length)
122 total_len = size_no_descriptors;
124 /* Return immediately if no security descriptors. */
125 if (sd->num_entries == 0)
126 goto out_align_total_length;
128 /* Allocate a new buffer for the sizes array */
129 sd->sizes = MALLOC(sizes_size);
133 /* Copy the sizes array into the new buffer */
134 for (u32 i = 0; i < sd->num_entries; i++) {
135 sd->sizes[i] = le64_to_cpu(sd_disk->sizes[i]);
136 if (sd->sizes[i] > 0xffffffff)
140 p = (const u8*)sd_disk + size_no_descriptors;
142 /* Allocate the array of pointers to the security descriptors, then read
143 * them into separate buffers. */
144 sd->descriptors = CALLOC(sd->num_entries, sizeof(sd->descriptors[0]));
145 if (!sd->descriptors)
148 for (u32 i = 0; i < sd->num_entries; i++) {
149 if (sd->sizes[i] == 0)
151 total_len += sd->sizes[i];
152 if (total_len > (u64)sd->total_length)
154 sd->descriptors[i] = memdup(p, sd->sizes[i]);
155 if (!sd->descriptors[i])
159 out_align_total_length:
160 total_len = (total_len + 7) & ~7;
161 sd->total_length = (sd->total_length + 7) & ~7;
162 if (total_len != sd->total_length) {
163 WARNING("Expected WIM security data total length of "
164 "%u bytes, but calculated %u bytes",
165 sd->total_length, (unsigned)total_len);
171 ERROR("WIM security data is invalid!");
172 ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
175 ERROR("Out of memory while reading WIM security data!");
176 ret = WIMLIB_ERR_NOMEM;
178 free_wim_security_data(sd);
184 * Writes the security data for a WIM image to an in-memory buffer.
187 write_wim_security_data(const struct wim_security_data * restrict sd,
190 DEBUG("Writing security data (total_length = %"PRIu32", num_entries "
191 "= %"PRIu32")", sd->total_length, sd->num_entries);
194 struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p;
195 u32 num_entries = sd->num_entries;
197 sd_disk->total_length = cpu_to_le32(sd->total_length);
198 sd_disk->num_entries = cpu_to_le32(num_entries);
200 for (u32 i = 0; i < num_entries; i++)
201 sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]);
203 p = (u8*)&sd_disk->sizes[num_entries];
205 for (u32 i = 0; i < num_entries; i++)
206 p = mempcpy(p, sd->descriptors[i], sd->sizes[i]);
208 while ((uintptr_t)p & 7)
211 wimlib_assert(p - orig_p == sd->total_length);
213 DEBUG("Successfully wrote security data.");
218 free_wim_security_data(struct wim_security_data *sd)
221 u8 **descriptors = sd->descriptors;
222 u32 num_entries = sd->num_entries;
224 while (num_entries--)
225 FREE(*descriptors++);
227 FREE(sd->descriptors);
234 u8 hash[SHA1_HASH_SIZE];
235 struct rb_node rb_node;
239 free_sd_tree(struct rb_node *node)
242 free_sd_tree(node->rb_left);
243 free_sd_tree(node->rb_right);
244 FREE(container_of(node, struct sd_node, rb_node));
248 /* Frees a security descriptor index set. */
250 destroy_sd_set(struct wim_sd_set *sd_set, bool rollback)
253 struct wim_security_data *sd = sd_set->sd;
254 u8 **descriptors = sd->descriptors + sd_set->orig_num_entries;
255 u32 num_entries = sd->num_entries - sd_set->orig_num_entries;
256 while (num_entries--)
257 FREE(*descriptors++);
258 sd->num_entries = sd_set->orig_num_entries;
260 free_sd_tree(sd_set->rb_root.rb_node);
263 /* Inserts a a new node into the security descriptor index tree. */
265 insert_sd_node(struct wim_sd_set *set, struct sd_node *new)
267 struct rb_root *root = &set->rb_root;
268 struct rb_node **p = &(root->rb_node);
269 struct rb_node *rb_parent = NULL;
272 struct sd_node *this = container_of(*p, struct sd_node, rb_node);
273 int cmp = hashes_cmp(new->hash, this->hash);
277 p = &((*p)->rb_left);
279 p = &((*p)->rb_right);
281 return false; /* Duplicate security descriptor */
283 rb_link_node(&new->rb_node, rb_parent, p);
284 rb_insert_color(&new->rb_node, root);
288 /* Returns the index of the security descriptor having a SHA1 message digest of
289 * @hash. If not found, return -1. */
291 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE])
293 struct rb_node *node = set->rb_root.rb_node;
296 struct sd_node *sd_node = container_of(node, struct sd_node, rb_node);
297 int cmp = hashes_cmp(hash, sd_node->hash);
299 node = node->rb_left;
301 node = node->rb_right;
303 return sd_node->security_id;
309 * Adds a security descriptor to the indexed security descriptor set as well as
310 * the corresponding `struct wim_security_data', and returns the new security
311 * ID; or, if there is an existing security descriptor that is the same, return
312 * the security ID for it. If a new security descriptor cannot be allocated,
316 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
318 u8 hash[SHA1_HASH_SIZE];
324 struct wim_security_data *sd;
327 sha1_buffer(descriptor, size, hash);
329 security_id = lookup_sd(sd_set, hash);
330 if (security_id >= 0) /* Identical descriptor already exists */
333 /* Need to add a new security descriptor */
336 new = MALLOC(sizeof(*new));
340 descr_copy = memdup(descriptor, size);
345 new->security_id = sd->num_entries;
346 copy_hash(new->hash, hash);
348 /* There typically are only a few dozen security descriptors in a
349 * directory tree, so expanding the array of security descriptors by
350 * only 1 extra space each time should not be a problem. */
351 descriptors = REALLOC(sd->descriptors,
352 (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
355 sd->descriptors = descriptors;
356 sizes = REALLOC(sd->sizes,
357 (sd->num_entries + 1) * sizeof(sd->sizes[0]));
361 sd->descriptors[sd->num_entries] = descr_copy;
362 sd->sizes[sd->num_entries] = size;
364 DEBUG("There are now %u security descriptors", sd->num_entries);
365 bret = insert_sd_node(sd_set, new);
367 security_id = new->security_id;
377 /* Initialize a `struct sd_set' mapping from SHA1 message digests of security
378 * descriptors to indices into the security descriptors table of the WIM image
381 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
386 sd_set->rb_root.rb_node = NULL;
388 /* Remember the original number of security descriptors so that newly
389 * added ones can be rolled back if needed. */
390 sd_set->orig_num_entries = sd->num_entries;
391 for (u32 i = 0; i < sd->num_entries; i++) {
394 new = MALLOC(sizeof(struct sd_node));
396 ret = WIMLIB_ERR_NOMEM;
397 goto out_destroy_sd_set;
399 sha1_buffer(sd->descriptors[i], sd->sizes[i], new->hash);
400 new->security_id = i;
401 if (!insert_sd_node(sd_set, new))
402 FREE(new); /* Ignore duplicate security descriptor */
407 destroy_sd_set(sd_set, false);