#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/conf.h>
#include <openssl/ct.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "conf_local.h"
#include "crypto_local.h"
#include "err_local.h"
struct ctlog_st {
char *name;
uint8_t log_id[CT_V1_HASHLEN];
EVP_PKEY *public_key;
};
struct ctlog_store_st {
STACK_OF(CTLOG) *logs;
};
typedef struct ctlog_store_load_ctx_st {
CTLOG_STORE *log_store;
CONF *conf;
size_t invalid_log_entries;
} CTLOG_STORE_LOAD_CTX;
static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void);
static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX *ctx);
static CTLOG_STORE_LOAD_CTX *
ctlog_store_load_ctx_new(void)
{
CTLOG_STORE_LOAD_CTX *ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL)
CTerror(ERR_R_MALLOC_FAILURE);
return ctx;
}
static void
ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX *ctx)
{
free(ctx);
}
static int
ct_v1_log_id_from_pkey(EVP_PKEY *pkey, unsigned char log_id[CT_V1_HASHLEN])
{
int ret = 0;
unsigned char *pkey_der = NULL;
int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der);
if (pkey_der_len <= 0) {
CTerror(CT_R_LOG_KEY_INVALID);
goto err;
}
SHA256(pkey_der, pkey_der_len, log_id);
ret = 1;
err:
free(pkey_der);
return ret;
}
CTLOG_STORE *
CTLOG_STORE_new(void)
{
CTLOG_STORE *ret = calloc(1, sizeof(*ret));
if (ret == NULL) {
CTerror(ERR_R_MALLOC_FAILURE);
return NULL;
}
ret->logs = sk_CTLOG_new_null();
if (ret->logs == NULL)
goto err;
return ret;
err:
free(ret);
return NULL;
}
LCRYPTO_ALIAS(CTLOG_STORE_new);
void
CTLOG_STORE_free(CTLOG_STORE *store)
{
if (store != NULL) {
sk_CTLOG_pop_free(store->logs, CTLOG_free);
free(store);
}
}
LCRYPTO_ALIAS(CTLOG_STORE_free);
static int
ctlog_new_from_conf(CTLOG **ct_log, const CONF *conf, const char *section)
{
const char *description = NCONF_get_string(conf, section,
"description");
char *pkey_base64;
if (description == NULL) {
CTerror(CT_R_LOG_CONF_MISSING_DESCRIPTION);
return 0;
}
pkey_base64 = NCONF_get_string(conf, section, "key");
if (pkey_base64 == NULL) {
CTerror(CT_R_LOG_CONF_MISSING_KEY);
return 0;
}
return CTLOG_new_from_base64(ct_log, pkey_base64, description);
}
int
CTLOG_STORE_load_default_file(CTLOG_STORE *store)
{
return CTLOG_STORE_load_file(store, CTLOG_FILE);
}
LCRYPTO_ALIAS(CTLOG_STORE_load_default_file);
static int
ctlog_store_load_log(const char *log_name, int log_name_len, void *arg)
{
CTLOG_STORE_LOAD_CTX *load_ctx = arg;
CTLOG *ct_log = NULL;
char *tmp;
int ret = 0;
if (log_name == NULL)
return 1;
tmp = strndup(log_name, log_name_len);
if (tmp == NULL)
goto mem_err;
ret = ctlog_new_from_conf(&ct_log, load_ctx->conf, tmp);
free(tmp);
if (ret < 0) {
return ret;
}
if (ret == 0) {
++load_ctx->invalid_log_entries;
return 1;
}
if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) {
goto mem_err;
}
return 1;
mem_err:
CTLOG_free(ct_log);
CTerror(ERR_R_MALLOC_FAILURE);
return -1;
}
int
CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
{
int ret = 0;
char *enabled_logs;
CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
if (load_ctx == NULL)
return 0;
load_ctx->log_store = store;
load_ctx->conf = NCONF_new(NULL);
if (load_ctx->conf == NULL)
goto end;
if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
CTerror(CT_R_LOG_CONF_INVALID);
goto end;
}
enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
if (enabled_logs == NULL) {
CTerror(CT_R_LOG_CONF_INVALID);
goto end;
}
if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
load_ctx->invalid_log_entries > 0) {
CTerror(CT_R_LOG_CONF_INVALID);
goto end;
}
ret = 1;
end:
NCONF_free(load_ctx->conf);
ctlog_store_load_ctx_free(load_ctx);
return ret;
}
LCRYPTO_ALIAS(CTLOG_STORE_load_file);
CTLOG *
CTLOG_new(EVP_PKEY *public_key, const char *name)
{
CTLOG *ret = calloc(1, sizeof(*ret));
if (ret == NULL) {
CTerror(ERR_R_MALLOC_FAILURE);
return NULL;
}
ret->name = strdup(name);
if (ret->name == NULL) {
CTerror(ERR_R_MALLOC_FAILURE);
goto err;
}
if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1)
goto err;
ret->public_key = public_key;
return ret;
err:
CTLOG_free(ret);
return NULL;
}
LCRYPTO_ALIAS(CTLOG_new);
void
CTLOG_free(CTLOG *log)
{
if (log != NULL) {
free(log->name);
EVP_PKEY_free(log->public_key);
free(log);
}
}
LCRYPTO_ALIAS(CTLOG_free);
const char *
CTLOG_get0_name(const CTLOG *log)
{
return log->name;
}
LCRYPTO_ALIAS(CTLOG_get0_name);
void
CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id, size_t *log_id_len)
{
*log_id = log->log_id;
*log_id_len = CT_V1_HASHLEN;
}
LCRYPTO_ALIAS(CTLOG_get0_log_id);
EVP_PKEY *
CTLOG_get0_public_key(const CTLOG *log)
{
return log->public_key;
}
LCRYPTO_ALIAS(CTLOG_get0_public_key);
const CTLOG *
CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store, const uint8_t *log_id,
size_t log_id_len)
{
int i;
for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
const CTLOG *log = sk_CTLOG_value(store->logs, i);
if (memcmp(log->log_id, log_id, log_id_len) == 0)
return log;
}
return NULL;
}
LCRYPTO_ALIAS(CTLOG_STORE_get0_log_by_id);