1 /* 2 * Copyright 2015-2019 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 X509_digest(x->x509, evpmd, digest, NULL); 278 } else if (x->crl != NULL) { 279 type = TYPE_CRL; 280 name = X509_CRL_get_issuer(x->crl); 281 X509_CRL_digest(x->crl, evpmd, digest, NULL); 282 } else { 283 ++errs; 284 goto end; 285 } 286 if (name != NULL) { 287 if ((h == HASH_NEW) || (h == HASH_BOTH)) 288 errs += add_entry(type, X509_NAME_hash(name), filename, digest, 1, ~0); 289 if ((h == HASH_OLD) || (h == HASH_BOTH)) 290 errs += add_entry(type, X509_NAME_hash_old(name), filename, digest, 1, ~0); 291 } 292 293 end: 294 sk_X509_INFO_pop_free(inf, X509_INFO_free); 295 return errs; 296 } 297 298 static void str_free(char *s) 299 { 300 OPENSSL_free(s); 301 } 302 303 static int ends_with_dirsep(const char *path) 304 { 305 if (*path != '\0') 306 path += strlen(path) - 1; 307 # if defined __VMS 308 if (*path == ']' || *path == '>' || *path == ':') 309 return 1; 310 # elif defined _WIN32 311 if (*path == '\\') 312 return 1; 313 # endif 314 return *path == '/'; 315 } 316 317 /* 318 * Process a directory; return number of errors found. 319 */ 320 static int do_dir(const char *dirname, enum Hash h) 321 { 322 BUCKET *bp, *nextbp; 323 HENTRY *ep, *nextep; 324 OPENSSL_DIR_CTX *d = NULL; 325 struct stat st; 326 unsigned char idmask[MAX_COLLISIONS / 8]; 327 int n, numfiles, nextid, buflen, errs = 0; 328 size_t i; 329 const char *pathsep; 330 const char *filename; 331 char *buf, *copy = NULL; 332 STACK_OF(OPENSSL_STRING) *files = NULL; 333 334 if (app_access(dirname, W_OK) < 0) { 335 BIO_printf(bio_err, "Skipping %s, can't write\n", dirname); 336 return 1; 337 } 338 buflen = strlen(dirname); 339 pathsep = (buflen && !ends_with_dirsep(dirname)) ? "/": ""; 340 buflen += NAME_MAX + 1 + 1; 341 buf = app_malloc(buflen, "filename buffer"); 342 343 if (verbose) 344 BIO_printf(bio_out, "Doing %s\n", dirname); 345 346 if ((files = sk_OPENSSL_STRING_new_null()) == NULL) { 347 BIO_printf(bio_err, "Skipping %s, out of memory\n", dirname); 348 errs = 1; 349 goto err; 350 } 351 while ((filename = OPENSSL_DIR_read(&d, dirname)) != NULL) { 352 if ((copy = OPENSSL_strdup(filename)) == NULL 353 || sk_OPENSSL_STRING_push(files, copy) == 0) { 354 OPENSSL_free(copy); 355 BIO_puts(bio_err, "out of memory\n"); 356 errs = 1; 357 goto err; 358 } 359 } 360 OPENSSL_DIR_end(&d); 361 sk_OPENSSL_STRING_sort(files); 362 363 numfiles = sk_OPENSSL_STRING_num(files); 364 for (n = 0; n < numfiles; ++n) { 365 filename = sk_OPENSSL_STRING_value(files, n); 366 if (BIO_snprintf(buf, buflen, "%s%s%s", 367 dirname, pathsep, filename) >= buflen) 368 continue; 369 if (lstat(buf, &st) < 0) 370 continue; 371 if (S_ISLNK(st.st_mode) && handle_symlink(filename, buf) == 0) 372 continue; 373 errs += do_file(filename, buf, h); 374 } 375 376 for (i = 0; i < OSSL_NELEM(hash_table); i++) { 377 for (bp = hash_table[i]; bp; bp = nextbp) { 378 nextbp = bp->next; 379 nextid = 0; 380 memset(idmask, 0, (bp->num_needed + 7) / 8); 381 for (ep = bp->first_entry; ep; ep = ep->next) 382 if (ep->old_id < bp->num_needed) 383 bit_set(idmask, ep->old_id); 384 385 for (ep = bp->first_entry; ep; ep = nextep) { 386 nextep = ep->next; 387 if (ep->old_id < bp->num_needed) { 388 /* Link exists, and is used as-is */ 389 BIO_snprintf(buf, buflen, "%08x.%s%d", bp->hash, 390 suffixes[bp->type], ep->old_id); 391 if (verbose) 392 BIO_printf(bio_out, "link %s -> %s\n", 393 ep->filename, buf); 394 } else if (ep->need_symlink) { 395 /* New link needed (it may replace something) */ 396 while (bit_isset(idmask, nextid)) 397 nextid++; 398 399 BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", 400 dirname, pathsep, &n, bp->hash, 401 suffixes[bp->type], nextid); 402 if (verbose) 403 BIO_printf(bio_out, "link %s -> %s\n", 404 ep->filename, &buf[n]); 405 if (unlink(buf) < 0 && errno != ENOENT) { 406 BIO_printf(bio_err, 407 "%s: Can't unlink %s, %s\n", 408 opt_getprog(), buf, strerror(errno)); 409 errs++; 410 } 411 if (symlink(ep->filename, buf) < 0) { 412 BIO_printf(bio_err, 413 "%s: Can't symlink %s, %s\n", 414 opt_getprog(), ep->filename, 415 strerror(errno)); 416 errs++; 417 } 418 bit_set(idmask, nextid); 419 } else if (remove_links) { 420 /* Link to be deleted */ 421 BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", 422 dirname, pathsep, &n, bp->hash, 423 suffixes[bp->type], ep->old_id); 424 if (verbose) 425 BIO_printf(bio_out, "unlink %s\n", 426 &buf[n]); 427 if (unlink(buf) < 0 && errno != ENOENT) { 428 BIO_printf(bio_err, 429 "%s: Can't unlink %s, %s\n", 430 opt_getprog(), buf, strerror(errno)); 431 errs++; 432 } 433 } 434 OPENSSL_free(ep->filename); 435 OPENSSL_free(ep); 436 } 437 OPENSSL_free(bp); 438 } 439 hash_table[i] = NULL; 440 } 441 442 err: 443 sk_OPENSSL_STRING_pop_free(files, str_free); 444 OPENSSL_free(buf); 445 return errs; 446 } 447 448 typedef enum OPTION_choice { 449 OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, 450 OPT_COMPAT, OPT_OLD, OPT_N, OPT_VERBOSE 451 } OPTION_CHOICE; 452 453 const OPTIONS rehash_options[] = { 454 {OPT_HELP_STR, 1, '-', "Usage: %s [options] [cert-directory...]\n"}, 455 {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, 456 {"help", OPT_HELP, '-', "Display this summary"}, 457 {"h", OPT_HELP, '-', "Display this summary"}, 458 {"compat", OPT_COMPAT, '-', "Create both new- and old-style hash links"}, 459 {"old", OPT_OLD, '-', "Use old-style hash to generate links"}, 460 {"n", OPT_N, '-', "Do not remove existing links"}, 461 {"v", OPT_VERBOSE, '-', "Verbose output"}, 462 {NULL} 463 }; 464 465 466 int rehash_main(int argc, char **argv) 467 { 468 const char *env, *prog; 469 char *e, *m; 470 int errs = 0; 471 OPTION_CHOICE o; 472 enum Hash h = HASH_NEW; 473 474 prog = opt_init(argc, argv, rehash_options); 475 while ((o = opt_next()) != OPT_EOF) { 476 switch (o) { 477 case OPT_EOF: 478 case OPT_ERR: 479 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 480 goto end; 481 case OPT_HELP: 482 opt_help(rehash_options); 483 goto end; 484 case OPT_COMPAT: 485 h = HASH_BOTH; 486 break; 487 case OPT_OLD: 488 h = HASH_OLD; 489 break; 490 case OPT_N: 491 remove_links = 0; 492 break; 493 case OPT_VERBOSE: 494 verbose = 1; 495 break; 496 } 497 } 498 argc = opt_num_rest(); 499 argv = opt_rest(); 500 501 evpmd = EVP_sha1(); 502 evpmdsize = EVP_MD_size(evpmd); 503 504 if (*argv != NULL) { 505 while (*argv != NULL) 506 errs += do_dir(*argv++, h); 507 } else if ((env = getenv(X509_get_default_cert_dir_env())) != NULL) { 508 char lsc[2] = { LIST_SEPARATOR_CHAR, '\0' }; 509 m = OPENSSL_strdup(env); 510 for (e = strtok(m, lsc); e != NULL; e = strtok(NULL, lsc)) 511 errs += do_dir(e, h); 512 OPENSSL_free(m); 513 } else { 514 errs += do_dir(X509_get_default_cert_dir(), h); 515 } 516 517 end: 518 return errs; 519 } 520 521 #else 522 const OPTIONS rehash_options[] = { 523 {NULL} 524 }; 525 526 int rehash_main(int argc, char **argv) 527 { 528 BIO_printf(bio_err, "Not available; use c_rehash script\n"); 529 return 1; 530 } 531 532 #endif /* defined(OPENSSL_SYS_UNIX) || defined(__APPLE__) */ 533