xref: /openbsd/lib/libcrypto/ct/ct_log.c (revision 4bdff4be)
1 /*	$OpenBSD: ct_log.c,v 1.6 2023/07/08 07:22:58 beck 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 *
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
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
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 *
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 LCRYPTO_ALIAS(CTLOG_STORE_new);
166 
167 void
168 CTLOG_STORE_free(CTLOG_STORE *store)
169 {
170 	if (store != NULL) {
171 		sk_CTLOG_pop_free(store->logs, CTLOG_free);
172 		free(store);
173 	}
174 }
175 LCRYPTO_ALIAS(CTLOG_STORE_free);
176 
177 static int
178 ctlog_new_from_conf(CTLOG **ct_log, const CONF *conf, const char *section)
179 {
180 	const char *description = NCONF_get_string(conf, section,
181 	    "description");
182 	char *pkey_base64;
183 
184 	if (description == NULL) {
185 		CTerror(CT_R_LOG_CONF_MISSING_DESCRIPTION);
186 		return 0;
187 	}
188 
189 	pkey_base64 = NCONF_get_string(conf, section, "key");
190 	if (pkey_base64 == NULL) {
191 		CTerror(CT_R_LOG_CONF_MISSING_KEY);
192 		return 0;
193 	}
194 
195 	return CTLOG_new_from_base64(ct_log, pkey_base64, description);
196 }
197 
198 int
199 CTLOG_STORE_load_default_file(CTLOG_STORE *store)
200 {
201 	return CTLOG_STORE_load_file(store, CTLOG_FILE);
202 }
203 LCRYPTO_ALIAS(CTLOG_STORE_load_default_file);
204 
205 /*
206  * Called by CONF_parse_list, which stops if this returns <= 0,
207  * Otherwise, one bad log entry would stop loading of any of
208  * the following log entries.
209  * It may stop parsing and returns -1 on any internal (malloc) error.
210  */
211 static int
212 ctlog_store_load_log(const char *log_name, int log_name_len, void *arg)
213 {
214 	CTLOG_STORE_LOAD_CTX *load_ctx = arg;
215 	CTLOG *ct_log = NULL;
216 	/* log_name may not be null-terminated, so fix that before using it */
217 	char *tmp;
218 	int ret = 0;
219 
220 	/* log_name will be NULL for empty list entries */
221 	if (log_name == NULL)
222 		return 1;
223 
224 	tmp = strndup(log_name, log_name_len);
225 	if (tmp == NULL)
226 		goto mem_err;
227 
228 	ret = ctlog_new_from_conf(&ct_log, load_ctx->conf, tmp);
229 	free(tmp);
230 
231 	if (ret < 0) {
232 		/* Propagate any internal error */
233 		return ret;
234 	}
235 	if (ret == 0) {
236 		/* If we can't load this log, record that fact and skip it */
237 		++load_ctx->invalid_log_entries;
238 		return 1;
239 	}
240 
241 	if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) {
242 		goto mem_err;
243 	}
244 	return 1;
245 
246  mem_err:
247 	CTLOG_free(ct_log);
248 	CTerror(ERR_R_MALLOC_FAILURE);
249 	return -1;
250 }
251 
252 int
253 CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
254 {
255 	int ret = 0;
256 	char *enabled_logs;
257 	CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
258 
259 	if (load_ctx == NULL)
260 		return 0;
261 	load_ctx->log_store = store;
262 	load_ctx->conf = NCONF_new(NULL);
263 	if (load_ctx->conf == NULL)
264 		goto end;
265 
266 	if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
267 		CTerror(CT_R_LOG_CONF_INVALID);
268 		goto end;
269 	}
270 
271 	enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
272 	if (enabled_logs == NULL) {
273 		CTerror(CT_R_LOG_CONF_INVALID);
274 		goto end;
275 	}
276 
277 	if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
278 	    load_ctx->invalid_log_entries > 0) {
279 		CTerror(CT_R_LOG_CONF_INVALID);
280 		goto end;
281 	}
282 
283 	ret = 1;
284  end:
285 	NCONF_free(load_ctx->conf);
286 	ctlog_store_load_ctx_free(load_ctx);
287 	return ret;
288 }
289 LCRYPTO_ALIAS(CTLOG_STORE_load_file);
290 
291 /*
292  * Initialize a new CTLOG object.
293  * Takes ownership of the public key.
294  * Copies the name.
295  */
296 CTLOG *
297 CTLOG_new(EVP_PKEY *public_key, const char *name)
298 {
299 	CTLOG *ret = calloc(1, sizeof(*ret));
300 
301 	if (ret == NULL) {
302 		CTerror(ERR_R_MALLOC_FAILURE);
303 		return NULL;
304 	}
305 
306 	ret->name = strdup(name);
307 	if (ret->name == NULL) {
308 		CTerror(ERR_R_MALLOC_FAILURE);
309 		goto err;
310 	}
311 
312 	if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1)
313 		goto err;
314 
315 	ret->public_key = public_key;
316 	return ret;
317  err:
318 	CTLOG_free(ret);
319 	return NULL;
320 }
321 LCRYPTO_ALIAS(CTLOG_new);
322 
323 /* Frees CT log and associated structures */
324 void
325 CTLOG_free(CTLOG *log)
326 {
327 	if (log != NULL) {
328 		free(log->name);
329 		EVP_PKEY_free(log->public_key);
330 		free(log);
331 	}
332 }
333 LCRYPTO_ALIAS(CTLOG_free);
334 
335 const char *
336 CTLOG_get0_name(const CTLOG *log)
337 {
338 	return log->name;
339 }
340 LCRYPTO_ALIAS(CTLOG_get0_name);
341 
342 void
343 CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id, size_t *log_id_len)
344 {
345 	*log_id = log->log_id;
346 	*log_id_len = CT_V1_HASHLEN;
347 }
348 LCRYPTO_ALIAS(CTLOG_get0_log_id);
349 
350 EVP_PKEY *
351 CTLOG_get0_public_key(const CTLOG *log)
352 {
353 	return log->public_key;
354 }
355 LCRYPTO_ALIAS(CTLOG_get0_public_key);
356 
357 /*
358  * Given a log ID, finds the matching log.
359  * Returns NULL if no match found.
360  */
361 const CTLOG *
362 CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store, const uint8_t *log_id,
363     size_t log_id_len)
364 {
365 	int i;
366 
367 	for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
368 		const CTLOG *log = sk_CTLOG_value(store->logs, i);
369 		if (memcmp(log->log_id, log_id, log_id_len) == 0)
370 			return log;
371 	}
372 
373 	return NULL;
374 }
375 LCRYPTO_ALIAS(CTLOG_STORE_get0_log_by_id);
376