155714Skris/* crypto/x509/by_dir.c */ 255714Skris/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 355714Skris * All rights reserved. 455714Skris * 555714Skris * This package is an SSL implementation written 655714Skris * by Eric Young (eay@cryptsoft.com). 755714Skris * The implementation was written so as to conform with Netscapes SSL. 855714Skris * 955714Skris * This library is free for commercial and non-commercial use as long as 1055714Skris * the following conditions are aheared to. The following conditions 1155714Skris * apply to all code found in this distribution, be it the RC4, RSA, 1255714Skris * lhash, DES, etc., code; not just the SSL code. The SSL documentation 1355714Skris * included with this distribution is covered by the same copyright terms 1455714Skris * except that the holder is Tim Hudson (tjh@cryptsoft.com). 1555714Skris * 1655714Skris * Copyright remains Eric Young's, and as such any Copyright notices in 1755714Skris * the code are not to be removed. 1855714Skris * If this package is used in a product, Eric Young should be given attribution 1955714Skris * as the author of the parts of the library used. 2055714Skris * This can be in the form of a textual message at program startup or 2155714Skris * in documentation (online or textual) provided with the package. 2255714Skris * 2355714Skris * Redistribution and use in source and binary forms, with or without 2455714Skris * modification, are permitted provided that the following conditions 2555714Skris * are met: 2655714Skris * 1. Redistributions of source code must retain the copyright 2755714Skris * notice, this list of conditions and the following disclaimer. 2855714Skris * 2. Redistributions in binary form must reproduce the above copyright 2955714Skris * notice, this list of conditions and the following disclaimer in the 3055714Skris * documentation and/or other materials provided with the distribution. 3155714Skris * 3. All advertising materials mentioning features or use of this software 3255714Skris * must display the following acknowledgement: 3355714Skris * "This product includes cryptographic software written by 3455714Skris * Eric Young (eay@cryptsoft.com)" 3555714Skris * The word 'cryptographic' can be left out if the rouines from the library 3655714Skris * being used are not cryptographic related :-). 3755714Skris * 4. If you include any Windows specific code (or a derivative thereof) from 3855714Skris * the apps directory (application code) you must include an acknowledgement: 3955714Skris * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 4055714Skris * 4155714Skris * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 4255714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4455714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 4555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 4655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 4755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 4955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5155714Skris * SUCH DAMAGE. 5255714Skris * 5355714Skris * The licence and distribution terms for any publically available version or 5455714Skris * derivative of this code cannot be changed. i.e. this code cannot simply be 5555714Skris * copied and put under another distribution licence 5655714Skris * [including the GNU Public Licence.] 5755714Skris */ 5855714Skris 5955714Skris#include <stdio.h> 6055714Skris#include <time.h> 6155714Skris#include <errno.h> 6255714Skris 6355714Skris#include "cryptlib.h" 6459191Skris 6559191Skris#ifndef NO_SYS_TYPES_H 6659191Skris# include <sys/types.h> 6759191Skris#endif 68238405Sjkim#ifndef OPENSSL_NO_POSIX_IO 6959191Skris# include <sys/stat.h> 7059191Skris#endif 7159191Skris 7255714Skris#include <openssl/lhash.h> 7355714Skris#include <openssl/x509.h> 7455714Skris 75194206Ssimon 76238405Sjkimtypedef struct lookup_dir_hashes_st 77238405Sjkim { 78238405Sjkim unsigned long hash; 79238405Sjkim int suffix; 80238405Sjkim } BY_DIR_HASH; 81238405Sjkim 82238405Sjkimtypedef struct lookup_dir_entry_st 83238405Sjkim { 84238405Sjkim char *dir; 85238405Sjkim int dir_type; 86238405Sjkim STACK_OF(BY_DIR_HASH) *hashes; 87238405Sjkim } BY_DIR_ENTRY; 88238405Sjkim 8955714Skristypedef struct lookup_dir_st 9055714Skris { 9155714Skris BUF_MEM *buffer; 92238405Sjkim STACK_OF(BY_DIR_ENTRY) *dirs; 9355714Skris } BY_DIR; 9455714Skris 95238405SjkimDECLARE_STACK_OF(BY_DIR_HASH) 96238405SjkimDECLARE_STACK_OF(BY_DIR_ENTRY) 97238405Sjkim 9855714Skrisstatic int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 9955714Skris char **ret); 10055714Skrisstatic int new_dir(X509_LOOKUP *lu); 10155714Skrisstatic void free_dir(X509_LOOKUP *lu); 10255714Skrisstatic int add_cert_dir(BY_DIR *ctx,const char *dir,int type); 10355714Skrisstatic int get_cert_by_subject(X509_LOOKUP *xl,int type,X509_NAME *name, 10455714Skris X509_OBJECT *ret); 10555714SkrisX509_LOOKUP_METHOD x509_dir_lookup= 10655714Skris { 10755714Skris "Load certs from files in a directory", 10855714Skris new_dir, /* new */ 10955714Skris free_dir, /* free */ 11055714Skris NULL, /* init */ 11155714Skris NULL, /* shutdown */ 11255714Skris dir_ctrl, /* ctrl */ 11355714Skris get_cert_by_subject, /* get_by_subject */ 11455714Skris NULL, /* get_by_issuer_serial */ 11555714Skris NULL, /* get_by_fingerprint */ 11655714Skris NULL, /* get_by_alias */ 11755714Skris }; 11855714Skris 11955714SkrisX509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) 12055714Skris { 12155714Skris return(&x509_dir_lookup); 12255714Skris } 12355714Skris 12455714Skrisstatic int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 12555714Skris char **retp) 12655714Skris { 12755714Skris int ret=0; 12855714Skris BY_DIR *ld; 129160814Ssimon char *dir = NULL; 13055714Skris 13155714Skris ld=(BY_DIR *)ctx->method_data; 13255714Skris 13355714Skris switch (cmd) 13455714Skris { 13555714Skris case X509_L_ADD_DIR: 13655714Skris if (argl == X509_FILETYPE_DEFAULT) 13755714Skris { 138238405Sjkim dir=(char *)getenv(X509_get_default_cert_dir_env()); 139160814Ssimon if (dir) 140160814Ssimon ret=add_cert_dir(ld,dir,X509_FILETYPE_PEM); 141160814Ssimon else 142160814Ssimon ret=add_cert_dir(ld,X509_get_default_cert_dir(), 143160814Ssimon X509_FILETYPE_PEM); 14455714Skris if (!ret) 14555714Skris { 14655714Skris X509err(X509_F_DIR_CTRL,X509_R_LOADING_CERT_DIR); 14755714Skris } 14855714Skris } 14955714Skris else 15055714Skris ret=add_cert_dir(ld,argp,(int)argl); 15155714Skris break; 15255714Skris } 15355714Skris return(ret); 15455714Skris } 15555714Skris 15655714Skrisstatic int new_dir(X509_LOOKUP *lu) 15755714Skris { 15855714Skris BY_DIR *a; 15955714Skris 16068651Skris if ((a=(BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL) 16155714Skris return(0); 16255714Skris if ((a->buffer=BUF_MEM_new()) == NULL) 16355714Skris { 16468651Skris OPENSSL_free(a); 16555714Skris return(0); 16655714Skris } 16755714Skris a->dirs=NULL; 16855714Skris lu->method_data=(char *)a; 16955714Skris return(1); 17055714Skris } 17155714Skris 172238405Sjkimstatic void by_dir_hash_free(BY_DIR_HASH *hash) 173238405Sjkim { 174238405Sjkim OPENSSL_free(hash); 175238405Sjkim } 176238405Sjkim 177238405Sjkimstatic int by_dir_hash_cmp(const BY_DIR_HASH * const *a, 178238405Sjkim const BY_DIR_HASH * const *b) 179238405Sjkim { 180238405Sjkim if ((*a)->hash > (*b)->hash) 181238405Sjkim return 1; 182238405Sjkim if ((*a)->hash < (*b)->hash) 183238405Sjkim return -1; 184238405Sjkim return 0; 185238405Sjkim } 186238405Sjkim 187238405Sjkimstatic void by_dir_entry_free(BY_DIR_ENTRY *ent) 188238405Sjkim { 189238405Sjkim if (ent->dir) 190238405Sjkim OPENSSL_free(ent->dir); 191238405Sjkim if (ent->hashes) 192238405Sjkim sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free); 193238405Sjkim OPENSSL_free(ent); 194238405Sjkim } 195238405Sjkim 19655714Skrisstatic void free_dir(X509_LOOKUP *lu) 19755714Skris { 19855714Skris BY_DIR *a; 19955714Skris 20055714Skris a=(BY_DIR *)lu->method_data; 201238405Sjkim if (a->dirs != NULL) 202238405Sjkim sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free); 203238405Sjkim if (a->buffer != NULL) 204238405Sjkim BUF_MEM_free(a->buffer); 20568651Skris OPENSSL_free(a); 20655714Skris } 20755714Skris 20855714Skrisstatic int add_cert_dir(BY_DIR *ctx, const char *dir, int type) 20955714Skris { 21055714Skris int j,len; 21155714Skris const char *s,*ss,*p; 21255714Skris 21355714Skris if (dir == NULL || !*dir) 21455714Skris { 21555714Skris X509err(X509_F_ADD_CERT_DIR,X509_R_INVALID_DIRECTORY); 21655714Skris return 0; 21755714Skris } 21855714Skris 21955714Skris s=dir; 22055714Skris p=s; 221279264Sdelphij do 22255714Skris { 22355714Skris if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0')) 22455714Skris { 225238405Sjkim BY_DIR_ENTRY *ent; 22655714Skris ss=s; 22755714Skris s=p+1; 22855714Skris len=(int)(p-ss); 22955714Skris if (len == 0) continue; 230238405Sjkim for (j=0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) 231238405Sjkim { 232238405Sjkim ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j); 233238405Sjkim if (strlen(ent->dir) == (size_t)len && 234238405Sjkim strncmp(ent->dir,ss,(unsigned int)len) == 0) 235167612Ssimon break; 236238405Sjkim } 237238405Sjkim if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) 238167612Ssimon continue; 239238405Sjkim if (ctx->dirs == NULL) 24055714Skris { 241238405Sjkim ctx->dirs = sk_BY_DIR_ENTRY_new_null(); 242238405Sjkim if (!ctx->dirs) 24355714Skris { 24455714Skris X509err(X509_F_ADD_CERT_DIR,ERR_R_MALLOC_FAILURE); 245238405Sjkim return 0; 24655714Skris } 24755714Skris } 248238405Sjkim ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY)); 249238405Sjkim if (!ent) 250238405Sjkim return 0; 251238405Sjkim ent->dir_type = type; 252238405Sjkim ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp); 253238405Sjkim ent->dir = OPENSSL_malloc((unsigned int)len+1); 254238405Sjkim if (!ent->dir || !ent->hashes) 255238405Sjkim { 256238405Sjkim by_dir_entry_free(ent); 257238405Sjkim return 0; 258238405Sjkim } 259238405Sjkim strncpy(ent->dir,ss,(unsigned int)len); 260238405Sjkim ent->dir[len] = '\0'; 261238405Sjkim if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) 262238405Sjkim { 263238405Sjkim by_dir_entry_free(ent); 264238405Sjkim return 0; 265238405Sjkim } 26655714Skris } 267279264Sdelphij } while (*p++ != '\0'); 268238405Sjkim return 1; 26955714Skris } 27055714Skris 27155714Skrisstatic int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, 27255714Skris X509_OBJECT *ret) 27355714Skris { 27455714Skris BY_DIR *ctx; 27555714Skris union { 27655714Skris struct { 27755714Skris X509 st_x509; 27855714Skris X509_CINF st_x509_cinf; 27955714Skris } x509; 28055714Skris struct { 28155714Skris X509_CRL st_crl; 28255714Skris X509_CRL_INFO st_crl_info; 28355714Skris } crl; 28455714Skris } data; 28555714Skris int ok=0; 28655714Skris int i,j,k; 28755714Skris unsigned long h; 28855714Skris BUF_MEM *b=NULL; 28955714Skris X509_OBJECT stmp,*tmp; 29055714Skris const char *postfix=""; 29155714Skris 29255714Skris if (name == NULL) return(0); 29355714Skris 29455714Skris stmp.type=type; 29555714Skris if (type == X509_LU_X509) 29655714Skris { 29755714Skris data.x509.st_x509.cert_info= &data.x509.st_x509_cinf; 29855714Skris data.x509.st_x509_cinf.subject=name; 29955714Skris stmp.data.x509= &data.x509.st_x509; 30055714Skris postfix=""; 30155714Skris } 30255714Skris else if (type == X509_LU_CRL) 30355714Skris { 30455714Skris data.crl.st_crl.crl= &data.crl.st_crl_info; 30555714Skris data.crl.st_crl_info.issuer=name; 30655714Skris stmp.data.crl= &data.crl.st_crl; 30755714Skris postfix="r"; 30855714Skris } 30955714Skris else 31055714Skris { 31155714Skris X509err(X509_F_GET_CERT_BY_SUBJECT,X509_R_WRONG_LOOKUP_TYPE); 31255714Skris goto finish; 31355714Skris } 31455714Skris 31555714Skris if ((b=BUF_MEM_new()) == NULL) 31655714Skris { 31755714Skris X509err(X509_F_GET_CERT_BY_SUBJECT,ERR_R_BUF_LIB); 31855714Skris goto finish; 31955714Skris } 32055714Skris 32155714Skris ctx=(BY_DIR *)xl->method_data; 32255714Skris 32355714Skris h=X509_NAME_hash(name); 324238405Sjkim for (i=0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) 32555714Skris { 326238405Sjkim BY_DIR_ENTRY *ent; 327238405Sjkim int idx; 328238405Sjkim BY_DIR_HASH htmp, *hent; 329238405Sjkim ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); 330238405Sjkim j=strlen(ent->dir)+1+8+6+1+1; 33155714Skris if (!BUF_MEM_grow(b,j)) 33255714Skris { 33355714Skris X509err(X509_F_GET_CERT_BY_SUBJECT,ERR_R_MALLOC_FAILURE); 33455714Skris goto finish; 33555714Skris } 336238405Sjkim if (type == X509_LU_CRL && ent->hashes) 337238405Sjkim { 338238405Sjkim htmp.hash = h; 339238405Sjkim CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE); 340238405Sjkim idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 341238405Sjkim if (idx >= 0) 342238405Sjkim { 343238405Sjkim hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 344238405Sjkim k = hent->suffix; 345238405Sjkim } 346238405Sjkim else 347238405Sjkim { 348238405Sjkim hent = NULL; 349238405Sjkim k=0; 350238405Sjkim } 351238405Sjkim CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE); 352238405Sjkim } 353238405Sjkim else 354238405Sjkim { 355238405Sjkim k = 0; 356238405Sjkim hent = NULL; 357238405Sjkim } 35855714Skris for (;;) 35955714Skris { 360127128Snectar char c = '/'; 361127128Snectar#ifdef OPENSSL_SYS_VMS 362238405Sjkim c = ent->dir[strlen(ent->dir)-1]; 363127128Snectar if (c != ':' && c != '>' && c != ']') 364127128Snectar { 365127128Snectar /* If no separator is present, we assume the 366127128Snectar directory specifier is a logical name, and 367127128Snectar add a colon. We really should use better 368127128Snectar VMS routines for merging things like this, 369127128Snectar but this will do for now... 370127128Snectar -- Richard Levitte */ 371127128Snectar c = ':'; 372127128Snectar } 373127128Snectar else 374127128Snectar { 375127128Snectar c = '\0'; 376127128Snectar } 377127128Snectar#endif 378127128Snectar if (c == '\0') 379127128Snectar { 380127128Snectar /* This is special. When c == '\0', no 381127128Snectar directory separator should be added. */ 382127128Snectar BIO_snprintf(b->data,b->max, 383238405Sjkim "%s%08lx.%s%d",ent->dir,h, 384127128Snectar postfix,k); 385127128Snectar } 386127128Snectar else 387127128Snectar { 388127128Snectar BIO_snprintf(b->data,b->max, 389238405Sjkim "%s%c%08lx.%s%d",ent->dir,c,h, 390127128Snectar postfix,k); 391127128Snectar } 392238405Sjkim#ifndef OPENSSL_NO_POSIX_IO 393238405Sjkim#ifdef _WIN32 394238405Sjkim#define stat _stat 395238405Sjkim#endif 396238405Sjkim { 397238405Sjkim struct stat st; 39855714Skris if (stat(b->data,&st) < 0) 39955714Skris break; 400238405Sjkim } 401238405Sjkim#endif 40255714Skris /* found one. */ 40355714Skris if (type == X509_LU_X509) 40455714Skris { 40555714Skris if ((X509_load_cert_file(xl,b->data, 406238405Sjkim ent->dir_type)) == 0) 40755714Skris break; 40855714Skris } 40955714Skris else if (type == X509_LU_CRL) 41055714Skris { 41155714Skris if ((X509_load_crl_file(xl,b->data, 412238405Sjkim ent->dir_type)) == 0) 41355714Skris break; 41455714Skris } 41555714Skris /* else case will caught higher up */ 416238405Sjkim k++; 41755714Skris } 41855714Skris 41955714Skris /* we have added it to the cache so now pull 42055714Skris * it out again */ 421205128Ssimon CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 42268651Skris j = sk_X509_OBJECT_find(xl->store_ctx->objs,&stmp); 42372613Skris if(j != -1) tmp=sk_X509_OBJECT_value(xl->store_ctx->objs,j); 42468651Skris else tmp = NULL; 425205128Ssimon CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 42655714Skris 427238405Sjkim 428238405Sjkim /* If a CRL, update the last file suffix added for this */ 429238405Sjkim 430238405Sjkim if (type == X509_LU_CRL) 431238405Sjkim { 432238405Sjkim CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 433238405Sjkim /* Look for entry again in case another thread added 434238405Sjkim * an entry first. 435238405Sjkim */ 436238405Sjkim if (!hent) 437238405Sjkim { 438238405Sjkim htmp.hash = h; 439238405Sjkim idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 440238405Sjkim if (idx >= 0) 441238405Sjkim hent = 442238405Sjkim sk_BY_DIR_HASH_value(ent->hashes, idx); 443238405Sjkim } 444238405Sjkim if (!hent) 445238405Sjkim { 446238405Sjkim hent = OPENSSL_malloc(sizeof(BY_DIR_HASH)); 447238405Sjkim hent->hash = h; 448238405Sjkim hent->suffix = k; 449238405Sjkim if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) 450238405Sjkim { 451238405Sjkim CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 452238405Sjkim OPENSSL_free(hent); 453238405Sjkim ok = 0; 454238405Sjkim goto finish; 455238405Sjkim } 456238405Sjkim } 457238405Sjkim else if (hent->suffix < k) 458238405Sjkim hent->suffix = k; 459238405Sjkim 460238405Sjkim CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 461238405Sjkim 462238405Sjkim } 463238405Sjkim 46455714Skris if (tmp != NULL) 46555714Skris { 46655714Skris ok=1; 46755714Skris ret->type=tmp->type; 46855714Skris memcpy(&ret->data,&tmp->data,sizeof(ret->data)); 46955714Skris /* If we were going to up the reference count, 47055714Skris * we would need to do it on a perl 'type' 47155714Skris * basis */ 47255714Skris /* CRYPTO_add(&tmp->data.x509->references,1, 47355714Skris CRYPTO_LOCK_X509);*/ 47455714Skris goto finish; 47555714Skris } 47655714Skris } 47755714Skrisfinish: 47855714Skris if (b != NULL) BUF_MEM_free(b); 47955714Skris return(ok); 48055714Skris } 481