]>
Commit | Line | Data |
---|---|---|
2d21ac55 A |
1 | /* |
2 | * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
0a7de745 | 25 | * |
2d21ac55 A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
28 | ||
29 | #include <sys/param.h> | |
30 | #include <sys/kernel.h> | |
31 | #include <sys/proc_internal.h> | |
32 | #include <sys/systm.h> | |
33 | #include <sys/systm.h> | |
34 | #include <sys/mount_internal.h> | |
35 | #include <sys/filedesc.h> | |
36 | #include <sys/vnode_internal.h> | |
37 | #include <sys/imageboot.h> | |
6d2010ae | 38 | #include <kern/assert.h> |
2d21ac55 | 39 | |
813fb2f6 A |
40 | #include <sys/namei.h> |
41 | #include <sys/fcntl.h> | |
42 | #include <sys/vnode.h> | |
43 | #include <sys/sysproto.h> | |
44 | #include <sys/csr.h> | |
5ba3f43e | 45 | #include <miscfs/devfs/devfsdefs.h> |
813fb2f6 A |
46 | #include <libkern/crypto/sha2.h> |
47 | #include <libkern/crypto/rsa.h> | |
48 | #include <libkern/OSKextLibPrivate.h> | |
49 | ||
cb323159 A |
50 | #if CONFIG_IMAGEBOOT_IMG4 |
51 | #include <libkern/img4/interface.h> | |
52 | #include <img4/img4.h> | |
53 | #endif | |
54 | ||
813fb2f6 A |
55 | #include <kern/kalloc.h> |
56 | ||
2d21ac55 | 57 | #include <pexpert/pexpert.h> |
813fb2f6 | 58 | #include <kern/chunklist.h> |
2d21ac55 A |
59 | |
60 | extern struct filedesc filedesc0; | |
61 | ||
62 | extern int (*mountroot)(void); | |
5ba3f43e | 63 | extern char rootdevice[DEVMAXNAMESIZE]; |
2d21ac55 | 64 | |
cb323159 A |
65 | #if CONFIG_LOCKERBOOT |
66 | typedef struct _locker_mount_args { | |
67 | char lmnt_path[PATH_MAX]; | |
68 | uint16_t lmnt_preferred_hash; | |
69 | } locker_mount_args_t; | |
70 | #endif | |
71 | ||
2d21ac55 A |
72 | #define DEBUG_IMAGEBOOT 0 |
73 | ||
74 | #if DEBUG_IMAGEBOOT | |
cb323159 | 75 | #define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__) |
2d21ac55 A |
76 | #else |
77 | #define DBG_TRACE(...) do {} while(0) | |
78 | #endif | |
79 | ||
cb323159 A |
80 | #define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0) |
81 | #define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0) | |
82 | #define kfree_safe(x) do { if ((x)) { kfree_addr((x)); (x) = NULL; } } while (0) | |
83 | ||
5ba3f43e A |
84 | extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p); |
85 | extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p); | |
86 | ||
cb323159 A |
87 | static boolean_t imageboot_setup_new(imageboot_type_t type); |
88 | ||
89 | vnode_t imgboot_get_image_file(const char *path, off_t *fsize, int *errp); /* may be required by chunklist.c */ | |
90 | int read_file(const char *path, void **bufp, size_t *bufszp); /* may be required by chunklist.c */ | |
2d21ac55 A |
91 | |
92 | #define kIBFilePrefix "file://" | |
93 | ||
6d2010ae A |
94 | __private_extern__ int |
95 | imageboot_format_is_valid(const char *root_path) | |
96 | { | |
0a7de745 A |
97 | return strncmp(root_path, kIBFilePrefix, |
98 | strlen(kIBFilePrefix)) == 0; | |
6d2010ae A |
99 | } |
100 | ||
101 | static void | |
0a7de745 | 102 | vnode_get_and_drop_always(vnode_t vp) |
6d2010ae A |
103 | { |
104 | vnode_getalways(vp); | |
105 | vnode_rele(vp); | |
106 | vnode_put(vp); | |
107 | } | |
108 | ||
cb323159 | 109 | __private_extern__ imageboot_type_t |
2d21ac55 A |
110 | imageboot_needed(void) |
111 | { | |
cb323159 | 112 | imageboot_type_t result = IMAGEBOOT_NONE; |
2d21ac55 | 113 | char *root_path = NULL; |
0a7de745 | 114 | |
2d21ac55 A |
115 | DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__); |
116 | ||
117 | MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); | |
0a7de745 | 118 | if (root_path == NULL) { |
2d21ac55 | 119 | panic("%s: M_NAMEI zone exhausted", __FUNCTION__); |
0a7de745 | 120 | } |
2d21ac55 | 121 | |
cb323159 A |
122 | #if CONFIG_LOCKERBOOT |
123 | if (PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) { | |
124 | result = IMAGEBOOT_LOCKER; | |
125 | goto out; | |
126 | } | |
127 | #endif | |
128 | ||
6d2010ae | 129 | /* Check for first layer */ |
813fb2f6 | 130 | if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) || |
cb323159 A |
131 | #if CONFIG_IMAGEBOOT_IMG4 |
132 | PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) || | |
133 | #endif | |
0a7de745 A |
134 | PE_parse_boot_argn("rp", root_path, MAXPATHLEN) || |
135 | PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) || | |
136 | PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) { | |
6d2010ae A |
137 | goto out; |
138 | } | |
813fb2f6 | 139 | |
6d2010ae A |
140 | /* Sanity-check first layer */ |
141 | if (imageboot_format_is_valid(root_path)) { | |
142 | DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path); | |
143 | } else { | |
144 | goto out; | |
145 | } | |
2d21ac55 | 146 | |
cb323159 | 147 | result = IMAGEBOOT_DMG; |
6d2010ae A |
148 | |
149 | /* Check for second layer */ | |
150 | if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) || | |
0a7de745 | 151 | PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) { |
6d2010ae A |
152 | goto out; |
153 | } | |
154 | ||
155 | /* Sanity-check second layer */ | |
156 | if (imageboot_format_is_valid(root_path)) { | |
157 | DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path); | |
158 | } else { | |
159 | panic("%s: Invalid URL scheme for %s\n", | |
0a7de745 | 160 | __FUNCTION__, root_path); |
2d21ac55 | 161 | } |
6d2010ae A |
162 | |
163 | out: | |
2d21ac55 A |
164 | FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); |
165 | ||
0a7de745 | 166 | return result; |
2d21ac55 A |
167 | } |
168 | ||
169 | ||
170 | /* | |
6d2010ae A |
171 | * Swaps in new root filesystem based on image path. |
172 | * Current root filesystem is removed from mount list and | |
0a7de745 A |
173 | * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and |
174 | * "rootvnode" is reset. Root vnode of currentroot filesystem | |
6d2010ae | 175 | * is returned with usecount (no iocount). |
2d21ac55 | 176 | */ |
6d2010ae | 177 | __private_extern__ int |
cb323159 | 178 | imageboot_mount_image(const char *root_path, int height, imageboot_type_t type) |
2d21ac55 | 179 | { |
0a7de745 A |
180 | dev_t dev; |
181 | int error; | |
cb323159 A |
182 | /* |
183 | * Need to stash this here since we may do a kernel_mount() on /, which will | |
184 | * automatically update the rootvnode global. Note that vfs_mountroot() does | |
185 | * not update that global, which is a bit weird. | |
186 | */ | |
187 | vnode_t old_rootvnode = rootvnode; | |
0a7de745 A |
188 | vnode_t newdp; |
189 | mount_t new_rootfs; | |
cb323159 | 190 | boolean_t update_rootvnode = FALSE; |
2d21ac55 | 191 | |
cb323159 A |
192 | if (type == IMAGEBOOT_DMG) { |
193 | error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev); | |
194 | if (error) { | |
195 | panic("%s: di_root_image failed: %d\n", __FUNCTION__, error); | |
196 | } | |
197 | ||
198 | rootdev = dev; | |
199 | mountroot = NULL; | |
200 | printf("%s: root device 0x%x\n", __FUNCTION__, rootdev); | |
201 | error = vfs_mountroot(); | |
202 | if (error != 0) { | |
203 | panic("vfs_mountroot() failed.\n"); | |
204 | } | |
205 | ||
206 | update_rootvnode = TRUE; | |
2d21ac55 | 207 | } |
cb323159 A |
208 | #if CONFIG_LOCKERBOOT |
209 | else if (type == IMAGEBOOT_LOCKER) { | |
210 | locker_mount_args_t *mntargs = kalloc(sizeof(*mntargs)); | |
211 | if (!mntargs) { | |
212 | panic("could not alloc mount args"); | |
213 | } | |
2d21ac55 | 214 | |
cb323159 A |
215 | strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path)); |
216 | mntargs->lmnt_preferred_hash = 0; | |
217 | ||
218 | DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path); | |
219 | error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/", | |
220 | mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel()); | |
221 | if (error) { | |
222 | panic("failed to mount locker: %d", error); | |
223 | } | |
224 | kfree(mntargs, sizeof(*mntargs)); | |
225 | ||
226 | /* Clear the old mount association. */ | |
227 | old_rootvnode->v_mountedhere = NULL; | |
228 | rootvnode->v_mount->mnt_vnodecovered = NULL; | |
229 | } | |
230 | #endif | |
231 | else { | |
232 | panic("invalid imageboot type: %d", type); | |
6d2010ae | 233 | } |
2d21ac55 | 234 | |
6d2010ae A |
235 | /* |
236 | * Get the vnode for '/'. | |
237 | * Set fdp->fd_fd.fd_cdir to reference it. | |
238 | */ | |
0a7de745 | 239 | if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) { |
6d2010ae | 240 | panic("%s: cannot find root vnode", __FUNCTION__); |
0a7de745 | 241 | } |
cb323159 | 242 | DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name); |
2d21ac55 | 243 | |
cb323159 | 244 | if (old_rootvnode != NULL) { |
6d2010ae | 245 | /* remember the old rootvnode, but remove it from mountlist */ |
cb323159 | 246 | mount_t old_rootfs = old_rootvnode->v_mount; |
0a7de745 | 247 | |
b7266188 | 248 | mount_list_remove(old_rootfs); |
b7266188 A |
249 | mount_lock(old_rootfs); |
250 | #ifdef CONFIG_IMGSRC_ACCESS | |
251 | old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT; | |
252 | #endif /* CONFIG_IMGSRC_ACCESS */ | |
253 | old_rootfs->mnt_flag &= ~MNT_ROOTFS; | |
254 | mount_unlock(old_rootfs); | |
6d2010ae | 255 | } |
b7266188 | 256 | |
6d2010ae | 257 | /* switch to the new rootvnode */ |
cb323159 A |
258 | if (update_rootvnode) { |
259 | rootvnode = newdp; | |
260 | } | |
b7266188 | 261 | |
6d2010ae A |
262 | new_rootfs = rootvnode->v_mount; |
263 | mount_lock(new_rootfs); | |
264 | new_rootfs->mnt_flag |= MNT_ROOTFS; | |
265 | mount_unlock(new_rootfs); | |
b7266188 | 266 | |
6d2010ae A |
267 | vnode_ref(newdp); |
268 | vnode_put(newdp); | |
269 | filedesc0.fd_cdir = newdp; | |
270 | DBG_TRACE("%s: root switched\n", __FUNCTION__); | |
b7266188 | 271 | |
6d2010ae | 272 | if (old_rootvnode != NULL) { |
b7266188 | 273 | #ifdef CONFIG_IMGSRC_ACCESS |
0a7de745 A |
274 | if (height >= 0 && PE_imgsrc_mount_supported()) { |
275 | imgsrc_rootvnodes[height] = old_rootvnode; | |
276 | } else { | |
277 | vnode_get_and_drop_always(old_rootvnode); | |
278 | } | |
279 | #else | |
cb323159 | 280 | #pragma unused(height) |
6d2010ae | 281 | vnode_get_and_drop_always(old_rootvnode); |
b7266188 | 282 | #endif /* CONFIG_IMGSRC_ACCESS */ |
6d2010ae A |
283 | } |
284 | return 0; | |
285 | } | |
b7266188 | 286 | |
cb323159 | 287 | int |
813fb2f6 A |
288 | read_file(const char *path, void **bufp, size_t *bufszp) |
289 | { | |
290 | int err = 0; | |
291 | struct nameidata ndp = {}; | |
292 | struct vnode *vp = NULL; | |
293 | off_t fsize = 0; | |
294 | int resid = 0; | |
295 | char *buf = NULL; | |
296 | bool doclose = false; | |
297 | ||
298 | vfs_context_t ctx = vfs_context_kernel(); | |
299 | proc_t p = vfs_context_proc(ctx); | |
300 | kauth_cred_t kerncred = vfs_context_ucred(ctx); | |
301 | ||
5ba3f43e | 302 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx); |
813fb2f6 | 303 | if ((err = namei(&ndp)) != 0) { |
cb323159 | 304 | AUTHPRNT("namei failed (%s) - %d", path, err); |
813fb2f6 A |
305 | goto out; |
306 | } | |
307 | nameidone(&ndp); | |
308 | vp = ndp.ni_vp; | |
309 | ||
310 | if ((err = vnode_size(vp, &fsize, ctx)) != 0) { | |
cb323159 | 311 | AUTHPRNT("failed to get vnode size of %s - %d", path, err); |
813fb2f6 A |
312 | goto out; |
313 | } | |
314 | if (fsize < 0) { | |
315 | panic("negative file size"); | |
316 | } | |
317 | ||
318 | if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) { | |
cb323159 | 319 | AUTHPRNT("failed to open %s - %d", path, err); |
813fb2f6 A |
320 | goto out; |
321 | } | |
322 | doclose = true; | |
323 | ||
324 | /* if bufsz is non-zero, cap the read at bufsz bytes */ | |
325 | if (*bufszp && *bufszp < (size_t)fsize) { | |
326 | fsize = *bufszp; | |
327 | } | |
328 | ||
329 | buf = kalloc(fsize); | |
330 | if (buf == NULL) { | |
331 | err = ENOMEM; | |
332 | goto out; | |
333 | } | |
334 | ||
335 | if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, fsize, 0, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) { | |
cb323159 | 336 | AUTHPRNT("Cannot read %d bytes from %s - %d", (int)fsize, path, err); |
813fb2f6 A |
337 | goto out; |
338 | } | |
339 | ||
340 | if (resid) { | |
341 | /* didnt get everything we wanted */ | |
cb323159 | 342 | AUTHPRNT("Short read of %d bytes from %s - %d", (int)fsize, path, resid); |
813fb2f6 A |
343 | err = EINVAL; |
344 | goto out; | |
345 | } | |
346 | ||
347 | out: | |
348 | if (doclose) { | |
349 | VNOP_CLOSE(vp, FREAD, ctx); | |
350 | } | |
351 | if (vp) { | |
352 | vnode_put(vp); | |
353 | vp = NULL; | |
354 | } | |
355 | ||
356 | if (err) { | |
357 | kfree_safe(buf); | |
358 | } else { | |
359 | *bufp = buf; | |
360 | *bufszp = fsize; | |
361 | } | |
362 | ||
363 | return err; | |
364 | } | |
365 | ||
cb323159 A |
366 | #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST |
367 | vnode_t | |
368 | imgboot_get_image_file(const char *path, off_t *fsize, int *errp) | |
813fb2f6 | 369 | { |
813fb2f6 | 370 | struct nameidata ndp = {}; |
cb323159 | 371 | vnode_t vp = NULL; |
813fb2f6 | 372 | vfs_context_t ctx = vfs_context_kernel(); |
cb323159 | 373 | int err; |
813fb2f6 | 374 | |
cb323159 | 375 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx); |
813fb2f6 | 376 | if ((err = namei(&ndp)) != 0) { |
cb323159 A |
377 | AUTHPRNT("Cannot find %s - error %d", path, err); |
378 | } else { | |
379 | nameidone(&ndp); | |
380 | vp = ndp.ni_vp; | |
813fb2f6 | 381 | |
cb323159 | 382 | if (vp->v_type != VREG) { |
813fb2f6 | 383 | err = EINVAL; |
cb323159 A |
384 | AUTHPRNT("%s it not a regular file", path); |
385 | } else if (fsize) { | |
386 | if ((err = vnode_size(vp, fsize, ctx)) != 0) { | |
387 | AUTHPRNT("Cannot get file size of %s - error %d", path, err); | |
388 | } | |
813fb2f6 | 389 | } |
813fb2f6 A |
390 | } |
391 | ||
cb323159 A |
392 | if (err) { |
393 | *errp = err; | |
813fb2f6 A |
394 | vp = NULL; |
395 | } | |
cb323159 | 396 | return vp; |
813fb2f6 | 397 | } |
cb323159 | 398 | #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */ |
813fb2f6 | 399 | |
cb323159 | 400 | #if CONFIG_IMAGEBOOT_IMG4 |
813fb2f6 | 401 | |
cb323159 | 402 | #define APTICKET_NAME "apticket.der" |
813fb2f6 | 403 | |
cb323159 A |
404 | static char * |
405 | imgboot_get_apticket_path(const char *rootpath) | |
406 | { | |
407 | size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME); | |
408 | char *path = kalloc(plen); | |
813fb2f6 | 409 | |
cb323159 A |
410 | if (path) { |
411 | char *slash; | |
813fb2f6 | 412 | |
cb323159 A |
413 | strlcpy(path, rootpath, plen); |
414 | slash = strrchr(path, '/'); | |
415 | if (slash == NULL) { | |
416 | slash = path; | |
417 | } else { | |
418 | slash++; | |
419 | } | |
420 | strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1); | |
813fb2f6 | 421 | } |
cb323159 | 422 | return path; |
813fb2f6 A |
423 | } |
424 | ||
425 | static int | |
cb323159 | 426 | authenticate_root_with_img4(const char *rootpath) |
813fb2f6 | 427 | { |
cb323159 A |
428 | errno_t rv; |
429 | img4_t i4; | |
430 | img4_payload_t i4pl; | |
431 | vnode_t vp; | |
432 | char *ticket_path; | |
433 | size_t tcksz = 0; | |
434 | void *tckbuf = NULL; | |
813fb2f6 | 435 | |
cb323159 | 436 | DBG_TRACE("Check %s\n", rootpath); |
813fb2f6 | 437 | |
cb323159 A |
438 | if (img4if == NULL) { |
439 | AUTHPRNT("AppleImage4 is not ready"); | |
440 | return EAGAIN; | |
813fb2f6 A |
441 | } |
442 | ||
cb323159 A |
443 | ticket_path = imgboot_get_apticket_path(rootpath); |
444 | if (ticket_path == NULL) { | |
445 | AUTHPRNT("Cannot construct ticket path - out of memory"); | |
446 | return ENOMEM; | |
813fb2f6 A |
447 | } |
448 | ||
cb323159 A |
449 | rv = read_file(ticket_path, &tckbuf, &tcksz); |
450 | if (rv) { | |
451 | AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv); | |
452 | goto out_with_ticket_path; | |
813fb2f6 A |
453 | } |
454 | ||
cb323159 | 455 | DBG_TRACE("Got %d bytes of manifest from %s\n", (int)tcksz, ticket_path); |
813fb2f6 | 456 | |
cb323159 A |
457 | rv = img4_init(&i4, 0, tckbuf, tcksz, NULL); |
458 | if (rv) { | |
459 | AUTHPRNT("Cannot initialise verification handle - error %d", rv); | |
460 | goto out_with_ticket_bytes; | |
813fb2f6 A |
461 | } |
462 | ||
cb323159 A |
463 | vp = imgboot_get_image_file(rootpath, NULL, &rv); |
464 | if (vp == NULL) { | |
465 | /* Error message had been printed already */ | |
813fb2f6 A |
466 | goto out; |
467 | } | |
468 | ||
cb323159 A |
469 | rv = img4_payload_init_with_vnode_4xnu(&i4pl, 'rosi', vp, I4PLF_UNWRAPPED); |
470 | if (rv) { | |
471 | AUTHPRNT("failed to init payload: %d", rv); | |
813fb2f6 A |
472 | goto out; |
473 | } | |
474 | ||
cb323159 A |
475 | rv = img4_get_trusted_external_payload(&i4, &i4pl, IMG4_ENVIRONMENT_PPL, NULL, NULL); |
476 | if (rv) { | |
477 | AUTHPRNT("failed to validate root image %s: %d", rootpath, rv); | |
813fb2f6 A |
478 | } |
479 | ||
cb323159 | 480 | img4_payload_destroy(&i4pl); |
813fb2f6 | 481 | out: |
cb323159 A |
482 | img4_destroy(&i4); |
483 | out_with_ticket_bytes: | |
484 | kfree_safe(tckbuf); | |
485 | out_with_ticket_path: | |
486 | kfree_safe(ticket_path); | |
487 | return rv; | |
813fb2f6 | 488 | } |
cb323159 | 489 | #endif /* CONFIG_IMAGEBOOT_IMG4 */ |
813fb2f6 | 490 | |
813fb2f6 | 491 | |
5ba3f43e A |
492 | /* |
493 | * Attach the image at 'path' as a ramdisk and mount it as our new rootfs. | |
494 | * All existing mounts are first umounted. | |
495 | */ | |
496 | static int | |
497 | imageboot_mount_ramdisk(const char *path) | |
498 | { | |
499 | int err = 0; | |
500 | size_t bufsz = 0; | |
501 | void *buf = NULL; | |
502 | dev_t dev; | |
503 | vnode_t newdp; | |
504 | mount_t new_rootfs; | |
505 | ||
506 | /* Read our target image from disk */ | |
507 | err = read_file(path, &buf, &bufsz); | |
508 | if (err) { | |
509 | printf("%s: failed: read_file() = %d\n", __func__, err); | |
510 | goto out; | |
511 | } | |
512 | DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz); | |
513 | ||
514 | #if CONFIG_IMGSRC_ACCESS | |
515 | /* Re-add all root mounts to the mount list in the correct order... */ | |
516 | mount_list_remove(rootvnode->v_mount); | |
517 | for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) { | |
518 | struct vnode *vn = imgsrc_rootvnodes[i]; | |
519 | if (vn) { | |
520 | vnode_getalways(vn); | |
521 | imgsrc_rootvnodes[i] = NULLVP; | |
522 | ||
523 | mount_t mnt = vn->v_mount; | |
524 | mount_lock(mnt); | |
525 | mnt->mnt_flag |= MNT_ROOTFS; | |
526 | mount_list_add(mnt); | |
527 | mount_unlock(mnt); | |
528 | ||
529 | vnode_rele(vn); | |
530 | vnode_put(vn); | |
531 | } | |
532 | } | |
533 | mount_list_add(rootvnode->v_mount); | |
534 | #endif | |
535 | ||
536 | /* ... and unmount everything */ | |
537 | vnode_get_and_drop_always(rootvnode); | |
538 | filedesc0.fd_cdir = NULL; | |
539 | rootvnode = NULL; | |
540 | vfs_unmountall(); | |
541 | ||
542 | /* Attach the ramfs image ... */ | |
543 | err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev); | |
544 | if (err) { | |
545 | printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err); | |
546 | goto out; | |
547 | } | |
548 | ||
549 | /* ... and mount it */ | |
550 | rootdev = dev; | |
551 | mountroot = NULL; | |
552 | err = vfs_mountroot(); | |
553 | if (err) { | |
554 | printf("%s: failed: vfs_mountroot() = %d\n", __func__, err); | |
555 | goto out; | |
556 | } | |
557 | ||
558 | /* Switch to new root vnode */ | |
0a7de745 | 559 | if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) { |
5ba3f43e A |
560 | panic("%s: cannot find root vnode", __func__); |
561 | } | |
562 | rootvnode = newdp; | |
563 | rootvnode->v_flag |= VROOT; | |
564 | new_rootfs = rootvnode->v_mount; | |
565 | mount_lock(new_rootfs); | |
566 | new_rootfs->mnt_flag |= MNT_ROOTFS; | |
567 | mount_unlock(new_rootfs); | |
568 | ||
569 | vnode_ref(newdp); | |
570 | vnode_put(newdp); | |
571 | filedesc0.fd_cdir = newdp; | |
572 | ||
573 | DBG_TRACE("%s: root switched\n", __func__); | |
574 | ||
575 | out: | |
576 | if (err) { | |
577 | kfree_safe(buf); | |
578 | } | |
579 | return err; | |
580 | } | |
581 | ||
cb323159 A |
582 | /* |
583 | * If the path is in <file://> URL format then we allocate memory and decode it, | |
584 | * otherwise return the same pointer. | |
585 | * | |
586 | * Caller is expected to check if the pointers are different. | |
587 | */ | |
588 | static char * | |
589 | url_to_path(char *url_path) | |
590 | { | |
591 | char *path = url_path; | |
592 | size_t len = strlen(kIBFilePrefix); | |
593 | ||
594 | if (strncmp(kIBFilePrefix, url_path, len) == 0) { | |
595 | /* its a URL - remove the file:// prefix and percent-decode */ | |
596 | url_path += len; | |
597 | ||
598 | len = strlen(url_path); | |
599 | if (len) { | |
600 | /* Make a copy of the path to URL-decode */ | |
601 | path = kalloc(len + 1); | |
602 | if (path == NULL) { | |
603 | panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len); | |
604 | } | |
605 | ||
606 | strlcpy(path, url_path, len + 1); | |
607 | url_decode(path); | |
608 | } else { | |
609 | panic("Bogus imageboot path URL - missing path\n"); | |
610 | } | |
611 | ||
612 | DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__, url_path, path); | |
613 | } | |
614 | ||
615 | return path; | |
616 | } | |
617 | ||
813fb2f6 | 618 | static boolean_t |
cb323159 | 619 | imageboot_setup_new(imageboot_type_t type) |
6d2010ae A |
620 | { |
621 | int error; | |
622 | char *root_path = NULL; | |
623 | int height = 0; | |
624 | boolean_t done = FALSE; | |
cb323159 | 625 | boolean_t auth_root = TRUE; |
5ba3f43e | 626 | boolean_t ramdisk_root = FALSE; |
6d2010ae A |
627 | |
628 | MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); | |
629 | assert(root_path != NULL); | |
630 | ||
cb323159 A |
631 | #if CONFIG_LOCKERBOOT |
632 | if (type == IMAGEBOOT_LOCKER) { | |
633 | if (!PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) { | |
634 | panic("locker boot with no locker given"); | |
635 | } | |
636 | ||
637 | DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__, rootvnode->v_mount->mnt_vtable->vfc_name); | |
638 | ||
639 | /* | |
640 | * The locker path is a path, not a URL, so just pass it directly to | |
641 | * imageboot_mount_image(). | |
642 | */ | |
643 | error = imageboot_mount_image(root_path, 0, type); | |
644 | if (error) { | |
645 | panic("failed to mount system locker: %d", error); | |
646 | } | |
647 | ||
648 | done = TRUE; | |
649 | goto out; | |
650 | } | |
651 | #endif /* CONFIG_LOCKERBOOT */ | |
652 | ||
5ba3f43e A |
653 | unsigned imgboot_arg; |
654 | if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) { | |
655 | ramdisk_root = TRUE; | |
656 | } | |
657 | ||
813fb2f6 | 658 | if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) { |
6d2010ae | 659 | printf("%s: container image url is %s\n", __FUNCTION__, root_path); |
cb323159 | 660 | error = imageboot_mount_image(root_path, height, type); |
6d2010ae A |
661 | if (error != 0) { |
662 | panic("Failed to mount container image."); | |
663 | } | |
664 | ||
665 | height++; | |
666 | } | |
667 | ||
cb323159 A |
668 | if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == FALSE && |
669 | PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) { | |
6d2010ae | 670 | if (height > 0) { |
cb323159 | 671 | panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG); |
6d2010ae A |
672 | } |
673 | goto out; | |
813fb2f6 A |
674 | } |
675 | ||
cb323159 | 676 | printf("%s: root image URL is '%s'\n", __func__, root_path); |
b7266188 | 677 | |
813fb2f6 A |
678 | #if CONFIG_CSR |
679 | if (auth_root && (csr_check(CSR_ALLOW_ANY_RECOVERY_OS) == 0)) { | |
680 | AUTHPRNT("CSR_ALLOW_ANY_RECOVERY_OS set, skipping root image authentication"); | |
cb323159 | 681 | auth_root = FALSE; |
2d21ac55 | 682 | } |
813fb2f6 A |
683 | #endif |
684 | ||
5ba3f43e | 685 | /* Make a copy of the path to URL-decode */ |
cb323159 A |
686 | char *path = url_to_path(root_path); |
687 | assert(path); | |
6d2010ae | 688 | |
cb323159 | 689 | #if CONFIG_IMAGEBOOT_CHUNKLIST |
5ba3f43e | 690 | if (auth_root) { |
813fb2f6 | 691 | AUTHDBG("authenticating root image at %s", path); |
cb323159 | 692 | error = authenticate_root_with_chunklist(path); |
813fb2f6 A |
693 | if (error) { |
694 | panic("root image authentication failed (err = %d)\n", error); | |
695 | } | |
696 | AUTHDBG("successfully authenticated %s", path); | |
5ba3f43e | 697 | } |
cb323159 | 698 | #endif |
00867663 | 699 | |
5ba3f43e A |
700 | if (ramdisk_root) { |
701 | error = imageboot_mount_ramdisk(path); | |
702 | } else { | |
cb323159 | 703 | error = imageboot_mount_image(root_path, height, type); |
813fb2f6 | 704 | } |
6d2010ae | 705 | |
cb323159 A |
706 | if (path != root_path) { |
707 | kfree_safe(path); | |
708 | } | |
5ba3f43e A |
709 | |
710 | if (error) { | |
711 | panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n", | |
0a7de745 | 712 | error, auth_root, ramdisk_root); |
6d2010ae A |
713 | } |
714 | ||
cb323159 | 715 | #if CONFIG_IMAGEBOOT_CHUNKLIST |
813fb2f6 A |
716 | if (auth_root) { |
717 | /* check that the image version matches the running kernel */ | |
718 | AUTHDBG("checking root image version"); | |
cb323159 | 719 | error = authenticate_root_version_check(); |
813fb2f6 A |
720 | if (error) { |
721 | panic("root image version check failed"); | |
722 | } else { | |
723 | AUTHDBG("root image version matches kernel"); | |
724 | } | |
725 | } | |
cb323159 | 726 | #endif |
813fb2f6 | 727 | |
6d2010ae A |
728 | done = TRUE; |
729 | ||
730 | out: | |
731 | FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); | |
732 | return done; | |
733 | } | |
734 | ||
735 | __private_extern__ void | |
cb323159 | 736 | imageboot_setup(imageboot_type_t type) |
6d2010ae A |
737 | { |
738 | int error = 0; | |
739 | char *root_path = NULL; | |
740 | ||
741 | DBG_TRACE("%s: entry\n", __FUNCTION__); | |
742 | ||
813fb2f6 | 743 | if (rootvnode == NULL) { |
6d2010ae A |
744 | panic("imageboot_setup: rootvnode is NULL."); |
745 | } | |
746 | ||
747 | /* | |
748 | * New boot-arg scheme: | |
cb323159 A |
749 | * root-dmg : the dmg that will be the root filesystem, authenticated by default. |
750 | * auth-root-dmg : same as root-dmg. | |
0a7de745 | 751 | * container-dmg : an optional dmg that contains the root-dmg. |
cb323159 A |
752 | * locker : the locker that will be the root filesystem -- mutually |
753 | * exclusive with any other boot-arg. | |
6d2010ae | 754 | */ |
cb323159 | 755 | if (imageboot_setup_new(type)) { |
6d2010ae A |
756 | return; |
757 | } | |
813fb2f6 | 758 | |
6d2010ae A |
759 | MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); |
760 | assert(root_path != NULL); | |
761 | ||
762 | /* | |
763 | * Look for outermost disk image to root from. If we're doing a nested boot, | |
764 | * there's some sense in which the outer image never needs to be the root filesystem, | |
765 | * but it does need very similar treatment: it must not be unmounted, needs a fake | |
0a7de745 | 766 | * device vnode created for it, and should not show up in getfsstat() until exposed |
6d2010ae A |
767 | * with MNT_IMGSRC. We just make it the temporary root. |
768 | */ | |
cb323159 A |
769 | #if CONFIG_IMAGEBOOT_IMG4 |
770 | if (PE_parse_boot_argn("arp0", root_path, MAXPATHLEN)) { | |
771 | char *path = url_to_path(root_path); | |
772 | ||
773 | assert(path); | |
774 | ||
775 | if (authenticate_root_with_img4(path)) { | |
776 | panic("Root image %s does not match the manifest\n", root_path); | |
777 | } | |
778 | if (path != root_path) { | |
779 | kfree_safe(path); | |
780 | } | |
781 | } else | |
782 | #endif /* CONFIG_IMAGEBOOT_IMG4 */ | |
0a7de745 A |
783 | if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) && |
784 | (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) { | |
6d2010ae A |
785 | panic("%s: no valid path to image.\n", __FUNCTION__); |
786 | } | |
787 | ||
cb323159 | 788 | DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path); |
0a7de745 | 789 | |
cb323159 | 790 | error = imageboot_mount_image(root_path, 0, type); |
6d2010ae A |
791 | if (error) { |
792 | panic("Failed on first stage of imageboot."); | |
793 | } | |
794 | ||
795 | /* | |
796 | * See if we are rooting from a nested image | |
797 | */ | |
0a7de745 | 798 | if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) { |
6d2010ae A |
799 | goto done; |
800 | } | |
0a7de745 | 801 | |
6d2010ae A |
802 | printf("%s: second level root image url is %s\n", __FUNCTION__, root_path); |
803 | ||
804 | /* | |
805 | * If we fail to set up second image, it's not a given that we | |
0a7de745 | 806 | * can safely root off the first. |
6d2010ae | 807 | */ |
cb323159 | 808 | error = imageboot_mount_image(root_path, 1, type); |
6d2010ae | 809 | if (error) { |
0a7de745 | 810 | panic("Failed on second stage of imageboot."); |
6d2010ae A |
811 | } |
812 | ||
2d21ac55 A |
813 | done: |
814 | FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); | |
815 | ||
816 | DBG_TRACE("%s: exit\n", __FUNCTION__); | |
817 | ||
6d2010ae | 818 | return; |
2d21ac55 | 819 | } |