155714Skris/* crypto/x509/by_dir.c */
255714Skris/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
355714Skris * All rights reserved.
455714Skris *
555714Skris * This package is an SSL implementation written
655714Skris * by Eric Young (eay@cryptsoft.com).
755714Skris * The implementation was written so as to conform with Netscapes SSL.
8296465Sdelphij *
955714Skris * This library is free for commercial and non-commercial use as long as
1055714Skris * the following conditions are aheared to.  The following conditions
1155714Skris * apply to all code found in this distribution, be it the RC4, RSA,
1255714Skris * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
1355714Skris * included with this distribution is covered by the same copyright terms
1455714Skris * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15296465Sdelphij *
1655714Skris * Copyright remains Eric Young's, and as such any Copyright notices in
1755714Skris * the code are not to be removed.
1855714Skris * If this package is used in a product, Eric Young should be given attribution
1955714Skris * as the author of the parts of the library used.
2055714Skris * This can be in the form of a textual message at program startup or
2155714Skris * in documentation (online or textual) provided with the package.
22296465Sdelphij *
2355714Skris * Redistribution and use in source and binary forms, with or without
2455714Skris * modification, are permitted provided that the following conditions
2555714Skris * are met:
2655714Skris * 1. Redistributions of source code must retain the copyright
2755714Skris *    notice, this list of conditions and the following disclaimer.
2855714Skris * 2. Redistributions in binary form must reproduce the above copyright
2955714Skris *    notice, this list of conditions and the following disclaimer in the
3055714Skris *    documentation and/or other materials provided with the distribution.
3155714Skris * 3. All advertising materials mentioning features or use of this software
3255714Skris *    must display the following acknowledgement:
3355714Skris *    "This product includes cryptographic software written by
3455714Skris *     Eric Young (eay@cryptsoft.com)"
3555714Skris *    The word 'cryptographic' can be left out if the rouines from the library
3655714Skris *    being used are not cryptographic related :-).
37296465Sdelphij * 4. If you include any Windows specific code (or a derivative thereof) from
3855714Skris *    the apps directory (application code) you must include an acknowledgement:
3955714Skris *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40296465Sdelphij *
4155714Skris * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
4255714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4455714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5155714Skris * SUCH DAMAGE.
52296465Sdelphij *
5355714Skris * The licence and distribution terms for any publically available version or
5455714Skris * derivative of this code cannot be changed.  i.e. this code cannot simply be
5555714Skris * copied and put under another distribution licence
5655714Skris * [including the GNU Public Licence.]
5755714Skris */
5855714Skris
5955714Skris#include <stdio.h>
6055714Skris#include <time.h>
6155714Skris#include <errno.h>
6255714Skris
6355714Skris#include "cryptlib.h"
6459191Skris
6559191Skris#ifndef NO_SYS_TYPES_H
6659191Skris# include <sys/types.h>
6759191Skris#endif
6859191Skris#ifdef MAC_OS_pre_X
6959191Skris# include <stat.h>
7059191Skris#else
7159191Skris# include <sys/stat.h>
7259191Skris#endif
7359191Skris
7455714Skris#include <openssl/lhash.h>
7555714Skris#include <openssl/x509.h>
7655714Skris
77194206Ssimon#ifdef _WIN32
78296465Sdelphij# define stat    _stat
79194206Ssimon#endif
80194206Ssimon
81296465Sdelphijtypedef struct lookup_dir_st {
82296465Sdelphij    BUF_MEM *buffer;
83296465Sdelphij    int num_dirs;
84296465Sdelphij    char **dirs;
85296465Sdelphij    int *dirs_type;
86296465Sdelphij    int num_dirs_alloced;
87296465Sdelphij} BY_DIR;
8855714Skris
8955714Skrisstatic int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
90296465Sdelphij                    char **ret);
9155714Skrisstatic int new_dir(X509_LOOKUP *lu);
9255714Skrisstatic void free_dir(X509_LOOKUP *lu);
93296465Sdelphijstatic int add_cert_dir(BY_DIR *ctx, const char *dir, int type);
94296465Sdelphijstatic int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
95296465Sdelphij                               X509_OBJECT *ret);
96296465SdelphijX509_LOOKUP_METHOD x509_dir_lookup = {
97296465Sdelphij    "Load certs from files in a directory",
98296465Sdelphij    new_dir,                    /* new */
99296465Sdelphij    free_dir,                   /* free */
100296465Sdelphij    NULL,                       /* init */
101296465Sdelphij    NULL,                       /* shutdown */
102296465Sdelphij    dir_ctrl,                   /* ctrl */
103296465Sdelphij    get_cert_by_subject,        /* get_by_subject */
104296465Sdelphij    NULL,                       /* get_by_issuer_serial */
105296465Sdelphij    NULL,                       /* get_by_fingerprint */
106296465Sdelphij    NULL,                       /* get_by_alias */
107296465Sdelphij};
10855714Skris
10955714SkrisX509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void)
110296465Sdelphij{
111296465Sdelphij    return (&x509_dir_lookup);
112296465Sdelphij}
11355714Skris
11455714Skrisstatic int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
115296465Sdelphij                    char **retp)
116296465Sdelphij{
117296465Sdelphij    int ret = 0;
118296465Sdelphij    BY_DIR *ld;
119296465Sdelphij    char *dir = NULL;
12055714Skris
121296465Sdelphij    ld = (BY_DIR *)ctx->method_data;
12255714Skris
123296465Sdelphij    switch (cmd) {
124296465Sdelphij    case X509_L_ADD_DIR:
125296465Sdelphij        if (argl == X509_FILETYPE_DEFAULT) {
126296465Sdelphij            dir = (char *)Getenv(X509_get_default_cert_dir_env());
127296465Sdelphij            if (dir)
128296465Sdelphij                ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM);
129296465Sdelphij            else
130296465Sdelphij                ret = add_cert_dir(ld, X509_get_default_cert_dir(),
131296465Sdelphij                                   X509_FILETYPE_PEM);
132296465Sdelphij            if (!ret) {
133296465Sdelphij                X509err(X509_F_DIR_CTRL, X509_R_LOADING_CERT_DIR);
134296465Sdelphij            }
135296465Sdelphij        } else
136296465Sdelphij            ret = add_cert_dir(ld, argp, (int)argl);
137296465Sdelphij        break;
138296465Sdelphij    }
139296465Sdelphij    return (ret);
140296465Sdelphij}
14155714Skris
14255714Skrisstatic int new_dir(X509_LOOKUP *lu)
143296465Sdelphij{
144296465Sdelphij    BY_DIR *a;
14555714Skris
146296465Sdelphij    if ((a = (BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL)
147296465Sdelphij        return (0);
148296465Sdelphij    if ((a->buffer = BUF_MEM_new()) == NULL) {
149296465Sdelphij        OPENSSL_free(a);
150296465Sdelphij        return (0);
151296465Sdelphij    }
152296465Sdelphij    a->num_dirs = 0;
153296465Sdelphij    a->dirs = NULL;
154296465Sdelphij    a->dirs_type = NULL;
155296465Sdelphij    a->num_dirs_alloced = 0;
156296465Sdelphij    lu->method_data = (char *)a;
157296465Sdelphij    return (1);
158296465Sdelphij}
15955714Skris
16055714Skrisstatic void free_dir(X509_LOOKUP *lu)
161296465Sdelphij{
162296465Sdelphij    BY_DIR *a;
163296465Sdelphij    int i;
16455714Skris
165296465Sdelphij    a = (BY_DIR *)lu->method_data;
166296465Sdelphij    for (i = 0; i < a->num_dirs; i++)
167296465Sdelphij        if (a->dirs[i] != NULL)
168296465Sdelphij            OPENSSL_free(a->dirs[i]);
169296465Sdelphij    if (a->dirs != NULL)
170296465Sdelphij        OPENSSL_free(a->dirs);
171296465Sdelphij    if (a->dirs_type != NULL)
172296465Sdelphij        OPENSSL_free(a->dirs_type);
173296465Sdelphij    if (a->buffer != NULL)
174296465Sdelphij        BUF_MEM_free(a->buffer);
175296465Sdelphij    OPENSSL_free(a);
176296465Sdelphij}
17755714Skris
17855714Skrisstatic int add_cert_dir(BY_DIR *ctx, const char *dir, int type)
179296465Sdelphij{
180296465Sdelphij    int j, len;
181296465Sdelphij    int *ip;
182296465Sdelphij    const char *s, *ss, *p;
183296465Sdelphij    char **pp;
18455714Skris
185296465Sdelphij    if (dir == NULL || !*dir) {
186296465Sdelphij        X509err(X509_F_ADD_CERT_DIR, X509_R_INVALID_DIRECTORY);
187296465Sdelphij        return 0;
188296465Sdelphij    }
18955714Skris
190296465Sdelphij    s = dir;
191296465Sdelphij    p = s;
192296465Sdelphij    for (;; p++) {
193296465Sdelphij        if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0')) {
194296465Sdelphij            ss = s;
195296465Sdelphij            s = p + 1;
196296465Sdelphij            len = (int)(p - ss);
197296465Sdelphij            if (len == 0)
198296465Sdelphij                continue;
199296465Sdelphij            for (j = 0; j < ctx->num_dirs; j++)
200296465Sdelphij                if (strlen(ctx->dirs[j]) == (size_t)len &&
201296465Sdelphij                    strncmp(ctx->dirs[j], ss, (unsigned int)len) == 0)
202296465Sdelphij                    break;
203296465Sdelphij            if (j < ctx->num_dirs)
204296465Sdelphij                continue;
205296465Sdelphij            if (ctx->num_dirs_alloced < (ctx->num_dirs + 1)) {
206296465Sdelphij                ctx->num_dirs_alloced += 10;
207296465Sdelphij                pp = (char **)OPENSSL_malloc(ctx->num_dirs_alloced *
208296465Sdelphij                                             sizeof(char *));
209296465Sdelphij                ip = (int *)OPENSSL_malloc(ctx->num_dirs_alloced *
210296465Sdelphij                                           sizeof(int));
211296465Sdelphij                if ((pp == NULL) || (ip == NULL)) {
212296465Sdelphij                    X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE);
213296465Sdelphij                    return (0);
214296465Sdelphij                }
215296465Sdelphij                memcpy(pp, ctx->dirs, (ctx->num_dirs_alloced - 10) *
216296465Sdelphij                       sizeof(char *));
217296465Sdelphij                memcpy(ip, ctx->dirs_type, (ctx->num_dirs_alloced - 10) *
218296465Sdelphij                       sizeof(int));
219296465Sdelphij                if (ctx->dirs != NULL)
220296465Sdelphij                    OPENSSL_free(ctx->dirs);
221296465Sdelphij                if (ctx->dirs_type != NULL)
222296465Sdelphij                    OPENSSL_free(ctx->dirs_type);
223296465Sdelphij                ctx->dirs = pp;
224296465Sdelphij                ctx->dirs_type = ip;
225296465Sdelphij            }
226296465Sdelphij            ctx->dirs_type[ctx->num_dirs] = type;
227296465Sdelphij            ctx->dirs[ctx->num_dirs] =
228296465Sdelphij                (char *)OPENSSL_malloc((unsigned int)len + 1);
229296465Sdelphij            if (ctx->dirs[ctx->num_dirs] == NULL)
230296465Sdelphij                return (0);
231296465Sdelphij            strncpy(ctx->dirs[ctx->num_dirs], ss, (unsigned int)len);
232296465Sdelphij            ctx->dirs[ctx->num_dirs][len] = '\0';
233296465Sdelphij            ctx->num_dirs++;
234296465Sdelphij        }
235296465Sdelphij        if (*p == '\0')
236296465Sdelphij            break;
237296465Sdelphij    }
238296465Sdelphij    return (1);
239296465Sdelphij}
24055714Skris
24155714Skrisstatic int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
242296465Sdelphij                               X509_OBJECT *ret)
243296465Sdelphij{
244296465Sdelphij    BY_DIR *ctx;
245296465Sdelphij    union {
246296465Sdelphij        struct {
247296465Sdelphij            X509 st_x509;
248296465Sdelphij            X509_CINF st_x509_cinf;
249296465Sdelphij        } x509;
250296465Sdelphij        struct {
251296465Sdelphij            X509_CRL st_crl;
252296465Sdelphij            X509_CRL_INFO st_crl_info;
253296465Sdelphij        } crl;
254296465Sdelphij    } data;
255296465Sdelphij    int ok = 0;
256296465Sdelphij    int i, j, k;
257296465Sdelphij    unsigned long h;
258296465Sdelphij    BUF_MEM *b = NULL;
259296465Sdelphij    struct stat st;
260296465Sdelphij    X509_OBJECT stmp, *tmp;
261296465Sdelphij    const char *postfix = "";
26255714Skris
263296465Sdelphij    if (name == NULL)
264296465Sdelphij        return (0);
26555714Skris
266296465Sdelphij    stmp.type = type;
267296465Sdelphij    if (type == X509_LU_X509) {
268296465Sdelphij        data.x509.st_x509.cert_info = &data.x509.st_x509_cinf;
269296465Sdelphij        data.x509.st_x509_cinf.subject = name;
270296465Sdelphij        stmp.data.x509 = &data.x509.st_x509;
271296465Sdelphij        postfix = "";
272296465Sdelphij    } else if (type == X509_LU_CRL) {
273296465Sdelphij        data.crl.st_crl.crl = &data.crl.st_crl_info;
274296465Sdelphij        data.crl.st_crl_info.issuer = name;
275296465Sdelphij        stmp.data.crl = &data.crl.st_crl;
276296465Sdelphij        postfix = "r";
277296465Sdelphij    } else {
278296465Sdelphij        X509err(X509_F_GET_CERT_BY_SUBJECT, X509_R_WRONG_LOOKUP_TYPE);
279296465Sdelphij        goto finish;
280296465Sdelphij    }
28155714Skris
282296465Sdelphij    if ((b = BUF_MEM_new()) == NULL) {
283296465Sdelphij        X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_BUF_LIB);
284296465Sdelphij        goto finish;
285296465Sdelphij    }
28655714Skris
287296465Sdelphij    ctx = (BY_DIR *)xl->method_data;
288296465Sdelphij
289296465Sdelphij    h = X509_NAME_hash(name);
290296465Sdelphij    for (i = 0; i < ctx->num_dirs; i++) {
291296465Sdelphij        j = strlen(ctx->dirs[i]) + 1 + 8 + 6 + 1 + 1;
292296465Sdelphij        if (!BUF_MEM_grow(b, j)) {
293296465Sdelphij            X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE);
294296465Sdelphij            goto finish;
295296465Sdelphij        }
296296465Sdelphij        k = 0;
297296465Sdelphij        for (;;) {
298296465Sdelphij            char c = '/';
299127128Snectar#ifdef OPENSSL_SYS_VMS
300296465Sdelphij            c = ctx->dirs[i][strlen(ctx->dirs[i]) - 1];
301296465Sdelphij            if (c != ':' && c != '>' && c != ']') {
302296465Sdelphij                /*
303296465Sdelphij                 * If no separator is present, we assume the directory
304296465Sdelphij                 * specifier is a logical name, and add a colon.  We really
305296465Sdelphij                 * should use better VMS routines for merging things like
306296465Sdelphij                 * this, but this will do for now... -- Richard Levitte
307296465Sdelphij                 */
308296465Sdelphij                c = ':';
309296465Sdelphij            } else {
310296465Sdelphij                c = '\0';
311296465Sdelphij            }
312127128Snectar#endif
313296465Sdelphij            if (c == '\0') {
314296465Sdelphij                /*
315296465Sdelphij                 * This is special.  When c == '\0', no directory separator
316296465Sdelphij                 * should be added.
317296465Sdelphij                 */
318296465Sdelphij                BIO_snprintf(b->data, b->max,
319296465Sdelphij                             "%s%08lx.%s%d", ctx->dirs[i], h, postfix, k);
320296465Sdelphij            } else {
321296465Sdelphij                BIO_snprintf(b->data, b->max,
322296465Sdelphij                             "%s%c%08lx.%s%d", ctx->dirs[i], c, h,
323296465Sdelphij                             postfix, k);
324296465Sdelphij            }
325296465Sdelphij            k++;
326296465Sdelphij            if (stat(b->data, &st) < 0)
327296465Sdelphij                break;
328296465Sdelphij            /* found one. */
329296465Sdelphij            if (type == X509_LU_X509) {
330296465Sdelphij                if ((X509_load_cert_file(xl, b->data,
331296465Sdelphij                                         ctx->dirs_type[i])) == 0)
332296465Sdelphij                    break;
333296465Sdelphij            } else if (type == X509_LU_CRL) {
334296465Sdelphij                if ((X509_load_crl_file(xl, b->data, ctx->dirs_type[i])) == 0)
335296465Sdelphij                    break;
336296465Sdelphij            }
337296465Sdelphij            /* else case will caught higher up */
338296465Sdelphij        }
33955714Skris
340296465Sdelphij        /*
341296465Sdelphij         * we have added it to the cache so now pull it out again
342296465Sdelphij         */
343296465Sdelphij        CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
344296465Sdelphij        j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
345296465Sdelphij        if (j != -1)
346296465Sdelphij            tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
347296465Sdelphij        else
348296465Sdelphij            tmp = NULL;
349296465Sdelphij        CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
35055714Skris
351296465Sdelphij        if (tmp != NULL) {
352296465Sdelphij            ok = 1;
353296465Sdelphij            ret->type = tmp->type;
354296465Sdelphij            memcpy(&ret->data, &tmp->data, sizeof(ret->data));
355296465Sdelphij            /*
356296465Sdelphij             * If we were going to up the reference count, we would need to
357296465Sdelphij             * do it on a perl 'type' basis
358296465Sdelphij             */
359296465Sdelphij        /*- CRYPTO_add(&tmp->data.x509->references,1,
360296465Sdelphij                    CRYPTO_LOCK_X509);*/
361296465Sdelphij            goto finish;
362296465Sdelphij        }
363296465Sdelphij    }
364296465Sdelphij finish:
365296465Sdelphij    if (b != NULL)
366296465Sdelphij        BUF_MEM_free(b);
367296465Sdelphij    return (ok);
368296465Sdelphij}
369