1 /* $OpenBSD: rdboot.c,v 1.8 2020/12/09 18:10:19 krw 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(void); 51 52 struct cmd_state cmd; 53 int octbootfd = -1; 54 const char version[] = "1.3"; 55 56 int 57 main(void) 58 { 59 char rootdev[PATH_MAX]; 60 int fd, hasboot; 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 } 95 96 hasboot = read_conf(); 97 98 for (;;) { 99 if (hasboot <= 0) { 100 do { 101 printf("boot> "); 102 } while (!getcmd()); 103 } 104 105 if (loadrandom() == 0) 106 cmd.boothowto |= RB_GOODRANDOM; 107 108 kexec(); 109 110 hasboot = 0; 111 strlcpy(cmd.image, KERNEL, sizeof(cmd.image)); 112 printf("will try %s\n", cmd.image); 113 } 114 115 return 0; 116 } 117 118 int 119 loadrandom(void) 120 { 121 char buf[BOOTRANDOM_MAX]; 122 struct stat sb; 123 int fd, ret = 0; 124 125 /* Read the file from the device specified by the kernel path. */ 126 if (disk_open(cmd.path) == NULL) 127 return -1; 128 fd = open(BOOTRANDOM, O_RDONLY); 129 if (fd == -1) { 130 fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM, 131 strerror(errno)); 132 disk_close(); 133 return -1; 134 } 135 if (fstat(fd, &sb) == 0) { 136 if (sb.st_mode & S_ISTXT) { 137 printf("NOTE: random seed is being reused.\n"); 138 ret = -1; 139 } 140 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) 141 ret = -1; 142 fchmod(fd, sb.st_mode | S_ISTXT); 143 } else { 144 ret = -1; 145 } 146 close(fd); 147 disk_close(); 148 149 /* 150 * Push the whole buffer to the entropy pool. 151 * The kernel will use the entropy on kexec(). 152 * It does not matter if some of the buffer content is uninitialized. 153 */ 154 fd = open(DEVRANDOM, O_WRONLY); 155 if (fd == -1) { 156 fprintf(stderr, "%s: cannot open %s: %s", __func__, 157 DEVRANDOM, strerror(errno)); 158 return -1; 159 } 160 write(fd, buf, sizeof(buf)); 161 close(fd); 162 return ret; 163 } 164 165 void 166 kexec(void) 167 { 168 struct octboot_kexec_args kargs; 169 struct stat sb; 170 char boothowtostr[32]; 171 char rootdev[32]; 172 char *kimg = NULL; 173 const char *path; 174 ssize_t n; 175 off_t pos; 176 int argc, fd = -1, ret; 177 178 path = disk_open(cmd.path); 179 if (path == NULL) 180 return; 181 182 fd = open(path, O_RDONLY); 183 if (fd == -1) 184 goto load_failed; 185 if (fstat(fd, &sb) == -1) 186 goto load_failed; 187 if (!S_ISREG(sb.st_mode) || sb.st_size == 0) { 188 errno = ENOEXEC; 189 goto load_failed; 190 } 191 192 kimg = malloc(sb.st_size); 193 if (kimg == NULL) 194 goto load_failed; 195 196 pos = 0; 197 while (pos < sb.st_size) { 198 n = read(fd, kimg + pos, sb.st_size - pos); 199 if (n == -1) 200 goto load_failed; 201 pos += n; 202 } 203 204 close(fd); 205 disk_close(); 206 207 memset(&kargs, 0, sizeof(kargs)); 208 kargs.kimg = kimg; 209 kargs.klen = sb.st_size; 210 argc = 0; 211 if (cmd.boothowto != 0) { 212 snprintf(boothowtostr, sizeof(boothowtostr), "boothowto=%d", 213 cmd.boothowto); 214 kargs.argv[argc++] = boothowtostr; 215 } 216 if (cmd.hasduid) { 217 snprintf(rootdev, sizeof(rootdev), 218 "rootdev=%02x%02x%02x%02x%02x%02x%02x%02x", 219 cmd.bootduid[0], cmd.bootduid[1], 220 cmd.bootduid[2], cmd.bootduid[3], 221 cmd.bootduid[4], cmd.bootduid[5], 222 cmd.bootduid[6], cmd.bootduid[7]); 223 kargs.argv[argc++] = rootdev; 224 } 225 226 printf("booting %s\n", cmd.path); 227 ret = ioctl(octbootfd, OBIOC_KEXEC, &kargs); 228 if (ret == -1) 229 fprintf(stderr, "failed to execute kernel %s: %s\n", 230 cmd.path, strerror(errno)); 231 else 232 fprintf(stderr, "kexec() returned unexpectedly\n"); 233 free(kimg); 234 return; 235 236 load_failed: 237 fprintf(stderr, "failed to load kernel %s: %s\n", 238 cmd.path, strerror(errno)); 239 if (fd != -1) 240 close(fd); 241 disk_close(); 242 free(kimg); 243 } 244