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