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