1 /* $OpenBSD: vnconfig.c,v 1.14 2024/11/09 10:57:06 sobrado Exp $ */ 2 /* 3 * Copyright (c) 1993 University of Utah. 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * the Systems Programming Group of the University of Utah Computer 9 * Science Department. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 e */ 35 36 #include <sys/param.h> /* DEV_BSIZE */ 37 #include <sys/ioctl.h> 38 #include <sys/stat.h> 39 #include <sys/disklabel.h> 40 41 #include <dev/vndioctl.h> 42 43 #include <blf.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <readpassphrase.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <limits.h> 53 #include <util.h> 54 55 #define DEFAULT_VND "vnd0" 56 57 #define VND_CONFIG 1 58 #define VND_UNCONFIG 2 59 #define VND_GETINFO 3 60 61 int verbose = 0; 62 63 __dead void usage(void); 64 int config(char *, char *, struct disklabel *, char *, size_t); 65 int unconfig(char *); 66 int getinfo(const char *, int *availp); 67 char *get_pkcs_key(char *, char *); 68 69 int 70 main(int argc, char **argv) 71 { 72 int ch, rv, action, opt_k = 0, opt_K = 0, opt_l = 0, opt_u = 0; 73 char *key = NULL, *rounds = NULL, *saltopt = NULL; 74 char *file, *vnd; 75 size_t keylen = 0; 76 extern char *__progname; 77 struct disklabel *dp = NULL; 78 79 action = VND_CONFIG; 80 81 while ((ch = getopt(argc, argv, "kK:lo:S:t:uv")) != -1) { 82 switch (ch) { 83 case 'k': 84 opt_k = 1; 85 break; 86 case 'K': 87 opt_K = 1; 88 rounds = optarg; 89 break; 90 case 'l': 91 opt_l = 1; 92 break; 93 case 'S': 94 saltopt = optarg; 95 break; 96 case 't': 97 dp = getdiskbyname(optarg); 98 if (dp == NULL) 99 errx(1, "unknown disk type: %s", optarg); 100 break; 101 case 'u': 102 opt_u = 1; 103 break; 104 case 'v': 105 verbose = 1; 106 break; 107 default: 108 usage(); 109 /* NOTREACHED */ 110 } 111 } 112 argc -= optind; 113 argv += optind; 114 115 if (opt_l + opt_u > 1) 116 errx(1, "-l and -u are mutually exclusive options"); 117 118 if (opt_l) 119 action = VND_GETINFO; 120 else if (opt_u) 121 action = VND_UNCONFIG; 122 123 if (saltopt && (!opt_K)) 124 errx(1, "-S only makes sense when used with -K"); 125 126 if (action == VND_CONFIG) { 127 if (argc == 1) { 128 file = argv[0]; 129 vnd = NULL; 130 } else if (argc == 2) { 131 vnd = argv[0]; 132 file = argv[1]; 133 } else 134 usage(); 135 136 if (opt_k || opt_K) 137 fprintf(stderr, "WARNING: Consider using softraid crypto.\n"); 138 if (opt_k) { 139 if (opt_K) 140 errx(1, "-k and -K are mutually exclusive"); 141 key = getpass("Encryption key: "); 142 if (key == NULL || (keylen = strlen(key)) == 0) 143 errx(1, "Need an encryption key"); 144 } else if (opt_K) { 145 key = get_pkcs_key(rounds, saltopt); 146 keylen = BLF_MAXUTILIZED; 147 } 148 rv = config(file, vnd, dp, key, keylen); 149 } else if (action == VND_UNCONFIG && argc == 1) 150 rv = unconfig(argv[0]); 151 else if (action == VND_GETINFO) 152 rv = getinfo(argc ? argv[0] : NULL, NULL); 153 else 154 usage(); 155 156 exit(rv); 157 } 158 159 char * 160 get_pkcs_key(char *arg, char *saltopt) 161 { 162 char passphrase[128] = {'\0'}; 163 char saltbuf[128] = {'\0'}, saltfilebuf[PATH_MAX]; 164 char *key = NULL; 165 char *saltfile; 166 const char *errstr; 167 int rounds; 168 169 rounds = strtonum(arg, 1000, INT_MAX, &errstr); 170 if (errstr) 171 err(1, "rounds: %s", errstr); 172 if (readpassphrase("Encryption key: ", passphrase, sizeof(passphrase), 173 RPP_REQUIRE_TTY) == NULL) 174 errx(1, "Unable to read passphrase"); 175 if (saltopt) 176 saltfile = saltopt; 177 else { 178 printf("Salt file: "); 179 fflush(stdout); 180 saltfile = fgets(saltfilebuf, sizeof(saltfilebuf), stdin); 181 if (saltfile) 182 saltfile[strcspn(saltfile, "\n")] = '\0'; 183 } 184 if (!saltfile || saltfile[0] == '\0') 185 warnx("Skipping salt file, insecure"); 186 else { 187 int fd; 188 189 fd = open(saltfile, O_RDONLY); 190 if (fd == -1) { 191 int *s; 192 193 fprintf(stderr, "Salt file not found, attempting to " 194 "create one\n"); 195 fd = open(saltfile, O_RDWR|O_CREAT|O_EXCL, 0600); 196 if (fd == -1) 197 err(1, "Unable to create salt file: '%s'", 198 saltfile); 199 for (s = (int *)saltbuf; 200 s < (int *)(saltbuf + sizeof(saltbuf)); s++) 201 *s = arc4random(); 202 if (write(fd, saltbuf, sizeof(saltbuf)) 203 != sizeof(saltbuf)) 204 err(1, "Unable to write salt file: '%s'", 205 saltfile); 206 fprintf(stderr, "Salt file created as '%s'\n", 207 saltfile); 208 } else { 209 if (read(fd, saltbuf, sizeof(saltbuf)) 210 != sizeof(saltbuf)) 211 err(1, "Unable to read salt file: '%s'", 212 saltfile); 213 } 214 close(fd); 215 } 216 if ((key = calloc(1, BLF_MAXUTILIZED)) == NULL) 217 err(1, NULL); 218 if (pkcs5_pbkdf2(passphrase, sizeof(passphrase), saltbuf, 219 sizeof (saltbuf), key, BLF_MAXUTILIZED, rounds)) 220 errx(1, "pkcs5_pbkdf2 failed"); 221 explicit_bzero(passphrase, sizeof(passphrase)); 222 223 return (key); 224 } 225 226 int 227 getinfo(const char *vname, int *availp) 228 { 229 int vd, print_all = 0; 230 struct vnd_user vnu; 231 232 if (vname == NULL) { 233 vname = DEFAULT_VND; 234 print_all = 1; 235 } 236 237 vd = opendev(vname, O_RDONLY, OPENDEV_PART, NULL); 238 if (vd == -1) 239 err(1, "open: %s", vname); 240 241 vnu.vnu_unit = -1; 242 243 query: 244 if (ioctl(vd, VNDIOCGET, &vnu) == -1) { 245 if (print_all && errno == ENXIO && vnu.vnu_unit > 0) 246 goto end; 247 err(1, "ioctl: %s", vname); 248 } 249 250 if (availp) { 251 if (!vnu.vnu_ino) { 252 *availp = vnu.vnu_unit; 253 close(vd); 254 return (0); 255 } 256 vnu.vnu_unit++; 257 goto query; 258 } 259 260 fprintf(stdout, "vnd%d: ", vnu.vnu_unit); 261 262 if (!vnu.vnu_ino) 263 fprintf(stdout, "not in use\n"); 264 else 265 fprintf(stdout, "covering %s on %s, inode %llu\n", 266 vnu.vnu_file, devname(vnu.vnu_dev, S_IFBLK), 267 (unsigned long long)vnu.vnu_ino); 268 269 if (print_all) { 270 vnu.vnu_unit++; 271 goto query; 272 } 273 274 end: 275 close(vd); 276 if (availp) 277 return (-1); 278 return (0); 279 } 280 281 int 282 config(char *file, char *dev, struct disklabel *dp, char *key, size_t keylen) 283 { 284 struct vnd_ioctl vndio; 285 char *rdev; 286 int fd, rv = -1; 287 int unit, print_dev = 0; 288 289 if (dev == NULL) { 290 if (getinfo(NULL, &unit) == -1) 291 err(1, "no devices available"); 292 print_dev = 1; 293 asprintf(&dev, "vnd%d", unit); 294 } 295 296 if ((fd = opendev(dev, O_RDONLY, OPENDEV_PART, &rdev)) == -1) { 297 err(4, "%s", rdev); 298 goto out; 299 } 300 301 memset(&vndio, 0, sizeof vndio); 302 vndio.vnd_file = file; 303 vndio.vnd_type = (dp && dp->d_type) ? dp->d_type : DTYPE_VND; 304 vndio.vnd_secsize = (dp && dp->d_secsize) ? dp->d_secsize : DEV_BSIZE; 305 vndio.vnd_nsectors = (dp && dp->d_nsectors) ? dp->d_nsectors : 100; 306 vndio.vnd_ntracks = (dp && dp->d_ntracks) ? dp->d_ntracks : 1; 307 vndio.vnd_key = (u_char *)key; 308 vndio.vnd_keylen = keylen; 309 310 /* 311 * Configure the device 312 */ 313 rv = ioctl(fd, VNDIOCSET, &vndio); 314 if (rv) 315 warn("VNDIOCSET"); 316 else { 317 if (print_dev) 318 printf("%s\n", dev); 319 if (verbose) 320 fprintf(stderr, "%s: %llu bytes on %s\n", dev, 321 vndio.vnd_size, file); 322 } 323 324 close(fd); 325 fflush(stdout); 326 out: 327 if (key) 328 explicit_bzero(key, keylen); 329 explicit_bzero(&vndio.vnd_keylen, sizeof vndio.vnd_keylen); 330 return (rv == -1); 331 } 332 333 int 334 unconfig(char *vnd) 335 { 336 struct vnd_ioctl vndio; 337 int fd, rv = -1; 338 char *rdev; 339 340 if ((fd = opendev(vnd, O_RDONLY, OPENDEV_PART, &rdev)) == -1) 341 err(4, "%s", rdev); 342 343 memset(&vndio, 0, sizeof vndio); 344 vndio.vnd_file = vnd; 345 346 /* 347 * Clear (un-configure) the device 348 */ 349 rv = ioctl(fd, VNDIOCCLR, &vndio); 350 if (rv) 351 warn("VNDIOCCLR"); 352 else if (verbose) 353 fprintf(stderr, "%s: cleared\n", vnd); 354 355 close(fd); 356 fflush(stdout); 357 return (rv == -1); 358 } 359 360 __dead void 361 usage(void) 362 { 363 fprintf(stderr, 364 "usage: vnconfig [-v] [-k | -K rounds [-S saltfile]] " 365 "[-t disktype] [vnd_dev]\n" 366 " image\n" 367 " vnconfig -l [vnd_dev]\n" 368 " vnconfig -u [-v] vnd_dev\n"); 369 exit(1); 370 } 371