1229970Sadrian/* $OpenBSD: by_dir.c,v 1.47 2024/03/25 00:05:49 beck Exp $ */ 2229970Sadrian/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 3229970Sadrian * All rights reserved. 4229970Sadrian * 5229970Sadrian * This package is an SSL implementation written 6229970Sadrian * by Eric Young (eay@cryptsoft.com). 7229970Sadrian * The implementation was written so as to conform with Netscapes SSL. 8229970Sadrian * 9229970Sadrian * This library is free for commercial and non-commercial use as long as 10229970Sadrian * the following conditions are aheared to. The following conditions 11229970Sadrian * apply to all code found in this distribution, be it the RC4, RSA, 12229970Sadrian * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13229970Sadrian * included with this distribution is covered by the same copyright terms 14229970Sadrian * except that the holder is Tim Hudson (tjh@cryptsoft.com). 15229970Sadrian * 16229970Sadrian * Copyright remains Eric Young's, and as such any Copyright notices in 17229970Sadrian * the code are not to be removed. 18229970Sadrian * If this package is used in a product, Eric Young should be given attribution 19229970Sadrian * as the author of the parts of the library used. 20229970Sadrian * This can be in the form of a textual message at program startup or 21229970Sadrian * in documentation (online or textual) provided with the package. 22229970Sadrian * 23229970Sadrian * Redistribution and use in source and binary forms, with or without 24229970Sadrian * modification, are permitted provided that the following conditions 25229970Sadrian * are met: 26229970Sadrian * 1. Redistributions of source code must retain the copyright 27229970Sadrian * notice, this list of conditions and the following disclaimer. 28229970Sadrian * 2. Redistributions in binary form must reproduce the above copyright 29229970Sadrian * notice, this list of conditions and the following disclaimer in the 30229970Sadrian * documentation and/or other materials provided with the distribution. 31229970Sadrian * 3. All advertising materials mentioning features or use of this software 32229970Sadrian * must display the following acknowledgement: 33229970Sadrian * "This product includes cryptographic software written by 34229970Sadrian * Eric Young (eay@cryptsoft.com)" 35229970Sadrian * The word 'cryptographic' can be left out if the rouines from the library 36229970Sadrian * being used are not cryptographic related :-). 37229970Sadrian * 4. If you include any Windows specific code (or a derivative thereof) from 38229970Sadrian * the apps directory (application code) you must include an acknowledgement: 39229970Sadrian * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 40229970Sadrian * 41229970Sadrian * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42229970Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43232763Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44232763Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45232763Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46232763Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47232763Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48232763Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49232763Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50229970Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51229970Sadrian * SUCH DAMAGE. 52229970Sadrian * 53229970Sadrian * The licence and distribution terms for any publically available version or 54229970Sadrian * derivative of this code cannot be changed. i.e. this code cannot simply be 55229970Sadrian * copied and put under another distribution licence 56229970Sadrian * [including the GNU Public Licence.] 57229970Sadrian */ 58229970Sadrian 59229970Sadrian#include <errno.h> 60229970Sadrian#include <stdio.h> 61229970Sadrian#include <string.h> 62229970Sadrian#include <time.h> 63229970Sadrian#include <unistd.h> 64229970Sadrian 65229970Sadrian#include <openssl/opensslconf.h> 66229970Sadrian 67229970Sadrian#include <openssl/err.h> 68229970Sadrian#include <openssl/x509.h> 69229970Sadrian 70229970Sadrian#include "x509_local.h" 71229970Sadrian 72229970Sadriantypedef struct lookup_dir_hashes_st { 73229970Sadrian unsigned long hash; 74229970Sadrian int suffix; 75229970Sadrian} BY_DIR_HASH; 76229970Sadrian 77229970Sadriantypedef struct lookup_dir_entry_st { 78229970Sadrian char *dir; 79229970Sadrian int dir_type; 80229970Sadrian STACK_OF(BY_DIR_HASH) *hashes; 81229970Sadrian} BY_DIR_ENTRY; 82229970Sadrian 83229970Sadriantypedef struct lookup_dir_st { 84229970Sadrian BUF_MEM *buffer; 85229970Sadrian STACK_OF(BY_DIR_ENTRY) *dirs; 86229970Sadrian} BY_DIR; 87229970Sadrian 88229970SadrianDECLARE_STACK_OF(BY_DIR_HASH) 89229970SadrianDECLARE_STACK_OF(BY_DIR_ENTRY) 90229970Sadrian 91229970Sadrianstatic int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 92229970Sadrian char **ret); 93229970Sadrianstatic int new_dir(X509_LOOKUP *lu); 94231378Sedstatic void free_dir(X509_LOOKUP *lu); 95229970Sadrianstatic int add_cert_dir(BY_DIR *ctx, const char *dir, int type); 96229970Sadrianstatic int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, 97229970Sadrian X509_OBJECT *ret); 98229970Sadrian 99229970Sadrianstatic X509_LOOKUP_METHOD x509_dir_lookup = { 100229970Sadrian .name = "Load certs from files in a directory", 101229970Sadrian .new_item = new_dir, 102229970Sadrian .free = free_dir, 103243857Sglebius .ctrl = dir_ctrl, 104229970Sadrian .get_by_subject = get_cert_by_subject, 105229970Sadrian}; 106229970Sadrian 107229970SadrianX509_LOOKUP_METHOD * 108229970SadrianX509_LOOKUP_hash_dir(void) 109229970Sadrian{ 110229970Sadrian return &x509_dir_lookup; 111231378Sed} 112229970SadrianLCRYPTO_ALIAS(X509_LOOKUP_hash_dir); 113229970Sadrian 114229970Sadrianstatic int 115229970Sadriandir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, 116229970Sadrian char **retp) 117229970Sadrian{ 118229970Sadrian BY_DIR *ld = ctx->method_data; 119229970Sadrian int ret = 0; 120229970Sadrian 121229970Sadrian switch (cmd) { 122229970Sadrian case X509_L_ADD_DIR: 123229970Sadrian if (argl == X509_FILETYPE_DEFAULT) { 124229970Sadrian ret = add_cert_dir(ld, X509_get_default_cert_dir(), 125229970Sadrian X509_FILETYPE_PEM); 126229970Sadrian if (!ret) { 127229970Sadrian X509error(X509_R_LOADING_CERT_DIR); 128229970Sadrian } 129229970Sadrian } else 130229970Sadrian ret = add_cert_dir(ld, argp, (int)argl); 131229970Sadrian break; 132229970Sadrian } 133229970Sadrian return ret; 134229970Sadrian} 135229970Sadrian 136229970Sadrianstatic int 137229970Sadriannew_dir(X509_LOOKUP *lu) 138229970Sadrian{ 139229970Sadrian BY_DIR *a; 140229970Sadrian 141229970Sadrian if ((a = malloc(sizeof(*a))) == NULL) { 142229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 143229970Sadrian return 0; 144229970Sadrian } 145229970Sadrian if ((a->buffer = BUF_MEM_new()) == NULL) { 146229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 147229970Sadrian free(a); 148229970Sadrian return 0; 149229970Sadrian } 150229970Sadrian a->dirs = NULL; 151229970Sadrian lu->method_data = a; 152229970Sadrian return 1; 153229970Sadrian} 154229970Sadrian 155229970Sadrianstatic void 156229970Sadrianby_dir_hash_free(BY_DIR_HASH *hash) 157229970Sadrian{ 158229970Sadrian free(hash); 159229970Sadrian} 160229970Sadrian 161229970Sadrianstatic int 162229970Sadrianby_dir_hash_cmp(const BY_DIR_HASH * const *a, 163229970Sadrian const BY_DIR_HASH * const *b) 164229970Sadrian{ 165229970Sadrian if ((*a)->hash > (*b)->hash) 166229970Sadrian return 1; 167229970Sadrian if ((*a)->hash < (*b)->hash) 168229970Sadrian return -1; 169229970Sadrian return 0; 170229970Sadrian} 171229970Sadrian 172229970Sadrianstatic void 173229970Sadrianby_dir_entry_free(BY_DIR_ENTRY *ent) 174229970Sadrian{ 175229970Sadrian free(ent->dir); 176229970Sadrian sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free); 177229970Sadrian free(ent); 178229970Sadrian} 179229970Sadrian 180229970Sadrianstatic void 181229970Sadrianfree_dir(X509_LOOKUP *lu) 182229970Sadrian{ 183229970Sadrian BY_DIR *a; 184229970Sadrian 185229970Sadrian a = lu->method_data; 186229970Sadrian sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free); 187229970Sadrian BUF_MEM_free(a->buffer); 188229970Sadrian free(a); 189229970Sadrian} 190229970Sadrian 191229970Sadrianstatic int 192229970Sadrianadd_cert_dir(BY_DIR *ctx, const char *dir, int type) 193229970Sadrian{ 194229970Sadrian int j; 195229970Sadrian const char *s, *ss, *p; 196229970Sadrian ptrdiff_t len; 197229970Sadrian 198229970Sadrian if (dir == NULL || !*dir) { 199229970Sadrian X509error(X509_R_INVALID_DIRECTORY); 200229970Sadrian return 0; 201229970Sadrian } 202229970Sadrian 203229970Sadrian s = dir; 204229970Sadrian p = s; 205229970Sadrian do { 206229970Sadrian if ((*p == ':') || (*p == '\0')) { 207229970Sadrian BY_DIR_ENTRY *ent; 208229970Sadrian 209229970Sadrian ss = s; 210229970Sadrian s = p + 1; 211229970Sadrian len = p - ss; 212229970Sadrian if (len == 0) 213229970Sadrian continue; 214229970Sadrian for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) { 215229970Sadrian ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j); 216229970Sadrian if (strlen(ent->dir) == (size_t)len && 217229970Sadrian strncmp(ent->dir, ss, (size_t)len) == 0) 218229970Sadrian break; 219229970Sadrian } 220229970Sadrian if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) 221229970Sadrian continue; 222229970Sadrian if (ctx->dirs == NULL) { 223229970Sadrian ctx->dirs = sk_BY_DIR_ENTRY_new_null(); 224229970Sadrian if (ctx->dirs == NULL) { 225229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 226229970Sadrian return 0; 227229970Sadrian } 228229970Sadrian } 229229970Sadrian ent = malloc(sizeof(*ent)); 230229970Sadrian if (ent == NULL) { 231229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 232229970Sadrian return 0; 233238938Smonthadar } 234238938Smonthadar ent->dir_type = type; 235238938Smonthadar ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp); 236238938Smonthadar ent->dir = strndup(ss, (size_t)len); 237229970Sadrian if (ent->dir == NULL || ent->hashes == NULL) { 238229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 239229970Sadrian by_dir_entry_free(ent); 240229970Sadrian return 0; 241229970Sadrian } 242229970Sadrian if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) { 243229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 244243857Sglebius by_dir_entry_free(ent); 245229970Sadrian return 0; 246229970Sadrian } 247229970Sadrian } 248229970Sadrian } while (*p++ != '\0'); 249229970Sadrian return 1; 250229970Sadrian} 251229970Sadrian 252229970Sadrianstatic int 253229970Sadrianget_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, 254229970Sadrian X509_OBJECT *ret) 255229970Sadrian{ 256229970Sadrian BY_DIR *ctx; 257229970Sadrian union { 258229970Sadrian struct { 259229970Sadrian X509 st_x509; 260229970Sadrian X509_CINF st_x509_cinf; 261229970Sadrian } x509; 262229970Sadrian struct { 263229970Sadrian X509_CRL st_crl; 264229970Sadrian X509_CRL_INFO st_crl_info; 265229970Sadrian } crl; 266229970Sadrian } data; 267229970Sadrian int ok = 0; 268229970Sadrian int i, j, k; 269229970Sadrian unsigned long h; 270229970Sadrian BUF_MEM *b = NULL; 271239760Sadrian X509_OBJECT stmp, *tmp; 272229970Sadrian const char *postfix=""; 273229970Sadrian 274229970Sadrian if (name == NULL) 275229970Sadrian return 0; 276229970Sadrian 277229970Sadrian stmp.type = type; 278229970Sadrian if (type == X509_LU_X509) { 279229970Sadrian data.x509.st_x509.cert_info = &data.x509.st_x509_cinf; 280229970Sadrian data.x509.st_x509_cinf.subject = name; 281239760Sadrian stmp.data.x509 = &data.x509.st_x509; 282239760Sadrian postfix=""; 283229970Sadrian } else if (type == X509_LU_CRL) { 284229970Sadrian data.crl.st_crl.crl = &data.crl.st_crl_info; 285229970Sadrian data.crl.st_crl_info.issuer = name; 286229970Sadrian stmp.data.crl = &data.crl.st_crl; 287229970Sadrian postfix="r"; 288229970Sadrian } else { 289229970Sadrian X509error(X509_R_WRONG_LOOKUP_TYPE); 290229970Sadrian goto finish; 291229970Sadrian } 292229970Sadrian 293229970Sadrian if ((b = BUF_MEM_new()) == NULL) { 294229970Sadrian X509error(ERR_R_BUF_LIB); 295238938Smonthadar goto finish; 296238938Smonthadar } 297229970Sadrian 298239760Sadrian ctx = xl->method_data; 299229970Sadrian 300229970Sadrian h = X509_NAME_hash(name); 301229970Sadrian for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) { 302239760Sadrian BY_DIR_ENTRY *ent; 303229970Sadrian int idx; 304229970Sadrian BY_DIR_HASH htmp, *hent; 305229970Sadrian 306229970Sadrian ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); 307229970Sadrian j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1; 308229970Sadrian if (!BUF_MEM_grow(b, j)) { 309229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 310229970Sadrian goto finish; 311229970Sadrian } 312229970Sadrian if (type == X509_LU_CRL) { 313229970Sadrian htmp.hash = h; 314229970Sadrian CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE); 315229970Sadrian idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 316229970Sadrian if (idx >= 0) { 317229970Sadrian hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 318229970Sadrian k = hent->suffix; 319229970Sadrian } else { 320229970Sadrian hent = NULL; 321229970Sadrian k = 0; 322229970Sadrian } 323229970Sadrian CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE); 324229970Sadrian } else { 325239760Sadrian k = 0; 326229970Sadrian hent = NULL; 327229970Sadrian } 328229970Sadrian for (;;) { 329244399Smonthadar (void) snprintf(b->data, b->max, "%s/%08lx.%s%d", 330244399Smonthadar ent->dir, h, postfix, k); 331244399Smonthadar /* 332229970Sadrian * Found one. Attempt to load it. This could fail for 333229970Sadrian * any number of reasons from the file can't be opened, 334232978Sadrian * the file contains garbage, etc. Clear the error stack 335229970Sadrian * to avoid exposing the lower level error. These all 336229970Sadrian * boil down to "we could not find CA/CRL". 337229970Sadrian */ 338244389Smonthadar if (type == X509_LU_X509) { 339244399Smonthadar if ((X509_load_cert_file(xl, b->data, 340244399Smonthadar ent->dir_type)) == 0) { 341244389Smonthadar ERR_clear_error(); 342229970Sadrian break; 343229970Sadrian } 344229970Sadrian } else if (type == X509_LU_CRL) { 345229970Sadrian if ((X509_load_crl_file(xl, b->data, 346229970Sadrian ent->dir_type)) == 0) { 347229970Sadrian ERR_clear_error(); 348229970Sadrian break; 349229970Sadrian } 350229970Sadrian } 351229970Sadrian /* The lack of a CA or CRL will be caught higher up. */ 352229970Sadrian k++; 353229970Sadrian } 354229970Sadrian 355229970Sadrian /* we have added it to the cache so now pull it out again */ 356244388Smonthadar CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 357229970Sadrian j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp); 358229970Sadrian tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j); 359239760Sadrian CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 360239760Sadrian 361239760Sadrian /* If a CRL, update the last file suffix added for this */ 362229970Sadrian if (type == X509_LU_CRL) { 363229970Sadrian CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 364229970Sadrian /* 365229970Sadrian * Look for entry again in case another thread added 366229970Sadrian * an entry first. 367229970Sadrian */ 368229970Sadrian if (hent == NULL) { 369229970Sadrian htmp.hash = h; 370229970Sadrian idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); 371229970Sadrian hent = sk_BY_DIR_HASH_value(ent->hashes, idx); 372229970Sadrian } 373229970Sadrian if (hent == NULL) { 374229970Sadrian hent = malloc(sizeof(*hent)); 375229970Sadrian if (hent == NULL) { 376229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 377229970Sadrian CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 378229970Sadrian ok = 0; 379229970Sadrian goto finish; 380229970Sadrian } 381229970Sadrian hent->hash = h; 382229970Sadrian hent->suffix = k; 383229970Sadrian if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) { 384229970Sadrian X509error(ERR_R_MALLOC_FAILURE); 385229970Sadrian CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 386229970Sadrian free(hent); 387229970Sadrian ok = 0; 388229970Sadrian goto finish; 389229970Sadrian } 390229970Sadrian } else if (hent->suffix < k) 391229970Sadrian hent->suffix = k; 392229970Sadrian 393229970Sadrian CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 394229970Sadrian 395229970Sadrian } 396229970Sadrian 397229970Sadrian if (tmp != NULL) { 398229970Sadrian ok = 1; 399229970Sadrian ret->type = tmp->type; 400229970Sadrian memcpy(&ret->data, &tmp->data, sizeof(ret->data)); 401229970Sadrian goto finish; 402229970Sadrian } 403229970Sadrian } 404229970Sadrianfinish: 405229970Sadrian BUF_MEM_free(b); 406229970Sadrian return ok; 407229970Sadrian} 408229970Sadrian