1 /* $OpenBSD: by_dir.c,v 1.41 2021/11/10 14:34:21 schwarze Exp $ */ 2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 3 * All rights reserved. 4 * 5 * This package is an SSL implementation written 6 * by Eric Young (eay@cryptsoft.com). 7 * The implementation was written so as to conform with Netscapes SSL. 8 * 9 * This library is free for commercial and non-commercial use as long as 10 * the following conditions are aheared to. The following conditions 11 * apply to all code found in this distribution, be it the RC4, RSA, 12 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13 * included with this distribution is covered by the same copyright terms 14 * except that the holder is Tim Hudson (tjh@cryptsoft.com). 15 * 16 * Copyright remains Eric Young's, and as such any Copyright notices in 17 * the code are not to be removed. 18 * If this package is used in a product, Eric Young should be given attribution 19 * as the author of the parts of the library used. 20 * This can be in the form of a textual message at program startup or 21 * in documentation (online or textual) provided with the package. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * "This product includes cryptographic software written by 34 * Eric Young (eay@cryptsoft.com)" 35 * The word 'cryptographic' can be left out if the rouines from the library 36 * being used are not cryptographic related :-). 37 * 4. If you include any Windows specific code (or a derivative thereof) from 38 * the apps directory (application code) you must include an acknowledgement: 39 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 40 * 41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * The licence and distribution terms for any publically available version or 54 * derivative of this code cannot be changed. i.e. this code cannot simply be 55 * copied and put under another distribution licence 56 * [including the GNU Public Licence.] 57 */ 58 59 #include <sys/stat.h> 60 #include <sys/types.h> 61 62 #include <errno.h> 63 #include <stdio.h> 64 #include <string.h> 65 #include <time.h> 66 #include <unistd.h> 67 68 #include <openssl/opensslconf.h> 69 70 #include <openssl/err.h> 71 #include <openssl/x509.h> 72 73 #include "x509_lcl.h" 74 75 typedef struct lookup_dir_hashes_st { 76 unsigned long hash; 77 int suffix; 78 } BY_DIR_HASH; 79 80 typedef struct lookup_dir_entry_st { 81 char *dir; 82 int dir_type; 83 STACK_OF(BY_DIR_HASH) *hashes; 84 } BY_DIR_ENTRY; 85 86 typedef struct lookup_dir_st { 87 BUF_MEM *buffer; 88 STACK_OF(BY_DIR_ENTRY) *dirs; 89 } BY_DIR; 90 91 DECLARE_STACK_OF(BY_DIR_HASH) 92 DECLARE_STACK_OF(BY_DIR_ENTRY) 93 94 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 95 char **ret); 96 static int new_dir(X509_LOOKUP *lu); 97 static void free_dir(X509_LOOKUP *lu); 98 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type); 99 static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, 100 X509_OBJECT *ret); 101 102 static X509_LOOKUP_METHOD x509_dir_lookup = { 103 .name = "Load certs from files in a directory", 104 .new_item = new_dir, 105 .free = free_dir, 106 .init = NULL, 107 .shutdown = NULL, 108 .ctrl = dir_ctrl, 109 .get_by_subject = get_cert_by_subject, 110 .get_by_issuer_serial = NULL, 111 .get_by_fingerprint = NULL, 112 .get_by_alias = NULL, 113 }; 114 115 X509_LOOKUP_METHOD * 116 X509_LOOKUP_hash_dir(void) 117 { 118 return &x509_dir_lookup; 119 } 120 121 static int 122 dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 123 char **retp) 124 { 125 int ret = 0; 126 BY_DIR *ld; 127 128 ld = (BY_DIR *)ctx->method_data; 129 130 switch (cmd) { 131 case X509_L_ADD_DIR: 132 if (argl == X509_FILETYPE_DEFAULT) { 133 ret = add_cert_dir(ld, X509_get_default_cert_dir(), 134 X509_FILETYPE_PEM); 135 if (!ret) { 136 X509error(X509_R_LOADING_CERT_DIR); 137 } 138 } else 139 ret = add_cert_dir(ld, argp, (int)argl); 140 break; 141 } 142 return ret; 143 } 144 145 static int 146 new_dir(X509_LOOKUP *lu) 147 { 148 BY_DIR *a; 149 150 if ((a = malloc(sizeof(*a))) == NULL) { 151 X509error(ERR_R_MALLOC_FAILURE); 152 return 0; 153 } 154 if ((a->buffer = BUF_MEM_new()) == NULL) { 155 X509error(ERR_R_MALLOC_FAILURE); 156 free(a); 157 return 0; 158 } 159 a->dirs = NULL; 160 lu->method_data = (char *)a; 161 return 1; 162 } 163 164 static void 165 by_dir_hash_free(BY_DIR_HASH *hash) 166 { 167 free(hash); 168 } 169 170 static int 171 by_dir_hash_cmp(const BY_DIR_HASH * const *a, 172 const BY_DIR_HASH * const *b) 173 { 174 if ((*a)->hash > (*b)->hash) 175 return 1; 176 if ((*a)->hash < (*b)->hash) 177 return -1; 178 return 0; 179 } 180 181 static void 182 by_dir_entry_free(BY_DIR_ENTRY *ent) 183 { 184 free(ent->dir); 185 sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free); 186 free(ent); 187 } 188 189 static void 190 free_dir(X509_LOOKUP *lu) 191 { 192 BY_DIR *a; 193 194 a = (BY_DIR *)lu->method_data; 195 sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free); 196 BUF_MEM_free(a->buffer); 197 free(a); 198 } 199 200 static int 201 add_cert_dir(BY_DIR *ctx, const char *dir, int type) 202 { 203 int j; 204 const char *s, *ss, *p; 205 ptrdiff_t len; 206 207 if (dir == NULL || !*dir) { 208 X509error(X509_R_INVALID_DIRECTORY); 209 return 0; 210 } 211 212 s = dir; 213 p = s; 214 do { 215 if ((*p == ':') || (*p == '\0')) { 216 BY_DIR_ENTRY *ent; 217 218 ss = s; 219 s = p + 1; 220 len = p - ss; 221 if (len == 0) 222 continue; 223 for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) { 224 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j); 225 if (strlen(ent->dir) == (size_t)len && 226 strncmp(ent->dir, ss, (size_t)len) == 0) 227 break; 228 } 229 if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) 230 continue; 231 if (ctx->dirs == NULL) { 232 ctx->dirs = sk_BY_DIR_ENTRY_new_null(); 233 if (ctx->dirs == NULL) { 234 X509error(ERR_R_MALLOC_FAILURE); 235 return 0; 236 } 237 } 238 ent = malloc(sizeof(*ent)); 239 if (ent == NULL) { 240 X509error(ERR_R_MALLOC_FAILURE); 241 return 0; 242 } 243 ent->dir_type = type; 244 ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp); 245 ent->dir = strndup(ss, (size_t)len); 246 if (ent->dir == NULL || ent->hashes == NULL) { 247 X509error(ERR_R_MALLOC_FAILURE); 248 by_dir_entry_free(ent); 249 return 0; 250 } 251 if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) { 252 X509error(ERR_R_MALLOC_FAILURE); 253 by_dir_entry_free(ent); 254 return 0; 255 } 256 } 257 } while (*p++ != '\0'); 258 return 1; 259 } 260 261 static int 262 get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, 263 X509_OBJECT *ret) 264 { 265 BY_DIR *ctx; 266 union { 267 struct { 268 X509 st_x509; 269 X509_CINF st_x509_cinf; 270 } x509; 271 struct { 272 X509_CRL st_crl; 273 X509_CRL_INFO st_crl_info; 274 } crl; 275 } data; 276 int ok = 0; 277 int i, j, k; 278 unsigned long h; 279 BUF_MEM *b = NULL; 280 X509_OBJECT stmp, *tmp; 281 const char *postfix=""; 282 283 if (name == NULL) 284 return 0; 285 286 stmp.type = type; 287 if (type == X509_LU_X509) { 288 data.x509.st_x509.cert_info = &data.x509.st_x509_cinf; 289 data.x509.st_x509_cinf.subject = name; 290 stmp.data.x509 = &data.x509.st_x509; 291 postfix=""; 292 } else if (type == X509_LU_CRL) { 293 data.crl.st_crl.crl = &data.crl.st_crl_info; 294 data.crl.st_crl_info.issuer = name; 295 stmp.data.crl = &data.crl.st_crl; 296 postfix="r"; 297 } else { 298 X509error(X509_R_WRONG_LOOKUP_TYPE); 299 goto finish; 300 } 301 302 if ((b = BUF_MEM_new()) == NULL) { 303 X509error(ERR_R_BUF_LIB); 304 goto finish; 305 } 306 307 ctx = (BY_DIR *)xl->method_data; 308 309 h = X509_NAME_hash(name); 310 for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) { 311 BY_DIR_ENTRY *ent; 312 int idx; 313 BY_DIR_HASH htmp, *hent; 314 315 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); 316 j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1; 317 if (!BUF_MEM_grow(b, j)) { 318 X509error(ERR_R_MALLOC_FAILURE); 319 goto finish; 320 } 321 if (type == X509_LU_CRL) { 322 htmp.hash = h; 323 CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE); 324 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 325 if (idx >= 0) { 326 hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 327 k = hent->suffix; 328 } else { 329 hent = NULL; 330 k = 0; 331 } 332 CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE); 333 } else { 334 k = 0; 335 hent = NULL; 336 } 337 for (;;) { 338 (void) snprintf(b->data, b->max, "%s/%08lx.%s%d", 339 ent->dir, h, postfix, k); 340 341 { 342 struct stat st; 343 if (stat(b->data, &st) < 0) 344 break; 345 } 346 /* found one. */ 347 if (type == X509_LU_X509) { 348 if ((X509_load_cert_file(xl, b->data, 349 ent->dir_type)) == 0) 350 break; 351 } else if (type == X509_LU_CRL) { 352 if ((X509_load_crl_file(xl, b->data, 353 ent->dir_type)) == 0) 354 break; 355 } 356 /* else case will caught higher up */ 357 k++; 358 } 359 360 /* we have added it to the cache so now pull it out again */ 361 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 362 j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp); 363 tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j); 364 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 365 366 /* If a CRL, update the last file suffix added for this */ 367 if (type == X509_LU_CRL) { 368 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 369 /* 370 * Look for entry again in case another thread added 371 * an entry first. 372 */ 373 if (hent == NULL) { 374 htmp.hash = h; 375 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 376 hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 377 } 378 if (hent == NULL) { 379 hent = malloc(sizeof(*hent)); 380 if (hent == NULL) { 381 X509error(ERR_R_MALLOC_FAILURE); 382 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 383 ok = 0; 384 goto finish; 385 } 386 hent->hash = h; 387 hent->suffix = k; 388 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) { 389 X509error(ERR_R_MALLOC_FAILURE); 390 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 391 free(hent); 392 ok = 0; 393 goto finish; 394 } 395 } else if (hent->suffix < k) 396 hent->suffix = k; 397 398 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 399 400 } 401 402 if (tmp != NULL) { 403 ok = 1; 404 ret->type = tmp->type; 405 memcpy(&ret->data, &tmp->data, sizeof(ret->data)); 406 goto finish; 407 } 408 } 409 finish: 410 BUF_MEM_free(b); 411 return ok; 412 } 413