1229970Sadrian/* $OpenBSD: by_dir.c,v 1.47 2024/03/25 00:05:49 beck Exp $ */
2229970Sadrian/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3229970Sadrian * All rights reserved.
4229970Sadrian *
5229970Sadrian * This package is an SSL implementation written
6229970Sadrian * by Eric Young (eay@cryptsoft.com).
7229970Sadrian * The implementation was written so as to conform with Netscapes SSL.
8229970Sadrian *
9229970Sadrian * This library is free for commercial and non-commercial use as long as
10229970Sadrian * the following conditions are aheared to.  The following conditions
11229970Sadrian * apply to all code found in this distribution, be it the RC4, RSA,
12229970Sadrian * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13229970Sadrian * included with this distribution is covered by the same copyright terms
14229970Sadrian * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15229970Sadrian *
16229970Sadrian * Copyright remains Eric Young's, and as such any Copyright notices in
17229970Sadrian * the code are not to be removed.
18229970Sadrian * If this package is used in a product, Eric Young should be given attribution
19229970Sadrian * as the author of the parts of the library used.
20229970Sadrian * This can be in the form of a textual message at program startup or
21229970Sadrian * in documentation (online or textual) provided with the package.
22229970Sadrian *
23229970Sadrian * Redistribution and use in source and binary forms, with or without
24229970Sadrian * modification, are permitted provided that the following conditions
25229970Sadrian * are met:
26229970Sadrian * 1. Redistributions of source code must retain the copyright
27229970Sadrian *    notice, this list of conditions and the following disclaimer.
28229970Sadrian * 2. Redistributions in binary form must reproduce the above copyright
29229970Sadrian *    notice, this list of conditions and the following disclaimer in the
30229970Sadrian *    documentation and/or other materials provided with the distribution.
31229970Sadrian * 3. All advertising materials mentioning features or use of this software
32229970Sadrian *    must display the following acknowledgement:
33229970Sadrian *    "This product includes cryptographic software written by
34229970Sadrian *     Eric Young (eay@cryptsoft.com)"
35229970Sadrian *    The word 'cryptographic' can be left out if the rouines from the library
36229970Sadrian *    being used are not cryptographic related :-).
37229970Sadrian * 4. If you include any Windows specific code (or a derivative thereof) from
38229970Sadrian *    the apps directory (application code) you must include an acknowledgement:
39229970Sadrian *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40229970Sadrian *
41229970Sadrian * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42229970Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43232763Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44232763Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45232763Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46232763Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47232763Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48232763Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49232763Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50229970Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51229970Sadrian * SUCH DAMAGE.
52229970Sadrian *
53229970Sadrian * The licence and distribution terms for any publically available version or
54229970Sadrian * derivative of this code cannot be changed.  i.e. this code cannot simply be
55229970Sadrian * copied and put under another distribution licence
56229970Sadrian * [including the GNU Public Licence.]
57229970Sadrian */
58229970Sadrian
59229970Sadrian#include <errno.h>
60229970Sadrian#include <stdio.h>
61229970Sadrian#include <string.h>
62229970Sadrian#include <time.h>
63229970Sadrian#include <unistd.h>
64229970Sadrian
65229970Sadrian#include <openssl/opensslconf.h>
66229970Sadrian
67229970Sadrian#include <openssl/err.h>
68229970Sadrian#include <openssl/x509.h>
69229970Sadrian
70229970Sadrian#include "x509_local.h"
71229970Sadrian
72229970Sadriantypedef struct lookup_dir_hashes_st {
73229970Sadrian	unsigned long hash;
74229970Sadrian	int suffix;
75229970Sadrian} BY_DIR_HASH;
76229970Sadrian
77229970Sadriantypedef struct lookup_dir_entry_st {
78229970Sadrian	char *dir;
79229970Sadrian	int dir_type;
80229970Sadrian	STACK_OF(BY_DIR_HASH) *hashes;
81229970Sadrian} BY_DIR_ENTRY;
82229970Sadrian
83229970Sadriantypedef struct lookup_dir_st {
84229970Sadrian	BUF_MEM *buffer;
85229970Sadrian	STACK_OF(BY_DIR_ENTRY) *dirs;
86229970Sadrian} BY_DIR;
87229970Sadrian
88229970SadrianDECLARE_STACK_OF(BY_DIR_HASH)
89229970SadrianDECLARE_STACK_OF(BY_DIR_ENTRY)
90229970Sadrian
91229970Sadrianstatic int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
92229970Sadrian    char **ret);
93229970Sadrianstatic int new_dir(X509_LOOKUP *lu);
94231378Sedstatic void free_dir(X509_LOOKUP *lu);
95229970Sadrianstatic int add_cert_dir(BY_DIR *ctx, const char *dir, int type);
96229970Sadrianstatic int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
97229970Sadrian    X509_OBJECT *ret);
98229970Sadrian
99229970Sadrianstatic X509_LOOKUP_METHOD x509_dir_lookup = {
100229970Sadrian	.name = "Load certs from files in a directory",
101229970Sadrian	.new_item = new_dir,
102229970Sadrian	.free = free_dir,
103243857Sglebius	.ctrl = dir_ctrl,
104229970Sadrian	.get_by_subject = get_cert_by_subject,
105229970Sadrian};
106229970Sadrian
107229970SadrianX509_LOOKUP_METHOD *
108229970SadrianX509_LOOKUP_hash_dir(void)
109229970Sadrian{
110229970Sadrian	return &x509_dir_lookup;
111231378Sed}
112229970SadrianLCRYPTO_ALIAS(X509_LOOKUP_hash_dir);
113229970Sadrian
114229970Sadrianstatic int
115229970Sadriandir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
116229970Sadrian    char **retp)
117229970Sadrian{
118229970Sadrian	BY_DIR *ld = ctx->method_data;
119229970Sadrian	int ret = 0;
120229970Sadrian
121229970Sadrian	switch (cmd) {
122229970Sadrian	case X509_L_ADD_DIR:
123229970Sadrian		if (argl == X509_FILETYPE_DEFAULT) {
124229970Sadrian			ret = add_cert_dir(ld, X509_get_default_cert_dir(),
125229970Sadrian			    X509_FILETYPE_PEM);
126229970Sadrian			if (!ret) {
127229970Sadrian				X509error(X509_R_LOADING_CERT_DIR);
128229970Sadrian			}
129229970Sadrian		} else
130229970Sadrian			ret = add_cert_dir(ld, argp, (int)argl);
131229970Sadrian		break;
132229970Sadrian	}
133229970Sadrian	return ret;
134229970Sadrian}
135229970Sadrian
136229970Sadrianstatic int
137229970Sadriannew_dir(X509_LOOKUP *lu)
138229970Sadrian{
139229970Sadrian	BY_DIR *a;
140229970Sadrian
141229970Sadrian	if ((a = malloc(sizeof(*a))) == NULL) {
142229970Sadrian		X509error(ERR_R_MALLOC_FAILURE);
143229970Sadrian		return 0;
144229970Sadrian	}
145229970Sadrian	if ((a->buffer = BUF_MEM_new()) == NULL) {
146229970Sadrian		X509error(ERR_R_MALLOC_FAILURE);
147229970Sadrian		free(a);
148229970Sadrian		return 0;
149229970Sadrian	}
150229970Sadrian	a->dirs = NULL;
151229970Sadrian	lu->method_data = a;
152229970Sadrian	return 1;
153229970Sadrian}
154229970Sadrian
155229970Sadrianstatic void
156229970Sadrianby_dir_hash_free(BY_DIR_HASH *hash)
157229970Sadrian{
158229970Sadrian	free(hash);
159229970Sadrian}
160229970Sadrian
161229970Sadrianstatic int
162229970Sadrianby_dir_hash_cmp(const BY_DIR_HASH * const *a,
163229970Sadrian    const BY_DIR_HASH * const *b)
164229970Sadrian{
165229970Sadrian	if ((*a)->hash > (*b)->hash)
166229970Sadrian		return 1;
167229970Sadrian	if ((*a)->hash < (*b)->hash)
168229970Sadrian		return -1;
169229970Sadrian	return 0;
170229970Sadrian}
171229970Sadrian
172229970Sadrianstatic void
173229970Sadrianby_dir_entry_free(BY_DIR_ENTRY *ent)
174229970Sadrian{
175229970Sadrian	free(ent->dir);
176229970Sadrian	sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
177229970Sadrian	free(ent);
178229970Sadrian}
179229970Sadrian
180229970Sadrianstatic void
181229970Sadrianfree_dir(X509_LOOKUP *lu)
182229970Sadrian{
183229970Sadrian	BY_DIR *a;
184229970Sadrian
185229970Sadrian	a = lu->method_data;
186229970Sadrian	sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
187229970Sadrian	BUF_MEM_free(a->buffer);
188229970Sadrian	free(a);
189229970Sadrian}
190229970Sadrian
191229970Sadrianstatic int
192229970Sadrianadd_cert_dir(BY_DIR *ctx, const char *dir, int type)
193229970Sadrian{
194229970Sadrian	int j;
195229970Sadrian	const char *s, *ss, *p;
196229970Sadrian	ptrdiff_t len;
197229970Sadrian
198229970Sadrian	if (dir == NULL || !*dir) {
199229970Sadrian		X509error(X509_R_INVALID_DIRECTORY);
200229970Sadrian		return 0;
201229970Sadrian	}
202229970Sadrian
203229970Sadrian	s = dir;
204229970Sadrian	p = s;
205229970Sadrian	do {
206229970Sadrian		if ((*p == ':') || (*p == '\0')) {
207229970Sadrian			BY_DIR_ENTRY *ent;
208229970Sadrian
209229970Sadrian			ss = s;
210229970Sadrian			s = p + 1;
211229970Sadrian			len = p - ss;
212229970Sadrian			if (len == 0)
213229970Sadrian				continue;
214229970Sadrian			for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
215229970Sadrian				ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
216229970Sadrian				if (strlen(ent->dir) == (size_t)len &&
217229970Sadrian				    strncmp(ent->dir, ss, (size_t)len) == 0)
218229970Sadrian					break;
219229970Sadrian			}
220229970Sadrian			if (j < sk_BY_DIR_ENTRY_num(ctx->dirs))
221229970Sadrian				continue;
222229970Sadrian			if (ctx->dirs == NULL) {
223229970Sadrian				ctx->dirs = sk_BY_DIR_ENTRY_new_null();
224229970Sadrian				if (ctx->dirs == NULL) {
225229970Sadrian					X509error(ERR_R_MALLOC_FAILURE);
226229970Sadrian					return 0;
227229970Sadrian				}
228229970Sadrian			}
229229970Sadrian			ent = malloc(sizeof(*ent));
230229970Sadrian			if (ent == NULL) {
231229970Sadrian				X509error(ERR_R_MALLOC_FAILURE);
232229970Sadrian				return 0;
233238938Smonthadar			}
234238938Smonthadar			ent->dir_type = type;
235238938Smonthadar			ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
236238938Smonthadar			ent->dir = strndup(ss, (size_t)len);
237229970Sadrian			if (ent->dir == NULL || ent->hashes == NULL) {
238229970Sadrian				X509error(ERR_R_MALLOC_FAILURE);
239229970Sadrian				by_dir_entry_free(ent);
240229970Sadrian				return 0;
241229970Sadrian			}
242229970Sadrian			if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
243229970Sadrian				X509error(ERR_R_MALLOC_FAILURE);
244243857Sglebius				by_dir_entry_free(ent);
245229970Sadrian				return 0;
246229970Sadrian			}
247229970Sadrian		}
248229970Sadrian	} while (*p++ != '\0');
249229970Sadrian	return 1;
250229970Sadrian}
251229970Sadrian
252229970Sadrianstatic int
253229970Sadrianget_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
254229970Sadrian    X509_OBJECT *ret)
255229970Sadrian{
256229970Sadrian	BY_DIR *ctx;
257229970Sadrian	union	{
258229970Sadrian		struct	{
259229970Sadrian			X509 st_x509;
260229970Sadrian			X509_CINF st_x509_cinf;
261229970Sadrian		} x509;
262229970Sadrian		struct	{
263229970Sadrian			X509_CRL st_crl;
264229970Sadrian			X509_CRL_INFO st_crl_info;
265229970Sadrian		} crl;
266229970Sadrian	} data;
267229970Sadrian	int ok = 0;
268229970Sadrian	int i, j, k;
269229970Sadrian	unsigned long h;
270229970Sadrian	BUF_MEM *b = NULL;
271239760Sadrian	X509_OBJECT stmp, *tmp;
272229970Sadrian	const char *postfix="";
273229970Sadrian
274229970Sadrian	if (name == NULL)
275229970Sadrian		return 0;
276229970Sadrian
277229970Sadrian	stmp.type = type;
278229970Sadrian	if (type == X509_LU_X509) {
279229970Sadrian		data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
280229970Sadrian		data.x509.st_x509_cinf.subject = name;
281239760Sadrian		stmp.data.x509 = &data.x509.st_x509;
282239760Sadrian		postfix="";
283229970Sadrian	} else if (type == X509_LU_CRL) {
284229970Sadrian		data.crl.st_crl.crl = &data.crl.st_crl_info;
285229970Sadrian		data.crl.st_crl_info.issuer = name;
286229970Sadrian		stmp.data.crl = &data.crl.st_crl;
287229970Sadrian		postfix="r";
288229970Sadrian	} else {
289229970Sadrian		X509error(X509_R_WRONG_LOOKUP_TYPE);
290229970Sadrian		goto finish;
291229970Sadrian	}
292229970Sadrian
293229970Sadrian	if ((b = BUF_MEM_new()) == NULL) {
294229970Sadrian		X509error(ERR_R_BUF_LIB);
295238938Smonthadar		goto finish;
296238938Smonthadar	}
297229970Sadrian
298239760Sadrian	ctx = xl->method_data;
299229970Sadrian
300229970Sadrian	h = X509_NAME_hash(name);
301229970Sadrian	for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
302239760Sadrian		BY_DIR_ENTRY *ent;
303229970Sadrian		int idx;
304229970Sadrian		BY_DIR_HASH htmp, *hent;
305229970Sadrian
306229970Sadrian		ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
307229970Sadrian		j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
308229970Sadrian		if (!BUF_MEM_grow(b, j)) {
309229970Sadrian			X509error(ERR_R_MALLOC_FAILURE);
310229970Sadrian			goto finish;
311229970Sadrian		}
312229970Sadrian		if (type == X509_LU_CRL) {
313229970Sadrian			htmp.hash = h;
314229970Sadrian			CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
315229970Sadrian			idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
316229970Sadrian			if (idx >= 0) {
317229970Sadrian				hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
318229970Sadrian				k = hent->suffix;
319229970Sadrian			} else {
320229970Sadrian				hent = NULL;
321229970Sadrian				k = 0;
322229970Sadrian			}
323229970Sadrian			CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
324229970Sadrian		} else {
325239760Sadrian			k = 0;
326229970Sadrian			hent = NULL;
327229970Sadrian		}
328229970Sadrian		for (;;) {
329244399Smonthadar			(void) snprintf(b->data, b->max, "%s/%08lx.%s%d",
330244399Smonthadar			    ent->dir, h, postfix, k);
331244399Smonthadar			/*
332229970Sadrian			 * Found one. Attempt to load it. This could fail for
333229970Sadrian			 * any number of reasons from the file can't be opened,
334232978Sadrian			 * the file contains garbage, etc. Clear the error stack
335229970Sadrian			 * to avoid exposing the lower level error. These all
336229970Sadrian			 * boil down to "we could not find CA/CRL".
337229970Sadrian			 */
338244389Smonthadar			if (type == X509_LU_X509) {
339244399Smonthadar				if ((X509_load_cert_file(xl, b->data,
340244399Smonthadar				    ent->dir_type)) == 0) {
341244389Smonthadar					ERR_clear_error();
342229970Sadrian					break;
343229970Sadrian				}
344229970Sadrian			} else if (type == X509_LU_CRL) {
345229970Sadrian				if ((X509_load_crl_file(xl, b->data,
346229970Sadrian				    ent->dir_type)) == 0) {
347229970Sadrian					ERR_clear_error();
348229970Sadrian					break;
349229970Sadrian				}
350229970Sadrian			}
351229970Sadrian			/* The lack of a CA or CRL will be caught higher up. */
352229970Sadrian			k++;
353229970Sadrian		}
354229970Sadrian
355229970Sadrian		/* we have added it to the cache so now pull it out again */
356244388Smonthadar		CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
357229970Sadrian		j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
358229970Sadrian		tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
359239760Sadrian		CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
360239760Sadrian
361239760Sadrian		/* If a CRL, update the last file suffix added for this */
362229970Sadrian		if (type == X509_LU_CRL) {
363229970Sadrian			CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
364229970Sadrian			/*
365229970Sadrian			 * Look for entry again in case another thread added
366229970Sadrian			 * an entry first.
367229970Sadrian			 */
368229970Sadrian			if (hent == NULL) {
369229970Sadrian				htmp.hash = h;
370229970Sadrian				idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
371229970Sadrian				hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
372229970Sadrian			}
373229970Sadrian			if (hent == NULL) {
374229970Sadrian				hent = malloc(sizeof(*hent));
375229970Sadrian				if (hent == NULL) {
376229970Sadrian					X509error(ERR_R_MALLOC_FAILURE);
377229970Sadrian					CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
378229970Sadrian					ok = 0;
379229970Sadrian					goto finish;
380229970Sadrian				}
381229970Sadrian				hent->hash = h;
382229970Sadrian				hent->suffix = k;
383229970Sadrian				if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
384229970Sadrian					X509error(ERR_R_MALLOC_FAILURE);
385229970Sadrian					CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
386229970Sadrian					free(hent);
387229970Sadrian					ok = 0;
388229970Sadrian					goto finish;
389229970Sadrian				}
390229970Sadrian			} else if (hent->suffix < k)
391229970Sadrian				hent->suffix = k;
392229970Sadrian
393229970Sadrian			CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
394229970Sadrian
395229970Sadrian		}
396229970Sadrian
397229970Sadrian		if (tmp != NULL) {
398229970Sadrian			ok = 1;
399229970Sadrian			ret->type = tmp->type;
400229970Sadrian			memcpy(&ret->data, &tmp->data, sizeof(ret->data));
401229970Sadrian			goto finish;
402229970Sadrian		}
403229970Sadrian	}
404229970Sadrianfinish:
405229970Sadrian	BUF_MEM_free(b);
406229970Sadrian	return ok;
407229970Sadrian}
408229970Sadrian