1 /* crypto/x509/by_dir.c */ 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 <stdio.h> 60 #include <time.h> 61 #include <errno.h> 62 63 #include "cryptlib.h" 64 65 #ifndef NO_SYS_TYPES_H 66 # include <sys/types.h> 67 #endif 68 #ifndef OPENSSL_NO_POSIX_IO 69 # include <sys/stat.h> 70 #endif 71 72 #include <openssl/lhash.h> 73 #include <openssl/x509.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 X509_LOOKUP_METHOD x509_dir_lookup = { 102 "Load certs from files in a directory", 103 new_dir, /* new */ 104 free_dir, /* free */ 105 NULL, /* init */ 106 NULL, /* shutdown */ 107 dir_ctrl, /* ctrl */ 108 get_cert_by_subject, /* get_by_subject */ 109 NULL, /* get_by_issuer_serial */ 110 NULL, /* get_by_fingerprint */ 111 NULL, /* get_by_alias */ 112 }; 113 114 X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) 115 { 116 return (&x509_dir_lookup); 117 } 118 119 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 120 char **retp) 121 { 122 int ret = 0; 123 BY_DIR *ld; 124 char *dir = NULL; 125 126 ld = (BY_DIR *)ctx->method_data; 127 128 switch (cmd) { 129 case X509_L_ADD_DIR: 130 if (argl == X509_FILETYPE_DEFAULT) { 131 dir = (char *)getenv(X509_get_default_cert_dir_env()); 132 if (dir) 133 ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM); 134 else 135 ret = add_cert_dir(ld, X509_get_default_cert_dir(), 136 X509_FILETYPE_PEM); 137 if (!ret) { 138 X509err(X509_F_DIR_CTRL, X509_R_LOADING_CERT_DIR); 139 } 140 } else 141 ret = add_cert_dir(ld, argp, (int)argl); 142 break; 143 } 144 return (ret); 145 } 146 147 static int new_dir(X509_LOOKUP *lu) 148 { 149 BY_DIR *a; 150 151 if ((a = (BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL) 152 return (0); 153 if ((a->buffer = BUF_MEM_new()) == NULL) { 154 OPENSSL_free(a); 155 return (0); 156 } 157 a->dirs = NULL; 158 lu->method_data = (char *)a; 159 return (1); 160 } 161 162 static void by_dir_hash_free(BY_DIR_HASH *hash) 163 { 164 OPENSSL_free(hash); 165 } 166 167 static int by_dir_hash_cmp(const BY_DIR_HASH *const *a, 168 const BY_DIR_HASH *const *b) 169 { 170 if ((*a)->hash > (*b)->hash) 171 return 1; 172 if ((*a)->hash < (*b)->hash) 173 return -1; 174 return 0; 175 } 176 177 static void by_dir_entry_free(BY_DIR_ENTRY *ent) 178 { 179 if (ent->dir) 180 OPENSSL_free(ent->dir); 181 if (ent->hashes) 182 sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free); 183 OPENSSL_free(ent); 184 } 185 186 static void free_dir(X509_LOOKUP *lu) 187 { 188 BY_DIR *a; 189 190 a = (BY_DIR *)lu->method_data; 191 if (a->dirs != NULL) 192 sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free); 193 if (a->buffer != NULL) 194 BUF_MEM_free(a->buffer); 195 OPENSSL_free(a); 196 } 197 198 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) 199 { 200 int j, len; 201 const char *s, *ss, *p; 202 203 if (dir == NULL || !*dir) { 204 X509err(X509_F_ADD_CERT_DIR, X509_R_INVALID_DIRECTORY); 205 return 0; 206 } 207 208 s = dir; 209 p = s; 210 do { 211 if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0')) { 212 BY_DIR_ENTRY *ent; 213 ss = s; 214 s = p + 1; 215 len = (int)(p - ss); 216 if (len == 0) 217 continue; 218 for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) { 219 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j); 220 if (strlen(ent->dir) == (size_t)len && 221 strncmp(ent->dir, ss, (unsigned int)len) == 0) 222 break; 223 } 224 if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) 225 continue; 226 if (ctx->dirs == NULL) { 227 ctx->dirs = sk_BY_DIR_ENTRY_new_null(); 228 if (!ctx->dirs) { 229 X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE); 230 return 0; 231 } 232 } 233 ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY)); 234 if (!ent) 235 return 0; 236 ent->dir_type = type; 237 ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp); 238 ent->dir = OPENSSL_malloc((unsigned int)len + 1); 239 if (!ent->dir || !ent->hashes) { 240 by_dir_entry_free(ent); 241 return 0; 242 } 243 strncpy(ent->dir, ss, (unsigned int)len); 244 ent->dir[len] = '\0'; 245 if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) { 246 by_dir_entry_free(ent); 247 return 0; 248 } 249 } 250 } while (*p++ != '\0'); 251 return 1; 252 } 253 254 static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, 255 X509_OBJECT *ret) 256 { 257 BY_DIR *ctx; 258 union { 259 struct { 260 X509 st_x509; 261 X509_CINF st_x509_cinf; 262 } x509; 263 struct { 264 X509_CRL st_crl; 265 X509_CRL_INFO st_crl_info; 266 } crl; 267 } data; 268 int ok = 0; 269 int i, j, k; 270 unsigned long h; 271 BUF_MEM *b = NULL; 272 X509_OBJECT stmp, *tmp; 273 const char *postfix = ""; 274 275 if (name == NULL) 276 return (0); 277 278 stmp.type = type; 279 if (type == X509_LU_X509) { 280 data.x509.st_x509.cert_info = &data.x509.st_x509_cinf; 281 data.x509.st_x509_cinf.subject = name; 282 stmp.data.x509 = &data.x509.st_x509; 283 postfix = ""; 284 } else if (type == X509_LU_CRL) { 285 data.crl.st_crl.crl = &data.crl.st_crl_info; 286 data.crl.st_crl_info.issuer = name; 287 stmp.data.crl = &data.crl.st_crl; 288 postfix = "r"; 289 } else { 290 X509err(X509_F_GET_CERT_BY_SUBJECT, X509_R_WRONG_LOOKUP_TYPE); 291 goto finish; 292 } 293 294 if ((b = BUF_MEM_new()) == NULL) { 295 X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_BUF_LIB); 296 goto finish; 297 } 298 299 ctx = (BY_DIR *)xl->method_data; 300 301 h = X509_NAME_hash(name); 302 for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) { 303 BY_DIR_ENTRY *ent; 304 int idx; 305 BY_DIR_HASH htmp, *hent; 306 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); 307 j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1; 308 if (!BUF_MEM_grow(b, j)) { 309 X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); 310 goto finish; 311 } 312 if (type == X509_LU_CRL && ent->hashes) { 313 htmp.hash = h; 314 CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE); 315 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 316 if (idx >= 0) { 317 hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 318 k = hent->suffix; 319 } else { 320 hent = NULL; 321 k = 0; 322 } 323 CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE); 324 } else { 325 k = 0; 326 hent = NULL; 327 } 328 for (;;) { 329 char c = '/'; 330 #ifdef OPENSSL_SYS_VMS 331 c = ent->dir[strlen(ent->dir) - 1]; 332 if (c != ':' && c != '>' && c != ']') { 333 /* 334 * If no separator is present, we assume the directory 335 * specifier is a logical name, and add a colon. We really 336 * should use better VMS routines for merging things like 337 * this, but this will do for now... -- Richard Levitte 338 */ 339 c = ':'; 340 } else { 341 c = '\0'; 342 } 343 #endif 344 if (c == '\0') { 345 /* 346 * This is special. When c == '\0', no directory separator 347 * should be added. 348 */ 349 BIO_snprintf(b->data, b->max, 350 "%s%08lx.%s%d", ent->dir, h, postfix, k); 351 } else { 352 BIO_snprintf(b->data, b->max, 353 "%s%c%08lx.%s%d", ent->dir, c, h, postfix, k); 354 } 355 #ifndef OPENSSL_NO_POSIX_IO 356 # ifdef _WIN32 357 # define stat _stat 358 # endif 359 { 360 struct stat st; 361 if (stat(b->data, &st) < 0) 362 break; 363 } 364 #endif 365 /* found one. */ 366 if (type == X509_LU_X509) { 367 if ((X509_load_cert_file(xl, b->data, ent->dir_type)) == 0) 368 break; 369 } else if (type == X509_LU_CRL) { 370 if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0) 371 break; 372 } 373 /* else case will caught higher up */ 374 k++; 375 } 376 377 /* 378 * we have added it to the cache so now pull it out again 379 */ 380 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 381 j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp); 382 if (j != -1) 383 tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j); 384 else 385 tmp = NULL; 386 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 387 388 /* If a CRL, update the last file suffix added for this */ 389 390 if (type == X509_LU_CRL) { 391 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 392 /* 393 * Look for entry again in case another thread added an entry 394 * first. 395 */ 396 if (!hent) { 397 htmp.hash = h; 398 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 399 if (idx >= 0) 400 hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 401 } 402 if (!hent) { 403 hent = OPENSSL_malloc(sizeof(BY_DIR_HASH)); 404 if (hent == NULL) { 405 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 406 X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); 407 goto finish; 408 } 409 hent->hash = h; 410 hent->suffix = k; 411 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) { 412 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 413 OPENSSL_free(hent); 414 ok = 0; 415 goto finish; 416 } 417 } else if (hent->suffix < k) 418 hent->suffix = k; 419 420 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 421 422 } 423 424 if (tmp != NULL) { 425 ok = 1; 426 ret->type = tmp->type; 427 memcpy(&ret->data, &tmp->data, sizeof(ret->data)); 428 /* 429 * If we were going to up the reference count, we would need to 430 * do it on a perl 'type' basis 431 */ 432 /*- CRYPTO_add(&tmp->data.x509->references,1, 433 CRYPTO_LOCK_X509);*/ 434 goto finish; 435 } 436 } 437 finish: 438 if (b != NULL) 439 BUF_MEM_free(b); 440 return (ok); 441 } 442