xref: /openssl/crypto/ct/ct_log.c (revision e077455e)
1 /*
2  * Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <openssl/conf.h>
14 #include <openssl/ct.h>
15 #include <openssl/err.h>
16 #include <openssl/evp.h>
17 #include <openssl/safestack.h>
18 
19 #include "internal/cryptlib.h"
20 
21 /*
22  * Information about a CT log server.
23  */
24 struct ctlog_st {
25     OSSL_LIB_CTX *libctx;
26     char *propq;
27     char *name;
28     uint8_t log_id[CT_V1_HASHLEN];
29     EVP_PKEY *public_key;
30 };
31 
32 /*
33  * A store for multiple CTLOG instances.
34  * It takes ownership of any CTLOG instances added to it.
35  */
36 struct ctlog_store_st {
37     OSSL_LIB_CTX *libctx;
38     char *propq;
39     STACK_OF(CTLOG) *logs;
40 };
41 
42 /* The context when loading a CT log list from a CONF file. */
43 typedef struct ctlog_store_load_ctx_st {
44     CTLOG_STORE *log_store;
45     CONF *conf;
46     size_t invalid_log_entries;
47 } CTLOG_STORE_LOAD_CTX;
48 
49 /*
50  * Creates an empty context for loading a CT log store.
51  * It should be populated before use.
52  */
53 static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void);
54 
55 /*
56  * Deletes a CT log store load context.
57  * Does not delete any of the fields.
58  */
59 static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx);
60 
ctlog_store_load_ctx_new(void)61 static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void)
62 {
63     CTLOG_STORE_LOAD_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
64 
65     return ctx;
66 }
67 
ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX * ctx)68 static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx)
69 {
70     OPENSSL_free(ctx);
71 }
72 
73 /* Converts a log's public key into a SHA256 log ID */
ct_v1_log_id_from_pkey(CTLOG * log,EVP_PKEY * pkey)74 static int ct_v1_log_id_from_pkey(CTLOG *log, EVP_PKEY *pkey)
75 {
76     int ret = 0;
77     unsigned char *pkey_der = NULL;
78     int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der);
79     unsigned int len;
80     EVP_MD *sha256 = NULL;
81 
82     if (pkey_der_len <= 0) {
83         ERR_raise(ERR_LIB_CT, CT_R_LOG_KEY_INVALID);
84         goto err;
85     }
86     sha256 = EVP_MD_fetch(log->libctx, "SHA2-256", log->propq);
87     if (sha256 == NULL) {
88         ERR_raise(ERR_LIB_CT, ERR_R_EVP_LIB);
89         goto err;
90     }
91 
92     ret = EVP_Digest(pkey_der, pkey_der_len, log->log_id, &len, sha256,
93                      NULL);
94 err:
95     EVP_MD_free(sha256);
96     OPENSSL_free(pkey_der);
97     return ret;
98 }
99 
CTLOG_STORE_new_ex(OSSL_LIB_CTX * libctx,const char * propq)100 CTLOG_STORE *CTLOG_STORE_new_ex(OSSL_LIB_CTX *libctx, const char *propq)
101 {
102     CTLOG_STORE *ret = OPENSSL_zalloc(sizeof(*ret));
103 
104     if (ret == NULL)
105         return NULL;
106 
107     ret->libctx = libctx;
108     if (propq != NULL) {
109         ret->propq = OPENSSL_strdup(propq);
110         if (ret->propq == NULL)
111             goto err;
112     }
113 
114     ret->logs = sk_CTLOG_new_null();
115     if (ret->logs == NULL) {
116         ERR_raise(ERR_LIB_CT, ERR_R_CRYPTO_LIB);
117         goto err;
118     }
119 
120     return ret;
121 err:
122     CTLOG_STORE_free(ret);
123     return NULL;
124 }
125 
CTLOG_STORE_new(void)126 CTLOG_STORE *CTLOG_STORE_new(void)
127 {
128     return CTLOG_STORE_new_ex(NULL, NULL);
129 }
130 
CTLOG_STORE_free(CTLOG_STORE * store)131 void CTLOG_STORE_free(CTLOG_STORE *store)
132 {
133     if (store != NULL) {
134         OPENSSL_free(store->propq);
135         sk_CTLOG_pop_free(store->logs, CTLOG_free);
136         OPENSSL_free(store);
137     }
138 }
139 
ctlog_new_from_conf(CTLOG_STORE * store,CTLOG ** ct_log,const CONF * conf,const char * section)140 static int ctlog_new_from_conf(CTLOG_STORE *store, CTLOG **ct_log,
141                                const CONF *conf, const char *section)
142 {
143     const char *description = NCONF_get_string(conf, section, "description");
144     char *pkey_base64;
145 
146     if (description == NULL) {
147         ERR_raise(ERR_LIB_CT, CT_R_LOG_CONF_MISSING_DESCRIPTION);
148         return 0;
149     }
150 
151     pkey_base64 = NCONF_get_string(conf, section, "key");
152     if (pkey_base64 == NULL) {
153         ERR_raise(ERR_LIB_CT, CT_R_LOG_CONF_MISSING_KEY);
154         return 0;
155     }
156 
157     return CTLOG_new_from_base64_ex(ct_log, pkey_base64, description,
158                                     store->libctx, store->propq);
159 }
160 
CTLOG_STORE_load_default_file(CTLOG_STORE * store)161 int CTLOG_STORE_load_default_file(CTLOG_STORE *store)
162 {
163     const char *fpath = ossl_safe_getenv(CTLOG_FILE_EVP);
164 
165     if (fpath == NULL)
166       fpath = CTLOG_FILE;
167 
168     return CTLOG_STORE_load_file(store, fpath);
169 }
170 
171 /*
172  * Called by CONF_parse_list, which stops if this returns <= 0,
173  * Otherwise, one bad log entry would stop loading of any of
174  * the following log entries.
175  * It may stop parsing and returns -1 on any internal (malloc) error.
176  */
ctlog_store_load_log(const char * log_name,int log_name_len,void * arg)177 static int ctlog_store_load_log(const char *log_name, int log_name_len,
178                                 void *arg)
179 {
180     CTLOG_STORE_LOAD_CTX *load_ctx = arg;
181     CTLOG *ct_log = NULL;
182     /* log_name may not be null-terminated, so fix that before using it */
183     char *tmp;
184     int ret = 0;
185 
186     /* log_name will be NULL for empty list entries */
187     if (log_name == NULL)
188         return 1;
189 
190     tmp = OPENSSL_strndup(log_name, log_name_len);
191     if (tmp == NULL)
192         return -1;
193 
194     ret = ctlog_new_from_conf(load_ctx->log_store, &ct_log, load_ctx->conf, tmp);
195     OPENSSL_free(tmp);
196 
197     if (ret < 0) {
198         /* Propagate any internal error */
199         return ret;
200     }
201     if (ret == 0) {
202         /* If we can't load this log, record that fact and skip it */
203         ++load_ctx->invalid_log_entries;
204         return 1;
205     }
206 
207     if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) {
208         CTLOG_free(ct_log);
209         ERR_raise(ERR_LIB_CT, ERR_R_CRYPTO_LIB);
210         return -1;
211     }
212     return 1;
213 }
214 
CTLOG_STORE_load_file(CTLOG_STORE * store,const char * file)215 int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
216 {
217     int ret = 0;
218     char *enabled_logs;
219     CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
220 
221     if (load_ctx == NULL)
222         return 0;
223     load_ctx->log_store = store;
224     load_ctx->conf = NCONF_new(NULL);
225     if (load_ctx->conf == NULL)
226         goto end;
227 
228     if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
229         ERR_raise(ERR_LIB_CT, CT_R_LOG_CONF_INVALID);
230         goto end;
231     }
232 
233     enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
234     if (enabled_logs == NULL) {
235         ERR_raise(ERR_LIB_CT, CT_R_LOG_CONF_INVALID);
236         goto end;
237     }
238 
239     if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
240         load_ctx->invalid_log_entries > 0) {
241         ERR_raise(ERR_LIB_CT, CT_R_LOG_CONF_INVALID);
242         goto end;
243     }
244 
245     ret = 1;
246 end:
247     NCONF_free(load_ctx->conf);
248     ctlog_store_load_ctx_free(load_ctx);
249     return ret;
250 }
251 
252 /*
253  * Initialize a new CTLOG object.
254  * Takes ownership of the public key.
255  * Copies the name.
256  */
CTLOG_new_ex(EVP_PKEY * public_key,const char * name,OSSL_LIB_CTX * libctx,const char * propq)257 CTLOG *CTLOG_new_ex(EVP_PKEY *public_key, const char *name, OSSL_LIB_CTX *libctx,
258                     const char *propq)
259 {
260     CTLOG *ret = OPENSSL_zalloc(sizeof(*ret));
261 
262     if (ret == NULL)
263         return NULL;
264 
265     ret->libctx = libctx;
266     if (propq != NULL) {
267         ret->propq = OPENSSL_strdup(propq);
268         if (ret->propq == NULL)
269             goto err;
270     }
271 
272     ret->name = OPENSSL_strdup(name);
273     if (ret->name == NULL)
274         goto err;
275 
276     if (ct_v1_log_id_from_pkey(ret, public_key) != 1)
277         goto err;
278 
279     ret->public_key = public_key;
280     return ret;
281 err:
282     CTLOG_free(ret);
283     return NULL;
284 }
285 
CTLOG_new(EVP_PKEY * public_key,const char * name)286 CTLOG *CTLOG_new(EVP_PKEY *public_key, const char *name)
287 {
288     return CTLOG_new_ex(public_key, name, NULL, NULL);
289 }
290 
291 /* Frees CT log and associated structures */
CTLOG_free(CTLOG * log)292 void CTLOG_free(CTLOG *log)
293 {
294     if (log != NULL) {
295         OPENSSL_free(log->name);
296         EVP_PKEY_free(log->public_key);
297         OPENSSL_free(log->propq);
298         OPENSSL_free(log);
299     }
300 }
301 
CTLOG_get0_name(const CTLOG * log)302 const char *CTLOG_get0_name(const CTLOG *log)
303 {
304     return log->name;
305 }
306 
CTLOG_get0_log_id(const CTLOG * log,const uint8_t ** log_id,size_t * log_id_len)307 void CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id,
308                        size_t *log_id_len)
309 {
310     *log_id = log->log_id;
311     *log_id_len = CT_V1_HASHLEN;
312 }
313 
CTLOG_get0_public_key(const CTLOG * log)314 EVP_PKEY *CTLOG_get0_public_key(const CTLOG *log)
315 {
316     return log->public_key;
317 }
318 
319 /*
320  * Given a log ID, finds the matching log.
321  * Returns NULL if no match found.
322  */
CTLOG_STORE_get0_log_by_id(const CTLOG_STORE * store,const uint8_t * log_id,size_t log_id_len)323 const CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store,
324                                         const uint8_t *log_id,
325                                         size_t log_id_len)
326 {
327     int i;
328 
329     for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
330         const CTLOG *log = sk_CTLOG_value(store->logs, i);
331         if (memcmp(log->log_id, log_id, log_id_len) == 0)
332             return log;
333     }
334 
335     return NULL;
336 }
337