From: Alberto Leiva Popper Date: Thu, 25 Apr 2024 00:00:58 +0000 (-0600) Subject: Add certificate extensions to --mode=print X-Git-Tag: 1.6.2~35 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=f2eef7da2dc82e8b0a40f2fb59614a2ae93cd93a;p=thirdparty%2FFORT-validator.git Add certificate extensions to --mode=print Hmm. I think this bumps the minimum required LibreSSL to v3.5.0. Progress for #122. --- diff --git a/src/Makefile.am b/src/Makefile.am index 8fb6b427..a6d44c54 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,6 +19,7 @@ fort_SOURCES += file.h file.c fort_SOURCES += init.h init.c fort_SOURCES += json_util.c json_util.h fort_SOURCES += line_file.h line_file.c +fort_SOURCES += libcrypto_util.h libcrypto_util.c fort_SOURCES += log.h log.c fort_SOURCES += nid.h nid.c fort_SOURCES += output_printer.h output_printer.c diff --git a/src/asn1/asn1c/Certificate.c b/src/asn1/asn1c/Certificate.c index 42158543..8b4fbbe4 100644 --- a/src/asn1/asn1c/Certificate.c +++ b/src/asn1/asn1c/Certificate.c @@ -3,106 +3,8 @@ #include #include #include "alloc.h" - -/* Swallows @bio. */ -static json_t * -bio2json(BIO *bio) -{ - BUF_MEM *buffer; - json_t *json; - - json = (BIO_get_mem_ptr(bio, &buffer) > 0) - ? json_stringn(buffer->data, buffer->length) - : NULL; - - BIO_free_all(bio); - return json; -} - -/* Swallows @bio. */ -static char * -bio2str(BIO *bio) -{ - BUF_MEM *buffer; - char *str; - - str = (BIO_get_mem_ptr(bio, &buffer) > 0) - ? pstrndup(buffer->data, buffer->length) - : NULL; - - BIO_free_all(bio); - return str; -} - -static json_t * -asn1int2json(ASN1_INTEGER const *asn1int) -{ - BIGNUM *bignum; - char *str; - json_t *json; - - if (asn1int == NULL) - return NULL; - - bignum = ASN1_INTEGER_to_BN(asn1int, NULL); - str = BN_bn2hex(bignum); - - json = json_string(str); - - OPENSSL_free(str); - BN_free(bignum); - - return json; -} - -static json_t * -name2json(X509_NAME const *name) -{ - json_t *root; - json_t *child; - int i; - - root = json_object(); - if (root == NULL) - return NULL; - - for (i = 0; i < X509_NAME_entry_count(name); i++) { - X509_NAME_ENTRY *entry; - int nid; - const ASN1_STRING *data; - - entry = X509_NAME_get_entry(name, i); - nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); - - data = X509_NAME_ENTRY_get_data(entry); - if (data == NULL) - goto fail; - child = json_stringn((char *)data->data, data->length); - - if (json_object_set_new(root, OBJ_nid2ln(nid), child) < 0) - goto fail; - } - - return root; - -fail: json_decref(root); - return NULL; -} - -static json_t * -asn1time2json(ASN1_TIME const *time) -{ - BIO *bio = BIO_new(BIO_s_mem()); - if (bio == NULL) - return NULL; - - if (!ASN1_TIME_print_ex(bio, time, ASN1_DTFLGS_ISO8601)) { - BIO_free_all(bio); - return NULL; - } - - return bio2json(bio); -} +#include "extension.h" +#include "libcrypto_util.h" static json_t * validity2json(X509 *x) @@ -170,47 +72,72 @@ fail: json_decref(root); } static json_t * -bitstr2json(ASN1_BIT_STRING const *bitstr) +iuid2json(X509 const *x) { - BIO *bio; - unsigned char *data; - int length; - int i; + const ASN1_BIT_STRING *iuid; + X509_get0_uids(x, &iuid, NULL); + return asn1str2json(iuid); +} - if (bitstr == NULL) - return json_null(); +static json_t * +suid2json(X509 const *x) +{ + const ASN1_BIT_STRING *suid; + X509_get0_uids(x, NULL, &suid); + return asn1str2json(suid); +} - bio = BIO_new(BIO_s_mem()); - if (bio == NULL) - return NULL; +static json_t * +ext2json_known(struct extension_metadata const *meta, X509_EXTENSION *ext) +{ + void *decoded; + json_t *json; - data = bitstr->data; - length = bitstr->length; + decoded = X509V3_EXT_d2i(ext); + if (decoded == NULL) + return NULL; - for (i = 0; i < length; i++) { - if (BIO_printf(bio, "%02x", data[i]) <= 0) { - BIO_free_all(bio); - return NULL; - } - } + json = meta->to_json(decoded); - return bio2json(bio); + meta->destructor(decoded); + return json; } static json_t * -iuid2json(X509 const *x) +ext2json_unknown(X509_EXTENSION *ext) { - const ASN1_BIT_STRING *iuid; - X509_get0_uids(x, &iuid, NULL); - return bitstr2json(iuid); + BIO *bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + return NULL; + + /* TODO Those flags are kinda interesting */ + if (!X509V3_EXT_print(bio, ext, 0, 0)) { + BIO_free_all(bio); + return NULL; + } + + return bio2json(bio); } static json_t * -suid2json(X509 const *x) +ext2json(X509_EXTENSION *ext) { - const ASN1_BIT_STRING *suid; - X509_get0_uids(x, NULL, &suid); - return bitstr2json(suid); + struct extension_metadata const **array, *meta; + int nid; + + array = ext_metadatas(); + nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); + + for (meta = *array; meta != NULL; array++, meta = *array) { + if (meta->nid == nid) { + if (meta->to_json != NULL) + return ext2json_known(meta, ext); + else + break; + } + } + + return ext2json_unknown(ext); } static json_t * @@ -254,17 +181,8 @@ exts2json(const STACK_OF(X509_EXTENSION) *exts) /* Child 1: Critical */ if (json_object_set_new(node, "critical", X509_EXTENSION_get_critical(ex) ? json_true() : json_false()) < 0) goto fail; - /* Child 2: Value */ - bio = BIO_new(BIO_s_mem()); - if (bio == NULL) - goto fail; - /* TODO Those flags are kinda interesting */ - if (!X509V3_EXT_print(bio, ex, 0, 0)) { - BIO_free_all(bio); - goto fail; - } - if (json_object_set_new(node, "value", bio2json(bio)) < 0) + if (json_object_set_new(node, "value", ext2json(ex))) goto fail; } @@ -287,7 +205,7 @@ tbsCert2json(X509 *x) goto fail; if (json_object_set_new(tbsCert, "serialNumber", asn1int2json(X509_get0_serialNumber(x))) < 0) goto fail; - if (json_object_set_new(tbsCert, "signature", json_string(OBJ_nid2ln(X509_get_signature_nid(x)))) < 0) + if (json_object_set_new(tbsCert, "signature", json_string(OBJ_nid2sn(X509_get_signature_nid(x)))) < 0) goto fail; if (json_object_set_new(tbsCert, "issuer", name2json(X509_get_issuer_name(x))) < 0) goto fail; @@ -320,7 +238,7 @@ sigAlgorithm2json(X509 *cert) X509_get0_signature(NULL, &palg, cert); X509_ALGOR_get0(&paobj, NULL, NULL, palg); - return json_string(OBJ_nid2ln(OBJ_obj2nid(paobj))); + return oid2json(paobj); } static json_t * @@ -328,7 +246,7 @@ sigValue2json(X509 *cert) { const ASN1_BIT_STRING *signature; X509_get0_signature(&signature, NULL, cert); - return bitstr2json(signature); + return asn1str2json(signature); } static json_t * @@ -359,6 +277,7 @@ Certificate_encode_json(ANY_t *ber) { const unsigned char *tmp; X509 *cert; + json_t *root; /* * "If the call is successful *in is incremented to the byte following @@ -372,7 +291,7 @@ Certificate_encode_json(ANY_t *ber) if (cert == NULL) return NULL; - json_t *root = x509_to_json(cert); + root = x509_to_json(cert); if (root == NULL) goto fail; diff --git a/src/extension.c b/src/extension.c index 11d3e3c3..b0657abd 100644 --- a/src/extension.c +++ b/src/extension.c @@ -5,158 +5,705 @@ #include #include "cert_stack.h" #include "common.h" +#include "libcrypto_util.h" #include "log.h" #include "nid.h" #include "thread_var.h" #include "crypto/hash.h" -static struct extension_metadata IR2 = { - "Amended IP Resources", - -1, +static json_t * +unimplemented(void const *arg) +{ + return arg ? json_string("") : json_null(); +} + +static json_t * +bc2json(void const *ext) +{ + BASIC_CONSTRAINTS const *bc = ext; + json_t *root; + + root = json_object(); + if (root == NULL) + return NULL; + + if (json_object_set_new(root, "cA", json_boolean(bc->ca)) < 0) + goto fail; + if (json_object_set_new(root, "pathLenConstraint", asn1int2json(bc->pathlen)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static void +bc_destroy(void *bc) +{ + BASIC_CONSTRAINTS_free(bc); +} + +static const struct extension_metadata BC = { + "Basic Constraints", + NID_basic_constraints, true, + bc2json, + bc_destroy, }; -static struct extension_metadata AR2 = { - "Amended AS Resources", - -1, +static json_t * +ski2json(void const *ext) +{ + return asn1str2json(ext); +} + +static void +ski_destroy(void *ski) +{ + ASN1_OCTET_STRING_free(ski); +} + +static const struct extension_metadata SKI = { + "Subject Key Identifier", + NID_subject_key_identifier, + false, + ski2json, + ski_destroy, +}; + +static json_t * +aki2json(void const *ext) +{ + AUTHORITY_KEYID const *aki = ext; + json_t *root; + + root = json_object(); + if (root == NULL) + return NULL; + + if (json_object_set_new(root, "keyIdentifier", asn1str2json(aki->keyid)) < 0) + goto fail; + if (json_object_set_new(root, "authorityCertIssuer", unimplemented(aki->issuer)) < 0) + goto fail; + if (json_object_set_new(root, "authorityCertSerialNumber", asn1int2json(aki->serial)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static void +aki_destroy(void *aki) +{ + AUTHORITY_KEYID_free(aki); +} + +static const struct extension_metadata AKI = { + "Authority Key Identifier", + NID_authority_key_identifier, + false, + aki2json, + aki_destroy, +}; + +static json_t * +ku2json(void const *ext) +{ + ASN1_BIT_STRING const *ku = ext; + unsigned char data[2]; + json_t *root; + + if (ku->length < 1 || 2 < ku->length) + return NULL; + memset(data, 0, sizeof(data)); + memcpy(data, ku->data, ku->length); + + root = json_object(); + if (root == NULL) + return NULL; + + if (json_object_set_new(root, "digitalSignature", json_boolean(ku->data[0] & 0x80u)) < 0) + goto fail; + if (json_object_set_new(root, "contentCommitment", json_boolean(ku->data[0] & 0x40u)) < 0) + goto fail; + if (json_object_set_new(root, "keyEncipherment", json_boolean(ku->data[0] & 0x20u)) < 0) + goto fail; + if (json_object_set_new(root, "dataEncipherment", json_boolean(ku->data[0] & 0x10u)) < 0) + goto fail; + if (json_object_set_new(root, "keyAgreement", json_boolean(ku->data[0] & 0x08u)) < 0) + goto fail; + if (json_object_set_new(root, "keyCertSign", json_boolean(ku->data[0] & 0x04u)) < 0) + goto fail; + if (json_object_set_new(root, "cRLSign", json_boolean(ku->data[0] & 0x02u)) < 0) + goto fail; + if (json_object_set_new(root, "encipherOnly", json_boolean(ku->data[0] & 0x01u)) < 0) + goto fail; + if (json_object_set_new(root, "decipherOnly", json_boolean(ku->data[1] & 0x80u)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static void +ku_destroy(void *ku) +{ + ASN1_BIT_STRING_free(ku); +} + +static const struct extension_metadata KU = { + "Key Usage", + NID_key_usage, true, + ku2json, + ku_destroy, }; -int extension_init(void) +static json_t * +dpname2json(DIST_POINT_NAME const *dpn) { - IR2.nid = nid_ipAddrBlocksv2(); - AR2.nid = nid_autonomousSysIdsv2(); - return 0; + if (dpn == NULL) + return json_null(); + if (dpn->type) /* if relativename */ + return name2json(dpn->dpname); + return gns2json(dpn->name.fullname); } -struct extension_metadata const *ext_bc(void) +static json_t * +cdp2json(void const *ext) { - static const struct extension_metadata BC = { - "Basic Constraints", - NID_basic_constraints, - true, - }; - return &BC; + STACK_OF(DIST_POINT) const *crldp = ext; + json_t *root, *child; + DIST_POINT *dp; + int d; + + root = json_array(); + if (root == NULL) + return NULL; + + for (d = 0; d < sk_DIST_POINT_num(crldp); d++) { + dp = sk_DIST_POINT_value(crldp, 0); + if (json_array_append_new(root, child = json_object()) < 0) + goto fail; + if (json_object_set_new(child, "distributionPoint", dpname2json(dp->distpoint)) < 0) + goto fail; + if (json_object_set_new(child, "reasons", unimplemented(dp->reasons)) < 0) + goto fail; + if (json_object_set_new(child, "cRLIssuer", gns2json(dp->CRLissuer)) < 0) + goto fail; + } + + return root; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_ski(void) +static void +cdp_destroy(void *crldp) { - static const struct extension_metadata SKI = { - "Subject Key Identifier", - NID_subject_key_identifier, - false, - }; - return &SKI; + sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); } -struct extension_metadata const *ext_aki(void) +static const struct extension_metadata CDP = { + "CRL Distribution Points", + NID_crl_distribution_points, + false, + cdp2json, + cdp_destroy, +}; + +static json_t * +aia2json(void const *ext) { - static const struct extension_metadata AKI = { - "Authority Key Identifier", - NID_authority_key_identifier, - false, - }; - return &AKI; + AUTHORITY_INFO_ACCESS const *ia = ext; + ACCESS_DESCRIPTION *ad; + json_t *root, *child; + int i; + + root = json_array(); + if (root == NULL) + return NULL; + + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ia); i++) { + ad = sk_ACCESS_DESCRIPTION_value(ia, i); + if (json_array_append_new(root, child = json_object()) < 0) + goto fail; + if (json_object_set_new(child, "accessMethod", oid2json(ad->method)) < 0) + goto fail; + if (json_object_set_new(child, "accessLocation", gn2json(ad->location)) < 0) + goto fail; + } + + return root; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_ku(void) +static void +aia_destroy(void *aia) { - static const struct extension_metadata KU = { - "Key Usage", - NID_key_usage, - true, - }; - return &KU; + AUTHORITY_INFO_ACCESS_free(aia); } -struct extension_metadata const *ext_cdp(void) +static const struct extension_metadata AIA = { + "Authority Information Access", + NID_info_access, + false, + aia2json, + aia_destroy, +}; + +static const struct extension_metadata SIA = { + "Subject Information Access", + NID_sinfo_access , + false, + aia2json, + aia_destroy, +}; + +static json_t * +pq2json(POLICYQUALINFO const *pqi) { - static const struct extension_metadata CDP = { - "CRL Distribution Points", - NID_crl_distribution_points, - false, - }; - return &CDP; + json_t *root; + + if (pqi == NULL) + return json_null(); + + root = json_object(); + if (root == NULL) + return NULL; + + if (json_object_set_new(root, "policyQualifierId", oid2json(pqi->pqualid)) < 0) + goto fail; + if (json_object_set_new(root, "qualifier", unimplemented(&pqi->d)) < 0) + goto fail; + + return NULL; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_aia(void) +static json_t * +pqs2json(STACK_OF(POLICYQUALINFO) const *pqs) { - static const struct extension_metadata AIA = { - "Authority Information Access", - NID_info_access, - false, - }; - return &AIA; + json_t *root; + int i; + + if (pqs == NULL) + return json_null(); + + root = json_array(); + if (root == NULL) + return NULL; + + for (i = 0; i < sk_POLICYQUALINFO_num(pqs); i++) + if (json_array_append_new(root, pq2json(sk_POLICYQUALINFO_value(pqs, i))) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_sia(void) +static json_t * +pi2json(POLICYINFO const *pi) { - static const struct extension_metadata SIA = { - "Subject Information Access", - NID_sinfo_access , - false, - }; - return &SIA; + json_t *root; + + if (pi == NULL) + return json_null(); + + root = json_object(); + if (root == NULL) + return NULL; + + if (json_object_set_new(root, "policyIdentifier", oid2json(pi->policyid)) < 0) + goto fail; + if (json_object_set_new(root, "policyQualifiers", pqs2json(pi->qualifiers)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_cp(void) +static json_t * +cp2json(void const *ext) { - static const struct extension_metadata CP = { - "Certificate Policies", - NID_certificate_policies, - true, - }; - return &CP; + CERTIFICATEPOLICIES const *cp = ext; + json_t *root; + int i; + + root = json_array(); + if (root == NULL) + return NULL; + + for (i = 0; i < sk_POLICYINFO_num(cp); i++) + if (json_array_append_new(root, pi2json(sk_POLICYINFO_value(cp, i))) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_ir(void) +static void +cp_destroy(void *cp) { - static const struct extension_metadata IR = { - "IP Resources", - NID_sbgp_ipAddrBlock, - true, - }; - return &IR; + CERTIFICATEPOLICIES_free(cp); } -struct extension_metadata const *ext_ar(void) +static const struct extension_metadata CP = { + "Certificate Policies", + NID_certificate_policies, + true, + cp2json, + cp_destroy, +}; + +static json_t * +p2json(ASN1_BIT_STRING const *ap, int af) { - static const struct extension_metadata AR = { - "AS Resources", - NID_sbgp_autonomousSysNum, - true, - }; - return &AR; + unsigned char bin[16]; + char str[INET6_ADDRSTRLEN]; + unsigned int length; + json_t *root; + + if (ap == NULL) + return json_null(); + + memset(bin, 0, sizeof(bin)); + memcpy(bin, ap->data, ap->length); + if (inet_ntop(af, bin, str, INET6_ADDRSTRLEN) == NULL) + return NULL; + + length = 8 * ap->length; + if (ap->flags & ASN1_STRING_FLAG_BITS_LEFT) + length -= ap->flags & 7; + + root = json_object(); + if (root == NULL) + return NULL; + if (json_object_set_new(root, "address", json_string(str)) < 0) + goto fail; + if (json_object_set_new(root, "length", json_integer(length)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static json_t * +iaor2json(IPAddressOrRange const *iaor, int af) +{ + json_t *root; + + if (iaor == NULL) + return json_null(); + + switch (iaor->type) { + case IPAddressOrRange_addressPrefix: + return p2json(iaor->u.addressPrefix, af); + case IPAddressOrRange_addressRange: + return unimplemented(iaor->u.addressRange); + } + + json_decref(root); + return NULL; } -struct extension_metadata const *ext_ir2(void) +static json_t * +iac2json(IPAddressChoice const *iac, int af) { - return &IR2; + json_t *root; + IPAddressOrRanges *iaor; + int i; + + if (iac == NULL) + return json_null(); + + switch (iac->type) { + case IPAddressChoice_inherit: + return json_string("inherit"); + + case IPAddressChoice_addressesOrRanges: + iaor = iac->u.addressesOrRanges; + if (iaor == NULL) + return json_null(); + root = json_array(); + if (root == NULL) + goto fail; + for (i = 0; i < sk_IPAddressOrRange_num(iaor); i++) + if (json_array_append_new(root, iaor2json(sk_IPAddressOrRange_value(iaor, i), af)) < 0) + goto fail; + return root; + } + + return NULL; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_ar2(void) +static json_t * +iaf2json(IPAddressFamily const *iaf) { - return &AR2; + json_t *root; + ASN1_OCTET_STRING *af; + char const *family; + int afid; + + if (iaf == NULL) + return json_null(); + + root = json_object(); + if (root == NULL) + return NULL; + + af = iaf->addressFamily; + if (af->length != 2) + goto fail; + + if (af->data[0] == 0 && af->data[1] == 1) { + family = "IPv4"; + afid = AF_INET; + } else if (af->data[0] == 0 && af->data[1] == 2) { + family = "IPv6"; + afid = AF_INET6; + } else { + goto fail; + } + + if (json_object_set_new(root, "addressFamily", json_string(family)) < 0) + goto fail; + if (json_object_set_new(root, "ipAddressChoice", iac2json(iaf->ipAddressChoice, afid)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; } -struct extension_metadata const *ext_cn(void) +static json_t * +ir2json(void const *ext) { - static const struct extension_metadata CN = { - "CRL Number", - NID_crl_number, - false, - }; - return &CN; + STACK_OF(IPAddressFamily) const *iafs = ext; + json_t *root; + int i; + + root = json_array(); + if (root == NULL) + return NULL; + + for (i = 0; i < sk_IPAddressFamily_num(iafs); i++) + if (json_array_append_new(root, iaf2json(sk_IPAddressFamily_value(iafs, i))) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static void +ir_destroy(void *ir) +{ + sk_IPAddressFamily_pop_free(ir, IPAddressFamily_free); +} + +static const struct extension_metadata IR = { + "IP Resources", + NID_sbgp_ipAddrBlock, + true, + ir2json, + ir_destroy, +}; + +static json_t * +asr2json(ASRange const *range) +{ + json_t *root; + + if (range == NULL) + return json_null(); + + root = json_object(); + if (root == NULL) + return NULL; + if (json_object_set_new(root, "min", asn1int2json(range->min)) < 0) + goto fail; + if (json_object_set_new(root, "max", asn1int2json(range->max)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static json_t * +aor2json(ASIdOrRange const *aor) +{ + json_t *root; + + if (aor == NULL) + return json_null(); + + switch (aor->type) { + case ASIdOrRange_id: + return asn1int2json(aor->u.id); + case ASIdOrRange_range: + return asr2json(aor->u.range); + } + + json_decref(root); + return NULL; +} + +static json_t * +asidc2json(ASIdentifierChoice const *asidc) +{ + json_t *root; + ASIdOrRanges *iaor; + int i; + + if (asidc == NULL) + return json_null(); + + switch (asidc->type) { + case ASIdentifierChoice_inherit: + return json_string("inherit"); + + case ASIdentifierChoice_asIdsOrRanges: + iaor = asidc->u.asIdsOrRanges; + if (iaor == NULL) + return json_null(); + root = json_array(); + if (root == NULL) + goto fail; + for (i = 0; i < sk_ASIdOrRange_num(iaor); i++) + if (json_array_append_new(root, aor2json(sk_ASIdOrRange_value(iaor, i))) < 0) + goto fail; + return root; + } + + return NULL; + +fail: json_decref(root); + return NULL; +} + +static json_t * +ar2json(void const *ext) +{ + ASIdentifiers const *asid = ext; + json_t *root; + + if (asid == NULL) + return json_null(); + + root = json_object(); + if (root == NULL) + return NULL; + if (json_object_set_new(root, "asnum", asidc2json(asid->asnum)) < 0) + goto fail; + if (json_object_set_new(root, "rdi", asidc2json(asid->rdi)) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} + +static void +ar_destroy(void *ar) +{ + ASIdentifiers_free(ar); +} + +static const struct extension_metadata AR = { + "AS Resources", + NID_sbgp_autonomousSysNum, + true, + ar2json, + ar_destroy, +}; + +static struct extension_metadata IR2 = { + "Amended IP Resources", + -1, + true, + ir2json, + ir_destroy, +}; + +static struct extension_metadata AR2 = { + "Amended AS Resources", + -1, + true, + ar2json, + ir_destroy, +}; + +static const struct extension_metadata CN = { + "CRL Number", + NID_crl_number, + false, +}; + +static const struct extension_metadata EKU = { + "Extended Key Usage", + NID_ext_key_usage, + false, +}; + +int extension_init(void) +{ + IR2.nid = nid_ipAddrBlocksv2(); + AR2.nid = nid_autonomousSysIdsv2(); + return 0; } -struct extension_metadata const *ext_eku(void) +struct extension_metadata const *ext_bc(void) { return &BC; } +struct extension_metadata const *ext_ski(void) { return &SKI; } +struct extension_metadata const *ext_aki(void) { return &AKI; } +struct extension_metadata const *ext_ku(void) { return &KU; } +struct extension_metadata const *ext_cdp(void) { return &CDP; } +struct extension_metadata const *ext_aia(void) { return &AIA; } +struct extension_metadata const *ext_sia(void) { return &SIA; } +struct extension_metadata const *ext_cp(void) { return &CP; } +struct extension_metadata const *ext_ir(void) { return &IR; } +struct extension_metadata const *ext_ar(void) { return &AR; } +struct extension_metadata const *ext_ir2(void) { return &IR2; } +struct extension_metadata const *ext_ar2(void) { return &AR2; } +struct extension_metadata const *ext_cn(void) { return &CN; } +struct extension_metadata const *ext_eku(void) { return &EKU; } + +struct extension_metadata const ** +ext_metadatas(void) { - static const struct extension_metadata EKU = { - "Extended Key Usage", - NID_ext_key_usage, - false, + static struct extension_metadata const *array[] = { + &BC, &SKI, &AKI, &KU, + &CDP, &AIA, &SIA, &CP, + &IR, &AR, &IR2, &AR2, + &CN, &EKU, NULL }; - return &EKU; + return array; } static int diff --git a/src/extension.h b/src/extension.h index 33a4022d..e97e2b6e 100644 --- a/src/extension.h +++ b/src/extension.h @@ -1,7 +1,7 @@ #ifndef SRC_EXTENSION_H_ #define SRC_EXTENSION_H_ -#include +#include #include #include #include @@ -10,6 +10,8 @@ struct extension_metadata { char const *name; int nid; bool critical; + json_t *(*to_json)(void const *); + void (*destructor)(void *); /* TODO use this more */ }; struct extension_handler { @@ -40,6 +42,8 @@ struct extension_metadata const *ext_ar2(void); struct extension_metadata const *ext_cn(void); struct extension_metadata const *ext_eku(void); +struct extension_metadata const **ext_metadatas(void); + int handle_extensions(struct extension_handler *, STACK_OF(X509_EXTENSION) const *); diff --git a/src/libcrypto_util.c b/src/libcrypto_util.c new file mode 100644 index 00000000..c5d93e33 --- /dev/null +++ b/src/libcrypto_util.c @@ -0,0 +1,181 @@ +#include "libcrypto_util.h" + +#include +#include + +#include "alloc.h" + +/* Swallows @bio. */ +char * +bio2str(BIO *bio) +{ + BUF_MEM *buffer; + char *str; + + str = (BIO_get_mem_ptr(bio, &buffer) > 0) + ? pstrndup(buffer->data, buffer->length) + : NULL; + + BIO_free_all(bio); + return str; +} + +/* Swallows @bio. */ +json_t * +bio2json(BIO *bio) +{ + BUF_MEM *buffer; + json_t *json; + + json = (BIO_get_mem_ptr(bio, &buffer) > 0) + ? json_stringn(buffer->data, buffer->length) + : NULL; + + BIO_free_all(bio); + return json; +} + +json_t * +oid2json(ASN1_OBJECT const *oid) +{ + return oid ? json_string(OBJ_nid2sn(OBJ_obj2nid(oid))) : json_null(); +} + +json_t * +asn1int2json(ASN1_INTEGER const *asn1int) +{ + BIGNUM *bignum; + char *str; + json_t *json; + + if (asn1int == NULL) + return json_null(); + + bignum = ASN1_INTEGER_to_BN(asn1int, NULL); + str = BN_bn2hex(bignum); + + json = json_string(str); + + OPENSSL_free(str); + BN_free(bignum); + + return json; +} + +json_t * +asn1str2json(ASN1_STRING const *str) +{ + BIO *bio; + int i; + + if (str == NULL) + return json_null(); + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + return NULL; + + for (i = 0; i < str->length; i++) { + if (BIO_printf(bio, "%02x", str->data[i]) <= 0) { + BIO_free_all(bio); + return NULL; + } + } + + return bio2json(bio); +} + +json_t * +asn1time2json(ASN1_TIME const *time) +{ + BIO *bio; + + if (time == NULL) + return json_null(); + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + return NULL; + + if (!ASN1_TIME_print_ex(bio, time, ASN1_DTFLGS_ISO8601)) { + BIO_free_all(bio); + return NULL; + } + + return bio2json(bio); +} + +json_t * +name2json(X509_NAME const *name) +{ + json_t *root; + json_t *child; + int i; + + if (name == NULL) + return json_null(); + + root = json_object(); + if (root == NULL) + return NULL; + + for (i = 0; i < X509_NAME_entry_count(name); i++) { + X509_NAME_ENTRY *entry; + int nid; + const ASN1_STRING *data; + + entry = X509_NAME_get_entry(name, i); + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); + + data = X509_NAME_ENTRY_get_data(entry); + if (data == NULL) + goto fail; + child = json_stringn((char *)data->data, data->length); + + if (json_object_set_new(root, OBJ_nid2sn(nid), child) < 0) + goto fail; + } + + return root; + +fail: json_decref(root); + return NULL; +} + +json_t * +gn2json(GENERAL_NAME const *gn) +{ + ASN1_IA5STRING *str; + int type; + + if (gn == NULL) + return json_null(); + + str = GENERAL_NAME_get0_value(gn, &type); // FIXME open call hierarchy FIXME getter review + return (type == GEN_URI) + ? json_stringn((char const *)str->data, str->length) + : json_string(""); +} + +json_t * +gns2json(GENERAL_NAMES const *gns) +{ + json_t *root; + int n; + + if (gns == NULL) + return json_null(); + + root = json_array(); + if (root == NULL) + return NULL; + + for (n = 0; n < sk_GENERAL_NAME_num(gns); n++) + if (json_array_append_new(root, gn2json(sk_GENERAL_NAME_value(gns, n))) < 0) + goto fail; + + return root; + +fail: json_decref(root); + return NULL; +} diff --git a/src/libcrypto_util.h b/src/libcrypto_util.h new file mode 100644 index 00000000..e1da52f0 --- /dev/null +++ b/src/libcrypto_util.h @@ -0,0 +1,19 @@ +#ifndef SRC_LIBCRYPTO_UTIL_H_ +#define SRC_LIBCRYPTO_UTIL_H_ + +#include +#include +#include +#include + +char *bio2str(BIO *); +json_t *bio2json(BIO *); +json_t *oid2json(ASN1_OBJECT const *); +json_t *asn1int2json(ASN1_INTEGER const *); +json_t *asn1str2json(ASN1_STRING const *); /* octet string, bit string, etc */ +json_t *asn1time2json(ASN1_TIME const *); +json_t *name2json(X509_NAME const *); +json_t *gn2json(GENERAL_NAME const *); +json_t *gns2json(GENERAL_NAMES const *); + +#endif /* SRC_LIBCRYPTO_UTIL_H_ */