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