]> wimlib.net Git - wimlib/blob - src/security.c
lzx-compress.c: Rename lzx_record_ctx.matches
[wimlib] / src / security.c
1 /*
2  * security.c
3  *
4  * Read and write the per-WIM-image table of security descriptors.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013, 2014 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
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)
15  * any later version.
16  *
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
20  * details.
21  *
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/.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "wimlib/assert.h"
31 #include "wimlib/avl_tree.h"
32 #include "wimlib/endianness.h"
33 #include "wimlib/error.h"
34 #include "wimlib/security.h"
35 #include "wimlib/sha1.h"
36 #include "wimlib/util.h"
37
38 struct wim_security_data_disk {
39         le32 total_length;
40         le32 num_entries;
41         le64 sizes[];
42 } _packed_attribute;
43
44 struct wim_security_data *
45 new_wim_security_data(void)
46 {
47         return CALLOC(1, sizeof(struct wim_security_data));
48 }
49
50 /*
51  * Reads the security data from the metadata resource of a WIM image.
52  *
53  * @buf
54  *      Buffer containing an uncompressed WIM metadata resource.
55  * @buf_len
56  *      Length of the uncompressed metadata resource, in bytes.
57  * @sd_ret
58  *      On success, a pointer to the resulting security data structure will be
59  *      returned here.
60  *
61  * Note: There is no `offset' argument because the security data is located at
62  * the beginning of the metadata resource.
63  *
64  * Return values:
65  *      WIMLIB_ERR_SUCCESS (0)
66  *      WIMLIB_ERR_INVALID_METADATA_RESOURCE
67  *      WIMLIB_ERR_NOMEM
68  */
69 int
70 read_wim_security_data(const u8 *buf, size_t buf_len,
71                        struct wim_security_data **sd_ret)
72 {
73         struct wim_security_data *sd;
74         int ret;
75         u64 total_len;
76         u64 sizes_size;
77         u64 size_no_descriptors;
78         const struct wim_security_data_disk *sd_disk;
79         const u8 *p;
80
81         if (buf_len < 8)
82                 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
83
84         sd = new_wim_security_data();
85         if (!sd)
86                 goto out_of_memory;
87
88         sd_disk = (const struct wim_security_data_disk *)buf;
89         sd->total_length = le32_to_cpu(sd_disk->total_length);
90         sd->num_entries = le32_to_cpu(sd_disk->num_entries);
91
92         DEBUG("Reading security data: num_entries=%u, total_length=%u",
93               sd->num_entries, sd->total_length);
94
95         /* Length field of 0 is a special case that really means length
96          * of 8. */
97         if (sd->total_length == 0)
98                 sd->total_length = 8;
99
100         /* The security_id field of each dentry is a signed 32-bit integer, so
101          * the possible indices into the security descriptors table are 0
102          * through 0x7fffffff.  Which means 0x80000000 security descriptors
103          * maximum.  Not like you should ever have anywhere close to that many
104          * security descriptors! */
105         if (sd->num_entries > 0x80000000)
106                 goto out_invalid_sd;
107
108         /* Verify the listed total length of the security data is big enough to
109          * include the sizes array, verify that the file data is big enough to
110          * include it as well, then allocate the array of sizes.
111          *
112          * Note: The total length of the security data must fit in a 32-bit
113          * integer, even though each security descriptor size is a 64-bit
114          * integer.  This is stupid, and we need to be careful not to actually
115          * let the security descriptor sizes be over 0xffffffff.  */
116         if (sd->total_length > buf_len)
117                 goto out_invalid_sd;
118
119         sizes_size = (u64)sd->num_entries * sizeof(u64);
120         size_no_descriptors = 8 + sizes_size;
121         if (size_no_descriptors > sd->total_length)
122                 goto out_invalid_sd;
123
124         total_len = size_no_descriptors;
125
126         /* Return immediately if no security descriptors. */
127         if (sd->num_entries == 0)
128                 goto out_align_total_length;
129
130         /* Allocate a new buffer for the sizes array */
131         sd->sizes = MALLOC(sizes_size);
132         if (!sd->sizes)
133                 goto out_of_memory;
134
135         /* Copy the sizes array into the new buffer */
136         for (u32 i = 0; i < sd->num_entries; i++) {
137                 sd->sizes[i] = le64_to_cpu(sd_disk->sizes[i]);
138                 if (sd->sizes[i] > 0xffffffff)
139                         goto out_invalid_sd;
140         }
141
142         p = (const u8*)sd_disk + size_no_descriptors;
143
144         /* Allocate the array of pointers to the security descriptors, then read
145          * them into separate buffers. */
146         sd->descriptors = CALLOC(sd->num_entries, sizeof(sd->descriptors[0]));
147         if (!sd->descriptors)
148                 goto out_of_memory;
149
150         for (u32 i = 0; i < sd->num_entries; i++) {
151                 if (sd->sizes[i] == 0)
152                         continue;
153                 total_len += sd->sizes[i];
154                 if (total_len > (u64)sd->total_length)
155                         goto out_invalid_sd;
156                 sd->descriptors[i] = memdup(p, sd->sizes[i]);
157                 if (!sd->descriptors[i])
158                         goto out_of_memory;
159                 p += sd->sizes[i];
160         }
161 out_align_total_length:
162         total_len = (total_len + 7) & ~7;
163         sd->total_length = (sd->total_length + 7) & ~7;
164         if (total_len != sd->total_length) {
165                 WARNING("Expected WIM security data total length of "
166                         "%u bytes, but calculated %u bytes",
167                         sd->total_length, (unsigned)total_len);
168         }
169         *sd_ret = sd;
170         ret = 0;
171         goto out;
172 out_invalid_sd:
173         ERROR("WIM security data is invalid!");
174         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
175         goto out_free_sd;
176 out_of_memory:
177         ERROR("Out of memory while reading WIM security data!");
178         ret = WIMLIB_ERR_NOMEM;
179 out_free_sd:
180         free_wim_security_data(sd);
181 out:
182         return ret;
183 }
184
185 /*
186  * Writes the security data for a WIM image to an in-memory buffer.
187  */
188 u8 *
189 write_wim_security_data(const struct wim_security_data * restrict sd,
190                         u8 * restrict p)
191 {
192         DEBUG("Writing security data (total_length = %"PRIu32", num_entries "
193               "= %"PRIu32")", sd->total_length, sd->num_entries);
194
195         u8 *orig_p = p;
196         struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p;
197         u32 num_entries = sd->num_entries;
198
199         sd_disk->total_length = cpu_to_le32(sd->total_length);
200         sd_disk->num_entries = cpu_to_le32(num_entries);
201
202         for (u32 i = 0; i < num_entries; i++)
203                 sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]);
204
205         p = (u8*)&sd_disk->sizes[num_entries];
206
207         for (u32 i = 0; i < num_entries; i++)
208                 p = mempcpy(p, sd->descriptors[i], sd->sizes[i]);
209
210         while ((uintptr_t)p & 7)
211                 *p++ = 0;
212
213         wimlib_assert(p - orig_p == sd->total_length);
214
215         DEBUG("Successfully wrote security data.");
216         return p;
217 }
218
219 void
220 free_wim_security_data(struct wim_security_data *sd)
221 {
222         if (sd) {
223                 u8 **descriptors = sd->descriptors;
224                 u32 num_entries  = sd->num_entries;
225                 if (descriptors)
226                         while (num_entries--)
227                                 FREE(*descriptors++);
228                 FREE(sd->sizes);
229                 FREE(sd->descriptors);
230                 FREE(sd);
231         }
232 }
233
234 struct sd_node {
235         int32_t security_id;
236         u8 hash[SHA1_HASH_SIZE];
237         struct avl_tree_node index_node;
238 };
239
240 #define SD_NODE(avl_node) \
241         avl_tree_entry(avl_node, struct sd_node, index_node)
242
243 static void
244 free_sd_tree(struct avl_tree_node *node)
245 {
246         if (node) {
247                 free_sd_tree(node->left);
248                 free_sd_tree(node->right);
249                 FREE(SD_NODE(node));
250         }
251 }
252
253 void
254 rollback_new_security_descriptors(struct wim_sd_set *sd_set)
255 {
256         struct wim_security_data *sd = sd_set->sd;
257         u8 **descriptors = sd->descriptors + sd_set->orig_num_entries;
258         u32 num_entries  = sd->num_entries - sd_set->orig_num_entries;
259         while (num_entries--)
260                 FREE(*descriptors++);
261         sd->num_entries = sd_set->orig_num_entries;
262 }
263
264 /* Frees a security descriptor index set. */
265 void
266 destroy_sd_set(struct wim_sd_set *sd_set)
267 {
268         free_sd_tree(sd_set->root);
269 }
270
271 static int
272 _avl_cmp_nodes_by_hash(const struct avl_tree_node *n1,
273                        const struct avl_tree_node *n2)
274 {
275         return hashes_cmp(SD_NODE(n1)->hash, SD_NODE(n2)->hash);
276 }
277
278 /* Inserts a new node into the security descriptor index tree.  Returns true
279  * if successful (not a duplicate).  */
280 static bool
281 insert_sd_node(struct wim_sd_set *set, struct sd_node *new)
282 {
283         return NULL == avl_tree_insert(&set->root, &new->index_node,
284                                        _avl_cmp_nodes_by_hash);
285 }
286
287 /* Returns the index of the security descriptor having a SHA1 message digest of
288  * @hash.  If not found, return -1. */
289 static int32_t
290 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE])
291 {
292         struct avl_tree_node *res;
293         struct sd_node dummy;
294
295         copy_hash(dummy.hash, hash);
296         res = avl_tree_lookup_node(set->root, &dummy.index_node,
297                                    _avl_cmp_nodes_by_hash);
298         if (!res)
299                 return -1;
300         return SD_NODE(res)->security_id;
301 }
302
303 /*
304  * Adds a security descriptor to the indexed security descriptor set as well as
305  * the corresponding `struct wim_security_data', and returns the new security
306  * ID; or, if there is an existing security descriptor that is the same, return
307  * the security ID for it.  If a new security descriptor cannot be allocated,
308  * return -1.
309  */
310 int32_t
311 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
312 {
313         u8 hash[SHA1_HASH_SIZE];
314         int32_t security_id;
315         struct sd_node *new;
316         u8 **descriptors;
317         u64 *sizes;
318         u8 *descr_copy;
319         struct wim_security_data *sd;
320         bool bret;
321
322         sha1_buffer(descriptor, size, hash);
323
324         security_id = lookup_sd(sd_set, hash);
325         if (security_id >= 0) /* Identical descriptor already exists */
326                 goto out;
327
328         /* Need to add a new security descriptor */
329         security_id = -1;
330
331         new = MALLOC(sizeof(*new));
332         if (!new)
333                 goto out;
334
335         descr_copy = memdup(descriptor, size);
336         if (!descr_copy)
337                 goto out_free_node;
338
339         sd = sd_set->sd;
340         new->security_id = sd->num_entries;
341         copy_hash(new->hash, hash);
342
343         /* There typically are only a few dozen security descriptors in a
344          * directory tree, so expanding the array of security descriptors by
345          * only 1 extra space each time should not be a problem. */
346         descriptors = REALLOC(sd->descriptors,
347                               (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
348         if (!descriptors)
349                 goto out_free_descr;
350         sd->descriptors = descriptors;
351         sizes = REALLOC(sd->sizes,
352                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
353         if (!sizes)
354                 goto out_free_descr;
355         sd->sizes = sizes;
356         sd->descriptors[sd->num_entries] = descr_copy;
357         sd->sizes[sd->num_entries] = size;
358         sd->num_entries++;
359         DEBUG("There are now %u security descriptors", sd->num_entries);
360         bret = insert_sd_node(sd_set, new);
361         wimlib_assert(bret);
362         security_id = new->security_id;
363         goto out;
364 out_free_descr:
365         FREE(descr_copy);
366 out_free_node:
367         FREE(new);
368 out:
369         return security_id;
370 }
371
372 /* Initialize a `struct sd_set' mapping from SHA1 message digests of security
373  * descriptors to indices into the security descriptors table of the WIM image
374  * (security IDs).  */
375 int
376 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
377 {
378         int ret;
379
380         sd_set->sd = sd;
381         sd_set->root = NULL;
382
383         /* Remember the original number of security descriptors so that newly
384          * added ones can be rolled back if needed. */
385         sd_set->orig_num_entries = sd->num_entries;
386         for (u32 i = 0; i < sd->num_entries; i++) {
387                 struct sd_node *new;
388
389                 new = MALLOC(sizeof(struct sd_node));
390                 if (!new) {
391                         ret = WIMLIB_ERR_NOMEM;
392                         goto out_destroy_sd_set;
393                 }
394                 sha1_buffer(sd->descriptors[i], sd->sizes[i], new->hash);
395                 new->security_id = i;
396                 if (!insert_sd_node(sd_set, new))
397                         FREE(new); /* Ignore duplicate security descriptor */
398         }
399         ret = 0;
400         goto out;
401 out_destroy_sd_set:
402         destroy_sd_set(sd_set);
403 out:
404         return ret;
405 }