1 /* $OpenBSD: rdboot.c,v 1.4 2023/10/20 19:58:16 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/kexec.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 kexecfd = -1; 54 const char version[] = "0.3"; 55 56 int 57 main(void) 58 { 59 u_char bootduid[8]; 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 kexecfd = open("/dev/kexec", O_WRONLY); 71 if (kexecfd == -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(kexecfd, KIOC_GETBOOTDUID, bootduid) == -1) { 81 fprintf(stderr, "cannot get bootduid from kernel: %s\n", 82 strerror(errno)); 83 } else { 84 memcpy(cmd.bootduid, bootduid, sizeof(cmd.bootduid)); 85 } 86 87 disk_init(); 88 89 if (upgrade()) { 90 strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image)); 91 printf("upgrade detected: switching to %s\n", cmd.image); 92 isupgrade = 1; 93 } 94 95 hasboot = read_conf(); 96 97 for (;;) { 98 if (hasboot <= 0) { 99 do { 100 printf("boot> "); 101 } while (!getcmd()); 102 } 103 104 if (loadrandom() == 0) 105 cmd.boothowto |= RB_GOODRANDOM; 106 107 kexec(isupgrade); 108 109 hasboot = 0; 110 strlcpy(cmd.image, KERNEL, sizeof(cmd.image)); 111 printf("will try %s\n", cmd.image); 112 } 113 114 return 0; 115 } 116 117 int 118 loadrandom(void) 119 { 120 char buf[BOOTRANDOM_MAX]; 121 struct stat sb; 122 int fd, ret = 0; 123 124 /* Read the file from the device specified by the kernel path. */ 125 if (disk_open(cmd.path) == NULL) 126 return -1; 127 fd = open(BOOTRANDOM, O_RDONLY); 128 if (fd == -1) { 129 fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM, 130 strerror(errno)); 131 disk_close(); 132 return -1; 133 } 134 if (fstat(fd, &sb) == 0) { 135 if (sb.st_mode & S_ISTXT) { 136 printf("NOTE: random seed is being reused.\n"); 137 ret = -1; 138 } 139 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) 140 ret = -1; 141 fchmod(fd, sb.st_mode | S_ISTXT); 142 } else { 143 ret = -1; 144 } 145 close(fd); 146 disk_close(); 147 148 /* 149 * Push the whole buffer to the entropy pool. 150 * The kernel will use the entropy on kexec(). 151 * It does not matter if some of the buffer content is uninitialized. 152 */ 153 fd = open(DEVRANDOM, O_WRONLY); 154 if (fd == -1) { 155 fprintf(stderr, "%s: cannot open %s: %s", __func__, 156 DEVRANDOM, strerror(errno)); 157 return -1; 158 } 159 write(fd, buf, sizeof(buf)); 160 close(fd); 161 return ret; 162 } 163 164 void 165 kexec(int isupgrade) 166 { 167 struct kexec_args kargs; 168 struct stat sb; 169 char *kimg = NULL; 170 const char *path; 171 ssize_t n; 172 off_t pos; 173 int fd = -1, ret; 174 175 path = disk_open(cmd.path); 176 if (path == NULL) 177 return; 178 179 fd = open(path, O_RDONLY); 180 if (fd == -1) 181 goto load_failed; 182 if (fstat(fd, &sb) == -1) 183 goto load_failed; 184 if (!S_ISREG(sb.st_mode) || sb.st_size == 0) { 185 errno = ENOEXEC; 186 goto load_failed; 187 } 188 189 /* Prevent re-upgrade: chmod a-x bsd.upgrade */ 190 if (isupgrade) { 191 sb.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); 192 if (fchmod(fd, sb.st_mode) == -1) 193 printf("fchmod a-x %s: failed\n", path); 194 } 195 196 kimg = malloc(sb.st_size); 197 if (kimg == NULL) 198 goto load_failed; 199 200 pos = 0; 201 while (pos < sb.st_size) { 202 n = read(fd, kimg + pos, sb.st_size - pos); 203 if (n == -1) 204 goto load_failed; 205 pos += n; 206 } 207 208 close(fd); 209 disk_close(); 210 211 memset(&kargs, 0, sizeof(kargs)); 212 kargs.kimg = kimg; 213 kargs.klen = sb.st_size; 214 kargs.boothowto = cmd.boothowto; 215 memcpy(kargs.bootduid, cmd.bootduid, sizeof(kargs.bootduid)); 216 217 printf("booting %s\n", cmd.path); 218 ret = ioctl(kexecfd, KIOC_KEXEC, &kargs); 219 if (ret == -1) 220 fprintf(stderr, "failed to execute kernel %s: %s\n", 221 cmd.path, strerror(errno)); 222 else 223 fprintf(stderr, "kexec() returned unexpectedly\n"); 224 free(kimg); 225 return; 226 227 load_failed: 228 fprintf(stderr, "failed to load kernel %s: %s\n", 229 cmd.path, strerror(errno)); 230 if (fd != -1) 231 close(fd); 232 disk_close(); 233 free(kimg); 234 } 235