1 /* $OpenBSD: ct_log.c,v 1.5 2021/12/18 16:34:52 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 <stdlib.h>
62 #include <string.h>
63
64 #include <openssl/conf.h>
65 #include <openssl/ct.h>
66 #include <openssl/err.h>
67 #include <openssl/evp.h>
68 #include <openssl/safestack.h>
69
70 #include "cryptlib.h"
71
72
73 /*
74 * Information about a CT log server.
75 */
76 struct ctlog_st {
77 char *name;
78 uint8_t log_id[CT_V1_HASHLEN];
79 EVP_PKEY *public_key;
80 };
81
82 /*
83 * A store for multiple CTLOG instances.
84 * It takes ownership of any CTLOG instances added to it.
85 */
86 struct ctlog_store_st {
87 STACK_OF(CTLOG) *logs;
88 };
89
90 /* The context when loading a CT log list from a CONF file. */
91 typedef struct ctlog_store_load_ctx_st {
92 CTLOG_STORE *log_store;
93 CONF *conf;
94 size_t invalid_log_entries;
95 } CTLOG_STORE_LOAD_CTX;
96
97 /*
98 * Creates an empty context for loading a CT log store.
99 * It should be populated before use.
100 */
101 static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void);
102
103 /*
104 * Deletes a CT log store load context.
105 * Does not delete any of the fields.
106 */
107 static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX *ctx);
108
109 static CTLOG_STORE_LOAD_CTX *
ctlog_store_load_ctx_new(void)110 ctlog_store_load_ctx_new(void)
111 {
112 CTLOG_STORE_LOAD_CTX *ctx = calloc(1, sizeof(*ctx));
113
114 if (ctx == NULL)
115 CTerror(ERR_R_MALLOC_FAILURE);
116
117 return ctx;
118 }
119
120 static void
ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX * ctx)121 ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX *ctx)
122 {
123 free(ctx);
124 }
125
126 /* Converts a log's public key into a SHA256 log ID */
127 static int
ct_v1_log_id_from_pkey(EVP_PKEY * pkey,unsigned char log_id[CT_V1_HASHLEN])128 ct_v1_log_id_from_pkey(EVP_PKEY *pkey, unsigned char log_id[CT_V1_HASHLEN])
129 {
130 int ret = 0;
131 unsigned char *pkey_der = NULL;
132 int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der);
133
134 if (pkey_der_len <= 0) {
135 CTerror(CT_R_LOG_KEY_INVALID);
136 goto err;
137 }
138
139 SHA256(pkey_der, pkey_der_len, log_id);
140 ret = 1;
141 err:
142 free(pkey_der);
143 return ret;
144 }
145
146 CTLOG_STORE *
CTLOG_STORE_new(void)147 CTLOG_STORE_new(void)
148 {
149 CTLOG_STORE *ret = calloc(1, sizeof(*ret));
150
151 if (ret == NULL) {
152 CTerror(ERR_R_MALLOC_FAILURE);
153 return NULL;
154 }
155
156 ret->logs = sk_CTLOG_new_null();
157 if (ret->logs == NULL)
158 goto err;
159
160 return ret;
161 err:
162 free(ret);
163 return NULL;
164 }
165
166 void
CTLOG_STORE_free(CTLOG_STORE * store)167 CTLOG_STORE_free(CTLOG_STORE *store)
168 {
169 if (store != NULL) {
170 sk_CTLOG_pop_free(store->logs, CTLOG_free);
171 free(store);
172 }
173 }
174
175 static int
ctlog_new_from_conf(CTLOG ** ct_log,const CONF * conf,const char * section)176 ctlog_new_from_conf(CTLOG **ct_log, const CONF *conf, const char *section)
177 {
178 const char *description = NCONF_get_string(conf, section,
179 "description");
180 char *pkey_base64;
181
182 if (description == NULL) {
183 CTerror(CT_R_LOG_CONF_MISSING_DESCRIPTION);
184 return 0;
185 }
186
187 pkey_base64 = NCONF_get_string(conf, section, "key");
188 if (pkey_base64 == NULL) {
189 CTerror(CT_R_LOG_CONF_MISSING_KEY);
190 return 0;
191 }
192
193 return CTLOG_new_from_base64(ct_log, pkey_base64, description);
194 }
195
196 int
CTLOG_STORE_load_default_file(CTLOG_STORE * store)197 CTLOG_STORE_load_default_file(CTLOG_STORE *store)
198 {
199 return CTLOG_STORE_load_file(store, CTLOG_FILE);
200 }
201
202 /*
203 * Called by CONF_parse_list, which stops if this returns <= 0,
204 * Otherwise, one bad log entry would stop loading of any of
205 * the following log entries.
206 * It may stop parsing and returns -1 on any internal (malloc) error.
207 */
208 static int
ctlog_store_load_log(const char * log_name,int log_name_len,void * arg)209 ctlog_store_load_log(const char *log_name, int log_name_len, void *arg)
210 {
211 CTLOG_STORE_LOAD_CTX *load_ctx = arg;
212 CTLOG *ct_log = NULL;
213 /* log_name may not be null-terminated, so fix that before using it */
214 char *tmp;
215 int ret = 0;
216
217 /* log_name will be NULL for empty list entries */
218 if (log_name == NULL)
219 return 1;
220
221 tmp = strndup(log_name, log_name_len);
222 if (tmp == NULL)
223 goto mem_err;
224
225 ret = ctlog_new_from_conf(&ct_log, load_ctx->conf, tmp);
226 free(tmp);
227
228 if (ret < 0) {
229 /* Propagate any internal error */
230 return ret;
231 }
232 if (ret == 0) {
233 /* If we can't load this log, record that fact and skip it */
234 ++load_ctx->invalid_log_entries;
235 return 1;
236 }
237
238 if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) {
239 goto mem_err;
240 }
241 return 1;
242
243 mem_err:
244 CTLOG_free(ct_log);
245 CTerror(ERR_R_MALLOC_FAILURE);
246 return -1;
247 }
248
249 int
CTLOG_STORE_load_file(CTLOG_STORE * store,const char * file)250 CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
251 {
252 int ret = 0;
253 char *enabled_logs;
254 CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
255
256 if (load_ctx == NULL)
257 return 0;
258 load_ctx->log_store = store;
259 load_ctx->conf = NCONF_new(NULL);
260 if (load_ctx->conf == NULL)
261 goto end;
262
263 if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
264 CTerror(CT_R_LOG_CONF_INVALID);
265 goto end;
266 }
267
268 enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
269 if (enabled_logs == NULL) {
270 CTerror(CT_R_LOG_CONF_INVALID);
271 goto end;
272 }
273
274 if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
275 load_ctx->invalid_log_entries > 0) {
276 CTerror(CT_R_LOG_CONF_INVALID);
277 goto end;
278 }
279
280 ret = 1;
281 end:
282 NCONF_free(load_ctx->conf);
283 ctlog_store_load_ctx_free(load_ctx);
284 return ret;
285 }
286
287 /*
288 * Initialize a new CTLOG object.
289 * Takes ownership of the public key.
290 * Copies the name.
291 */
292 CTLOG *
CTLOG_new(EVP_PKEY * public_key,const char * name)293 CTLOG_new(EVP_PKEY *public_key, const char *name)
294 {
295 CTLOG *ret = calloc(1, sizeof(*ret));
296
297 if (ret == NULL) {
298 CTerror(ERR_R_MALLOC_FAILURE);
299 return NULL;
300 }
301
302 ret->name = strdup(name);
303 if (ret->name == NULL) {
304 CTerror(ERR_R_MALLOC_FAILURE);
305 goto err;
306 }
307
308 if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1)
309 goto err;
310
311 ret->public_key = public_key;
312 return ret;
313 err:
314 CTLOG_free(ret);
315 return NULL;
316 }
317
318 /* Frees CT log and associated structures */
319 void
CTLOG_free(CTLOG * log)320 CTLOG_free(CTLOG *log)
321 {
322 if (log != NULL) {
323 free(log->name);
324 EVP_PKEY_free(log->public_key);
325 free(log);
326 }
327 }
328
329 const char *
CTLOG_get0_name(const CTLOG * log)330 CTLOG_get0_name(const CTLOG *log)
331 {
332 return log->name;
333 }
334
335 void
CTLOG_get0_log_id(const CTLOG * log,const uint8_t ** log_id,size_t * log_id_len)336 CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id, size_t *log_id_len)
337 {
338 *log_id = log->log_id;
339 *log_id_len = CT_V1_HASHLEN;
340 }
341
342 EVP_PKEY *
CTLOG_get0_public_key(const CTLOG * log)343 CTLOG_get0_public_key(const CTLOG *log)
344 {
345 return log->public_key;
346 }
347
348 /*
349 * Given a log ID, finds the matching log.
350 * Returns NULL if no match found.
351 */
352 const CTLOG *
CTLOG_STORE_get0_log_by_id(const CTLOG_STORE * store,const uint8_t * log_id,size_t log_id_len)353 CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store, const uint8_t *log_id,
354 size_t log_id_len)
355 {
356 int i;
357
358 for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
359 const CTLOG *log = sk_CTLOG_value(store->logs, i);
360 if (memcmp(log->log_id, log_id, log_id_len) == 0)
361 return log;
362 }
363
364 return NULL;
365 }
366