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