965625973cdbcfac51291dff264e7c894561ffc7
[openssl.git] / crypto / x509 / by_dir.c
1 /*
2  * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include "e_os.h"
11 #include "internal/cryptlib.h"
12 #include <stdio.h>
13 #include <time.h>
14 #include <errno.h>
15 #include <sys/types.h>
16
17 #ifndef OPENSSL_NO_POSIX_IO
18 # include <sys/stat.h>
19 #endif
20
21 #include <openssl/x509.h>
22 #include "crypto/x509.h"
23 #include "x509_local.h"
24
25 struct lookup_dir_hashes_st {
26     unsigned long hash;
27     int suffix;
28 };
29
30 struct lookup_dir_entry_st {
31     char *dir;
32     int dir_type;
33     STACK_OF(BY_DIR_HASH) *hashes;
34 };
35
36 typedef struct lookup_dir_st {
37     BUF_MEM *buffer;
38     STACK_OF(BY_DIR_ENTRY) *dirs;
39     CRYPTO_RWLOCK *lock;
40 } BY_DIR;
41
42 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
43                     char **retp);
44
45 static int new_dir(X509_LOOKUP *lu);
46 static void free_dir(X509_LOOKUP *lu);
47 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type);
48 static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type,
49                                const X509_NAME *name, X509_OBJECT *ret);
50 static int get_cert_by_subject_ex(X509_LOOKUP *xl, X509_LOOKUP_TYPE type,
51                                   const X509_NAME *name, X509_OBJECT *ret,
52                                   OSSL_LIB_CTX *libctx, const char *propq);
53 static X509_LOOKUP_METHOD x509_dir_lookup = {
54     "Load certs from files in a directory",
55     new_dir,                         /* new_item */
56     free_dir,                        /* free */
57     NULL,                            /* init */
58     NULL,                            /* shutdown */
59     dir_ctrl,                        /* ctrl */
60     get_cert_by_subject,             /* get_by_subject */
61     NULL,                            /* get_by_issuer_serial */
62     NULL,                            /* get_by_fingerprint */
63     NULL,                            /* get_by_alias */
64     get_cert_by_subject_ex,          /* get_by_subject_ex */
65     NULL,                            /* ctrl_ex */
66 };
67
68 X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void)
69 {
70     return &x509_dir_lookup;
71 }
72
73 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
74                     char **retp)
75 {
76     int ret = 0;
77     BY_DIR *ld = (BY_DIR *)ctx->method_data;
78
79     switch (cmd) {
80     case X509_L_ADD_DIR:
81         if (argl == X509_FILETYPE_DEFAULT) {
82             const char *dir = ossl_safe_getenv(X509_get_default_cert_dir_env());
83
84             if (dir)
85                 ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM);
86             else
87                 ret = add_cert_dir(ld, X509_get_default_cert_dir(),
88                                    X509_FILETYPE_PEM);
89             if (!ret) {
90                 ERR_raise(ERR_LIB_X509, X509_R_LOADING_CERT_DIR);
91             }
92         } else
93             ret = add_cert_dir(ld, argp, (int)argl);
94         break;
95     }
96     return ret;
97 }
98
99 static int new_dir(X509_LOOKUP *lu)
100 {
101     BY_DIR *a = OPENSSL_malloc(sizeof(*a));
102
103     if (a == NULL) {
104         ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
105         return 0;
106     }
107
108     if ((a->buffer = BUF_MEM_new()) == NULL) {
109         ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
110         goto err;
111     }
112     a->dirs = NULL;
113     a->lock = CRYPTO_THREAD_lock_new();
114     if (a->lock == NULL) {
115         BUF_MEM_free(a->buffer);
116         ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
117         goto err;
118     }
119     lu->method_data = a;
120     return 1;
121
122  err:
123     OPENSSL_free(a);
124     return 0;
125 }
126
127 static void by_dir_hash_free(BY_DIR_HASH *hash)
128 {
129     OPENSSL_free(hash);
130 }
131
132 static int by_dir_hash_cmp(const BY_DIR_HASH *const *a,
133                            const BY_DIR_HASH *const *b)
134 {
135     if ((*a)->hash > (*b)->hash)
136         return 1;
137     if ((*a)->hash < (*b)->hash)
138         return -1;
139     return 0;
140 }
141
142 static void by_dir_entry_free(BY_DIR_ENTRY *ent)
143 {
144     OPENSSL_free(ent->dir);
145     sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
146     OPENSSL_free(ent);
147 }
148
149 static void free_dir(X509_LOOKUP *lu)
150 {
151     BY_DIR *a = (BY_DIR *)lu->method_data;
152
153     sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
154     BUF_MEM_free(a->buffer);
155     CRYPTO_THREAD_lock_free(a->lock);
156     OPENSSL_free(a);
157 }
158
159 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type)
160 {
161     int j;
162     size_t len;
163     const char *s, *ss, *p;
164
165     if (dir == NULL || *dir == '\0') {
166         ERR_raise(ERR_LIB_X509, X509_R_INVALID_DIRECTORY);
167         return 0;
168     }
169
170     s = dir;
171     p = s;
172     do {
173         if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0')) {
174             BY_DIR_ENTRY *ent;
175
176             ss = s;
177             s = p + 1;
178             len = p - ss;
179             if (len == 0)
180                 continue;
181             for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
182                 ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
183                 if (strlen(ent->dir) == len && strncmp(ent->dir, ss, len) == 0)
184                     break;
185             }
186             if (j < sk_BY_DIR_ENTRY_num(ctx->dirs))
187                 continue;
188             if (ctx->dirs == NULL) {
189                 ctx->dirs = sk_BY_DIR_ENTRY_new_null();
190                 if (!ctx->dirs) {
191                     ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
192                     return 0;
193                 }
194             }
195             ent = OPENSSL_malloc(sizeof(*ent));
196             if (ent == NULL) {
197                 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
198                 return 0;
199             }
200             ent->dir_type = type;
201             ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
202             ent->dir = OPENSSL_strndup(ss, len);
203             if (ent->dir == NULL || ent->hashes == NULL) {
204                 by_dir_entry_free(ent);
205                 return 0;
206             }
207             if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
208                 by_dir_entry_free(ent);
209                 ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
210                 return 0;
211             }
212         }
213     } while (*p++ != '\0');
214     return 1;
215 }
216
217 static int get_cert_by_subject_ex(X509_LOOKUP *xl, X509_LOOKUP_TYPE type,
218                                   const X509_NAME *name, X509_OBJECT *ret,
219                                   OSSL_LIB_CTX *libctx, const char *propq)
220 {
221     BY_DIR *ctx;
222     union {
223         X509 st_x509;
224         X509_CRL crl;
225     } data;
226     int ok = 0;
227     int i, j, k;
228     unsigned long h;
229     BUF_MEM *b = NULL;
230     X509_OBJECT stmp, *tmp;
231     const char *postfix = "";
232
233     if (name == NULL)
234         return 0;
235
236     stmp.type = type;
237     if (type == X509_LU_X509) {
238         data.st_x509.cert_info.subject = (X509_NAME *)name; /* won't modify it */
239         stmp.data.x509 = &data.st_x509;
240     } else if (type == X509_LU_CRL) {
241         data.crl.crl.issuer = (X509_NAME *)name; /* won't modify it */
242         stmp.data.crl = &data.crl;
243         postfix = "r";
244     } else {
245         ERR_raise(ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE);
246         goto finish;
247     }
248
249     if ((b = BUF_MEM_new()) == NULL) {
250         ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB);
251         goto finish;
252     }
253
254     ctx = (BY_DIR *)xl->method_data;
255
256     h = X509_NAME_hash(name);
257     for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
258         BY_DIR_ENTRY *ent;
259         int idx;
260         BY_DIR_HASH htmp, *hent;
261
262         ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
263         j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
264         if (!BUF_MEM_grow(b, j)) {
265             ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
266             goto finish;
267         }
268         if (type == X509_LU_CRL && ent->hashes) {
269             htmp.hash = h;
270             CRYPTO_THREAD_read_lock(ctx->lock);
271             idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
272             if (idx >= 0) {
273                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
274                 k = hent->suffix;
275             } else {
276                 hent = NULL;
277                 k = 0;
278             }
279             CRYPTO_THREAD_unlock(ctx->lock);
280         } else {
281             k = 0;
282             hent = NULL;
283         }
284         for (;;) {
285             char c = '/';
286
287 #ifdef OPENSSL_SYS_VMS
288             c = ent->dir[strlen(ent->dir) - 1];
289             if (c != ':' && c != '>' && c != ']') {
290                 /*
291                  * If no separator is present, we assume the directory
292                  * specifier is a logical name, and add a colon.  We really
293                  * should use better VMS routines for merging things like
294                  * this, but this will do for now... -- Richard Levitte
295                  */
296                 c = ':';
297             } else {
298                 c = '\0';
299             }
300
301             if (c == '\0') {
302                 /*
303                  * This is special.  When c == '\0', no directory separator
304                  * should be added.
305                  */
306                 BIO_snprintf(b->data, b->max,
307                              "%s%08lx.%s%d", ent->dir, h, postfix, k);
308             } else
309 #endif
310             {
311                 BIO_snprintf(b->data, b->max,
312                              "%s%c%08lx.%s%d", ent->dir, c, h, postfix, k);
313             }
314 #ifndef OPENSSL_NO_POSIX_IO
315 # ifdef _WIN32
316 #  define stat _stat
317 # endif
318             {
319                 struct stat st;
320                 if (stat(b->data, &st) < 0)
321                     break;
322             }
323 #endif
324             /* found one. */
325             if (type == X509_LU_X509) {
326                 if ((X509_load_cert_file_ex(xl, b->data, ent->dir_type, libctx,
327                                             propq)) == 0)
328                     break;
329             } else if (type == X509_LU_CRL) {
330                 if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0)
331                     break;
332             }
333             /* else case will caught higher up */
334             k++;
335         }
336
337         /*
338          * we have added it to the cache so now pull it out again
339          */
340         X509_STORE_lock(xl->store_ctx);
341         j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
342         tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
343         X509_STORE_unlock(xl->store_ctx);
344
345         /* If a CRL, update the last file suffix added for this */
346
347         if (type == X509_LU_CRL) {
348             CRYPTO_THREAD_write_lock(ctx->lock);
349             /*
350              * Look for entry again in case another thread added an entry
351              * first.
352              */
353             if (hent == NULL) {
354                 htmp.hash = h;
355                 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
356                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
357             }
358             if (hent == NULL) {
359                 hent = OPENSSL_malloc(sizeof(*hent));
360                 if (hent == NULL) {
361                     CRYPTO_THREAD_unlock(ctx->lock);
362                     ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
363                     ok = 0;
364                     goto finish;
365                 }
366                 hent->hash = h;
367                 hent->suffix = k;
368                 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
369                     CRYPTO_THREAD_unlock(ctx->lock);
370                     OPENSSL_free(hent);
371                     ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
372                     ok = 0;
373                     goto finish;
374                 }
375             } else if (hent->suffix < k) {
376                 hent->suffix = k;
377             }
378
379             CRYPTO_THREAD_unlock(ctx->lock);
380
381         }
382
383         if (tmp != NULL) {
384             ok = 1;
385             ret->type = tmp->type;
386             memcpy(&ret->data, &tmp->data, sizeof(ret->data));
387
388             /*
389              * Clear any errors that might have been raised processing empty
390              * or malformed files.
391              */
392             ERR_clear_error();
393
394             goto finish;
395         }
396     }
397  finish:
398     BUF_MEM_free(b);
399     return ok;
400 }
401
402 static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type,
403                                const X509_NAME *name, X509_OBJECT *ret)
404 {
405     return get_cert_by_subject_ex(xl, type, name, ret, NULL, NULL);
406 }