• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2  * All rights reserved.
3  *
4  * This package is an SSL implementation written
5  * by Eric Young (eay@cryptsoft.com).
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 (tjh@cryptsoft.com).
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 (eay@cryptsoft.com)"
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 (tjh@cryptsoft.com)"
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 <string.h>
58 #include <sys/stat.h>
59 #include <sys/types.h>
60 
61 #include <openssl/buf.h>
62 #include <openssl/err.h>
63 #include <openssl/mem.h>
64 #include <openssl/thread.h>
65 #include <openssl/x509.h>
66 
67 #if !defined(OPENSSL_TRUSTY)
68 
69 #include "../internal.h"
70 #include "internal.h"
71 
72 typedef struct lookup_dir_hashes_st {
73   unsigned long hash;
74   int suffix;
75 } BY_DIR_HASH;
76 
77 typedef struct lookup_dir_entry_st {
78   char *dir;
79   int dir_type;
80   STACK_OF(BY_DIR_HASH) *hashes;
81 } BY_DIR_ENTRY;
82 
83 typedef struct lookup_dir_st {
84   BUF_MEM *buffer;
85   STACK_OF(BY_DIR_ENTRY) *dirs;
86 } BY_DIR;
87 
88 DEFINE_STACK_OF(BY_DIR_HASH)
89 DEFINE_STACK_OF(BY_DIR_ENTRY)
90 
91 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
92                     char **ret);
93 static int new_dir(X509_LOOKUP *lu);
94 static void free_dir(X509_LOOKUP *lu);
95 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type);
96 static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
97                                X509_OBJECT *ret);
98 static X509_LOOKUP_METHOD x509_dir_lookup = {
99     "Load certs from files in a directory",
100     new_dir,              // new
101     free_dir,             // free
102     NULL,                 // init
103     NULL,                 // shutdown
104     dir_ctrl,             // ctrl
105     get_cert_by_subject,  // get_by_subject
106 };
107 
X509_LOOKUP_hash_dir(void)108 X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) { return &x509_dir_lookup; }
109 
dir_ctrl(X509_LOOKUP * ctx,int cmd,const char * argp,long argl,char ** retp)110 static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
111                     char **retp) {
112   int ret = 0;
113   char *dir = NULL;
114 
115   BY_DIR *ld = ctx->method_data;
116 
117   switch (cmd) {
118     case X509_L_ADD_DIR:
119       if (argl == X509_FILETYPE_DEFAULT) {
120         dir = (char *)getenv(X509_get_default_cert_dir_env());
121         if (dir) {
122           ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM);
123         } else {
124           ret =
125               add_cert_dir(ld, X509_get_default_cert_dir(), X509_FILETYPE_PEM);
126         }
127         if (!ret) {
128           OPENSSL_PUT_ERROR(X509, X509_R_LOADING_CERT_DIR);
129         }
130       } else {
131         ret = add_cert_dir(ld, argp, (int)argl);
132       }
133       break;
134   }
135   return ret;
136 }
137 
new_dir(X509_LOOKUP * lu)138 static int new_dir(X509_LOOKUP *lu) {
139   BY_DIR *a;
140 
141   if ((a = (BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL) {
142     return 0;
143   }
144   if ((a->buffer = BUF_MEM_new()) == NULL) {
145     OPENSSL_free(a);
146     return 0;
147   }
148   a->dirs = NULL;
149   lu->method_data = a;
150   return 1;
151 }
152 
by_dir_hash_free(BY_DIR_HASH * hash)153 static void by_dir_hash_free(BY_DIR_HASH *hash) { OPENSSL_free(hash); }
154 
by_dir_hash_cmp(const BY_DIR_HASH * const * a,const BY_DIR_HASH * const * b)155 static int by_dir_hash_cmp(const BY_DIR_HASH *const *a,
156                            const BY_DIR_HASH *const *b) {
157   if ((*a)->hash > (*b)->hash) {
158     return 1;
159   }
160   if ((*a)->hash < (*b)->hash) {
161     return -1;
162   }
163   return 0;
164 }
165 
by_dir_entry_free(BY_DIR_ENTRY * ent)166 static void by_dir_entry_free(BY_DIR_ENTRY *ent) {
167   if (ent != NULL) {
168     OPENSSL_free(ent->dir);
169     sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
170     OPENSSL_free(ent);
171   }
172 }
173 
free_dir(X509_LOOKUP * lu)174 static void free_dir(X509_LOOKUP *lu) {
175   BY_DIR *a = lu->method_data;
176   if (a != NULL) {
177     sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
178     BUF_MEM_free(a->buffer);
179     OPENSSL_free(a);
180   }
181 }
182 
add_cert_dir(BY_DIR * ctx,const char * dir,int type)183 static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) {
184   size_t j, len;
185   const char *s, *ss, *p;
186 
187   if (dir == NULL || !*dir) {
188     OPENSSL_PUT_ERROR(X509, X509_R_INVALID_DIRECTORY);
189     return 0;
190   }
191 
192   s = dir;
193   p = s;
194   do {
195     if ((*p == ':') || (*p == '\0')) {
196       BY_DIR_ENTRY *ent;
197       ss = s;
198       s = p + 1;
199       len = p - ss;
200       if (len == 0) {
201         continue;
202       }
203       for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) {
204         ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
205         if (strlen(ent->dir) == len && strncmp(ent->dir, ss, len) == 0) {
206           break;
207         }
208       }
209       if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) {
210         continue;
211       }
212       if (ctx->dirs == NULL) {
213         ctx->dirs = sk_BY_DIR_ENTRY_new_null();
214         if (!ctx->dirs) {
215           return 0;
216         }
217       }
218       ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY));
219       if (!ent) {
220         return 0;
221       }
222       ent->dir_type = type;
223       ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
224       ent->dir = OPENSSL_malloc(len + 1);
225       if (!ent->dir || !ent->hashes) {
226         by_dir_entry_free(ent);
227         return 0;
228       }
229       OPENSSL_strlcpy(ent->dir, ss, len + 1);
230       if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
231         by_dir_entry_free(ent);
232         return 0;
233       }
234     }
235   } while (*p++ != '\0');
236   return 1;
237 }
238 
239 // g_ent_hashes_lock protects the |hashes| member of all |BY_DIR_ENTRY|
240 // objects.
241 static struct CRYPTO_STATIC_MUTEX g_ent_hashes_lock = CRYPTO_STATIC_MUTEX_INIT;
242 
get_cert_by_subject(X509_LOOKUP * xl,int type,X509_NAME * name,X509_OBJECT * ret)243 static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
244                                X509_OBJECT *ret) {
245   union {
246     struct {
247       X509 st_x509;
248       X509_CINF st_x509_cinf;
249     } x509;
250     struct {
251       X509_CRL st_crl;
252       X509_CRL_INFO st_crl_info;
253     } crl;
254   } data;
255   int ok = 0;
256   size_t i;
257   int j, k;
258   unsigned long h;
259   unsigned long hash_array[2];
260   int hash_index;
261   BUF_MEM *b = NULL;
262   X509_OBJECT stmp, *tmp;
263   const char *postfix = "";
264 
265   if (name == NULL) {
266     return 0;
267   }
268 
269   stmp.type = type;
270   if (type == X509_LU_X509) {
271     data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
272     data.x509.st_x509_cinf.subject = name;
273     stmp.data.x509 = &data.x509.st_x509;
274     postfix = "";
275   } else if (type == X509_LU_CRL) {
276     data.crl.st_crl.crl = &data.crl.st_crl_info;
277     data.crl.st_crl_info.issuer = name;
278     stmp.data.crl = &data.crl.st_crl;
279     postfix = "r";
280   } else {
281     OPENSSL_PUT_ERROR(X509, X509_R_WRONG_LOOKUP_TYPE);
282     goto finish;
283   }
284 
285   if ((b = BUF_MEM_new()) == NULL) {
286     OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
287     goto finish;
288   }
289 
290   BY_DIR *ctx = xl->method_data;
291 
292   hash_array[0] = X509_NAME_hash(name);
293   hash_array[1] = X509_NAME_hash_old(name);
294   for (hash_index = 0; hash_index < 2; ++hash_index) {
295     h = hash_array[hash_index];
296     for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
297       BY_DIR_ENTRY *ent;
298       size_t idx;
299       BY_DIR_HASH htmp, *hent;
300       ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
301       j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
302       if (!BUF_MEM_grow(b, j)) {
303         goto finish;
304       }
305       if (type == X509_LU_CRL && ent->hashes) {
306         htmp.hash = h;
307         CRYPTO_STATIC_MUTEX_lock_read(&g_ent_hashes_lock);
308         if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp)) {
309           hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
310           k = hent->suffix;
311         } else {
312           hent = NULL;
313           k = 0;
314         }
315         CRYPTO_STATIC_MUTEX_unlock_read(&g_ent_hashes_lock);
316       } else {
317         k = 0;
318         hent = NULL;
319       }
320       for (;;) {
321         BIO_snprintf(b->data, b->max, "%s/%08lx.%s%d", ent->dir, h, postfix,
322                      k);
323 #ifndef OPENSSL_NO_POSIX_IO
324 #if defined(_WIN32) && !defined(stat)
325 #define stat _stat
326 #endif
327         {
328           struct stat st;
329           if (stat(b->data, &st) < 0) {
330             break;
331           }
332         }
333 #endif
334         // found one.
335         if (type == X509_LU_X509) {
336           if ((X509_load_cert_file(xl, b->data, ent->dir_type)) == 0) {
337             break;
338           }
339         } else if (type == X509_LU_CRL) {
340           if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0) {
341             break;
342           }
343         }
344         // else case will caught higher up
345         k++;
346       }
347 
348       // we have added it to the cache so now pull it out again
349       CRYPTO_MUTEX_lock_write(&xl->store_ctx->objs_lock);
350       tmp = NULL;
351       sk_X509_OBJECT_sort(xl->store_ctx->objs);
352       if (sk_X509_OBJECT_find(xl->store_ctx->objs, &idx, &stmp)) {
353         tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, idx);
354       }
355       CRYPTO_MUTEX_unlock_write(&xl->store_ctx->objs_lock);
356 
357       // If a CRL, update the last file suffix added for this
358 
359       if (type == X509_LU_CRL) {
360         CRYPTO_STATIC_MUTEX_lock_write(&g_ent_hashes_lock);
361         // Look for entry again in case another thread added an entry
362         // first.
363         if (!hent) {
364           htmp.hash = h;
365           sk_BY_DIR_HASH_sort(ent->hashes);
366           if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp)) {
367             hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
368           }
369         }
370         if (!hent) {
371           hent = OPENSSL_malloc(sizeof(BY_DIR_HASH));
372           if (hent == NULL) {
373             CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
374             ok = 0;
375             goto finish;
376           }
377           hent->hash = h;
378           hent->suffix = k;
379           if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
380             CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
381             OPENSSL_free(hent);
382             ok = 0;
383             goto finish;
384           }
385           sk_BY_DIR_HASH_sort(ent->hashes);
386         } else if (hent->suffix < k) {
387           hent->suffix = k;
388         }
389 
390         CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
391       }
392 
393       if (tmp != NULL) {
394         ok = 1;
395         ret->type = tmp->type;
396         OPENSSL_memcpy(&ret->data, &tmp->data, sizeof(ret->data));
397 
398         // Clear any errors that might have been raised processing empty
399         // or malformed files.
400         ERR_clear_error();
401 
402         // If we were going to up the reference count, we would need
403         // to do it on a perl 'type' basis
404         goto finish;
405       }
406     }
407   }
408 finish:
409   BUF_MEM_free(b);
410   return ok;
411 }
412 
413 #endif  // OPENSSL_TRUSTY
414