ct_log.c 7.1 KB
Newer Older
R
Rich Salz 已提交
1 2
/*
 * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
R
Rob Percival 已提交
3
 *
R
Rich Salz 已提交
4 5 6 7
 * Licensed under the OpenSSL license (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
R
Rob Percival 已提交
8 9
 */

10 11 12
#include <stdlib.h>
#include <string.h>

R
Rob Percival 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include <openssl/conf.h>
#include <openssl/ct.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/safestack.h>

#include "internal/cryptlib.h"

/*
 * Information about a CT log server.
 */
struct ctlog_st {
    char *name;
    uint8_t log_id[CT_V1_HASHLEN];
    EVP_PKEY *public_key;
};

/*
 * A store for multiple CTLOG instances.
 * It takes ownership of any CTLOG instances added to it.
 */
struct ctlog_store_st {
    STACK_OF(CTLOG) *logs;
};

/* The context when loading a CT log list from a CONF file. */
typedef struct ctlog_store_load_ctx_st {
    CTLOG_STORE *log_store;
    CONF *conf;
42
    size_t invalid_log_entries;
R
Rob Percival 已提交
43 44 45 46 47 48
} CTLOG_STORE_LOAD_CTX;

/*
 * Creates an empty context for loading a CT log store.
 * It should be populated before use.
 */
49
static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new();
R
Rob Percival 已提交
50 51 52 53 54

/*
 * Deletes a CT log store load context.
 * Does not delete any of the fields.
 */
55
static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx);
R
Rob Percival 已提交
56

57
static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new()
R
Rob Percival 已提交
58
{
59 60
    CTLOG_STORE_LOAD_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));

R
Rob Percival 已提交
61 62 63 64 65 66 67
    if (ctx == NULL) {
        CTerr(CT_F_CTLOG_STORE_LOAD_CTX_NEW, ERR_R_MALLOC_FAILURE);
        goto err;
    }

    return ctx;
err:
68
    ctlog_store_load_ctx_free(ctx);
R
Rob Percival 已提交
69 70 71
    return NULL;
}

72
static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx)
R
Rob Percival 已提交
73 74 75 76 77
{
    OPENSSL_free(ctx);
}

/* Converts a log's public key into a SHA256 log ID */
78
static int ct_v1_log_id_from_pkey(EVP_PKEY *pkey,
R
Rob Percival 已提交
79 80 81 82 83
                                  unsigned char log_id[CT_V1_HASHLEN])
{
    int ret = 0;
    unsigned char *pkey_der = NULL;
    int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der);
84

R
Rob Percival 已提交
85 86 87 88
    if (pkey_der_len <= 0) {
        CTerr(CT_F_CT_V1_LOG_ID_FROM_PKEY, CT_R_LOG_KEY_INVALID);
        goto err;
    }
89

R
Rob Percival 已提交
90 91 92 93 94 95 96 97 98
    SHA256(pkey_der, pkey_der_len, log_id);
    ret = 1;
err:
    OPENSSL_free(pkey_der);
    return ret;
}

CTLOG_STORE *CTLOG_STORE_new(void)
{
99 100
    CTLOG_STORE *ret = OPENSSL_zalloc(sizeof(*ret));

R
Rob Percival 已提交
101 102
    if (ret == NULL)
        goto err;
103

R
Rob Percival 已提交
104 105 106
    ret->logs = sk_CTLOG_new_null();
    if (ret->logs == NULL)
        goto err;
107

R
Rob Percival 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121
    return ret;
err:
    CTLOG_STORE_free(ret);
    return NULL;
}

void CTLOG_STORE_free(CTLOG_STORE *store)
{
    if (store != NULL) {
        sk_CTLOG_pop_free(store->logs, CTLOG_free);
        OPENSSL_free(store);
    }
}

122
static CTLOG *ctlog_new_from_conf(const CONF *conf, const char *section)
R
Rob Percival 已提交
123 124
{
    CTLOG *ret = NULL;
125
    char *description = NCONF_get_string(conf, section, "description");
R
Rob Percival 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    char *pkey_base64;

    if (description == NULL) {
        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_DESCRIPTION);
        goto end;
    }

    pkey_base64 = NCONF_get_string(conf, section, "key");
    if (pkey_base64 == NULL) {
        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_KEY);
        goto end;
    }

    ret = CTLOG_new_from_base64(pkey_base64, description);
    if (ret == NULL) {
        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_INVALID);
        goto end;
    }

end:
    return ret;
}

int CTLOG_STORE_load_default_file(CTLOG_STORE *store)
{
151 152
    const char *fpath = getenv(CTLOG_FILE_EVP);

R
Rob Percival 已提交
153 154
    if (fpath == NULL)
      fpath = CTLOG_FILE;
155

R
Rob Percival 已提交
156 157 158
    return CTLOG_STORE_load_file(store, fpath);
}

159 160 161 162 163 164 165
/*
 * Called by CONF_parse_list, which stops if this returns <= 0, so don't unless
 * something very bad happens. Otherwise, one bad log entry would stop loading
 * of any of the following log entries.
 */
static int ctlog_store_load_log(const char *log_name, int log_name_len,
                                void *arg)
R
Rob Percival 已提交
166 167 168 169
{
    CTLOG_STORE_LOAD_CTX *load_ctx = arg;
    CTLOG *ct_log;
    /* log_name may not be null-terminated, so fix that before using it */
170
    char *tmp;
171

172 173 174 175 176
    /* log_name will be NULL for empty list entries */
    if (log_name == NULL)
        return 1;

    tmp = OPENSSL_strndup(log_name, log_name_len);
177
    ct_log = ctlog_new_from_conf(load_ctx->conf, tmp);
R
Rob Percival 已提交
178
    OPENSSL_free(tmp);
179 180 181 182 183
    if (ct_log == NULL) {
        /* If we can't load this log, record that fact and skip it */
        ++load_ctx->invalid_log_entries;
        return 1;
    }
R
Rob Percival 已提交
184 185 186 187 188 189 190

    sk_CTLOG_push(load_ctx->log_store->logs, ct_log);
    return 1;
}

int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
{
191
    int ret = 0;
R
Rob Percival 已提交
192
    char *enabled_logs;
193 194
    CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();

R
Rob Percival 已提交
195 196 197 198 199
    load_ctx->log_store = store;
    load_ctx->conf = NCONF_new(NULL);
    if (load_ctx->conf == NULL)
        goto end;

200
    if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
R
Rob Percival 已提交
201 202 203 204 205
        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
        goto end;
    }

    enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
206 207 208 209 210
    if (enabled_logs == NULL) {
        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
        goto end;
    }

211 212
    if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
        load_ctx->invalid_log_entries > 0) {
213 214 215
        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
        goto end;
    }
R
Rob Percival 已提交
216

217
    ret = 1;
R
Rob Percival 已提交
218 219
end:
    NCONF_free(load_ctx->conf);
220
    ctlog_store_load_ctx_free(load_ctx);
R
Rob Percival 已提交
221 222 223 224 225 226 227 228 229 230
    return ret;
}

/*
 * Initialize a new CTLOG object.
 * Takes ownership of the public key.
 * Copies the name.
 */
CTLOG *CTLOG_new(EVP_PKEY *public_key, const char *name)
{
231 232
    CTLOG *ret = CTLOG_new_null();

R
Rob Percival 已提交
233 234
    if (ret == NULL)
        goto err;
235

R
Rob Percival 已提交
236 237 238
    ret->name = OPENSSL_strdup(name);
    if (ret->name == NULL)
        goto err;
239

R
Rob Percival 已提交
240
    ret->public_key = public_key;
241
    if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1)
R
Rob Percival 已提交
242
        goto err;
243

R
Rob Percival 已提交
244 245 246 247 248 249 250 251
    return ret;
err:
    CTLOG_free(ret);
    return NULL;
}

CTLOG *CTLOG_new_null(void)
{
252 253
    CTLOG *ret = OPENSSL_zalloc(sizeof(*ret));

R
Rob Percival 已提交
254 255
    if (ret == NULL)
        CTerr(CT_F_CTLOG_NEW_NULL, ERR_R_MALLOC_FAILURE);
256

R
Rob Percival 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269
    return ret;
}

/* Frees CT log and associated structures */
void CTLOG_free(CTLOG *log)
{
    if (log != NULL) {
        OPENSSL_free(log->name);
        EVP_PKEY_free(log->public_key);
        OPENSSL_free(log);
    }
}

270
const char *CTLOG_get0_name(const CTLOG *log)
R
Rob Percival 已提交
271 272 273 274
{
    return log->name;
}

275 276
void CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id,
                       size_t *log_id_len)
R
Rob Percival 已提交
277 278 279 280 281
{
    *log_id = log->log_id;
    *log_id_len = CT_V1_HASHLEN;
}

282
EVP_PKEY *CTLOG_get0_public_key(const CTLOG *log)
R
Rob Percival 已提交
283 284 285 286 287 288 289 290
{
    return log->public_key;
}

/*
 * Given a log ID, finds the matching log.
 * Returns NULL if no match found.
 */
291 292 293
const CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store,
                                        const uint8_t *log_id,
                                        size_t log_id_len)
R
Rob Percival 已提交
294 295
{
    int i;
296

R
Rob Percival 已提交
297
    for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
298
        const CTLOG *log = sk_CTLOG_value(store->logs, i);
R
Rob Percival 已提交
299 300 301
        if (memcmp(log->log_id, log_id, log_id_len) == 0)
            return log;
    }
302

R
Rob Percival 已提交
303 304
    return NULL;
}