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