1 /* $OpenBSD: rdboot.c,v 1.9 2023/10/20 19:55:49 kn Exp $ */
2
3 /*
4 * Copyright (c) 2019-2020 Visa Hankala
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/ioctl.h>
22 #include <sys/mount.h>
23 #include <sys/reboot.h>
24 #include <sys/select.h>
25 #include <sys/stat.h>
26
27 #include <err.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <paths.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <termios.h>
35 #include <unistd.h>
36 #include <util.h>
37
38 #include <machine/octboot.h>
39 #include <machine/param.h>
40
41 #include "cmd.h"
42 #include "disk.h"
43
44 #define DEVRANDOM "/dev/random"
45 #define BOOTRANDOM "/etc/random.seed"
46 #define BOOTRANDOM_MAX 256 /* no point being greater than RC4STATE */
47 #define KERNEL "/bsd"
48
49 int loadrandom(void);
50 void kexec(int);
51
52 struct cmd_state cmd;
53 int octbootfd = -1;
54 const char version[] = "1.4";
55
56 int
main(void)57 main(void)
58 {
59 char rootdev[PATH_MAX];
60 int fd, hasboot, isupgrade = 0;
61
62 fd = open(_PATH_CONSOLE, O_RDWR);
63 login_tty(fd);
64
65 /* Keep stdout unbuffered to mimic ordinary bootblocks. */
66 setvbuf(stdout, NULL, _IONBF, 0);
67
68 printf(">> OpenBSD/" MACHINE " BOOT %s\n", version);
69
70 octbootfd = open("/dev/octboot", O_WRONLY);
71 if (octbootfd == -1)
72 err(1, "cannot open boot control device");
73
74 memset(&cmd, 0, sizeof(cmd));
75 cmd.boothowto = 0;
76 cmd.conf = "/etc/boot.conf";
77 strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
78 cmd.timeout = 5;
79
80 if (ioctl(octbootfd, OBIOC_GETROOTDEV, rootdev) == -1) {
81 if (errno != ENOENT)
82 fprintf(stderr, "cannot get rootdev from kernel: %s\n",
83 strerror(errno));
84 } else {
85 snprintf(cmd.bootdev, sizeof(cmd.bootdev), "%s%sa",
86 rootdev, isduid(rootdev, OPENDEV_PART) ? "." : "");
87 }
88
89 disk_init();
90
91 if (upgrade()) {
92 strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image));
93 printf("upgrade detected: switching to %s\n", cmd.image);
94 isupgrade = 1;
95 }
96
97 hasboot = read_conf();
98
99 for (;;) {
100 if (hasboot <= 0) {
101 do {
102 printf("boot> ");
103 } while (!getcmd());
104 }
105
106 if (loadrandom() == 0)
107 cmd.boothowto |= RB_GOODRANDOM;
108
109 kexec(isupgrade);
110
111 hasboot = 0;
112 strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
113 printf("will try %s\n", cmd.image);
114 }
115
116 return 0;
117 }
118
119 int
loadrandom(void)120 loadrandom(void)
121 {
122 char buf[BOOTRANDOM_MAX];
123 struct stat sb;
124 int fd, ret = 0;
125
126 /* Read the file from the device specified by the kernel path. */
127 if (disk_open(cmd.path) == NULL)
128 return -1;
129 fd = open(BOOTRANDOM, O_RDONLY);
130 if (fd == -1) {
131 fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM,
132 strerror(errno));
133 disk_close();
134 return -1;
135 }
136 if (fstat(fd, &sb) == 0) {
137 if (sb.st_mode & S_ISTXT) {
138 printf("NOTE: random seed is being reused.\n");
139 ret = -1;
140 }
141 if (read(fd, buf, sizeof(buf)) != sizeof(buf))
142 ret = -1;
143 fchmod(fd, sb.st_mode | S_ISTXT);
144 } else {
145 ret = -1;
146 }
147 close(fd);
148 disk_close();
149
150 /*
151 * Push the whole buffer to the entropy pool.
152 * The kernel will use the entropy on kexec().
153 * It does not matter if some of the buffer content is uninitialized.
154 */
155 fd = open(DEVRANDOM, O_WRONLY);
156 if (fd == -1) {
157 fprintf(stderr, "%s: cannot open %s: %s", __func__,
158 DEVRANDOM, strerror(errno));
159 return -1;
160 }
161 write(fd, buf, sizeof(buf));
162 close(fd);
163 return ret;
164 }
165
166 void
kexec(int isupgrade)167 kexec(int isupgrade)
168 {
169 struct octboot_kexec_args kargs;
170 struct stat sb;
171 char boothowtostr[32];
172 char rootdev[32];
173 char *kimg = NULL;
174 const char *path;
175 ssize_t n;
176 off_t pos;
177 int argc, fd = -1, ret;
178
179 path = disk_open(cmd.path);
180 if (path == NULL)
181 return;
182
183 fd = open(path, O_RDONLY);
184 if (fd == -1)
185 goto load_failed;
186 if (fstat(fd, &sb) == -1)
187 goto load_failed;
188 if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
189 errno = ENOEXEC;
190 goto load_failed;
191 }
192
193 /* Prevent re-upgrade: chmod a-x bsd.upgrade */
194 if (isupgrade) {
195 sb.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
196 if (fchmod(fd, sb.st_mode) == -1)
197 printf("fchmod a-x %s: failed\n", path);
198 }
199
200 kimg = malloc(sb.st_size);
201 if (kimg == NULL)
202 goto load_failed;
203
204 pos = 0;
205 while (pos < sb.st_size) {
206 n = read(fd, kimg + pos, sb.st_size - pos);
207 if (n == -1)
208 goto load_failed;
209 pos += n;
210 }
211
212 close(fd);
213 disk_close();
214
215 memset(&kargs, 0, sizeof(kargs));
216 kargs.kimg = kimg;
217 kargs.klen = sb.st_size;
218 argc = 0;
219 if (cmd.boothowto != 0) {
220 snprintf(boothowtostr, sizeof(boothowtostr), "boothowto=%d",
221 cmd.boothowto);
222 kargs.argv[argc++] = boothowtostr;
223 }
224 if (cmd.hasduid) {
225 snprintf(rootdev, sizeof(rootdev),
226 "rootdev=%02x%02x%02x%02x%02x%02x%02x%02x",
227 cmd.bootduid[0], cmd.bootduid[1],
228 cmd.bootduid[2], cmd.bootduid[3],
229 cmd.bootduid[4], cmd.bootduid[5],
230 cmd.bootduid[6], cmd.bootduid[7]);
231 kargs.argv[argc++] = rootdev;
232 }
233
234 printf("booting %s\n", cmd.path);
235 ret = ioctl(octbootfd, OBIOC_KEXEC, &kargs);
236 if (ret == -1)
237 fprintf(stderr, "failed to execute kernel %s: %s\n",
238 cmd.path, strerror(errno));
239 else
240 fprintf(stderr, "kexec() returned unexpectedly\n");
241 free(kimg);
242 return;
243
244 load_failed:
245 fprintf(stderr, "failed to load kernel %s: %s\n",
246 cmd.path, strerror(errno));
247 if (fd != -1)
248 close(fd);
249 disk_close();
250 free(kimg);
251 }
252