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