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