hx509: preserve received certificate encodings
Keep the original DER encoding around when decoding certificates so hx509_cert_binary() does not rewrite certificates unnecessarily. Add regression coverage for direct certificate binary output and keyless stores.
This commit is contained in:
committed by
Nico Williams
parent
d67a2c36c4
commit
5ac91f2dc9
@@ -169,12 +169,15 @@ fuzz_jose_SOURCES = fuzz_jose.c
|
||||
fuzz_jose_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/base/libheimbase.la
|
||||
fuzz_jose.$(OBJEXT): $(HX509_PROTOS) $(nodist_include_HEADERS)
|
||||
test_expr_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
|
||||
test_cert_binary_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)\"
|
||||
test_cert_binary_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
|
||||
|
||||
TESTS = $(SCRIPT_TESTS) $(PROGRAM_TESTS)
|
||||
|
||||
PROGRAM_TESTS = \
|
||||
test_name \
|
||||
test_expr
|
||||
test_expr \
|
||||
test_cert_binary
|
||||
|
||||
SCRIPT_TESTS = \
|
||||
test_ca \
|
||||
|
||||
+38
-1
@@ -78,6 +78,7 @@ struct hx509_cert_data {
|
||||
unsigned int ref;
|
||||
char *friendlyname;
|
||||
Certificate *data;
|
||||
heim_octet_string encoding;
|
||||
hx509_private_key private_key;
|
||||
struct _hx509_cert_attrs attrs;
|
||||
hx509_name basename;
|
||||
@@ -635,6 +636,8 @@ cert_init(hx509_context context, heim_error_t *error)
|
||||
cert->release = NULL;
|
||||
cert->ctx = NULL;
|
||||
cert->data= NULL;
|
||||
cert->encoding.data = NULL;
|
||||
cert->encoding.length = 0;
|
||||
return cert;
|
||||
}
|
||||
|
||||
@@ -693,7 +696,23 @@ hx509_cert_copy_no_private_key(hx509_context context,
|
||||
hx509_cert src,
|
||||
heim_error_t *error)
|
||||
{
|
||||
return hx509_cert_init(context, src->data, error);
|
||||
hx509_cert cert;
|
||||
int ret;
|
||||
|
||||
cert = hx509_cert_init(context, src->data, error);
|
||||
if (cert == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = der_copy_octet_string(&src->encoding, &cert->encoding);
|
||||
if (ret) {
|
||||
hx509_cert_free(cert);
|
||||
if (error)
|
||||
*error = heim_error_create_enomem();
|
||||
errno = ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -747,6 +766,7 @@ hx509_cert_init_data(hx509_context context,
|
||||
heim_error_t *error)
|
||||
{
|
||||
hx509_cert cert;
|
||||
heim_octet_string os;
|
||||
Certificate t;
|
||||
size_t size;
|
||||
int ret;
|
||||
@@ -769,6 +789,19 @@ hx509_cert_init_data(hx509_context context,
|
||||
|
||||
cert = hx509_cert_init(context, &t, error);
|
||||
free_Certificate(&t);
|
||||
if (cert == NULL)
|
||||
return NULL;
|
||||
|
||||
os.length = len;
|
||||
os.data = rk_UNCONST(ptr);
|
||||
ret = der_copy_octet_string(&os, &cert->encoding);
|
||||
if (ret) {
|
||||
hx509_cert_free(cert);
|
||||
if (error)
|
||||
*error = heim_error_create_enomem();
|
||||
errno = ret;
|
||||
return NULL;
|
||||
}
|
||||
return cert;
|
||||
}
|
||||
|
||||
@@ -824,6 +857,7 @@ hx509_cert_free(hx509_cert cert)
|
||||
if (cert->data)
|
||||
free_Certificate(cert->data);
|
||||
free(cert->data);
|
||||
der_free_octet_string(&cert->encoding);
|
||||
|
||||
for (i = 0; i < cert->attrs.len; i++) {
|
||||
der_free_octet_string(&cert->attrs.val[i]->data);
|
||||
@@ -4057,6 +4091,9 @@ hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os)
|
||||
os->data = NULL;
|
||||
os->length = 0;
|
||||
|
||||
if (c->encoding.data != NULL)
|
||||
return der_copy_octet_string(&c->encoding, os);
|
||||
|
||||
ASN1_MALLOC_ENCODE(Certificate, os->data, os->length,
|
||||
_hx509_get_cert(c), &size, ret);
|
||||
if (ret) {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "hx_locl.h"
|
||||
#include <err.h>
|
||||
|
||||
#ifndef SRCDIR
|
||||
#define SRCDIR "."
|
||||
#endif
|
||||
|
||||
struct pem_cert {
|
||||
heim_octet_string der;
|
||||
int found;
|
||||
};
|
||||
|
||||
static int
|
||||
pem_cert_reader(hx509_context context, const char *type,
|
||||
const hx509_pem_header *headers,
|
||||
const void *data, size_t length, void *ctx)
|
||||
{
|
||||
struct pem_cert *pem = ctx;
|
||||
|
||||
(void)context;
|
||||
(void)headers;
|
||||
|
||||
if (strcmp(type, "CERTIFICATE") != 0)
|
||||
return 0;
|
||||
|
||||
if (pem->found)
|
||||
return 0;
|
||||
|
||||
pem->der.data = malloc(length);
|
||||
if (pem->der.data == NULL)
|
||||
return ENOMEM;
|
||||
memcpy(pem->der.data, data, length);
|
||||
pem->der.length = length;
|
||||
pem->found = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
read_cert_der(hx509_context context, const char *path, heim_octet_string *der)
|
||||
{
|
||||
struct pem_cert pem;
|
||||
FILE *f;
|
||||
int ret;
|
||||
|
||||
memset(&pem, 0, sizeof(pem));
|
||||
|
||||
f = fopen(path, "r");
|
||||
if (f == NULL)
|
||||
err(1, "fopen: %s", path);
|
||||
|
||||
ret = hx509_pem_read(context, f, pem_cert_reader, &pem);
|
||||
fclose(f);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_pem_read: %s", path);
|
||||
if (!pem.found)
|
||||
errx(1, "did not find a certificate in %s", path);
|
||||
|
||||
*der = pem.der;
|
||||
}
|
||||
|
||||
static void
|
||||
compare_octet_string(const char *what,
|
||||
const heim_octet_string *expected,
|
||||
const heim_octet_string *actual)
|
||||
{
|
||||
if (actual->length != expected->length)
|
||||
errx(1, "%s length changed from %lu to %lu",
|
||||
what, (unsigned long)expected->length,
|
||||
(unsigned long)actual->length);
|
||||
if (memcmp(actual->data, expected->data, expected->length) != 0)
|
||||
errx(1, "%s data changed", what);
|
||||
}
|
||||
|
||||
static void
|
||||
check_cert_binary(hx509_context context, const heim_octet_string *der)
|
||||
{
|
||||
heim_octet_string os;
|
||||
hx509_cert cert;
|
||||
int ret;
|
||||
|
||||
memset(&os, 0, sizeof(os));
|
||||
|
||||
cert = hx509_cert_init_data(context, der->data, der->length, NULL);
|
||||
if (cert == NULL)
|
||||
err(1, "hx509_cert_init_data");
|
||||
|
||||
ret = hx509_cert_binary(context, cert, &os);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_cert_binary");
|
||||
compare_octet_string("certificate encoding", der, &os);
|
||||
der_free_octet_string(&os);
|
||||
|
||||
hx509_cert_free(cert);
|
||||
}
|
||||
|
||||
static char *
|
||||
make_store_name(void)
|
||||
{
|
||||
char *store_name = NULL;
|
||||
|
||||
if (asprintf(&store_name, "FILE:%s/data/test.crt,%s/data/test.key",
|
||||
SRCDIR, SRCDIR) == -1 || store_name == NULL)
|
||||
err(1, "asprintf");
|
||||
|
||||
return store_name;
|
||||
}
|
||||
|
||||
static void
|
||||
check_keyless_store_cert_encoding(hx509_context context,
|
||||
const heim_octet_string *der)
|
||||
{
|
||||
heim_octet_string os;
|
||||
hx509_certs certs = NULL;
|
||||
hx509_cert cert = NULL;
|
||||
char *store_name;
|
||||
int ret;
|
||||
|
||||
memset(&os, 0, sizeof(os));
|
||||
|
||||
ret = hx509_certs_init(context, "MEMORY:test-keyless-certs",
|
||||
HX509_CERTS_NO_PRIVATE_KEYS, NULL, &certs);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_certs_init");
|
||||
|
||||
store_name = make_store_name();
|
||||
ret = hx509_certs_append(context, certs, NULL, store_name);
|
||||
free(store_name);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_certs_append");
|
||||
|
||||
ret = hx509_get_one_cert(context, certs, &cert);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_get_one_cert");
|
||||
if (hx509_cert_have_private_key(cert))
|
||||
errx(1, "HX509_CERTS_NO_PRIVATE_KEYS store kept a private key");
|
||||
|
||||
ret = hx509_cert_binary(context, cert, &os);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_cert_binary on keyless cert");
|
||||
compare_octet_string("keyless store certificate encoding", der, &os);
|
||||
|
||||
der_free_octet_string(&os);
|
||||
hx509_cert_free(cert);
|
||||
hx509_certs_free(&certs);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
heim_octet_string der;
|
||||
hx509_context context;
|
||||
char *cert_path = NULL;
|
||||
int ret;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
memset(&der, 0, sizeof(der));
|
||||
|
||||
ret = hx509_context_init(&context);
|
||||
if (ret)
|
||||
errx(1, "hx509_context_init failed with %d", ret);
|
||||
|
||||
if (asprintf(&cert_path, "%s/data/test.crt", SRCDIR) == -1 ||
|
||||
cert_path == NULL)
|
||||
err(1, "asprintf");
|
||||
|
||||
read_cert_der(context, cert_path, &der);
|
||||
free(cert_path);
|
||||
|
||||
check_cert_binary(context, &der);
|
||||
check_keyless_store_cert_encoding(context, &der);
|
||||
|
||||
der_free_octet_string(&der);
|
||||
hx509_context_free(&context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user