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