1 /* 2 * Copyright 2015-2020 The OpenSSL Project Authors. All Rights Reserved. 3 * Copyright (c) 2013-2014 Timo Teräs <timo.teras@gmail.com> 4 * 5 * Licensed under the OpenSSL license (the "License"). You may not use 6 * this file except in compliance with the License. You can obtain a copy 7 * in the file LICENSE in the source distribution or at 8 * https://www.openssl.org/source/license.html 9 */ 10 11 #include "apps.h" 12 #include "progs.h" 13 14 #if defined(OPENSSL_SYS_UNIX) || defined(__APPLE__) || \ 15 (defined(__VMS) && defined(__DECC) && __CRTL_VER >= 80300000) 16 # include <unistd.h> 17 # include <stdio.h> 18 # include <limits.h> 19 # include <errno.h> 20 # include <string.h> 21 # include <ctype.h> 22 # include <sys/stat.h> 23 24 /* 25 * Make sure that the processing of symbol names is treated the same as when 26 * libcrypto is built. This is done automatically for public headers (see 27 * include/openssl/__DECC_INCLUDE_PROLOGUE.H and __DECC_INCLUDE_EPILOGUE.H), 28 * but not for internal headers. 29 */ 30 # ifdef __VMS 31 # pragma names save 32 # pragma names as_is,shortened 33 # endif 34 35 # include "internal/o_dir.h" 36 37 # ifdef __VMS 38 # pragma names restore 39 # endif 40 41 # include <openssl/evp.h> 42 # include <openssl/pem.h> 43 # include <openssl/x509.h> 44 45 46 # ifndef PATH_MAX 47 # define PATH_MAX 4096 48 # endif 49 # ifndef NAME_MAX 50 # define NAME_MAX 255 51 # endif 52 # define MAX_COLLISIONS 256 53 54 # if defined(OPENSSL_SYS_VXWORKS) 55 /* 56 * VxWorks has no symbolic links 57 */ 58 59 # define lstat(path, buf) stat(path, buf) 60 61 int symlink(const char *target, const char *linkpath) 62 { 63 errno = ENOSYS; 64 return -1; 65 } 66 67 ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) 68 { 69 errno = ENOSYS; 70 return -1; 71 } 72 # endif 73 74 typedef struct hentry_st { 75 struct hentry_st *next; 76 char *filename; 77 unsigned short old_id; 78 unsigned char need_symlink; 79 unsigned char digest[EVP_MAX_MD_SIZE]; 80 } HENTRY; 81 82 typedef struct bucket_st { 83 struct bucket_st *next; 84 HENTRY *first_entry, *last_entry; 85 unsigned int hash; 86 unsigned short type; 87 unsigned short num_needed; 88 } BUCKET; 89 90 enum Type { 91 /* Keep in sync with |suffixes|, below. */ 92 TYPE_CERT=0, TYPE_CRL=1 93 }; 94 95 enum Hash { 96 HASH_OLD, HASH_NEW, HASH_BOTH 97 }; 98 99 100 static int evpmdsize; 101 static const EVP_MD *evpmd; 102 static int remove_links = 1; 103 static int verbose = 0; 104 static BUCKET *hash_table[257]; 105 106 static const char *suffixes[] = { "", "r" }; 107 static const char *extensions[] = { "pem", "crt", "cer", "crl" }; 108 109 110 static void bit_set(unsigned char *set, unsigned int bit) 111 { 112 set[bit >> 3] |= 1 << (bit & 0x7); 113 } 114 115 static int bit_isset(unsigned char *set, unsigned int bit) 116 { 117 return set[bit >> 3] & (1 << (bit & 0x7)); 118 } 119 120 121 /* 122 * Process an entry; return number of errors. 123 */ 124 static int add_entry(enum Type type, unsigned int hash, const char *filename, 125 const unsigned char *digest, int need_symlink, 126 unsigned short old_id) 127 { 128 static BUCKET nilbucket; 129 static HENTRY nilhentry; 130 BUCKET *bp; 131 HENTRY *ep, *found = NULL; 132 unsigned int ndx = (type + hash) % OSSL_NELEM(hash_table); 133 134 for (bp = hash_table[ndx]; bp; bp = bp->next) 135 if (bp->type == type && bp->hash == hash) 136 break; 137 if (bp == NULL) { 138 bp = app_malloc(sizeof(*bp), "hash bucket"); 139 *bp = nilbucket; 140 bp->next = hash_table[ndx]; 141 bp->type = type; 142 bp->hash = hash; 143 hash_table[ndx] = bp; 144 } 145 146 for (ep = bp->first_entry; ep; ep = ep->next) { 147 if (digest && memcmp(digest, ep->digest, evpmdsize) == 0) { 148 BIO_printf(bio_err, 149 "%s: warning: skipping duplicate %s in %s\n", 150 opt_getprog(), 151 type == TYPE_CERT ? "certificate" : "CRL", filename); 152 return 0; 153 } 154 if (strcmp(filename, ep->filename) == 0) { 155 found = ep; 156 if (digest == NULL) 157 break; 158 } 159 } 160 ep = found; 161 if (ep == NULL) { 162 if (bp->num_needed >= MAX_COLLISIONS) { 163 BIO_printf(bio_err, 164 "%s: error: hash table overflow for %s\n", 165 opt_getprog(), filename); 166 return 1; 167 } 168 ep = app_malloc(sizeof(*ep), "collision bucket"); 169 *ep = nilhentry; 170 ep->old_id = ~0; 171 ep->filename = OPENSSL_strdup(filename); 172 if (bp->last_entry) 173 bp->last_entry->next = ep; 174 if (bp->first_entry == NULL) 175 bp->first_entry = ep; 176 bp->last_entry = ep; 177 } 178 179 if (old_id < ep->old_id) 180 ep->old_id = old_id; 181 if (need_symlink && !ep->need_symlink) { 182 ep->need_symlink = 1; 183 bp->num_needed++; 184 memcpy(ep->digest, digest, evpmdsize); 185 } 186 return 0; 187 } 188 189 /* 190 * Check if a symlink goes to the right spot; return 0 if okay. 191 * This can be -1 if bad filename, or an error count. 192 */ 193 static int handle_symlink(const char *filename, const char *fullpath) 194 { 195 unsigned int hash = 0; 196 int i, type, id; 197 unsigned char ch; 198 char linktarget[PATH_MAX], *endptr; 199 ossl_ssize_t n; 200 201 for (i = 0; i < 8; i++) { 202 ch = filename[i]; 203 if (!isxdigit(ch)) 204 return -1; 205 hash <<= 4; 206 hash += OPENSSL_hexchar2int(ch); 207 } 208 if (filename[i++] != '.') 209 return -1; 210 for (type = OSSL_NELEM(suffixes) - 1; type > 0; type--) { 211 const char *suffix = suffixes[type]; 212 if (strncasecmp(suffix, &filename[i], strlen(suffix)) == 0) 213 break; 214 } 215 i += strlen(suffixes[type]); 216 217 id = strtoul(&filename[i], &endptr, 10); 218 if (*endptr != '\0') 219 return -1; 220 221 n = readlink(fullpath, linktarget, sizeof(linktarget)); 222 if (n < 0 || n >= (int)sizeof(linktarget)) 223 return -1; 224 linktarget[n] = 0; 225 226 return add_entry(type, hash, linktarget, NULL, 0, id); 227 } 228 229 /* 230 * process a file, return number of errors. 231 */ 232 static int do_file(const char *filename, const char *fullpath, enum Hash h) 233 { 234 STACK_OF (X509_INFO) *inf = NULL; 235 X509_INFO *x; 236 X509_NAME *name = NULL; 237 BIO *b; 238 const char *ext; 239 unsigned char digest[EVP_MAX_MD_SIZE]; 240 int type, errs = 0; 241 size_t i; 242 243 /* Does it end with a recognized extension? */ 244 if ((ext = strrchr(filename, '.')) == NULL) 245 goto end; 246 for (i = 0; i < OSSL_NELEM(extensions); i++) { 247 if (strcasecmp(extensions[i], ext + 1) == 0) 248 break; 249 } 250 if (i >= OSSL_NELEM(extensions)) 251 goto end; 252 253 /* Does it have X.509 data in it? */ 254 if ((b = BIO_new_file(fullpath, "r")) == NULL) { 255 BIO_printf(bio_err, "%s: error: skipping %s, cannot open file\n", 256 opt_getprog(), filename); 257 errs++; 258 goto end; 259 } 260 inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL); 261 BIO_free(b); 262 if (inf == NULL) 263 goto end; 264 265 if (sk_X509_INFO_num(inf) != 1) { 266 BIO_printf(bio_err, 267 "%s: warning: skipping %s," 268 "it does not contain exactly one certificate or CRL\n", 269 opt_getprog(), filename); 270 /* This is not an error. */ 271 goto end; 272 } 273 x = sk_X509_INFO_value(inf, 0); 274 if (x->x509 != NULL) { 275 type = TYPE_CERT; 276 name = X509_get_subject_name(x->x509); 277 if (!X509_digest(x->x509, evpmd, digest, NULL)) { 278 BIO_printf(bio_err, "out of memory\n"); 279 ++errs; 280 goto end; 281 } 282 } else if (x->crl != NULL) { 283 type = TYPE_CRL; 284 name = X509_CRL_get_issuer(x->crl); 285 if (!X509_CRL_digest(x->crl, evpmd, digest, NULL)) { 286 BIO_printf(bio_err, "out of memory\n"); 287 ++errs; 288 goto end; 289 } 290 } else { 291 ++errs; 292 goto end; 293 } 294 if (name != NULL) { 295 if ((h == HASH_NEW) || (h == HASH_BOTH)) 296 errs += add_entry(type, X509_NAME_hash(name), filename, digest, 1, ~0); 297 if ((h == HASH_OLD) || (h == HASH_BOTH)) 298 errs += add_entry(type, X509_NAME_hash_old(name), filename, digest, 1, ~0); 299 } 300 301 end: 302 sk_X509_INFO_pop_free(inf, X509_INFO_free); 303 return errs; 304 } 305 306 static void str_free(char *s) 307 { 308 OPENSSL_free(s); 309 } 310 311 static int ends_with_dirsep(const char *path) 312 { 313 if (*path != '\0') 314 path += strlen(path) - 1; 315 # if defined __VMS 316 if (*path == ']' || *path == '>' || *path == ':') 317 return 1; 318 # elif defined _WIN32 319 if (*path == '\\') 320 return 1; 321 # endif 322 return *path == '/'; 323 } 324 325 /* 326 * Process a directory; return number of errors found. 327 */ 328 static int do_dir(const char *dirname, enum Hash h) 329 { 330 BUCKET *bp, *nextbp; 331 HENTRY *ep, *nextep; 332 OPENSSL_DIR_CTX *d = NULL; 333 struct stat st; 334 unsigned char idmask[MAX_COLLISIONS / 8]; 335 int n, numfiles, nextid, buflen, errs = 0; 336 size_t i; 337 const char *pathsep; 338 const char *filename; 339 char *buf, *copy = NULL; 340 STACK_OF(OPENSSL_STRING) *files = NULL; 341 342 if (app_access(dirname, W_OK) < 0) { 343 BIO_printf(bio_err, "Skipping %s, can't write\n", dirname); 344 return 1; 345 } 346 buflen = strlen(dirname); 347 pathsep = (buflen && !ends_with_dirsep(dirname)) ? "/": ""; 348 buflen += NAME_MAX + 1 + 1; 349 buf = app_malloc(buflen, "filename buffer"); 350 351 if (verbose) 352 BIO_printf(bio_out, "Doing %s\n", dirname); 353 354 if ((files = sk_OPENSSL_STRING_new_null()) == NULL) { 355 BIO_printf(bio_err, "Skipping %s, out of memory\n", dirname); 356 errs = 1; 357 goto err; 358 } 359 while ((filename = OPENSSL_DIR_read(&d, dirname)) != NULL) { 360 if ((copy = OPENSSL_strdup(filename)) == NULL 361 || sk_OPENSSL_STRING_push(files, copy) == 0) { 362 OPENSSL_free(copy); 363 BIO_puts(bio_err, "out of memory\n"); 364 errs = 1; 365 goto err; 366 } 367 } 368 OPENSSL_DIR_end(&d); 369 sk_OPENSSL_STRING_sort(files); 370 371 numfiles = sk_OPENSSL_STRING_num(files); 372 for (n = 0; n < numfiles; ++n) { 373 filename = sk_OPENSSL_STRING_value(files, n); 374 if (BIO_snprintf(buf, buflen, "%s%s%s", 375 dirname, pathsep, filename) >= buflen) 376 continue; 377 if (lstat(buf, &st) < 0) 378 continue; 379 if (S_ISLNK(st.st_mode) && handle_symlink(filename, buf) == 0) 380 continue; 381 errs += do_file(filename, buf, h); 382 } 383 384 for (i = 0; i < OSSL_NELEM(hash_table); i++) { 385 for (bp = hash_table[i]; bp; bp = nextbp) { 386 nextbp = bp->next; 387 nextid = 0; 388 memset(idmask, 0, (bp->num_needed + 7) / 8); 389 for (ep = bp->first_entry; ep; ep = ep->next) 390 if (ep->old_id < bp->num_needed) 391 bit_set(idmask, ep->old_id); 392 393 for (ep = bp->first_entry; ep; ep = nextep) { 394 nextep = ep->next; 395 if (ep->old_id < bp->num_needed) { 396 /* Link exists, and is used as-is */ 397 BIO_snprintf(buf, buflen, "%08x.%s%d", bp->hash, 398 suffixes[bp->type], ep->old_id); 399 if (verbose) 400 BIO_printf(bio_out, "link %s -> %s\n", 401 ep->filename, buf); 402 } else if (ep->need_symlink) { 403 /* New link needed (it may replace something) */ 404 while (bit_isset(idmask, nextid)) 405 nextid++; 406 407 BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", 408 dirname, pathsep, &n, bp->hash, 409 suffixes[bp->type], nextid); 410 if (verbose) 411 BIO_printf(bio_out, "link %s -> %s\n", 412 ep->filename, &buf[n]); 413 if (unlink(buf) < 0 && errno != ENOENT) { 414 BIO_printf(bio_err, 415 "%s: Can't unlink %s, %s\n", 416 opt_getprog(), buf, strerror(errno)); 417 errs++; 418 } 419 if (symlink(ep->filename, buf) < 0) { 420 BIO_printf(bio_err, 421 "%s: Can't symlink %s, %s\n", 422 opt_getprog(), ep->filename, 423 strerror(errno)); 424 errs++; 425 } 426 bit_set(idmask, nextid); 427 } else if (remove_links) { 428 /* Link to be deleted */ 429 BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", 430 dirname, pathsep, &n, bp->hash, 431 suffixes[bp->type], ep->old_id); 432 if (verbose) 433 BIO_printf(bio_out, "unlink %s\n", 434 &buf[n]); 435 if (unlink(buf) < 0 && errno != ENOENT) { 436 BIO_printf(bio_err, 437 "%s: Can't unlink %s, %s\n", 438 opt_getprog(), buf, strerror(errno)); 439 errs++; 440 } 441 } 442 OPENSSL_free(ep->filename); 443 OPENSSL_free(ep); 444 } 445 OPENSSL_free(bp); 446 } 447 hash_table[i] = NULL; 448 } 449 450 err: 451 sk_OPENSSL_STRING_pop_free(files, str_free); 452 OPENSSL_free(buf); 453 return errs; 454 } 455 456 typedef enum OPTION_choice { 457 OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, 458 OPT_COMPAT, OPT_OLD, OPT_N, OPT_VERBOSE 459 } OPTION_CHOICE; 460 461 const OPTIONS rehash_options[] = { 462 {OPT_HELP_STR, 1, '-', "Usage: %s [options] [cert-directory...]\n"}, 463 {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, 464 {"help", OPT_HELP, '-', "Display this summary"}, 465 {"h", OPT_HELP, '-', "Display this summary"}, 466 {"compat", OPT_COMPAT, '-', "Create both new- and old-style hash links"}, 467 {"old", OPT_OLD, '-', "Use old-style hash to generate links"}, 468 {"n", OPT_N, '-', "Do not remove existing links"}, 469 {"v", OPT_VERBOSE, '-', "Verbose output"}, 470 {NULL} 471 }; 472 473 474 int rehash_main(int argc, char **argv) 475 { 476 const char *env, *prog; 477 char *e, *m; 478 int errs = 0; 479 OPTION_CHOICE o; 480 enum Hash h = HASH_NEW; 481 482 prog = opt_init(argc, argv, rehash_options); 483 while ((o = opt_next()) != OPT_EOF) { 484 switch (o) { 485 case OPT_EOF: 486 case OPT_ERR: 487 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 488 goto end; 489 case OPT_HELP: 490 opt_help(rehash_options); 491 goto end; 492 case OPT_COMPAT: 493 h = HASH_BOTH; 494 break; 495 case OPT_OLD: 496 h = HASH_OLD; 497 break; 498 case OPT_N: 499 remove_links = 0; 500 break; 501 case OPT_VERBOSE: 502 verbose = 1; 503 break; 504 } 505 } 506 argc = opt_num_rest(); 507 argv = opt_rest(); 508 509 evpmd = EVP_sha1(); 510 evpmdsize = EVP_MD_size(evpmd); 511 512 if (*argv != NULL) { 513 while (*argv != NULL) 514 errs += do_dir(*argv++, h); 515 } else if ((env = getenv(X509_get_default_cert_dir_env())) != NULL) { 516 char lsc[2] = { LIST_SEPARATOR_CHAR, '\0' }; 517 m = OPENSSL_strdup(env); 518 for (e = strtok(m, lsc); e != NULL; e = strtok(NULL, lsc)) 519 errs += do_dir(e, h); 520 OPENSSL_free(m); 521 } else { 522 errs += do_dir(X509_get_default_cert_dir(), h); 523 } 524 525 end: 526 return errs; 527 } 528 529 #else 530 const OPTIONS rehash_options[] = { 531 {NULL} 532 }; 533 534 int rehash_main(int argc, char **argv) 535 { 536 BIO_printf(bio_err, "Not available; use c_rehash script\n"); 537 return 1; 538 } 539 540 #endif /* defined(OPENSSL_SYS_UNIX) || defined(__APPLE__) */ 541