Coverage Report

Created: 2025-06-22 06:56

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