]> wimlib.net Git - wimlib/blob - src/security.c
read_metadata_resource(): Simplify check for short resource
[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 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/endianness.h"
32 #include "wimlib/error.h"
33 #include "wimlib/security.h"
34 #include "wimlib/security_descriptor.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  * @metadata_resource:  An array that contains the uncompressed metadata
54  *                              resource for the WIM image.
55  * @metadata_resource_len:      The length of @metadata_resource.
56  * @sd_ret:     A pointer to a pointer to a wim_security_data structure that
57  *              will be filled in with a pointer to a new wim_security_data
58  *              structure containing the security data on success.
59  *
60  * Note: There is no `offset' argument because the security data is located at
61  * the beginning of the metadata resource.
62  *
63  * Return values:
64  *      WIMLIB_ERR_SUCCESS (0)
65  *      WIMLIB_ERR_INVALID_METADATA_RESOURCE
66  *      WIMLIB_ERR_NOMEM
67  */
68 int
69 read_wim_security_data(const u8 metadata_resource[], size_t metadata_resource_len,
70                        struct wim_security_data **sd_ret)
71 {
72         struct wim_security_data *sd;
73         int ret;
74         u64 total_len;
75         u64 sizes_size;
76         u64 size_no_descriptors;
77         const struct wim_security_data_disk *sd_disk;
78         const u8 *p;
79
80         if (metadata_resource_len < 8)
81                 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
82
83         sd = new_wim_security_data();
84         if (!sd)
85                 goto out_of_memory;
86
87         sd_disk = (const struct wim_security_data_disk*)metadata_resource;
88         sd->total_length = le32_to_cpu(sd_disk->total_length);
89         sd->num_entries = le32_to_cpu(sd_disk->num_entries);
90
91         DEBUG("Reading security data: num_entries=%u, total_length=%u",
92               sd->num_entries, sd->total_length);
93
94         /* Length field of 0 is a special case that really means length
95          * of 8. */
96         if (sd->total_length == 0)
97                 sd->total_length = 8;
98
99         /* The security_id field of each dentry is a signed 32-bit integer, so
100          * the possible indices into the security descriptors table are 0
101          * through 0x7fffffff.  Which means 0x80000000 security descriptors
102          * maximum.  Not like you should ever have anywhere close to that many
103          * security descriptors! */
104         if (sd->num_entries > 0x80000000)
105                 goto out_invalid_sd;
106
107         /* Verify the listed total length of the security data is big enough to
108          * include the sizes array, verify that the file data is big enough to
109          * include it as well, then allocate the array of sizes.
110          *
111          * Note: The total length of the security data must fit in a 32-bit
112          * integer, even though each security descriptor size is a 64-bit
113          * integer.  This is stupid, and we need to be careful not to actually
114          * let the security descriptor sizes be over 0xffffffff.  */
115         if (sd->total_length > metadata_resource_len)
116                 goto out_invalid_sd;
117
118         sizes_size = (u64)sd->num_entries * sizeof(u64);
119         size_no_descriptors = 8 + sizes_size;
120         if (size_no_descriptors > sd->total_length)
121                 goto out_invalid_sd;
122
123         total_len = size_no_descriptors;
124
125         /* Return immediately if no security descriptors. */
126         if (sd->num_entries == 0)
127                 goto out_align_total_length;
128
129         /* Allocate a new buffer for the sizes array */
130         sd->sizes = MALLOC(sizes_size);
131         if (!sd->sizes)
132                 goto out_of_memory;
133
134         /* Copy the sizes array into the new buffer */
135         for (u32 i = 0; i < sd->num_entries; i++) {
136                 sd->sizes[i] = le64_to_cpu(sd_disk->sizes[i]);
137                 if (sd->sizes[i] > 0xffffffff)
138                         goto out_invalid_sd;
139         }
140
141         p = (const u8*)sd_disk + size_no_descriptors;
142
143         /* Allocate the array of pointers to the security descriptors, then read
144          * them into separate buffers. */
145         sd->descriptors = CALLOC(sd->num_entries, sizeof(sd->descriptors[0]));
146         if (!sd->descriptors)
147                 goto out_of_memory;
148
149         for (u32 i = 0; i < sd->num_entries; i++) {
150                 if (sd->sizes[i] == 0)
151                         continue;
152                 total_len += sd->sizes[i];
153                 if (total_len > (u64)sd->total_length)
154                         goto out_invalid_sd;
155                 sd->descriptors[i] = memdup(p, sd->sizes[i]);
156                 if (!sd->descriptors[i])
157                         goto out_of_memory;
158                 p += sd->sizes[i];
159         }
160 out_align_total_length:
161         total_len = (total_len + 7) & ~7;
162         sd->total_length = (sd->total_length + 7) & ~7;
163         if (total_len != sd->total_length) {
164                 WARNING("Expected WIM security data total length of "
165                         "%u bytes, but calculated %u bytes",
166                         sd->total_length, (unsigned)total_len);
167         }
168         if (sd->total_length > metadata_resource_len)
169                 goto out_invalid_sd;
170         *sd_ret = sd;
171         ret = 0;
172         goto out;
173 out_invalid_sd:
174         ERROR("WIM security data is invalid!");
175         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
176         goto out_free_sd;
177 out_of_memory:
178         ERROR("Out of memory while reading WIM security data!");
179         ret = WIMLIB_ERR_NOMEM;
180 out_free_sd:
181         free_wim_security_data(sd);
182 out:
183         return ret;
184 }
185
186 /*
187  * Writes the security data for a WIM image to an in-memory buffer.
188  */
189 u8 *
190 write_wim_security_data(const struct wim_security_data * restrict sd,
191                         u8 * restrict p)
192 {
193         DEBUG("Writing security data (total_length = %"PRIu32", num_entries "
194               "= %"PRIu32")", sd->total_length, sd->num_entries);
195
196         u8 *orig_p = p;
197         struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p;
198         u32 num_entries = sd->num_entries;
199
200         sd_disk->total_length = cpu_to_le32(sd->total_length);
201         sd_disk->num_entries = cpu_to_le32(num_entries);
202
203         for (u32 i = 0; i < num_entries; i++)
204                 sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]);
205
206         p = (u8*)&sd_disk->sizes[num_entries];
207
208         for (u32 i = 0; i < num_entries; i++)
209                 p = mempcpy(p, sd->descriptors[i], sd->sizes[i]);
210
211         while ((uintptr_t)p & 7)
212                 *p++ = 0;
213
214         wimlib_assert(p - orig_p == sd->total_length);
215
216         DEBUG("Successfully wrote security data.");
217         return p;
218 }
219
220 static void
221 print_acl(const wimlib_ACL *acl, const tchar *type, size_t max_size)
222 {
223         const u8 *p;
224
225         if (max_size < sizeof(wimlib_ACL))
226                 return;
227
228         u8 revision = acl->revision;
229         u16 acl_size = le16_to_cpu(acl->acl_size);
230         u16 ace_count = le16_to_cpu(acl->ace_count);
231
232         tprintf(T("    [%"TS" ACL]\n"), type);
233         tprintf(T("    Revision = %u\n"), revision);
234         tprintf(T("    ACL Size = %u\n"), acl_size);
235         tprintf(T("    ACE Count = %u\n"), ace_count);
236
237         p = (const u8*)acl + sizeof(wimlib_ACL);
238         for (u16 i = 0; i < ace_count; i++) {
239                 if (max_size < p + sizeof(wimlib_ACCESS_ALLOWED_ACE) - (const u8*)acl)
240                         break;
241                 const wimlib_ACCESS_ALLOWED_ACE *aaa = (const wimlib_ACCESS_ALLOWED_ACE*)p;
242                 tprintf(T("        [ACE]\n"));
243                 tprintf(T("        ACE type  = %d\n"), aaa->hdr.type);
244                 tprintf(T("        ACE flags = 0x%x\n"), aaa->hdr.flags);
245                 tprintf(T("        ACE size  = %u\n"), le16_to_cpu(aaa->hdr.size));
246                 tprintf(T("        ACE mask = %x\n"), le32_to_cpu(aaa->mask));
247                 tprintf(T("        SID start = %u\n"), le32_to_cpu(aaa->sid_start));
248                 p += le16_to_cpu(aaa->hdr.size);
249         }
250         tputchar(T('\n'));
251 }
252
253 static void
254 print_sid(const wimlib_SID *sid, const tchar *type, size_t max_size)
255 {
256         if (max_size < sizeof(wimlib_SID))
257                 return;
258
259         tprintf(T("    [%"TS" SID]\n"), type);
260         tprintf(T("    Revision = %u\n"), sid->revision);
261         tprintf(T("    Subauthority count = %u\n"), sid->sub_authority_count);
262         tprintf(T("    Identifier authority = "));
263         print_byte_field(sid->identifier_authority,
264                          sizeof(sid->identifier_authority), stdout);
265         tputchar(T('\n'));
266         if (max_size < sizeof(wimlib_SID) + (size_t)sid->sub_authority_count * sizeof(u32))
267                 return;
268         for (u8 i = 0; i < sid->sub_authority_count; i++) {
269                 tprintf(T("    Subauthority %u = %u\n"),
270                         i, le32_to_cpu(sid->sub_authority[i]));
271         }
272         tputchar(T('\n'));
273 }
274
275 static void
276 print_security_descriptor(const wimlib_SECURITY_DESCRIPTOR_RELATIVE *descr,
277                           size_t size)
278 {
279         u8 revision      = descr->revision;
280         u16 control      = le16_to_cpu(descr->control);
281         u32 owner_offset = le32_to_cpu(descr->owner_offset);
282         u32 group_offset = le32_to_cpu(descr->group_offset);
283         u32 dacl_offset  = le32_to_cpu(descr->dacl_offset);
284         u32 sacl_offset  = le32_to_cpu(descr->sacl_offset);
285
286         tprintf(T("Revision = %u\n"), revision);
287         tprintf(T("Security Descriptor Control = %#x\n"), control);
288         tprintf(T("Owner offset = %u\n"), owner_offset);
289         tprintf(T("Group offset = %u\n"), group_offset);
290         tprintf(T("Discretionary ACL offset = %u\n"), dacl_offset);
291         tprintf(T("System ACL offset = %u\n"), sacl_offset);
292
293         if (owner_offset != 0 && owner_offset <= size)
294                 print_sid((const wimlib_SID*)((const u8*)descr + owner_offset),
295                           T("Owner"), size - owner_offset);
296
297         if (group_offset != 0 && group_offset <= size)
298                 print_sid((const wimlib_SID*)((const u8*)descr + group_offset),
299                           T("Group"), size - group_offset);
300
301         if (dacl_offset != 0 && dacl_offset <= size)
302                 print_acl((const wimlib_ACL*)((const u8*)descr + dacl_offset),
303                           T("Discretionary"), size - dacl_offset);
304
305         if (sacl_offset != 0 && sacl_offset <= size)
306                 print_acl((const wimlib_ACL*)((const u8*)descr + sacl_offset),
307                           T("System"), size - sacl_offset);
308 }
309
310 /*
311  * Prints the security data for a WIM file.
312  */
313 void
314 print_wim_security_data(const struct wim_security_data *sd)
315 {
316         tputs(T("[SECURITY DATA]"));
317         tprintf(T("Length            = %"PRIu32" bytes\n"), sd->total_length);
318         tprintf(T("Number of Entries = %"PRIu32"\n"), sd->num_entries);
319
320         for (u32 i = 0; i < sd->num_entries; i++) {
321                 tprintf(T("[SECURITY_DESCRIPTOR_RELATIVE %"PRIu32", length = %"PRIu64"]\n"),
322                         i, sd->sizes[i]);
323                 print_security_descriptor((const wimlib_SECURITY_DESCRIPTOR_RELATIVE*)sd->descriptors[i],
324                                           sd->sizes[i]);
325                 tputchar(T('\n'));
326         }
327         tputchar(T('\n'));
328 }
329
330 void
331 free_wim_security_data(struct wim_security_data *sd)
332 {
333         if (sd) {
334                 u8 **descriptors = sd->descriptors;
335                 u32 num_entries  = sd->num_entries;
336                 if (descriptors)
337                         while (num_entries--)
338                                 FREE(*descriptors++);
339                 FREE(sd->sizes);
340                 FREE(sd->descriptors);
341                 FREE(sd);
342         }
343 }
344
345 struct sd_node {
346         int security_id;
347         u8 hash[SHA1_HASH_SIZE];
348         struct rb_node rb_node;
349 };
350
351 static void
352 free_sd_tree(struct rb_node *node)
353 {
354         if (node) {
355                 free_sd_tree(node->rb_left);
356                 free_sd_tree(node->rb_right);
357                 FREE(container_of(node, struct sd_node, rb_node));
358         }
359 }
360
361 /* Frees a security descriptor index set. */
362 void
363 destroy_sd_set(struct wim_sd_set *sd_set, bool rollback)
364 {
365         if (rollback) {
366                 struct wim_security_data *sd = sd_set->sd;
367                 u8 **descriptors = sd->descriptors + sd_set->orig_num_entries;
368                 u32 num_entries  = sd->num_entries - sd_set->orig_num_entries;
369                 while (num_entries--)
370                         FREE(*descriptors++);
371                 sd->num_entries = sd_set->orig_num_entries;
372         }
373         free_sd_tree(sd_set->rb_root.rb_node);
374 }
375
376 /* Inserts a a new node into the security descriptor index tree. */
377 static bool
378 insert_sd_node(struct wim_sd_set *set, struct sd_node *new)
379 {
380         struct rb_root *root = &set->rb_root;
381         struct rb_node **p = &(root->rb_node);
382         struct rb_node *rb_parent = NULL;
383
384         while (*p) {
385                 struct sd_node *this = container_of(*p, struct sd_node, rb_node);
386                 int cmp = hashes_cmp(new->hash, this->hash);
387
388                 rb_parent = *p;
389                 if (cmp < 0)
390                         p = &((*p)->rb_left);
391                 else if (cmp > 0)
392                         p = &((*p)->rb_right);
393                 else
394                         return false; /* Duplicate security descriptor */
395         }
396         rb_link_node(&new->rb_node, rb_parent, p);
397         rb_insert_color(&new->rb_node, root);
398         return true;
399 }
400
401 /* Returns the index of the security descriptor having a SHA1 message digest of
402  * @hash.  If not found, return -1. */
403 int
404 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE])
405 {
406         struct rb_node *node = set->rb_root.rb_node;
407
408         while (node) {
409                 struct sd_node *sd_node = container_of(node, struct sd_node, rb_node);
410                 int cmp = hashes_cmp(hash, sd_node->hash);
411                 if (cmp < 0)
412                         node = node->rb_left;
413                 else if (cmp > 0)
414                         node = node->rb_right;
415                 else
416                         return sd_node->security_id;
417         }
418         return -1;
419 }
420
421 /*
422  * Adds a security descriptor to the indexed security descriptor set as well as
423  * the corresponding `struct wim_security_data', and returns the new security
424  * ID; or, if there is an existing security descriptor that is the same, return
425  * the security ID for it.  If a new security descriptor cannot be allocated,
426  * return -1.
427  */
428 int
429 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
430 {
431         u8 hash[SHA1_HASH_SIZE];
432         int security_id;
433         struct sd_node *new;
434         u8 **descriptors;
435         u64 *sizes;
436         u8 *descr_copy;
437         struct wim_security_data *sd;
438         bool bret;
439
440         sha1_buffer(descriptor, size, hash);
441
442         security_id = lookup_sd(sd_set, hash);
443         if (security_id >= 0) /* Identical descriptor already exists */
444                 goto out;
445
446         /* Need to add a new security descriptor */
447         security_id = -1;
448
449         new = MALLOC(sizeof(*new));
450         if (!new)
451                 goto out;
452
453         descr_copy = memdup(descriptor, size);
454         if (!descr_copy)
455                 goto out_free_node;
456
457         sd = sd_set->sd;
458         new->security_id = sd->num_entries;
459         copy_hash(new->hash, hash);
460
461         /* There typically are only a few dozen security descriptors in a
462          * directory tree, so expanding the array of security descriptors by
463          * only 1 extra space each time should not be a problem. */
464         descriptors = REALLOC(sd->descriptors,
465                               (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
466         if (!descriptors)
467                 goto out_free_descr;
468         sd->descriptors = descriptors;
469         sizes = REALLOC(sd->sizes,
470                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
471         if (!sizes)
472                 goto out_free_descr;
473         sd->sizes = sizes;
474         sd->descriptors[sd->num_entries] = descr_copy;
475         sd->sizes[sd->num_entries] = size;
476         sd->num_entries++;
477         DEBUG("There are now %u security descriptors", sd->num_entries);
478         bret = insert_sd_node(sd_set, new);
479         wimlib_assert(bret);
480         security_id = new->security_id;
481         goto out;
482 out_free_descr:
483         FREE(descr_copy);
484 out_free_node:
485         FREE(new);
486 out:
487         return security_id;
488 }
489
490 /* Initialize a `struct sd_set' mapping from SHA1 message digests of security
491  * descriptors to indices into the security descriptors table of the WIM image
492  * (security IDs).  */
493 int
494 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
495 {
496         int ret;
497
498         sd_set->sd = sd;
499         sd_set->rb_root.rb_node = NULL;
500
501         /* Remember the original number of security descriptors so that newly
502          * added ones can be rolled back if needed. */
503         sd_set->orig_num_entries = sd->num_entries;
504         for (u32 i = 0; i < sd->num_entries; i++) {
505                 struct sd_node *new;
506
507                 new = MALLOC(sizeof(struct sd_node));
508                 if (!new) {
509                         ret = WIMLIB_ERR_NOMEM;
510                         goto out_destroy_sd_set;
511                 }
512                 sha1_buffer(sd->descriptors[i], sd->sizes[i], new->hash);
513                 new->security_id = i;
514                 if (!insert_sd_node(sd_set, new))
515                         FREE(new); /* Ignore duplicate security descriptor */
516         }
517         ret = 0;
518         goto out;
519 out_destroy_sd_set:
520         destroy_sd_set(sd_set, false);
521 out:
522         return ret;
523 }