nrelease - fix/improve livecd
[dragonfly.git] / crypto / libressl / crypto / x509 / by_dir.c
1 /* $OpenBSD: by_dir.c,v 1.41 2021/11/10 14:34:21 schwarze Exp $ */
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 <sys/stat.h>
60 #include <sys/types.h>
61
62 #include <errno.h>
63 #include <stdio.h>
64 #include <string.h>
65 #include <time.h>
66 #include <unistd.h>
67
68 #include <openssl/opensslconf.h>
69
70 #include <openssl/err.h>
71 #include <openssl/x509.h>
72
73 #include "x509_lcl.h"
74
75 typedef struct lookup_dir_hashes_st {
76         unsigned long hash;
77         int suffix;
78 } BY_DIR_HASH;
79
80 typedef struct lookup_dir_entry_st {
81         char *dir;
82         int dir_type;
83         STACK_OF(BY_DIR_HASH) *hashes;
84 } BY_DIR_ENTRY;
85
86 typedef struct lookup_dir_st {
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
102 static X509_LOOKUP_METHOD x509_dir_lookup = {
103         .name = "Load certs from files in a directory",
104         .new_item = new_dir,
105         .free = free_dir,
106         .init = NULL,
107         .shutdown = NULL,
108         .ctrl = dir_ctrl,
109         .get_by_subject = get_cert_by_subject,
110         .get_by_issuer_serial = NULL,
111         .get_by_fingerprint = NULL,
112         .get_by_alias = NULL,
113 };
114
115 X509_LOOKUP_METHOD *
116 X509_LOOKUP_hash_dir(void)
117 {
118         return &x509_dir_lookup;
119 }
120
121 static int
122 dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
123     char **retp)
124 {
125         int ret = 0;
126         BY_DIR *ld;
127
128         ld = (BY_DIR *)ctx->method_data;
129
130         switch (cmd) {
131         case X509_L_ADD_DIR:
132                 if (argl == X509_FILETYPE_DEFAULT) {
133                         ret = add_cert_dir(ld, X509_get_default_cert_dir(),
134                             X509_FILETYPE_PEM);
135                         if (!ret) {
136                                 X509error(X509_R_LOADING_CERT_DIR);
137                         }
138                 } else
139                         ret = add_cert_dir(ld, argp, (int)argl);
140                 break;
141         }
142         return ret;
143 }
144
145 static int
146 new_dir(X509_LOOKUP *lu)
147 {
148         BY_DIR *a;
149
150         if ((a = malloc(sizeof(*a))) == NULL) {
151                 X509error(ERR_R_MALLOC_FAILURE);
152                 return 0;
153         }
154         if ((a->buffer = BUF_MEM_new()) == NULL) {
155                 X509error(ERR_R_MALLOC_FAILURE);
156                 free(a);
157                 return 0;
158         }
159         a->dirs = NULL;
160         lu->method_data = (char *)a;
161         return 1;
162 }
163
164 static void
165 by_dir_hash_free(BY_DIR_HASH *hash)
166 {
167         free(hash);
168 }
169
170 static int
171 by_dir_hash_cmp(const BY_DIR_HASH * const *a,
172     const BY_DIR_HASH * const *b)
173 {
174         if ((*a)->hash > (*b)->hash)
175                 return 1;
176         if ((*a)->hash < (*b)->hash)
177                 return -1;
178         return 0;
179 }
180
181 static void
182 by_dir_entry_free(BY_DIR_ENTRY *ent)
183 {
184         free(ent->dir);
185         sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
186         free(ent);
187 }
188
189 static void
190 free_dir(X509_LOOKUP *lu)
191 {
192         BY_DIR *a;
193
194         a = (BY_DIR *)lu->method_data;
195         sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
196         BUF_MEM_free(a->buffer);
197         free(a);
198 }
199
200 static int
201 add_cert_dir(BY_DIR *ctx, const char *dir, int type)
202 {
203         int j;
204         const char *s, *ss, *p;
205         ptrdiff_t len;
206
207         if (dir == NULL || !*dir) {
208                 X509error(X509_R_INVALID_DIRECTORY);
209                 return 0;
210         }
211
212         s = dir;
213         p = s;
214         do {
215                 if ((*p == ':') || (*p == '\0')) {
216                         BY_DIR_ENTRY *ent;
217
218                         ss = s;
219                         s = p + 1;
220                         len = p - ss;
221                         if (len == 0)
222                                 continue;
223                         for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
224                                 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
225                                 if (strlen(ent->dir) == (size_t)len &&
226                                     strncmp(ent->dir, ss, (size_t)len) == 0)
227                                         break;
228                         }
229                         if (j < sk_BY_DIR_ENTRY_num(ctx->dirs))
230                                 continue;
231                         if (ctx->dirs == NULL) {
232                                 ctx->dirs = sk_BY_DIR_ENTRY_new_null();
233                                 if (ctx->dirs == NULL) {
234                                         X509error(ERR_R_MALLOC_FAILURE);
235                                         return 0;
236                                 }
237                         }
238                         ent = malloc(sizeof(*ent));
239                         if (ent == NULL) {
240                                 X509error(ERR_R_MALLOC_FAILURE);
241                                 return 0;
242                         }
243                         ent->dir_type = type;
244                         ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
245                         ent->dir = strndup(ss, (size_t)len);
246                         if (ent->dir == NULL || ent->hashes == NULL) {
247                                 X509error(ERR_R_MALLOC_FAILURE);
248                                 by_dir_entry_free(ent);
249                                 return 0;
250                         }
251                         if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
252                                 X509error(ERR_R_MALLOC_FAILURE);
253                                 by_dir_entry_free(ent);
254                                 return 0;
255                         }
256                 }
257         } while (*p++ != '\0');
258         return 1;
259 }
260
261 static int
262 get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
263     X509_OBJECT *ret)
264 {
265         BY_DIR *ctx;
266         union   {
267                 struct  {
268                         X509 st_x509;
269                         X509_CINF st_x509_cinf;
270                 } x509;
271                 struct  {
272                         X509_CRL st_crl;
273                         X509_CRL_INFO st_crl_info;
274                 } crl;
275         } data;
276         int ok = 0;
277         int i, j, k;
278         unsigned long h;
279         BUF_MEM *b = NULL;
280         X509_OBJECT stmp, *tmp;
281         const char *postfix="";
282
283         if (name == NULL)
284                 return 0;
285
286         stmp.type = type;
287         if (type == X509_LU_X509) {
288                 data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
289                 data.x509.st_x509_cinf.subject = name;
290                 stmp.data.x509 = &data.x509.st_x509;
291                 postfix="";
292         } else if (type == X509_LU_CRL) {
293                 data.crl.st_crl.crl = &data.crl.st_crl_info;
294                 data.crl.st_crl_info.issuer = name;
295                 stmp.data.crl = &data.crl.st_crl;
296                 postfix="r";
297         } else {
298                 X509error(X509_R_WRONG_LOOKUP_TYPE);
299                 goto finish;
300         }
301
302         if ((b = BUF_MEM_new()) == NULL) {
303                 X509error(ERR_R_BUF_LIB);
304                 goto finish;
305         }
306
307         ctx = (BY_DIR *)xl->method_data;
308
309         h = X509_NAME_hash(name);
310         for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
311                 BY_DIR_ENTRY *ent;
312                 int idx;
313                 BY_DIR_HASH htmp, *hent;
314
315                 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
316                 j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
317                 if (!BUF_MEM_grow(b, j)) {
318                         X509error(ERR_R_MALLOC_FAILURE);
319                         goto finish;
320                 }
321                 if (type == X509_LU_CRL) {
322                         htmp.hash = h;
323                         CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
324                         idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
325                         if (idx >= 0) {
326                                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
327                                 k = hent->suffix;
328                         } else {
329                                 hent = NULL;
330                                 k = 0;
331                         }
332                         CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
333                 } else {
334                         k = 0;
335                         hent = NULL;
336                 }
337                 for (;;) {
338                         (void) snprintf(b->data, b->max, "%s/%08lx.%s%d",
339                             ent->dir, h, postfix, k);
340
341                         {
342                                 struct stat st;
343                                 if (stat(b->data, &st) < 0)
344                                         break;
345                         }
346                         /* found one. */
347                         if (type == X509_LU_X509) {
348                                 if ((X509_load_cert_file(xl, b->data,
349                                     ent->dir_type)) == 0)
350                                         break;
351                         } else if (type == X509_LU_CRL) {
352                                 if ((X509_load_crl_file(xl, b->data,
353                                     ent->dir_type)) == 0)
354                                         break;
355                         }
356                         /* else case will caught higher up */
357                         k++;
358                 }
359
360                 /* we have added it to the cache so now pull it out again */
361                 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
362                 j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
363                 tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
364                 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
365
366                 /* If a CRL, update the last file suffix added for this */
367                 if (type == X509_LU_CRL) {
368                         CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
369                         /*
370                          * Look for entry again in case another thread added
371                          * an entry first.
372                          */
373                         if (hent == NULL) {
374                                 htmp.hash = h;
375                                 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
376                                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
377                         }
378                         if (hent == NULL) {
379                                 hent = malloc(sizeof(*hent));
380                                 if (hent == NULL) {
381                                         X509error(ERR_R_MALLOC_FAILURE);
382                                         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
383                                         ok = 0;
384                                         goto finish;
385                                 }
386                                 hent->hash = h;
387                                 hent->suffix = k;
388                                 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
389                                         X509error(ERR_R_MALLOC_FAILURE);
390                                         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
391                                         free(hent);
392                                         ok = 0;
393                                         goto finish;
394                                 }
395                         } else if (hent->suffix < k)
396                                 hent->suffix = k;
397
398                         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
399
400                 }
401
402                 if (tmp != NULL) {
403                         ok = 1;
404                         ret->type = tmp->type;
405                         memcpy(&ret->data, &tmp->data, sizeof(ret->data));
406                         goto finish;
407                 }
408         }
409 finish:
410         BUF_MEM_free(b);
411         return ok;
412 }