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