1 /* 2 * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #if defined (__TANDEM) && defined (_SPT_MODEL_) 11 /* 12 * These definitions have to come first in SPT due to scoping of the 13 * declarations in c99 associated with SPT use of stat. 14 */ 15 # include <sys/types.h> 16 # include <sys/stat.h> 17 #endif 18 19 #include "e_os.h" 20 #include "internal/cryptlib.h" 21 #include <stdio.h> 22 #include <time.h> 23 #include <errno.h> 24 #include <sys/types.h> 25 26 #ifndef OPENSSL_NO_POSIX_IO 27 # include <sys/stat.h> 28 #endif 29 30 #include <openssl/x509.h> 31 #include "crypto/x509.h" 32 #include "x509_local.h" 33 34 struct lookup_dir_hashes_st { 35 unsigned long hash; 36 int suffix; 37 }; 38 39 struct lookup_dir_entry_st { 40 char *dir; 41 int dir_type; 42 STACK_OF(BY_DIR_HASH) *hashes; 43 }; 44 45 typedef struct lookup_dir_st { 46 BUF_MEM *buffer; 47 STACK_OF(BY_DIR_ENTRY) *dirs; 48 CRYPTO_RWLOCK *lock; 49 } BY_DIR; 50 51 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 52 char **retp); 53 54 static int new_dir(X509_LOOKUP *lu); 55 static void free_dir(X509_LOOKUP *lu); 56 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type); 57 static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, 58 const X509_NAME *name, X509_OBJECT *ret); 59 static int get_cert_by_subject_ex(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, 60 const X509_NAME *name, X509_OBJECT *ret, 61 OSSL_LIB_CTX *libctx, const char *propq); 62 static X509_LOOKUP_METHOD x509_dir_lookup = { 63 "Load certs from files in a directory", 64 new_dir, /* new_item */ 65 free_dir, /* free */ 66 NULL, /* init */ 67 NULL, /* shutdown */ 68 dir_ctrl, /* ctrl */ 69 get_cert_by_subject, /* get_by_subject */ 70 NULL, /* get_by_issuer_serial */ 71 NULL, /* get_by_fingerprint */ 72 NULL, /* get_by_alias */ 73 get_cert_by_subject_ex, /* get_by_subject_ex */ 74 NULL, /* ctrl_ex */ 75 }; 76 77 X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) 78 { 79 return &x509_dir_lookup; 80 } 81 82 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 83 char **retp) 84 { 85 int ret = 0; 86 BY_DIR *ld = (BY_DIR *)ctx->method_data; 87 88 switch (cmd) { 89 case X509_L_ADD_DIR: 90 if (argl == X509_FILETYPE_DEFAULT) { 91 const char *dir = ossl_safe_getenv(X509_get_default_cert_dir_env()); 92 93 if (dir) 94 ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM); 95 else 96 ret = add_cert_dir(ld, X509_get_default_cert_dir(), 97 X509_FILETYPE_PEM); 98 if (!ret) { 99 ERR_raise(ERR_LIB_X509, X509_R_LOADING_CERT_DIR); 100 } 101 } else 102 ret = add_cert_dir(ld, argp, (int)argl); 103 break; 104 } 105 return ret; 106 } 107 108 static int new_dir(X509_LOOKUP *lu) 109 { 110 BY_DIR *a = OPENSSL_malloc(sizeof(*a)); 111 112 if (a == NULL) { 113 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 114 return 0; 115 } 116 117 if ((a->buffer = BUF_MEM_new()) == NULL) { 118 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 119 goto err; 120 } 121 a->dirs = NULL; 122 a->lock = CRYPTO_THREAD_lock_new(); 123 if (a->lock == NULL) { 124 BUF_MEM_free(a->buffer); 125 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 126 goto err; 127 } 128 lu->method_data = a; 129 return 1; 130 131 err: 132 OPENSSL_free(a); 133 return 0; 134 } 135 136 static void by_dir_hash_free(BY_DIR_HASH *hash) 137 { 138 OPENSSL_free(hash); 139 } 140 141 static int by_dir_hash_cmp(const BY_DIR_HASH *const *a, 142 const BY_DIR_HASH *const *b) 143 { 144 if ((*a)->hash > (*b)->hash) 145 return 1; 146 if ((*a)->hash < (*b)->hash) 147 return -1; 148 return 0; 149 } 150 151 static void by_dir_entry_free(BY_DIR_ENTRY *ent) 152 { 153 OPENSSL_free(ent->dir); 154 sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free); 155 OPENSSL_free(ent); 156 } 157 158 static void free_dir(X509_LOOKUP *lu) 159 { 160 BY_DIR *a = (BY_DIR *)lu->method_data; 161 162 sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free); 163 BUF_MEM_free(a->buffer); 164 CRYPTO_THREAD_lock_free(a->lock); 165 OPENSSL_free(a); 166 } 167 168 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) 169 { 170 int j; 171 size_t len; 172 const char *s, *ss, *p; 173 174 if (dir == NULL || *dir == '\0') { 175 ERR_raise(ERR_LIB_X509, X509_R_INVALID_DIRECTORY); 176 return 0; 177 } 178 179 s = dir; 180 p = s; 181 do { 182 if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0')) { 183 BY_DIR_ENTRY *ent; 184 185 ss = s; 186 s = p + 1; 187 len = p - ss; 188 if (len == 0) 189 continue; 190 for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) { 191 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j); 192 if (strlen(ent->dir) == len && strncmp(ent->dir, ss, len) == 0) 193 break; 194 } 195 if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) 196 continue; 197 if (ctx->dirs == NULL) { 198 ctx->dirs = sk_BY_DIR_ENTRY_new_null(); 199 if (!ctx->dirs) { 200 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 201 return 0; 202 } 203 } 204 ent = OPENSSL_malloc(sizeof(*ent)); 205 if (ent == NULL) { 206 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 207 return 0; 208 } 209 ent->dir_type = type; 210 ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp); 211 ent->dir = OPENSSL_strndup(ss, len); 212 if (ent->dir == NULL || ent->hashes == NULL) { 213 by_dir_entry_free(ent); 214 return 0; 215 } 216 if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) { 217 by_dir_entry_free(ent); 218 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 219 return 0; 220 } 221 } 222 } while (*p++ != '\0'); 223 return 1; 224 } 225 226 static int get_cert_by_subject_ex(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, 227 const X509_NAME *name, X509_OBJECT *ret, 228 OSSL_LIB_CTX *libctx, const char *propq) 229 { 230 BY_DIR *ctx; 231 union { 232 X509 st_x509; 233 X509_CRL crl; 234 } data; 235 int ok = 0; 236 int i, j, k; 237 unsigned long h; 238 BUF_MEM *b = NULL; 239 X509_OBJECT stmp, *tmp; 240 const char *postfix = ""; 241 242 if (name == NULL) 243 return 0; 244 245 stmp.type = type; 246 if (type == X509_LU_X509) { 247 data.st_x509.cert_info.subject = (X509_NAME *)name; /* won't modify it */ 248 stmp.data.x509 = &data.st_x509; 249 } else if (type == X509_LU_CRL) { 250 data.crl.crl.issuer = (X509_NAME *)name; /* won't modify it */ 251 stmp.data.crl = &data.crl; 252 postfix = "r"; 253 } else { 254 ERR_raise(ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE); 255 goto finish; 256 } 257 258 if ((b = BUF_MEM_new()) == NULL) { 259 ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); 260 goto finish; 261 } 262 263 ctx = (BY_DIR *)xl->method_data; 264 h = X509_NAME_hash_ex(name, libctx, propq, &i); 265 if (i == 0) 266 goto finish; 267 for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) { 268 BY_DIR_ENTRY *ent; 269 int idx; 270 BY_DIR_HASH htmp, *hent; 271 272 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); 273 j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1; 274 if (!BUF_MEM_grow(b, j)) { 275 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 276 goto finish; 277 } 278 if (type == X509_LU_CRL && ent->hashes) { 279 htmp.hash = h; 280 if (!CRYPTO_THREAD_read_lock(ctx->lock)) 281 goto finish; 282 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 283 if (idx >= 0) { 284 hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 285 k = hent->suffix; 286 } else { 287 hent = NULL; 288 k = 0; 289 } 290 CRYPTO_THREAD_unlock(ctx->lock); 291 } else { 292 k = 0; 293 hent = NULL; 294 } 295 for (;;) { 296 char c = '/'; 297 298 #ifdef OPENSSL_SYS_VMS 299 c = ent->dir[strlen(ent->dir) - 1]; 300 if (c != ':' && c != '>' && c != ']') { 301 /* 302 * If no separator is present, we assume the directory 303 * specifier is a logical name, and add a colon. We really 304 * should use better VMS routines for merging things like 305 * this, but this will do for now... -- Richard Levitte 306 */ 307 c = ':'; 308 } else { 309 c = '\0'; 310 } 311 312 if (c == '\0') { 313 /* 314 * This is special. When c == '\0', no directory separator 315 * should be added. 316 */ 317 BIO_snprintf(b->data, b->max, 318 "%s%08lx.%s%d", ent->dir, h, postfix, k); 319 } else 320 #endif 321 { 322 BIO_snprintf(b->data, b->max, 323 "%s%c%08lx.%s%d", ent->dir, c, h, postfix, k); 324 } 325 #ifndef OPENSSL_NO_POSIX_IO 326 # ifdef _WIN32 327 # define stat _stat 328 # endif 329 { 330 struct stat st; 331 if (stat(b->data, &st) < 0) 332 break; 333 } 334 #endif 335 /* found one. */ 336 if (type == X509_LU_X509) { 337 if ((X509_load_cert_file_ex(xl, b->data, ent->dir_type, libctx, 338 propq)) == 0) 339 break; 340 } else if (type == X509_LU_CRL) { 341 if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0) 342 break; 343 } 344 /* else case will caught higher up */ 345 k++; 346 } 347 348 /* 349 * we have added it to the cache so now pull it out again 350 */ 351 if (!X509_STORE_lock(xl->store_ctx)) 352 goto finish; 353 j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp); 354 tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j); 355 X509_STORE_unlock(xl->store_ctx); 356 357 /* 358 * If a CRL, update the last file suffix added for this. 359 * We don't need to add an entry if k is 0 as this is the initial value. 360 * This avoids the need for a write lock and sort operation in the 361 * simple case where no CRL is present for a hash. 362 */ 363 if (type == X509_LU_CRL && k > 0) { 364 if (!CRYPTO_THREAD_write_lock(ctx->lock)) 365 goto finish; 366 /* 367 * Look for entry again in case another thread added an entry 368 * first. 369 */ 370 if (hent == NULL) { 371 htmp.hash = h; 372 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 373 hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 374 } 375 if (hent == NULL) { 376 hent = OPENSSL_malloc(sizeof(*hent)); 377 if (hent == NULL) { 378 CRYPTO_THREAD_unlock(ctx->lock); 379 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 380 ok = 0; 381 goto finish; 382 } 383 hent->hash = h; 384 hent->suffix = k; 385 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) { 386 CRYPTO_THREAD_unlock(ctx->lock); 387 OPENSSL_free(hent); 388 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); 389 ok = 0; 390 goto finish; 391 } 392 393 /* 394 * Ensure stack is sorted so that subsequent sk_BY_DIR_HASH_find 395 * will not mutate the stack and therefore require a write lock. 396 */ 397 sk_BY_DIR_HASH_sort(ent->hashes); 398 } else if (hent->suffix < k) { 399 hent->suffix = k; 400 } 401 402 CRYPTO_THREAD_unlock(ctx->lock); 403 404 } 405 406 if (tmp != NULL) { 407 ok = 1; 408 ret->type = tmp->type; 409 memcpy(&ret->data, &tmp->data, sizeof(ret->data)); 410 411 /* 412 * Clear any errors that might have been raised processing empty 413 * or malformed files. 414 */ 415 ERR_clear_error(); 416 417 goto finish; 418 } 419 } 420 finish: 421 BUF_MEM_free(b); 422 return ok; 423 } 424 425 static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, 426 const X509_NAME *name, X509_OBJECT *ret) 427 { 428 return get_cert_by_subject_ex(xl, type, name, ret, NULL, NULL); 429 } 430