blob: e68ca5a35a6cea9a937751cae68a1d95c9ac94b7 [file] [log] [blame]
Adam Langleyd9e397b2015-01-22 14:27:53 -08001/* 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#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>
Adam Langleye9ada862015-05-11 17:20:37 -070066#include <openssl/thread.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080067#include <openssl/x509.h>
68
Adam Langleyf4e42722015-06-04 17:45:09 -070069#include "../internal.h"
70
David Benjamin4969cc92016-04-22 15:02:23 -040071typedef struct lookup_dir_hashes_st {
72 unsigned long hash;
73 int suffix;
74} BY_DIR_HASH;
Adam Langleyd9e397b2015-01-22 14:27:53 -080075
David Benjamin4969cc92016-04-22 15:02:23 -040076typedef struct lookup_dir_entry_st {
77 char *dir;
78 int dir_type;
79 STACK_OF(BY_DIR_HASH) *hashes;
80} BY_DIR_ENTRY;
Adam Langleyd9e397b2015-01-22 14:27:53 -080081
David Benjamin4969cc92016-04-22 15:02:23 -040082typedef struct lookup_dir_st {
83 BUF_MEM *buffer;
84 STACK_OF(BY_DIR_ENTRY) *dirs;
85} BY_DIR;
Adam Langleyd9e397b2015-01-22 14:27:53 -080086
87DECLARE_STACK_OF(BY_DIR_HASH)
88DECLARE_STACK_OF(BY_DIR_ENTRY)
89
90static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
David Benjamin4969cc92016-04-22 15:02:23 -040091 char **ret);
Adam Langleyd9e397b2015-01-22 14:27:53 -080092static int new_dir(X509_LOOKUP *lu);
93static void free_dir(X509_LOOKUP *lu);
David Benjamin4969cc92016-04-22 15:02:23 -040094static int add_cert_dir(BY_DIR *ctx, const char *dir, int type);
95static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
96 X509_OBJECT *ret);
97static X509_LOOKUP_METHOD x509_dir_lookup = {
98 "Load certs from files in a directory",
99 new_dir, /* new */
100 free_dir, /* free */
101 NULL, /* init */
102 NULL, /* shutdown */
103 dir_ctrl, /* ctrl */
104 get_cert_by_subject, /* get_by_subject */
105 NULL, /* get_by_issuer_serial */
106 NULL, /* get_by_fingerprint */
107 NULL, /* get_by_alias */
108};
Adam Langleyd9e397b2015-01-22 14:27:53 -0800109
110X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void)
David Benjamin4969cc92016-04-22 15:02:23 -0400111{
112 return (&x509_dir_lookup);
113}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800114
115static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
David Benjamin4969cc92016-04-22 15:02:23 -0400116 char **retp)
117{
118 int ret = 0;
119 BY_DIR *ld;
120 char *dir = NULL;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800121
David Benjamin4969cc92016-04-22 15:02:23 -0400122 ld = (BY_DIR *)ctx->method_data;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800123
David Benjamin4969cc92016-04-22 15:02:23 -0400124 switch (cmd) {
125 case X509_L_ADD_DIR:
126 if (argl == X509_FILETYPE_DEFAULT) {
127 dir = (char *)getenv(X509_get_default_cert_dir_env());
128 if (dir)
129 ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM);
130 else
131 ret = add_cert_dir(ld, X509_get_default_cert_dir(),
132 X509_FILETYPE_PEM);
133 if (!ret) {
134 OPENSSL_PUT_ERROR(X509, X509_R_LOADING_CERT_DIR);
135 }
136 } else
137 ret = add_cert_dir(ld, argp, (int)argl);
138 break;
139 }
140 return (ret);
141}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800142
143static int new_dir(X509_LOOKUP *lu)
David Benjamin4969cc92016-04-22 15:02:23 -0400144{
145 BY_DIR *a;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800146
David Benjamin4969cc92016-04-22 15:02:23 -0400147 if ((a = (BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL)
148 return (0);
149 if ((a->buffer = BUF_MEM_new()) == NULL) {
150 OPENSSL_free(a);
151 return (0);
152 }
153 a->dirs = NULL;
154 lu->method_data = (char *)a;
155 return (1);
156}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800157
158static void by_dir_hash_free(BY_DIR_HASH *hash)
David Benjamin4969cc92016-04-22 15:02:23 -0400159{
160 OPENSSL_free(hash);
161}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800162
David Benjamin4969cc92016-04-22 15:02:23 -0400163static int by_dir_hash_cmp(const BY_DIR_HASH **a, const BY_DIR_HASH **b)
164{
165 if ((*a)->hash > (*b)->hash)
166 return 1;
167 if ((*a)->hash < (*b)->hash)
168 return -1;
169 return 0;
170}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800171
172static void by_dir_entry_free(BY_DIR_ENTRY *ent)
David Benjamin4969cc92016-04-22 15:02:23 -0400173{
174 if (ent->dir)
175 OPENSSL_free(ent->dir);
176 if (ent->hashes)
177 sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
178 OPENSSL_free(ent);
179}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800180
181static void free_dir(X509_LOOKUP *lu)
David Benjamin4969cc92016-04-22 15:02:23 -0400182{
183 BY_DIR *a;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800184
David Benjamin4969cc92016-04-22 15:02:23 -0400185 a = (BY_DIR *)lu->method_data;
186 if (a->dirs != NULL)
187 sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
188 if (a->buffer != NULL)
189 BUF_MEM_free(a->buffer);
190 OPENSSL_free(a);
191}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800192
193static int add_cert_dir(BY_DIR *ctx, const char *dir, int type)
David Benjamin4969cc92016-04-22 15:02:23 -0400194{
195 size_t j, len;
196 const char *s, *ss, *p;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800197
David Benjamin4969cc92016-04-22 15:02:23 -0400198 if (dir == NULL || !*dir) {
199 OPENSSL_PUT_ERROR(X509, X509_R_INVALID_DIRECTORY);
200 return 0;
201 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800202
David Benjamin4969cc92016-04-22 15:02:23 -0400203 s = dir;
204 p = s;
205 do {
206 if ((*p == ':') || (*p == '\0')) {
207 BY_DIR_ENTRY *ent;
208 ss = s;
209 s = p + 1;
210 len = p - ss;
211 if (len == 0)
212 continue;
213 for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
214 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
215 if (strlen(ent->dir) == len &&
216 strncmp(ent->dir, ss, len) == 0)
217 break;
218 }
219 if (j < sk_BY_DIR_ENTRY_num(ctx->dirs))
220 continue;
221 if (ctx->dirs == NULL) {
222 ctx->dirs = sk_BY_DIR_ENTRY_new_null();
223 if (!ctx->dirs) {
224 OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
225 return 0;
226 }
227 }
228 ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY));
229 if (!ent)
230 return 0;
231 ent->dir_type = type;
232 ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
233 ent->dir = OPENSSL_malloc(len + 1);
234 if (!ent->dir || !ent->hashes) {
235 by_dir_entry_free(ent);
236 return 0;
237 }
238 strncpy(ent->dir, ss, len);
239 ent->dir[len] = '\0';
240 if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
241 by_dir_entry_free(ent);
242 return 0;
243 }
244 }
245 } while (*p++ != '\0');
246 return 1;
247}
Adam Langleyd9e397b2015-01-22 14:27:53 -0800248
David Benjamin4969cc92016-04-22 15:02:23 -0400249/*
250 * g_ent_hashes_lock protects the |hashes| member of all |BY_DIR_ENTRY|
251 * objects.
252 */
253static struct CRYPTO_STATIC_MUTEX g_ent_hashes_lock =
254 CRYPTO_STATIC_MUTEX_INIT;
Adam Langleyf4e42722015-06-04 17:45:09 -0700255
Adam Langleyd9e397b2015-01-22 14:27:53 -0800256static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
David Benjamin4969cc92016-04-22 15:02:23 -0400257 X509_OBJECT *ret)
258{
259 BY_DIR *ctx;
260 union {
261 struct {
262 X509 st_x509;
263 X509_CINF st_x509_cinf;
264 } x509;
265 struct {
266 X509_CRL st_crl;
267 X509_CRL_INFO st_crl_info;
268 } crl;
269 } data;
270 int ok = 0;
271 size_t i;
272 int j, k;
273 unsigned long h;
274 unsigned long hash_array[2];
275 int hash_index;
276 BUF_MEM *b = NULL;
277 X509_OBJECT stmp, *tmp;
278 const char *postfix = "";
Adam Langleyd9e397b2015-01-22 14:27:53 -0800279
David Benjamin4969cc92016-04-22 15:02:23 -0400280 if (name == NULL)
281 return (0);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800282
David Benjamin4969cc92016-04-22 15:02:23 -0400283 stmp.type = type;
284 if (type == X509_LU_X509) {
285 data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
286 data.x509.st_x509_cinf.subject = name;
287 stmp.data.x509 = &data.x509.st_x509;
288 postfix = "";
289 } else if (type == X509_LU_CRL) {
290 data.crl.st_crl.crl = &data.crl.st_crl_info;
291 data.crl.st_crl_info.issuer = name;
292 stmp.data.crl = &data.crl.st_crl;
293 postfix = "r";
294 } else {
295 OPENSSL_PUT_ERROR(X509, X509_R_WRONG_LOOKUP_TYPE);
296 goto finish;
297 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800298
David Benjamin4969cc92016-04-22 15:02:23 -0400299 if ((b = BUF_MEM_new()) == NULL) {
300 OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
301 goto finish;
302 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800303
David Benjamin4969cc92016-04-22 15:02:23 -0400304 ctx = (BY_DIR *)xl->method_data;
305
306 hash_array[0] = X509_NAME_hash(name);
307 hash_array[1] = X509_NAME_hash_old(name);
308 for (hash_index = 0; hash_index < 2; ++hash_index) {
309 h = hash_array[hash_index];
310 for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
311 BY_DIR_ENTRY *ent;
312 size_t idx;
313 BY_DIR_HASH htmp, *hent;
314 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
315 j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
316 if (!BUF_MEM_grow(b, j)) {
317 OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
318 goto finish;
319 }
320 if (type == X509_LU_CRL && ent->hashes) {
321 htmp.hash = h;
322 CRYPTO_STATIC_MUTEX_lock_read(&g_ent_hashes_lock);
323 if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp)) {
324 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
325 k = hent->suffix;
326 } else {
327 hent = NULL;
328 k = 0;
329 }
David Benjamind316cba2016-06-02 16:17:39 -0400330 CRYPTO_STATIC_MUTEX_unlock_read(&g_ent_hashes_lock);
David Benjamin4969cc92016-04-22 15:02:23 -0400331 } else {
332 k = 0;
333 hent = NULL;
334 }
335 for (;;) {
336 char c = '/';
Adam Langleyd9e397b2015-01-22 14:27:53 -0800337#ifdef OPENSSL_SYS_VMS
David Benjamin4969cc92016-04-22 15:02:23 -0400338 c = ent->dir[strlen(ent->dir) - 1];
339 if (c != ':' && c != '>' && c != ']') {
340 /*
341 * If no separator is present, we assume the directory
342 * specifier is a logical name, and add a colon. We
343 * really should use better VMS routines for merging
344 * things like this, but this will do for now... --
345 * Richard Levitte
346 */
347 c = ':';
348 } else {
349 c = '\0';
350 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800351#endif
David Benjamin4969cc92016-04-22 15:02:23 -0400352 if (c == '\0') {
353 /*
354 * This is special. When c == '\0', no directory
355 * separator should be added.
356 */
357 BIO_snprintf(b->data, b->max,
358 "%s%08lx.%s%d", ent->dir, h, postfix, k);
359 } else {
360 BIO_snprintf(b->data, b->max,
361 "%s%c%08lx.%s%d", ent->dir, c, h,
362 postfix, k);
363 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800364#ifndef OPENSSL_NO_POSIX_IO
David Benjamin7c0d06c2016-08-11 13:26:41 -0400365# if defined(_WIN32) && !defined(stat)
David Benjamin4969cc92016-04-22 15:02:23 -0400366# define stat _stat
367# endif
368 {
369 struct stat st;
370 if (stat(b->data, &st) < 0)
371 break;
372 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800373#endif
David Benjamin4969cc92016-04-22 15:02:23 -0400374 /* found one. */
375 if (type == X509_LU_X509) {
376 if ((X509_load_cert_file(xl, b->data,
377 ent->dir_type)) == 0)
378 break;
379 } else if (type == X509_LU_CRL) {
380 if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0)
381 break;
382 }
383 /* else case will caught higher up */
384 k++;
385 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800386
David Benjamin4969cc92016-04-22 15:02:23 -0400387 /*
388 * we have added it to the cache so now pull it out again
389 */
390 CRYPTO_MUTEX_lock_write(&xl->store_ctx->objs_lock);
391 tmp = NULL;
392 if (sk_X509_OBJECT_find(xl->store_ctx->objs, &idx, &stmp)) {
393 tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, idx);
394 }
David Benjamind316cba2016-06-02 16:17:39 -0400395 CRYPTO_MUTEX_unlock_write(&xl->store_ctx->objs_lock);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800396
David Benjamin4969cc92016-04-22 15:02:23 -0400397 /*
398 * If a CRL, update the last file suffix added for this
399 */
Adam Langleyd9e397b2015-01-22 14:27:53 -0800400
David Benjamin4969cc92016-04-22 15:02:23 -0400401 if (type == X509_LU_CRL) {
402 CRYPTO_STATIC_MUTEX_lock_write(&g_ent_hashes_lock);
403 /*
404 * Look for entry again in case another thread added an entry
405 * first.
406 */
407 if (!hent) {
408 htmp.hash = h;
409 if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp))
410 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
411 }
412 if (!hent) {
413 hent = OPENSSL_malloc(sizeof(BY_DIR_HASH));
414 if (hent == NULL) {
David Benjamind316cba2016-06-02 16:17:39 -0400415 CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
David Benjamin4969cc92016-04-22 15:02:23 -0400416 ok = 0;
417 goto finish;
418 }
419 hent->hash = h;
420 hent->suffix = k;
421 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
David Benjamind316cba2016-06-02 16:17:39 -0400422 CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
David Benjamin4969cc92016-04-22 15:02:23 -0400423 OPENSSL_free(hent);
424 ok = 0;
425 goto finish;
426 }
427 } else if (hent->suffix < k)
428 hent->suffix = k;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800429
David Benjamind316cba2016-06-02 16:17:39 -0400430 CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
David Benjamin4969cc92016-04-22 15:02:23 -0400431 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800432
David Benjamin4969cc92016-04-22 15:02:23 -0400433 if (tmp != NULL) {
434 ok = 1;
435 ret->type = tmp->type;
Robert Sloan69939df2017-01-09 10:53:07 -0800436 OPENSSL_memcpy(&ret->data, &tmp->data, sizeof(ret->data));
David Benjamin4969cc92016-04-22 15:02:23 -0400437 /*
438 * If we were going to up the reference count, we would need
439 * to do it on a perl 'type' basis
440 */
441 /*
442 * CRYPTO_add(&tmp->data.x509->references,1,
443 * CRYPTO_LOCK_X509);
444 */
445 goto finish;
446 }
447 }
448 }
449 finish:
450 if (b != NULL)
451 BUF_MEM_free(b);
452 return (ok);
453}