1 /* $OpenBSD: ct_log.c,v 1.9 2024/11/05 09:35:40 tb Exp $ */ 2 /* Author: Adam Eijdenberg <adam.eijdenberg@gmail.com>. */ 3 /* ==================================================================== 4 * Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * 3. All advertising materials mentioning features or use of this 19 * software must display the following acknowledgment: 20 * "This product includes software developed by the OpenSSL Project 21 * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" 22 * 23 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 24 * endorse or promote products derived from this software without 25 * prior written permission. For written permission, please contact 26 * openssl-core@openssl.org. 27 * 28 * 5. Products derived from this software may not be called "OpenSSL" 29 * nor may "OpenSSL" appear in their names without prior written 30 * permission of the OpenSSL Project. 31 * 32 * 6. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by the OpenSSL Project 35 * for use in the OpenSSL Toolkit (http://www.openssl.org/)" 36 * 37 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 38 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 40 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 43 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 46 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 48 * OF THE POSSIBILITY OF SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This product includes cryptographic software written by Eric Young 52 * (eay@cryptsoft.com). This product includes software written by Tim 53 * Hudson (tjh@cryptsoft.com). 54 * 55 * Licensed under the OpenSSL license (the "License"). You may not use 56 * this file except in compliance with the License. You can obtain a copy 57 * in the file LICENSE in the source distribution or at 58 * https://www.openssl.org/source/license.html 59 */ 60 61 #include <stdint.h> 62 #include <stdlib.h> 63 #include <string.h> 64 65 #include <openssl/asn1.h> 66 #include <openssl/conf.h> 67 #include <openssl/ct.h> 68 #include <openssl/err.h> 69 #include <openssl/evp.h> 70 #include <openssl/sha.h> 71 #include <openssl/x509.h> 72 73 #include "conf_local.h" 74 #include "crypto_local.h" 75 76 77 /* 78 * Information about a CT log server. 79 */ 80 struct ctlog_st { 81 char *name; 82 uint8_t log_id[CT_V1_HASHLEN]; 83 EVP_PKEY *public_key; 84 }; 85 86 /* 87 * A store for multiple CTLOG instances. 88 * It takes ownership of any CTLOG instances added to it. 89 */ 90 struct ctlog_store_st { 91 STACK_OF(CTLOG) *logs; 92 }; 93 94 /* The context when loading a CT log list from a CONF file. */ 95 typedef struct ctlog_store_load_ctx_st { 96 CTLOG_STORE *log_store; 97 CONF *conf; 98 size_t invalid_log_entries; 99 } CTLOG_STORE_LOAD_CTX; 100 101 /* 102 * Creates an empty context for loading a CT log store. 103 * It should be populated before use. 104 */ 105 static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void); 106 107 /* 108 * Deletes a CT log store load context. 109 * Does not delete any of the fields. 110 */ 111 static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX *ctx); 112 113 static CTLOG_STORE_LOAD_CTX * 114 ctlog_store_load_ctx_new(void) 115 { 116 CTLOG_STORE_LOAD_CTX *ctx = calloc(1, sizeof(*ctx)); 117 118 if (ctx == NULL) 119 CTerror(ERR_R_MALLOC_FAILURE); 120 121 return ctx; 122 } 123 124 static void 125 ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX *ctx) 126 { 127 free(ctx); 128 } 129 130 /* Converts a log's public key into a SHA256 log ID */ 131 static int 132 ct_v1_log_id_from_pkey(EVP_PKEY *pkey, unsigned char log_id[CT_V1_HASHLEN]) 133 { 134 int ret = 0; 135 unsigned char *pkey_der = NULL; 136 int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der); 137 138 if (pkey_der_len <= 0) { 139 CTerror(CT_R_LOG_KEY_INVALID); 140 goto err; 141 } 142 143 SHA256(pkey_der, pkey_der_len, log_id); 144 ret = 1; 145 err: 146 free(pkey_der); 147 return ret; 148 } 149 150 CTLOG_STORE * 151 CTLOG_STORE_new(void) 152 { 153 CTLOG_STORE *ret = calloc(1, sizeof(*ret)); 154 155 if (ret == NULL) { 156 CTerror(ERR_R_MALLOC_FAILURE); 157 return NULL; 158 } 159 160 ret->logs = sk_CTLOG_new_null(); 161 if (ret->logs == NULL) 162 goto err; 163 164 return ret; 165 err: 166 free(ret); 167 return NULL; 168 } 169 LCRYPTO_ALIAS(CTLOG_STORE_new); 170 171 void 172 CTLOG_STORE_free(CTLOG_STORE *store) 173 { 174 if (store != NULL) { 175 sk_CTLOG_pop_free(store->logs, CTLOG_free); 176 free(store); 177 } 178 } 179 LCRYPTO_ALIAS(CTLOG_STORE_free); 180 181 static int 182 ctlog_new_from_conf(CTLOG **ct_log, const CONF *conf, const char *section) 183 { 184 const char *description = NCONF_get_string(conf, section, 185 "description"); 186 char *pkey_base64; 187 188 if (description == NULL) { 189 CTerror(CT_R_LOG_CONF_MISSING_DESCRIPTION); 190 return 0; 191 } 192 193 pkey_base64 = NCONF_get_string(conf, section, "key"); 194 if (pkey_base64 == NULL) { 195 CTerror(CT_R_LOG_CONF_MISSING_KEY); 196 return 0; 197 } 198 199 return CTLOG_new_from_base64(ct_log, pkey_base64, description); 200 } 201 202 int 203 CTLOG_STORE_load_default_file(CTLOG_STORE *store) 204 { 205 return CTLOG_STORE_load_file(store, CTLOG_FILE); 206 } 207 LCRYPTO_ALIAS(CTLOG_STORE_load_default_file); 208 209 /* 210 * Called by CONF_parse_list, which stops if this returns <= 0, 211 * Otherwise, one bad log entry would stop loading of any of 212 * the following log entries. 213 * It may stop parsing and returns -1 on any internal (malloc) error. 214 */ 215 static int 216 ctlog_store_load_log(const char *log_name, int log_name_len, void *arg) 217 { 218 CTLOG_STORE_LOAD_CTX *load_ctx = arg; 219 CTLOG *ct_log = NULL; 220 /* log_name may not be null-terminated, so fix that before using it */ 221 char *tmp; 222 int ret = 0; 223 224 /* log_name will be NULL for empty list entries */ 225 if (log_name == NULL) 226 return 1; 227 228 tmp = strndup(log_name, log_name_len); 229 if (tmp == NULL) 230 goto mem_err; 231 232 ret = ctlog_new_from_conf(&ct_log, load_ctx->conf, tmp); 233 free(tmp); 234 235 if (ret < 0) { 236 /* Propagate any internal error */ 237 return ret; 238 } 239 if (ret == 0) { 240 /* If we can't load this log, record that fact and skip it */ 241 ++load_ctx->invalid_log_entries; 242 return 1; 243 } 244 245 if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) { 246 goto mem_err; 247 } 248 return 1; 249 250 mem_err: 251 CTLOG_free(ct_log); 252 CTerror(ERR_R_MALLOC_FAILURE); 253 return -1; 254 } 255 256 int 257 CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file) 258 { 259 int ret = 0; 260 char *enabled_logs; 261 CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new(); 262 263 if (load_ctx == NULL) 264 return 0; 265 load_ctx->log_store = store; 266 load_ctx->conf = NCONF_new(NULL); 267 if (load_ctx->conf == NULL) 268 goto end; 269 270 if (NCONF_load(load_ctx->conf, file, NULL) <= 0) { 271 CTerror(CT_R_LOG_CONF_INVALID); 272 goto end; 273 } 274 275 enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs"); 276 if (enabled_logs == NULL) { 277 CTerror(CT_R_LOG_CONF_INVALID); 278 goto end; 279 } 280 281 if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) || 282 load_ctx->invalid_log_entries > 0) { 283 CTerror(CT_R_LOG_CONF_INVALID); 284 goto end; 285 } 286 287 ret = 1; 288 end: 289 NCONF_free(load_ctx->conf); 290 ctlog_store_load_ctx_free(load_ctx); 291 return ret; 292 } 293 LCRYPTO_ALIAS(CTLOG_STORE_load_file); 294 295 /* 296 * Initialize a new CTLOG object. 297 * Takes ownership of the public key. 298 * Copies the name. 299 */ 300 CTLOG * 301 CTLOG_new(EVP_PKEY *public_key, const char *name) 302 { 303 CTLOG *ret = calloc(1, sizeof(*ret)); 304 305 if (ret == NULL) { 306 CTerror(ERR_R_MALLOC_FAILURE); 307 return NULL; 308 } 309 310 ret->name = strdup(name); 311 if (ret->name == NULL) { 312 CTerror(ERR_R_MALLOC_FAILURE); 313 goto err; 314 } 315 316 if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1) 317 goto err; 318 319 ret->public_key = public_key; 320 return ret; 321 err: 322 CTLOG_free(ret); 323 return NULL; 324 } 325 LCRYPTO_ALIAS(CTLOG_new); 326 327 /* Frees CT log and associated structures */ 328 void 329 CTLOG_free(CTLOG *log) 330 { 331 if (log != NULL) { 332 free(log->name); 333 EVP_PKEY_free(log->public_key); 334 free(log); 335 } 336 } 337 LCRYPTO_ALIAS(CTLOG_free); 338 339 const char * 340 CTLOG_get0_name(const CTLOG *log) 341 { 342 return log->name; 343 } 344 LCRYPTO_ALIAS(CTLOG_get0_name); 345 346 void 347 CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id, size_t *log_id_len) 348 { 349 *log_id = log->log_id; 350 *log_id_len = CT_V1_HASHLEN; 351 } 352 LCRYPTO_ALIAS(CTLOG_get0_log_id); 353 354 EVP_PKEY * 355 CTLOG_get0_public_key(const CTLOG *log) 356 { 357 return log->public_key; 358 } 359 LCRYPTO_ALIAS(CTLOG_get0_public_key); 360 361 /* 362 * Given a log ID, finds the matching log. 363 * Returns NULL if no match found. 364 */ 365 const CTLOG * 366 CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store, const uint8_t *log_id, 367 size_t log_id_len) 368 { 369 int i; 370 371 for (i = 0; i < sk_CTLOG_num(store->logs); ++i) { 372 const CTLOG *log = sk_CTLOG_value(store->logs, i); 373 if (memcmp(log->log_id, log_id, log_id_len) == 0) 374 return log; 375 } 376 377 return NULL; 378 } 379 LCRYPTO_ALIAS(CTLOG_STORE_get0_log_by_id); 380