]> wimlib.net Git - wimlib/blob - src/security.c
compress_parallel.c: Determine physical memory on OS X
[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/sha1.h"
35 #include "wimlib/util.h"
36
37 struct wim_security_data_disk {
38         le32 total_length;
39         le32 num_entries;
40         le64 sizes[];
41 } _packed_attribute;
42
43 struct wim_security_data *
44 new_wim_security_data(void)
45 {
46         return CALLOC(1, sizeof(struct wim_security_data));
47 }
48
49 /*
50  * Reads the security data from the metadata resource of a WIM image.
51  *
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.
58  *
59  * Note: There is no `offset' argument because the security data is located at
60  * the beginning of the metadata resource.
61  *
62  * Return values:
63  *      WIMLIB_ERR_SUCCESS (0)
64  *      WIMLIB_ERR_INVALID_METADATA_RESOURCE
65  *      WIMLIB_ERR_NOMEM
66  */
67 int
68 read_wim_security_data(const u8 metadata_resource[], size_t metadata_resource_len,
69                        struct wim_security_data **sd_ret)
70 {
71         struct wim_security_data *sd;
72         int ret;
73         u64 total_len;
74         u64 sizes_size;
75         u64 size_no_descriptors;
76         const struct wim_security_data_disk *sd_disk;
77         const u8 *p;
78
79         if (metadata_resource_len < 8)
80                 return WIMLIB_ERR_INVALID_METADATA_RESOURCE;
81
82         sd = new_wim_security_data();
83         if (!sd)
84                 goto out_of_memory;
85
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);
89
90         DEBUG("Reading security data: num_entries=%u, total_length=%u",
91               sd->num_entries, sd->total_length);
92
93         /* Length field of 0 is a special case that really means length
94          * of 8. */
95         if (sd->total_length == 0)
96                 sd->total_length = 8;
97
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)
104                 goto out_invalid_sd;
105
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.
109          *
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)
115                 goto out_invalid_sd;
116
117         sizes_size = (u64)sd->num_entries * sizeof(u64);
118         size_no_descriptors = 8 + sizes_size;
119         if (size_no_descriptors > sd->total_length)
120                 goto out_invalid_sd;
121
122         total_len = size_no_descriptors;
123
124         /* Return immediately if no security descriptors. */
125         if (sd->num_entries == 0)
126                 goto out_align_total_length;
127
128         /* Allocate a new buffer for the sizes array */
129         sd->sizes = MALLOC(sizes_size);
130         if (!sd->sizes)
131                 goto out_of_memory;
132
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)
137                         goto out_invalid_sd;
138         }
139
140         p = (const u8*)sd_disk + size_no_descriptors;
141
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)
146                 goto out_of_memory;
147
148         for (u32 i = 0; i < sd->num_entries; i++) {
149                 if (sd->sizes[i] == 0)
150                         continue;
151                 total_len += sd->sizes[i];
152                 if (total_len > (u64)sd->total_length)
153                         goto out_invalid_sd;
154                 sd->descriptors[i] = memdup(p, sd->sizes[i]);
155                 if (!sd->descriptors[i])
156                         goto out_of_memory;
157                 p += sd->sizes[i];
158         }
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);
166         }
167         *sd_ret = sd;
168         ret = 0;
169         goto out;
170 out_invalid_sd:
171         ERROR("WIM security data is invalid!");
172         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
173         goto out_free_sd;
174 out_of_memory:
175         ERROR("Out of memory while reading WIM security data!");
176         ret = WIMLIB_ERR_NOMEM;
177 out_free_sd:
178         free_wim_security_data(sd);
179 out:
180         return ret;
181 }
182
183 /*
184  * Writes the security data for a WIM image to an in-memory buffer.
185  */
186 u8 *
187 write_wim_security_data(const struct wim_security_data * restrict sd,
188                         u8 * restrict p)
189 {
190         DEBUG("Writing security data (total_length = %"PRIu32", num_entries "
191               "= %"PRIu32")", sd->total_length, sd->num_entries);
192
193         u8 *orig_p = p;
194         struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p;
195         u32 num_entries = sd->num_entries;
196
197         sd_disk->total_length = cpu_to_le32(sd->total_length);
198         sd_disk->num_entries = cpu_to_le32(num_entries);
199
200         for (u32 i = 0; i < num_entries; i++)
201                 sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]);
202
203         p = (u8*)&sd_disk->sizes[num_entries];
204
205         for (u32 i = 0; i < num_entries; i++)
206                 p = mempcpy(p, sd->descriptors[i], sd->sizes[i]);
207
208         while ((uintptr_t)p & 7)
209                 *p++ = 0;
210
211         wimlib_assert(p - orig_p == sd->total_length);
212
213         DEBUG("Successfully wrote security data.");
214         return p;
215 }
216
217 void
218 free_wim_security_data(struct wim_security_data *sd)
219 {
220         if (sd) {
221                 u8 **descriptors = sd->descriptors;
222                 u32 num_entries  = sd->num_entries;
223                 if (descriptors)
224                         while (num_entries--)
225                                 FREE(*descriptors++);
226                 FREE(sd->sizes);
227                 FREE(sd->descriptors);
228                 FREE(sd);
229         }
230 }
231
232 struct sd_node {
233         int security_id;
234         u8 hash[SHA1_HASH_SIZE];
235         struct rb_node rb_node;
236 };
237
238 static void
239 free_sd_tree(struct rb_node *node)
240 {
241         if (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));
245         }
246 }
247
248 /* Frees a security descriptor index set. */
249 void
250 destroy_sd_set(struct wim_sd_set *sd_set, bool rollback)
251 {
252         if (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;
259         }
260         free_sd_tree(sd_set->rb_root.rb_node);
261 }
262
263 /* Inserts a a new node into the security descriptor index tree. */
264 static bool
265 insert_sd_node(struct wim_sd_set *set, struct sd_node *new)
266 {
267         struct rb_root *root = &set->rb_root;
268         struct rb_node **p = &(root->rb_node);
269         struct rb_node *rb_parent = NULL;
270
271         while (*p) {
272                 struct sd_node *this = container_of(*p, struct sd_node, rb_node);
273                 int cmp = hashes_cmp(new->hash, this->hash);
274
275                 rb_parent = *p;
276                 if (cmp < 0)
277                         p = &((*p)->rb_left);
278                 else if (cmp > 0)
279                         p = &((*p)->rb_right);
280                 else
281                         return false; /* Duplicate security descriptor */
282         }
283         rb_link_node(&new->rb_node, rb_parent, p);
284         rb_insert_color(&new->rb_node, root);
285         return true;
286 }
287
288 /* Returns the index of the security descriptor having a SHA1 message digest of
289  * @hash.  If not found, return -1. */
290 static int
291 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE])
292 {
293         struct rb_node *node = set->rb_root.rb_node;
294
295         while (node) {
296                 struct sd_node *sd_node = container_of(node, struct sd_node, rb_node);
297                 int cmp = hashes_cmp(hash, sd_node->hash);
298                 if (cmp < 0)
299                         node = node->rb_left;
300                 else if (cmp > 0)
301                         node = node->rb_right;
302                 else
303                         return sd_node->security_id;
304         }
305         return -1;
306 }
307
308 /*
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,
313  * return -1.
314  */
315 int
316 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
317 {
318         u8 hash[SHA1_HASH_SIZE];
319         int security_id;
320         struct sd_node *new;
321         u8 **descriptors;
322         u64 *sizes;
323         u8 *descr_copy;
324         struct wim_security_data *sd;
325         bool bret;
326
327         sha1_buffer(descriptor, size, hash);
328
329         security_id = lookup_sd(sd_set, hash);
330         if (security_id >= 0) /* Identical descriptor already exists */
331                 goto out;
332
333         /* Need to add a new security descriptor */
334         security_id = -1;
335
336         new = MALLOC(sizeof(*new));
337         if (!new)
338                 goto out;
339
340         descr_copy = memdup(descriptor, size);
341         if (!descr_copy)
342                 goto out_free_node;
343
344         sd = sd_set->sd;
345         new->security_id = sd->num_entries;
346         copy_hash(new->hash, hash);
347
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]));
353         if (!descriptors)
354                 goto out_free_descr;
355         sd->descriptors = descriptors;
356         sizes = REALLOC(sd->sizes,
357                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
358         if (!sizes)
359                 goto out_free_descr;
360         sd->sizes = sizes;
361         sd->descriptors[sd->num_entries] = descr_copy;
362         sd->sizes[sd->num_entries] = size;
363         sd->num_entries++;
364         DEBUG("There are now %u security descriptors", sd->num_entries);
365         bret = insert_sd_node(sd_set, new);
366         wimlib_assert(bret);
367         security_id = new->security_id;
368         goto out;
369 out_free_descr:
370         FREE(descr_copy);
371 out_free_node:
372         FREE(new);
373 out:
374         return security_id;
375 }
376
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
379  * (security IDs).  */
380 int
381 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
382 {
383         int ret;
384
385         sd_set->sd = sd;
386         sd_set->rb_root.rb_node = NULL;
387
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++) {
392                 struct sd_node *new;
393
394                 new = MALLOC(sizeof(struct sd_node));
395                 if (!new) {
396                         ret = WIMLIB_ERR_NOMEM;
397                         goto out_destroy_sd_set;
398                 }
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 */
403         }
404         ret = 0;
405         goto out;
406 out_destroy_sd_set:
407         destroy_sd_set(sd_set, false);
408 out:
409         return ret;
410 }