Import LibreSSL v2.4.2 to vendor branch
[dragonfly.git] / crypto / libressl / crypto / x509 / by_dir.c
1 /* $OpenBSD: by_dir.c,v 1.36 2015/02/12 03:54:07 jsing 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/types.h>
60
61 #include <errno.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <time.h>
65 #include <unistd.h>
66
67 #include <openssl/opensslconf.h>
68
69 #include <openssl/err.h>
70 #include <openssl/lhash.h>
71 #include <openssl/x509.h>
72
73 # include <sys/stat.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                                 X509err(X509_F_DIR_CTRL, 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(BY_DIR))) == NULL)
151                 return (0);
152         if ((a->buffer = BUF_MEM_new()) == NULL) {
153                 free(a);
154                 return (0);
155         }
156         a->dirs = NULL;
157         lu->method_data = (char *)a;
158         return (1);
159 }
160
161 static void
162 by_dir_hash_free(BY_DIR_HASH *hash)
163 {
164         free(hash);
165 }
166
167 static int
168 by_dir_hash_cmp(const BY_DIR_HASH * const *a,
169     const BY_DIR_HASH * const *b)
170 {
171         if ((*a)->hash > (*b)->hash)
172                 return 1;
173         if ((*a)->hash < (*b)->hash)
174                 return -1;
175         return 0;
176 }
177
178 static void
179 by_dir_entry_free(BY_DIR_ENTRY *ent)
180 {
181         free(ent->dir);
182         if (ent->hashes)
183                 sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
184         free(ent);
185 }
186
187 static void
188 free_dir(X509_LOOKUP *lu)
189 {
190         BY_DIR *a;
191
192         a = (BY_DIR *)lu->method_data;
193         if (a->dirs != NULL)
194                 sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
195         if (a->buffer != NULL)
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                 X509err(X509_F_ADD_CERT_DIR, 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                         ss = s;
218                         s = p + 1;
219                         len = p - ss;
220                         if (len == 0)
221                                 continue;
222                         for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
223                                 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
224                                 if (strlen(ent->dir) == (size_t)len &&
225                                     strncmp(ent->dir, ss, (size_t)len) == 0)
226                                         break;
227                         }
228                         if (j < sk_BY_DIR_ENTRY_num(ctx->dirs))
229                                 continue;
230                         if (ctx->dirs == NULL) {
231                                 ctx->dirs = sk_BY_DIR_ENTRY_new_null();
232                                 if (!ctx->dirs) {
233                                         X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE);
234                                         return 0;
235                                 }
236                         }
237                         ent = malloc(sizeof(BY_DIR_ENTRY));
238                         if (!ent) {
239                                 X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE);
240                                 return 0;
241                         }
242                         ent->dir_type = type;
243                         ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
244                         ent->dir = strndup(ss, (size_t)len);
245                         if (!ent->dir || !ent->hashes) {
246                                 X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE);
247                                 by_dir_entry_free(ent);
248                                 return 0;
249                         }
250                         if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
251                                 X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE);
252                                 by_dir_entry_free(ent);
253                                 return 0;
254                         }
255                 }
256         } while (*p++ != '\0');
257         return 1;
258 }
259
260 static int
261 get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
262     X509_OBJECT *ret)
263 {
264         BY_DIR *ctx;
265         union   {
266                 struct  {
267                         X509 st_x509;
268                         X509_CINF st_x509_cinf;
269                 } x509;
270                 struct  {
271                         X509_CRL st_crl;
272                         X509_CRL_INFO st_crl_info;
273                 } crl;
274         } data;
275         int ok = 0;
276         int i, j, k;
277         unsigned long h;
278         BUF_MEM *b = NULL;
279         X509_OBJECT stmp, *tmp;
280         const char *postfix="";
281
282         if (name == NULL)
283                 return (0);
284
285         stmp.type = type;
286         if (type == X509_LU_X509) {
287                 data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
288                 data.x509.st_x509_cinf.subject = name;
289                 stmp.data.x509 = &data.x509.st_x509;
290                 postfix="";
291         } else if (type == X509_LU_CRL) {
292                 data.crl.st_crl.crl = &data.crl.st_crl_info;
293                 data.crl.st_crl_info.issuer = name;
294                 stmp.data.crl = &data.crl.st_crl;
295                 postfix="r";
296         } else {
297                 X509err(X509_F_GET_CERT_BY_SUBJECT, X509_R_WRONG_LOOKUP_TYPE);
298                 goto finish;
299         }
300
301         if ((b = BUF_MEM_new()) == NULL) {
302                 X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_BUF_LIB);
303                 goto finish;
304         }
305
306         ctx = (BY_DIR *)xl->method_data;
307
308         h = X509_NAME_hash(name);
309         for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
310                 BY_DIR_ENTRY *ent;
311                 int idx;
312                 BY_DIR_HASH htmp, *hent;
313                 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
314                 j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
315                 if (!BUF_MEM_grow(b, j)) {
316                         X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE);
317                         goto finish;
318                 }
319                 if (type == X509_LU_CRL && ent->hashes) {
320                         htmp.hash = h;
321                         CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
322                         idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
323                         if (idx >= 0) {
324                                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
325                                 k = hent->suffix;
326                         } else {
327                                 hent = NULL;
328                                 k = 0;
329                         }
330                         CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
331                 } else {
332                         k = 0;
333                         hent = NULL;
334                 }
335                 for (;;) {
336                         (void) snprintf(b->data, b->max, "%s/%08lx.%s%d",
337                             ent->dir, h, postfix, k);
338
339                         {
340                                 struct stat st;
341                                 if (stat(b->data, &st) < 0)
342                                         break;
343                         }
344                         /* found one. */
345                         if (type == X509_LU_X509) {
346                                 if ((X509_load_cert_file(xl, b->data,
347                                     ent->dir_type)) == 0)
348                                         break;
349                         } else if (type == X509_LU_CRL) {
350                                 if ((X509_load_crl_file(xl, b->data,
351                                     ent->dir_type)) == 0)
352                                         break;
353                         }
354                         /* else case will caught higher up */
355                         k++;
356                 }
357
358                 /* we have added it to the cache so now pull it out again */
359                 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
360                 j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
361                 if (j != -1)
362                         tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
363                 else
364                         tmp = NULL;
365                 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
366
367                 /* If a CRL, update the last file suffix added for this */
368                 if (type == X509_LU_CRL) {
369                         CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
370                         /*
371                          * Look for entry again in case another thread added
372                          * an entry first.
373                          */
374                         if (!hent) {
375                                 htmp.hash = h;
376                                 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
377                                 if (idx >= 0)
378                                         hent = sk_BY_DIR_HASH_value(
379                                             ent->hashes, idx);
380                         }
381                         if (!hent) {
382                                 hent = malloc(sizeof(BY_DIR_HASH));
383                                 if (!hent) {
384                                         X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE);
385                                         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
386                                         ok = 0;
387                                         goto finish;
388                                 }
389                                 hent->hash = h;
390                                 hent->suffix = k;
391                                 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
392                                         X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE);
393                                         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
394                                         free(hent);
395                                         ok = 0;
396                                         goto finish;
397                                 }
398                         } else if (hent->suffix < k)
399                                 hent->suffix = k;
400
401                         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
402
403                 }
404
405                 if (tmp != NULL) {
406                         ok = 1;
407                         ret->type = tmp->type;
408                         memcpy(&ret->data, &tmp->data, sizeof(ret->data));
409                         /*
410                          * If we were going to up the reference count,
411                          * we would need to do it on a perl 'type' basis
412                          */
413         /*              CRYPTO_add(&tmp->data.x509->references,1,
414                                 CRYPTO_LOCK_X509);*/
415                         goto finish;
416                 }
417         }
418 finish:
419         if (b != NULL)
420                 BUF_MEM_free(b);
421         return (ok);
422 }