1 /*
2  * Copyright (c) 2014 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include <lib/bootimage.h>
10 #include <lk/trace.h>
11 #include <lk/err.h>
12 #include <lk/debug.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <lib/bootimage_struct.h>
17 #include <lib/mincrypt/sha256.h>
18 
19 #define LOCAL_TRACE 1
20 
21 struct bootimage {
22     const uint8_t *ptr;
23     size_t len;
24 };
25 
validate_bootimage(bootimage_t * bi)26 static status_t validate_bootimage(bootimage_t *bi) {
27     if (!bi)
28         return ERR_INVALID_ARGS;
29 
30     /* is it large enough to hold the first entry */
31     if (bi->len < 4096) {
32         LTRACEF("bootentry too short\n");
33         return ERR_BAD_LEN;
34     }
35 
36     bootentry *be = (bootentry *)bi->ptr;
37 
38     /* check that the first entry is a file, type boot info, and is 4096 bytes at offset 0 */
39     if (be->kind != KIND_FILE ||
40             be->file.type != TYPE_BOOT_IMAGE ||
41             be->file.offset != 0 ||
42             be->file.length != 4096 ||
43             memcmp(be->file.name, BOOT_MAGIC, sizeof(be->file.name))) {
44         LTRACEF("invalid first entry\n");
45         return ERR_INVALID_ARGS;
46     }
47 
48     /* check the sha256 of the rest of the first page */
49     SHA256_CTX ctx;
50     SHA256_init(&ctx);
51 
52     SHA256_update(&ctx, be + 1, 4096 - sizeof(bootentry));
53     const uint8_t *hash = SHA256_final(&ctx);
54 
55     if (memcmp(hash, be->file.sha256, sizeof(be->file.sha256)) != 0) {
56         LTRACEF("bad hash of first section\n");
57 
58         return ERR_CHECKSUM_FAIL;
59     }
60 
61     /* look at the second entry, which should be a boot info structure */
62     if (be[1].kind != KIND_BOOT_INFO) {
63         LTRACEF("second entry not boot info\n");
64         return ERR_INVALID_ARGS;
65     }
66 
67     bootentry_info *info = &be[1].info;
68 
69     /* is the image a handled version */
70     if (info->version > BOOT_VERSION) {
71         LTRACEF("unhandled version 0x%x\n", info->version);
72         return ERR_INVALID_ARGS;
73     }
74 
75     /* is the image the right size? */
76     if (info->image_size > bi->len) {
77         LTRACEF("boot image block says image is too big (0x%x bytes)\n", info->image_size);
78         return ERR_INVALID_ARGS;
79     }
80 
81     /* trim the len to what the info block says */
82     bi->len = info->image_size;
83 
84     /* iterate over the remaining entries in the list */
85     for (size_t i = 2; i < info->entry_count; i++) {
86         if (be[i].kind == 0)
87             break;
88 
89         LTRACEF("%u: kind 0x%x\n", i, be[i].kind);
90 
91         switch (be[i].kind) {
92             case KIND_BOOT_INFO:
93                 break;
94             case KIND_BOARD:
95                 break;
96             case KIND_BUILD:
97                 break;
98             case KIND_FILE: {
99                 LTRACEF("\ttype %c%c%c%c offset 0x%x, length 0x%x\n",
100                         (be[i].file.type >> 0) & 0xff, (be[i].file.type >> 8) & 0xff,
101                         (be[i].file.type >> 16) & 0xff, (be[i].file.type >> 24) & 0xff,
102                         be[i].file.offset, be[i].file.length);
103 
104                 /* check that the file section is inside the overall image */
105                 uint32_t end = be[i].file.offset + be[i].file.length;
106                 if (end < be[i].file.offset || end > info->image_size) {
107                     LTRACEF("bad file section, size too large\n");
108                     return ERR_INVALID_ARGS;
109                 }
110 
111                 /* check the sha256 hash */
112                 SHA256_init(&ctx);
113 
114                 LTRACEF("\tvalidating SHA256 hash\n");
115                 SHA256_update(&ctx, (const uint8_t *)bi->ptr + be[i].file.offset, be[i].file.length);
116                 hash = SHA256_final(&ctx);
117 
118                 if (memcmp(hash, be[i].file.sha256, sizeof(be[i].file.sha256)) != 0) {
119                     LTRACEF("bad hash of file section\n");
120 
121                     return ERR_CHECKSUM_FAIL;
122                 }
123 
124                 break;
125             }
126             default:
127                 LTRACEF("unknown kind 0x%x\n", be[i].kind);
128                 return ERR_INVALID_ARGS;
129         }
130     }
131 
132     LTRACEF("image good\n");
133     return NO_ERROR;
134 }
135 
bootimage_open(const void * ptr,size_t len,bootimage_t ** bi)136 status_t bootimage_open(const void *ptr, size_t len, bootimage_t **bi) {
137     LTRACEF("ptr %p, len %zu\n", ptr, len);
138 
139     if (!bi)
140         return ERR_INVALID_ARGS;
141 
142     *bi = calloc(1, sizeof(bootimage_t));
143     if (!*bi)
144         return ERR_NO_MEMORY;
145 
146     (*bi)->ptr = ptr;
147     (*bi)->len = len;
148 
149     /* try to validate it */
150     status_t err = validate_bootimage(*bi);
151     if (err < 0) {
152         bootimage_close(*bi);
153         return err;
154     }
155 
156     return NO_ERROR;
157 }
158 
bootimage_close(bootimage_t * bi)159 status_t bootimage_close(bootimage_t *bi) {
160     if (bi)
161         free(bi);
162 
163     return NO_ERROR;
164 }
165 
bootimage_get_range(bootimage_t * bi,const void ** ptr,size_t * len)166 status_t bootimage_get_range(bootimage_t *bi, const void **ptr, size_t *len) {
167     if (!bi)
168         return ERR_INVALID_ARGS;
169 
170     if (ptr)
171         *ptr = bi->ptr;
172     if (len)
173         *len = bi->len;
174 
175     return NO_ERROR;
176 }
177 
bootimage_get_file_section(bootimage_t * bi,uint32_t type,const void ** ptr,size_t * len)178 status_t bootimage_get_file_section(bootimage_t *bi, uint32_t type, const void **ptr, size_t *len) {
179     if (!bi)
180         return ERR_INVALID_ARGS;
181 
182     bootentry *be = (bootentry *)bi->ptr;
183     bootentry_info *info = &be[1].info;
184 
185     for (size_t i = 2; i < info->entry_count; i++) {
186         if (be[i].kind == 0)
187             break;
188 
189         if (be[i].kind != KIND_FILE)
190             continue;
191 
192         if (type == be[i].file.type) {
193             if (ptr)
194                 *ptr = bi->ptr + be[i].file.offset;
195             if (len)
196                 *len = be[i].file.length;
197             return NO_ERROR;
198         }
199     }
200 
201     return ERR_NOT_FOUND;
202 }
203 
204