4 * Read and write the per-WIM-image table of security descriptors.
8 * Copyright 2012-2023 Eric Biggers
10 * This file is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU Lesser General Public License as published by the Free
12 * Software Foundation; either version 3 of the License, or (at your option) any
15 * This file is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this file; if not, see https://www.gnu.org/licenses/.
28 #include "wimlib/assert.h"
29 #include "wimlib/avl_tree.h"
30 #include "wimlib/endianness.h"
31 #include "wimlib/error.h"
32 #include "wimlib/security.h"
33 #include "wimlib/sha1.h"
34 #include "wimlib/util.h"
36 struct wim_security_data_disk {
40 } __attribute__((packed));
42 struct wim_security_data *
43 new_wim_security_data(void)
45 return CALLOC(1, sizeof(struct wim_security_data));
49 * Reads the security data from the metadata resource of a WIM image.
52 * Buffer containing an uncompressed WIM metadata resource.
54 * Length of the uncompressed metadata resource, in bytes.
56 * On success, a pointer to the resulting security data structure will be
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 *buf, size_t buf_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;
80 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
82 sd = new_wim_security_data();
86 sd_disk = (const struct wim_security_data_disk *)buf;
87 sd->total_length = ALIGN(le32_to_cpu(sd_disk->total_length), 8);
88 sd->num_entries = le32_to_cpu(sd_disk->num_entries);
90 /* Length field of 0 is a special case that really means length
92 if (sd->total_length == 0)
95 /* The security_id field of each dentry is a signed 32-bit integer, so
96 * the possible indices into the security descriptors table are 0
97 * through 0x7fffffff. Which means 0x80000000 security descriptors
98 * maximum. Not like you should ever have anywhere close to that many
99 * security descriptors! */
100 if (sd->num_entries > 0x80000000)
103 /* Verify the listed total length of the security data is big enough to
104 * include the sizes array, verify that the file data is big enough to
105 * include it as well, then allocate the array of sizes.
107 * Note: The total length of the security data must fit in a 32-bit
108 * integer, even though each security descriptor size is a 64-bit
109 * integer. This is stupid, and we need to be careful not to actually
110 * let the security descriptor sizes be over 0xffffffff. */
111 if (sd->total_length > buf_len)
114 sizes_size = (u64)sd->num_entries * sizeof(u64);
115 size_no_descriptors = 8 + sizes_size;
116 if (size_no_descriptors > sd->total_length)
119 total_len = size_no_descriptors;
121 /* Return immediately if no security descriptors. */
122 if (sd->num_entries == 0)
123 goto out_descriptors_ready;
125 /* Allocate a new buffer for the sizes array */
126 sd->sizes = MALLOC(sizes_size);
130 /* Copy the sizes array into the new buffer */
131 for (u32 i = 0; i < sd->num_entries; i++) {
132 sd->sizes[i] = le64_to_cpu(sd_disk->sizes[i]);
133 if (sd->sizes[i] > 0xffffffff)
137 p = (const u8*)sd_disk + size_no_descriptors;
139 /* Allocate the array of pointers to the security descriptors, then read
140 * them into separate buffers. */
141 sd->descriptors = CALLOC(sd->num_entries, sizeof(sd->descriptors[0]));
142 if (!sd->descriptors)
145 for (u32 i = 0; i < sd->num_entries; i++) {
146 if (sd->sizes[i] == 0)
148 total_len += sd->sizes[i];
149 if (total_len > (u64)sd->total_length)
151 sd->descriptors[i] = memdup(p, sd->sizes[i]);
152 if (!sd->descriptors[i])
156 out_descriptors_ready:
157 if (ALIGN(total_len, 8) != sd->total_length) {
158 WARNING("Stored WIM security data total length was "
159 "%"PRIu32" bytes, but calculated %"PRIu32" bytes",
160 sd->total_length, (u32)total_len);
166 ERROR("WIM security data is invalid!");
167 ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
170 ERROR("Out of memory while reading WIM security data!");
171 ret = WIMLIB_ERR_NOMEM;
173 free_wim_security_data(sd);
179 * Writes the security data for a WIM image to an in-memory buffer.
182 write_wim_security_data(const struct wim_security_data * restrict sd,
186 struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p;
187 u32 num_entries = sd->num_entries;
189 sd_disk->total_length = cpu_to_le32(sd->total_length);
190 sd_disk->num_entries = cpu_to_le32(num_entries);
192 for (u32 i = 0; i < num_entries; i++)
193 sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]);
195 p = (u8*)&sd_disk->sizes[num_entries];
197 for (u32 i = 0; i < num_entries; i++)
198 p = mempcpy(p, sd->descriptors[i], sd->sizes[i]);
200 while ((uintptr_t)p & 7)
203 wimlib_assert(p - orig_p == sd->total_length);
208 free_wim_security_data(struct wim_security_data *sd)
211 u8 **descriptors = sd->descriptors;
212 u32 num_entries = sd->num_entries;
214 while (num_entries--)
215 FREE(*descriptors++);
217 FREE(sd->descriptors);
224 u8 hash[SHA1_HASH_SIZE];
225 struct avl_tree_node index_node;
228 #define SD_NODE(avl_node) \
229 avl_tree_entry(avl_node, struct sd_node, index_node)
232 free_sd_tree(struct avl_tree_node *node)
235 free_sd_tree(node->left);
236 free_sd_tree(node->right);
242 rollback_new_security_descriptors(struct wim_sd_set *sd_set)
244 struct wim_security_data *sd = sd_set->sd;
247 for (i = sd_set->orig_num_entries; i < sd->num_entries; i++)
248 FREE(sd->descriptors[i]);
249 sd->num_entries = sd_set->orig_num_entries;
252 /* Frees a security descriptor index set. */
254 destroy_sd_set(struct wim_sd_set *sd_set)
256 free_sd_tree(sd_set->root);
260 _avl_cmp_nodes_by_hash(const struct avl_tree_node *n1,
261 const struct avl_tree_node *n2)
263 return hashes_cmp(SD_NODE(n1)->hash, SD_NODE(n2)->hash);
266 /* Inserts a new node into the security descriptor index tree. Returns true
267 * if successful (not a duplicate). */
269 insert_sd_node(struct wim_sd_set *set, struct sd_node *new)
271 return NULL == avl_tree_insert(&set->root, &new->index_node,
272 _avl_cmp_nodes_by_hash);
275 /* Returns the index of the security descriptor having a SHA1 message digest of
276 * @hash. If not found, return -1. */
278 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE])
280 struct avl_tree_node *res;
281 struct sd_node dummy;
283 copy_hash(dummy.hash, hash);
284 res = avl_tree_lookup_node(set->root, &dummy.index_node,
285 _avl_cmp_nodes_by_hash);
288 return SD_NODE(res)->security_id;
292 * Adds a security descriptor to the indexed security descriptor set as well as
293 * the corresponding `struct wim_security_data', and returns the new security
294 * ID; or, if there is an existing security descriptor that is the same, return
295 * the security ID for it. If a new security descriptor cannot be allocated,
299 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
301 u8 hash[SHA1_HASH_SIZE];
307 struct wim_security_data *sd;
310 sha1(descriptor, size, hash);
312 security_id = lookup_sd(sd_set, hash);
313 if (security_id >= 0) /* Identical descriptor already exists */
316 /* Need to add a new security descriptor */
319 new = MALLOC(sizeof(*new));
323 descr_copy = memdup(descriptor, size);
328 new->security_id = sd->num_entries;
329 copy_hash(new->hash, hash);
331 /* There typically are only a few dozen security descriptors in a
332 * directory tree, so expanding the array of security descriptors by
333 * only 1 extra space each time should not be a problem. */
334 descriptors = REALLOC(sd->descriptors,
335 (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
338 sd->descriptors = descriptors;
339 sizes = REALLOC(sd->sizes,
340 (sd->num_entries + 1) * sizeof(sd->sizes[0]));
344 sd->descriptors[sd->num_entries] = descr_copy;
345 sd->sizes[sd->num_entries] = size;
347 bret = insert_sd_node(sd_set, new);
349 security_id = new->security_id;
359 /* Initialize a `struct sd_set' mapping from SHA1 message digests of security
360 * descriptors to indices into the security descriptors table of the WIM image
363 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
370 /* Remember the original number of security descriptors so that newly
371 * added ones can be rolled back if needed. */
372 sd_set->orig_num_entries = sd->num_entries;
373 for (u32 i = 0; i < sd->num_entries; i++) {
376 new = MALLOC(sizeof(struct sd_node));
378 ret = WIMLIB_ERR_NOMEM;
379 goto out_destroy_sd_set;
381 sha1(sd->descriptors[i], sd->sizes[i], new->hash);
382 new->security_id = i;
383 if (!insert_sd_node(sd_set, new))
384 FREE(new); /* Ignore duplicate security descriptor */
389 destroy_sd_set(sd_set);