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
49int	loadrandom(void);
50void	kexec(int);
51
52struct cmd_state cmd;
53int octbootfd = -1;
54const char version[] = "1.4";
55
56int
57main(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
119int
120loadrandom(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
166void
167kexec(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
244load_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