]> wimlib.net Git - wimlib/blob - src/security.c
883402b0eae5931b6d8df7e4788a4b844e85a809
[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 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
13  * later version.
14  *
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
18  * details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this file; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
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"
35
36 struct wim_security_data_disk {
37         le32 total_length;
38         le32 num_entries;
39         le64 sizes[];
40 } _packed_attribute;
41
42 struct wim_security_data *
43 new_wim_security_data(void)
44 {
45         return CALLOC(1, sizeof(struct wim_security_data));
46 }
47
48 /*
49  * Reads the security data from the metadata resource of a WIM image.
50  *
51  * @buf
52  *      Buffer containing an uncompressed WIM metadata resource.
53  * @buf_len
54  *      Length of the uncompressed metadata resource, in bytes.
55  * @sd_ret
56  *      On success, a pointer to the resulting security data structure will be
57  *      returned here.
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 *buf, size_t buf_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 (buf_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 *)buf;
87         sd->total_length = le32_to_cpu(sd_disk->total_length);
88         sd->num_entries = le32_to_cpu(sd_disk->num_entries);
89
90         /* Length field of 0 is a special case that really means length
91          * of 8. */
92         if (sd->total_length == 0)
93                 sd->total_length = 8;
94
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)
101                 goto out_invalid_sd;
102
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.
106          *
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)
112                 goto out_invalid_sd;
113
114         sizes_size = (u64)sd->num_entries * sizeof(u64);
115         size_no_descriptors = 8 + sizes_size;
116         if (size_no_descriptors > sd->total_length)
117                 goto out_invalid_sd;
118
119         total_len = size_no_descriptors;
120
121         /* Return immediately if no security descriptors. */
122         if (sd->num_entries == 0)
123                 goto out_align_total_length;
124
125         /* Allocate a new buffer for the sizes array */
126         sd->sizes = MALLOC(sizes_size);
127         if (!sd->sizes)
128                 goto out_of_memory;
129
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)
134                         goto out_invalid_sd;
135         }
136
137         p = (const u8*)sd_disk + size_no_descriptors;
138
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)
143                 goto out_of_memory;
144
145         for (u32 i = 0; i < sd->num_entries; i++) {
146                 if (sd->sizes[i] == 0)
147                         continue;
148                 total_len += sd->sizes[i];
149                 if (total_len > (u64)sd->total_length)
150                         goto out_invalid_sd;
151                 sd->descriptors[i] = memdup(p, sd->sizes[i]);
152                 if (!sd->descriptors[i])
153                         goto out_of_memory;
154                 p += sd->sizes[i];
155         }
156 out_align_total_length:
157         total_len = ALIGN(total_len, 8);
158         sd->total_length = ALIGN(sd->total_length, 8);
159         if (total_len != sd->total_length) {
160                 WARNING("Expected WIM security data total length of "
161                         "%u bytes, but calculated %u bytes",
162                         sd->total_length, (unsigned)total_len);
163         }
164         *sd_ret = sd;
165         ret = 0;
166         goto out;
167 out_invalid_sd:
168         ERROR("WIM security data is invalid!");
169         ret = WIMLIB_ERR_INVALID_METADATA_RESOURCE;
170         goto out_free_sd;
171 out_of_memory:
172         ERROR("Out of memory while reading WIM security data!");
173         ret = WIMLIB_ERR_NOMEM;
174 out_free_sd:
175         free_wim_security_data(sd);
176 out:
177         return ret;
178 }
179
180 /*
181  * Writes the security data for a WIM image to an in-memory buffer.
182  */
183 u8 *
184 write_wim_security_data(const struct wim_security_data * restrict sd,
185                         u8 * restrict p)
186 {
187         u8 *orig_p = p;
188         struct wim_security_data_disk *sd_disk = (struct wim_security_data_disk*)p;
189         u32 num_entries = sd->num_entries;
190
191         sd_disk->total_length = cpu_to_le32(sd->total_length);
192         sd_disk->num_entries = cpu_to_le32(num_entries);
193
194         for (u32 i = 0; i < num_entries; i++)
195                 sd_disk->sizes[i] = cpu_to_le64(sd->sizes[i]);
196
197         p = (u8*)&sd_disk->sizes[num_entries];
198
199         for (u32 i = 0; i < num_entries; i++)
200                 p = mempcpy(p, sd->descriptors[i], sd->sizes[i]);
201
202         while ((uintptr_t)p & 7)
203                 *p++ = 0;
204
205         wimlib_assert(p - orig_p == sd->total_length);
206         return p;
207 }
208
209 void
210 free_wim_security_data(struct wim_security_data *sd)
211 {
212         if (sd) {
213                 u8 **descriptors = sd->descriptors;
214                 u32 num_entries  = sd->num_entries;
215                 if (descriptors)
216                         while (num_entries--)
217                                 FREE(*descriptors++);
218                 FREE(sd->sizes);
219                 FREE(sd->descriptors);
220                 FREE(sd);
221         }
222 }
223
224 struct sd_node {
225         s32 security_id;
226         u8 hash[SHA1_HASH_SIZE];
227         struct avl_tree_node index_node;
228 };
229
230 #define SD_NODE(avl_node) \
231         avl_tree_entry(avl_node, struct sd_node, index_node)
232
233 static void
234 free_sd_tree(struct avl_tree_node *node)
235 {
236         if (node) {
237                 free_sd_tree(node->left);
238                 free_sd_tree(node->right);
239                 FREE(SD_NODE(node));
240         }
241 }
242
243 void
244 rollback_new_security_descriptors(struct wim_sd_set *sd_set)
245 {
246         struct wim_security_data *sd = sd_set->sd;
247         u8 **descriptors = sd->descriptors + sd_set->orig_num_entries;
248         u32 num_entries  = sd->num_entries - sd_set->orig_num_entries;
249         while (num_entries--)
250                 FREE(*descriptors++);
251         sd->num_entries = sd_set->orig_num_entries;
252 }
253
254 /* Frees a security descriptor index set. */
255 void
256 destroy_sd_set(struct wim_sd_set *sd_set)
257 {
258         free_sd_tree(sd_set->root);
259 }
260
261 static int
262 _avl_cmp_nodes_by_hash(const struct avl_tree_node *n1,
263                        const struct avl_tree_node *n2)
264 {
265         return hashes_cmp(SD_NODE(n1)->hash, SD_NODE(n2)->hash);
266 }
267
268 /* Inserts a new node into the security descriptor index tree.  Returns true
269  * if successful (not a duplicate).  */
270 static bool
271 insert_sd_node(struct wim_sd_set *set, struct sd_node *new)
272 {
273         return NULL == avl_tree_insert(&set->root, &new->index_node,
274                                        _avl_cmp_nodes_by_hash);
275 }
276
277 /* Returns the index of the security descriptor having a SHA1 message digest of
278  * @hash.  If not found, return -1. */
279 static s32
280 lookup_sd(struct wim_sd_set *set, const u8 hash[SHA1_HASH_SIZE])
281 {
282         struct avl_tree_node *res;
283         struct sd_node dummy;
284
285         copy_hash(dummy.hash, hash);
286         res = avl_tree_lookup_node(set->root, &dummy.index_node,
287                                    _avl_cmp_nodes_by_hash);
288         if (!res)
289                 return -1;
290         return SD_NODE(res)->security_id;
291 }
292
293 /*
294  * Adds a security descriptor to the indexed security descriptor set as well as
295  * the corresponding `struct wim_security_data', and returns the new security
296  * ID; or, if there is an existing security descriptor that is the same, return
297  * the security ID for it.  If a new security descriptor cannot be allocated,
298  * return -1.
299  */
300 s32
301 sd_set_add_sd(struct wim_sd_set *sd_set, const char *descriptor, size_t size)
302 {
303         u8 hash[SHA1_HASH_SIZE];
304         s32 security_id;
305         struct sd_node *new;
306         u8 **descriptors;
307         u64 *sizes;
308         u8 *descr_copy;
309         struct wim_security_data *sd;
310         bool bret;
311
312         sha1_buffer(descriptor, size, hash);
313
314         security_id = lookup_sd(sd_set, hash);
315         if (security_id >= 0) /* Identical descriptor already exists */
316                 goto out;
317
318         /* Need to add a new security descriptor */
319         security_id = -1;
320
321         new = MALLOC(sizeof(*new));
322         if (!new)
323                 goto out;
324
325         descr_copy = memdup(descriptor, size);
326         if (!descr_copy)
327                 goto out_free_node;
328
329         sd = sd_set->sd;
330         new->security_id = sd->num_entries;
331         copy_hash(new->hash, hash);
332
333         /* There typically are only a few dozen security descriptors in a
334          * directory tree, so expanding the array of security descriptors by
335          * only 1 extra space each time should not be a problem. */
336         descriptors = REALLOC(sd->descriptors,
337                               (sd->num_entries + 1) * sizeof(sd->descriptors[0]));
338         if (!descriptors)
339                 goto out_free_descr;
340         sd->descriptors = descriptors;
341         sizes = REALLOC(sd->sizes,
342                         (sd->num_entries + 1) * sizeof(sd->sizes[0]));
343         if (!sizes)
344                 goto out_free_descr;
345         sd->sizes = sizes;
346         sd->descriptors[sd->num_entries] = descr_copy;
347         sd->sizes[sd->num_entries] = size;
348         sd->num_entries++;
349         bret = insert_sd_node(sd_set, new);
350         wimlib_assert(bret);
351         security_id = new->security_id;
352         goto out;
353 out_free_descr:
354         FREE(descr_copy);
355 out_free_node:
356         FREE(new);
357 out:
358         return security_id;
359 }
360
361 /* Initialize a `struct sd_set' mapping from SHA1 message digests of security
362  * descriptors to indices into the security descriptors table of the WIM image
363  * (security IDs).  */
364 int
365 init_sd_set(struct wim_sd_set *sd_set, struct wim_security_data *sd)
366 {
367         int ret;
368
369         sd_set->sd = sd;
370         sd_set->root = NULL;
371
372         /* Remember the original number of security descriptors so that newly
373          * added ones can be rolled back if needed. */
374         sd_set->orig_num_entries = sd->num_entries;
375         for (u32 i = 0; i < sd->num_entries; i++) {
376                 struct sd_node *new;
377
378                 new = MALLOC(sizeof(struct sd_node));
379                 if (!new) {
380                         ret = WIMLIB_ERR_NOMEM;
381                         goto out_destroy_sd_set;
382                 }
383                 sha1_buffer(sd->descriptors[i], sd->sizes[i], new->hash);
384                 new->security_id = i;
385                 if (!insert_sd_node(sd_set, new))
386                         FREE(new); /* Ignore duplicate security descriptor */
387         }
388         ret = 0;
389         goto out;
390 out_destroy_sd_set:
391         destroy_sd_set(sd_set);
392 out:
393         return ret;
394 }