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