Update copyright year
[openssl.git] / crypto / x509 / by_dir.c
1 /*
2  * Copyright 1995-2021 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     h = X509_NAME_hash_ex(name, libctx, propq, &i);
256     if (i == 0)
257         goto finish;
258     for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
259         BY_DIR_ENTRY *ent;
260         int idx;
261         BY_DIR_HASH htmp, *hent;
262
263         ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
264         j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
265         if (!BUF_MEM_grow(b, j)) {
266             ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
267             goto finish;
268         }
269         if (type == X509_LU_CRL && ent->hashes) {
270             htmp.hash = h;
271             CRYPTO_THREAD_read_lock(ctx->lock);
272             idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
273             if (idx >= 0) {
274                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
275                 k = hent->suffix;
276             } else {
277                 hent = NULL;
278                 k = 0;
279             }
280             CRYPTO_THREAD_unlock(ctx->lock);
281         } else {
282             k = 0;
283             hent = NULL;
284         }
285         for (;;) {
286             char c = '/';
287
288 #ifdef OPENSSL_SYS_VMS
289             c = ent->dir[strlen(ent->dir) - 1];
290             if (c != ':' && c != '>' && c != ']') {
291                 /*
292                  * If no separator is present, we assume the directory
293                  * specifier is a logical name, and add a colon.  We really
294                  * should use better VMS routines for merging things like
295                  * this, but this will do for now... -- Richard Levitte
296                  */
297                 c = ':';
298             } else {
299                 c = '\0';
300             }
301
302             if (c == '\0') {
303                 /*
304                  * This is special.  When c == '\0', no directory separator
305                  * should be added.
306                  */
307                 BIO_snprintf(b->data, b->max,
308                              "%s%08lx.%s%d", ent->dir, h, postfix, k);
309             } else
310 #endif
311             {
312                 BIO_snprintf(b->data, b->max,
313                              "%s%c%08lx.%s%d", ent->dir, c, h, postfix, k);
314             }
315 #ifndef OPENSSL_NO_POSIX_IO
316 # ifdef _WIN32
317 #  define stat _stat
318 # endif
319             {
320                 struct stat st;
321                 if (stat(b->data, &st) < 0)
322                     break;
323             }
324 #endif
325             /* found one. */
326             if (type == X509_LU_X509) {
327                 if ((X509_load_cert_file_ex(xl, b->data, ent->dir_type, libctx,
328                                             propq)) == 0)
329                     break;
330             } else if (type == X509_LU_CRL) {
331                 if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0)
332                     break;
333             }
334             /* else case will caught higher up */
335             k++;
336         }
337
338         /*
339          * we have added it to the cache so now pull it out again
340          */
341         X509_STORE_lock(xl->store_ctx);
342         j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
343         tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
344         X509_STORE_unlock(xl->store_ctx);
345
346         /* If a CRL, update the last file suffix added for this */
347
348         if (type == X509_LU_CRL) {
349             CRYPTO_THREAD_write_lock(ctx->lock);
350             /*
351              * Look for entry again in case another thread added an entry
352              * first.
353              */
354             if (hent == NULL) {
355                 htmp.hash = h;
356                 idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
357                 hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
358             }
359             if (hent == NULL) {
360                 hent = OPENSSL_malloc(sizeof(*hent));
361                 if (hent == NULL) {
362                     CRYPTO_THREAD_unlock(ctx->lock);
363                     ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
364                     ok = 0;
365                     goto finish;
366                 }
367                 hent->hash = h;
368                 hent->suffix = k;
369                 if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
370                     CRYPTO_THREAD_unlock(ctx->lock);
371                     OPENSSL_free(hent);
372                     ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
373                     ok = 0;
374                     goto finish;
375                 }
376             } else if (hent->suffix < k) {
377                 hent->suffix = k;
378             }
379
380             CRYPTO_THREAD_unlock(ctx->lock);
381
382         }
383
384         if (tmp != NULL) {
385             ok = 1;
386             ret->type = tmp->type;
387             memcpy(&ret->data, &tmp->data, sizeof(ret->data));
388
389             /*
390              * Clear any errors that might have been raised processing empty
391              * or malformed files.
392              */
393             ERR_clear_error();
394
395             goto finish;
396         }
397     }
398  finish:
399     BUF_MEM_free(b);
400     return ok;
401 }
402
403 static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type,
404                                const X509_NAME *name, X509_OBJECT *ret)
405 {
406     return get_cert_by_subject_ex(xl, type, name, ret, NULL, NULL);
407 }