xref: /aosp_15_r20/external/cronet/third_party/boringssl/src/crypto/x509/by_dir.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /* Copyright (C) 1995-1998 Eric Young ([email protected])
2  * All rights reserved.
3  *
4  * This package is an SSL implementation written
5  * by Eric Young ([email protected]).
6  * The implementation was written so as to conform with Netscapes SSL.
7  *
8  * This library is free for commercial and non-commercial use as long as
9  * the following conditions are aheared to.  The following conditions
10  * apply to all code found in this distribution, be it the RC4, RSA,
11  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
12  * included with this distribution is covered by the same copyright terms
13  * except that the holder is Tim Hudson ([email protected]).
14  *
15  * Copyright remains Eric Young's, and as such any Copyright notices in
16  * the code are not to be removed.
17  * If this package is used in a product, Eric Young should be given attribution
18  * as the author of the parts of the library used.
19  * This can be in the form of a textual message at program startup or
20  * in documentation (online or textual) provided with the package.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. All advertising materials mentioning features or use of this software
31  *    must display the following acknowledgement:
32  *    "This product includes cryptographic software written by
33  *     Eric Young ([email protected])"
34  *    The word 'cryptographic' can be left out if the rouines from the library
35  *    being used are not cryptographic related :-).
36  * 4. If you include any Windows specific code (or a derivative thereof) from
37  *    the apps directory (application code) you must include an acknowledgement:
38  *    "This product includes software written by Tim Hudson ([email protected])"
39  *
40  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  *
52  * The licence and distribution terms for any publically available version or
53  * derivative of this code cannot be changed.  i.e. this code cannot simply be
54  * copied and put under another distribution licence
55  * [including the GNU Public Licence.] */
56 
57 #include <inttypes.h>
58 #include <string.h>
59 
60 #include <openssl/buf.h>
61 #include <openssl/err.h>
62 #include <openssl/mem.h>
63 #include <openssl/thread.h>
64 #include <openssl/x509.h>
65 
66 #include "../internal.h"
67 #include "internal.h"
68 
69 typedef struct lookup_dir_hashes_st {
70   uint32_t hash;
71   int suffix;
72 } BY_DIR_HASH;
73 
74 typedef struct lookup_dir_entry_st {
75   CRYPTO_MUTEX lock;
76   char *dir;
77   int dir_type;
78   STACK_OF(BY_DIR_HASH) *hashes;
79 } BY_DIR_ENTRY;
80 
81 typedef struct lookup_dir_st {
82   STACK_OF(BY_DIR_ENTRY) *dirs;
83 } BY_DIR;
84 
85 DEFINE_STACK_OF(BY_DIR_HASH)
86 DEFINE_STACK_OF(BY_DIR_ENTRY)
87 
88 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
89                     char **ret);
90 static int new_dir(X509_LOOKUP *lu);
91 static void free_dir(X509_LOOKUP *lu);
92 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type);
93 static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
94                                X509_OBJECT *ret);
95 static const X509_LOOKUP_METHOD x509_dir_lookup = {
96     new_dir,              // new
97     free_dir,             // free
98     dir_ctrl,             // ctrl
99     get_cert_by_subject,  // get_by_subject
100 };
101 
X509_LOOKUP_hash_dir(void)102 const X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) {
103   return &x509_dir_lookup;
104 }
105 
dir_ctrl(X509_LOOKUP * ctx,int cmd,const char * argp,long argl,char ** retp)106 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
107                     char **retp) {
108   int ret = 0;
109   char *dir = NULL;
110 
111   BY_DIR *ld = ctx->method_data;
112 
113   switch (cmd) {
114     case X509_L_ADD_DIR:
115       if (argl == X509_FILETYPE_DEFAULT) {
116         dir = (char *)getenv(X509_get_default_cert_dir_env());
117         if (dir) {
118           ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM);
119         } else {
120           ret =
121               add_cert_dir(ld, X509_get_default_cert_dir(), X509_FILETYPE_PEM);
122         }
123         if (!ret) {
124           OPENSSL_PUT_ERROR(X509, X509_R_LOADING_CERT_DIR);
125         }
126       } else {
127         ret = add_cert_dir(ld, argp, (int)argl);
128       }
129       break;
130   }
131   return ret;
132 }
133 
new_dir(X509_LOOKUP * lu)134 static int new_dir(X509_LOOKUP *lu) {
135   BY_DIR *a;
136 
137   if ((a = (BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL) {
138     return 0;
139   }
140   a->dirs = NULL;
141   lu->method_data = a;
142   return 1;
143 }
144 
by_dir_hash_free(BY_DIR_HASH * hash)145 static void by_dir_hash_free(BY_DIR_HASH *hash) { OPENSSL_free(hash); }
146 
by_dir_hash_cmp(const BY_DIR_HASH * const * a,const BY_DIR_HASH * const * b)147 static int by_dir_hash_cmp(const BY_DIR_HASH *const *a,
148                            const BY_DIR_HASH *const *b) {
149   if ((*a)->hash > (*b)->hash) {
150     return 1;
151   }
152   if ((*a)->hash < (*b)->hash) {
153     return -1;
154   }
155   return 0;
156 }
157 
by_dir_entry_free(BY_DIR_ENTRY * ent)158 static void by_dir_entry_free(BY_DIR_ENTRY *ent) {
159   if (ent != NULL) {
160     CRYPTO_MUTEX_cleanup(&ent->lock);
161     OPENSSL_free(ent->dir);
162     sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
163     OPENSSL_free(ent);
164   }
165 }
166 
free_dir(X509_LOOKUP * lu)167 static void free_dir(X509_LOOKUP *lu) {
168   BY_DIR *a = lu->method_data;
169   if (a != NULL) {
170     sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
171     OPENSSL_free(a);
172   }
173 }
174 
175 #if defined(OPENSSL_WINDOWS)
176 #define DIR_HASH_SEPARATOR ';'
177 #else
178 #define DIR_HASH_SEPARATOR ':'
179 #endif
180 
add_cert_dir(BY_DIR * ctx,const char * dir,int type)181 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) {
182   size_t j, len;
183   const char *s, *ss, *p;
184 
185   if (dir == NULL || !*dir) {
186     OPENSSL_PUT_ERROR(X509, X509_R_INVALID_DIRECTORY);
187     return 0;
188   }
189 
190   s = dir;
191   p = s;
192   do {
193     if (*p == DIR_HASH_SEPARATOR || *p == '\0') {
194       BY_DIR_ENTRY *ent;
195       ss = s;
196       s = p + 1;
197       len = p - ss;
198       if (len == 0) {
199         continue;
200       }
201       for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
202         ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
203         if (strlen(ent->dir) == len && strncmp(ent->dir, ss, len) == 0) {
204           break;
205         }
206       }
207       if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) {
208         continue;
209       }
210       if (ctx->dirs == NULL) {
211         ctx->dirs = sk_BY_DIR_ENTRY_new_null();
212         if (!ctx->dirs) {
213           return 0;
214         }
215       }
216       ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY));
217       if (!ent) {
218         return 0;
219       }
220       CRYPTO_MUTEX_init(&ent->lock);
221       ent->dir_type = type;
222       ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
223       ent->dir = OPENSSL_strndup(ss, len);
224       if (ent->dir == NULL || ent->hashes == NULL ||
225           !sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
226         by_dir_entry_free(ent);
227         return 0;
228       }
229     }
230   } while (*p++ != '\0');
231   return 1;
232 }
233 
get_cert_by_subject(X509_LOOKUP * xl,int type,X509_NAME * name,X509_OBJECT * ret)234 static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
235                                X509_OBJECT *ret) {
236   union {
237     struct {
238       X509 st_x509;
239       X509_CINF st_x509_cinf;
240     } x509;
241     struct {
242       X509_CRL st_crl;
243       X509_CRL_INFO st_crl_info;
244     } crl;
245   } data;
246   int ok = 0;
247   size_t i;
248   int j, k;
249   uint32_t h;
250   uint32_t hash_array[2];
251   int hash_index;
252   BUF_MEM *b = NULL;
253   X509_OBJECT stmp, *tmp;
254   const char *postfix = "";
255 
256   if (name == NULL) {
257     return 0;
258   }
259 
260   stmp.type = type;
261   if (type == X509_LU_X509) {
262     data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
263     data.x509.st_x509_cinf.subject = name;
264     stmp.data.x509 = &data.x509.st_x509;
265     postfix = "";
266   } else if (type == X509_LU_CRL) {
267     data.crl.st_crl.crl = &data.crl.st_crl_info;
268     data.crl.st_crl_info.issuer = name;
269     stmp.data.crl = &data.crl.st_crl;
270     postfix = "r";
271   } else {
272     OPENSSL_PUT_ERROR(X509, X509_R_WRONG_LOOKUP_TYPE);
273     goto finish;
274   }
275 
276   if ((b = BUF_MEM_new()) == NULL) {
277     OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
278     goto finish;
279   }
280 
281   BY_DIR *ctx = xl->method_data;
282 
283   hash_array[0] = X509_NAME_hash(name);
284   hash_array[1] = X509_NAME_hash_old(name);
285   for (hash_index = 0; hash_index < 2; ++hash_index) {
286     h = hash_array[hash_index];
287     for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
288       BY_DIR_ENTRY *ent;
289       size_t idx;
290       BY_DIR_HASH htmp, *hent;
291       ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
292       j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
293       if (!BUF_MEM_grow(b, j)) {
294         goto finish;
295       }
296       if (type == X509_LU_CRL && ent->hashes) {
297         htmp.hash = h;
298         CRYPTO_MUTEX_lock_read(&ent->lock);
299         if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp)) {
300           hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
301           k = hent->suffix;
302         } else {
303           hent = NULL;
304           k = 0;
305         }
306         CRYPTO_MUTEX_unlock_read(&ent->lock);
307       } else {
308         k = 0;
309         hent = NULL;
310       }
311       for (;;) {
312         snprintf(b->data, b->max, "%s/%08" PRIx32 ".%s%d", ent->dir, h, postfix,
313                  k);
314         if (type == X509_LU_X509) {
315           if ((X509_load_cert_file(xl, b->data, ent->dir_type)) == 0) {
316             // Don't expose the lower level error, All of these boil
317             // down to "we could not find a CA".
318             ERR_clear_error();
319             break;
320           }
321         } else if (type == X509_LU_CRL) {
322           if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0) {
323             // Don't expose the lower level error, All of these boil
324             // down to "we could not find a CRL".
325             ERR_clear_error();
326             break;
327           }
328         }
329         // The lack of a CA or CRL will be caught higher up
330         k++;
331       }
332 
333       // we have added it to the cache so now pull it out again
334       CRYPTO_MUTEX_lock_write(&xl->store_ctx->objs_lock);
335       tmp = NULL;
336       sk_X509_OBJECT_sort(xl->store_ctx->objs);
337       if (sk_X509_OBJECT_find(xl->store_ctx->objs, &idx, &stmp)) {
338         tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, idx);
339       }
340       CRYPTO_MUTEX_unlock_write(&xl->store_ctx->objs_lock);
341 
342       // If a CRL, update the last file suffix added for this
343 
344       if (type == X509_LU_CRL) {
345         CRYPTO_MUTEX_lock_write(&ent->lock);
346         // Look for entry again in case another thread added an entry
347         // first.
348         if (!hent) {
349           htmp.hash = h;
350           sk_BY_DIR_HASH_sort(ent->hashes);
351           if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp)) {
352             hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
353           }
354         }
355         if (!hent) {
356           hent = OPENSSL_malloc(sizeof(BY_DIR_HASH));
357           if (hent == NULL) {
358             CRYPTO_MUTEX_unlock_write(&ent->lock);
359             ok = 0;
360             goto finish;
361           }
362           hent->hash = h;
363           hent->suffix = k;
364           if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
365             CRYPTO_MUTEX_unlock_write(&ent->lock);
366             OPENSSL_free(hent);
367             ok = 0;
368             goto finish;
369           }
370           sk_BY_DIR_HASH_sort(ent->hashes);
371         } else if (hent->suffix < k) {
372           hent->suffix = k;
373         }
374 
375         CRYPTO_MUTEX_unlock_write(&ent->lock);
376       }
377 
378       if (tmp != NULL) {
379         ok = 1;
380         ret->type = tmp->type;
381         OPENSSL_memcpy(&ret->data, &tmp->data, sizeof(ret->data));
382 
383         // Clear any errors that might have been raised processing empty
384         // or malformed files.
385         ERR_clear_error();
386 
387         // If we were going to up the reference count, we would need
388         // to do it on a perl 'type' basis
389         goto finish;
390       }
391     }
392   }
393 finish:
394   BUF_MEM_free(b);
395   return ok;
396 }
397 
X509_LOOKUP_add_dir(X509_LOOKUP * lookup,const char * name,int type)398 int X509_LOOKUP_add_dir(X509_LOOKUP *lookup, const char *name, int type) {
399   return X509_LOOKUP_ctrl(lookup, X509_L_ADD_DIR, name, type, NULL);
400 }
401