diff options
Diffstat (limited to 'src/libstrongswan/plugins/x509')
-rw-r--r-- | src/libstrongswan/plugins/x509/Makefile.in | 4 | ||||
-rw-r--r-- | src/libstrongswan/plugins/x509/x509_cert.c | 1192 | ||||
-rw-r--r-- | src/libstrongswan/plugins/x509/x509_crl.c | 173 | ||||
-rw-r--r-- | src/libstrongswan/plugins/x509/x509_plugin.c | 16 |
4 files changed, 1096 insertions, 289 deletions
diff --git a/src/libstrongswan/plugins/x509/Makefile.in b/src/libstrongswan/plugins/x509/Makefile.in index b1cc2f168..57deab98e 100644 --- a/src/libstrongswan/plugins/x509/Makefile.in +++ b/src/libstrongswan/plugins/x509/Makefile.in @@ -221,9 +221,7 @@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ -ipsecgid = @ipsecgid@ ipsecgroup = @ipsecgroup@ -ipsecuid = @ipsecuid@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -262,6 +260,8 @@ sbindir = @sbindir@ scepclient_plugins = @scepclient_plugins@ scripts_plugins = @scripts_plugins@ sharedstatedir = @sharedstatedir@ +soup_CFLAGS = @soup_CFLAGS@ +soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ diff --git a/src/libstrongswan/plugins/x509/x509_cert.c b/src/libstrongswan/plugins/x509/x509_cert.c index 559090aa0..526dbe8c6 100644 --- a/src/libstrongswan/plugins/x509/x509_cert.c +++ b/src/libstrongswan/plugins/x509/x509_cert.c @@ -117,7 +117,7 @@ struct private_x509_cert_t { linked_list_t *subjectAltNames; /** - * List of crlDistributionPoints as allocated char* + * List of crlDistributionPoints as x509_cdp_t* */ linked_list_t *crl_uris; @@ -132,6 +132,26 @@ struct private_x509_cert_t { linked_list_t *ipAddrBlocks; /** + * List of permitted name constraints + */ + linked_list_t *permitted_names; + + /** + * List of exluced name constraints + */ + linked_list_t *excluded_names; + + /** + * List of certificatePolicies, as x509_cert_policy_t + */ + linked_list_t *cert_policies; + + /** + * List of policyMappings, as x509_policy_mapping_t + */ + linked_list_t *policy_mappings; + + /** * certificate's embedded public key */ public_key_t *public_key; @@ -154,7 +174,22 @@ struct private_x509_cert_t { /** * Path Length Constraint */ - int pathLenConstraint; + u_char pathLenConstraint; + + /** + * requireExplicitPolicy Constraint + */ + u_char require_explicit; + + /** + * inhibitPolicyMapping Constraint + */ + u_char inhibit_mapping; + + /** + * inhibitAnyPolicy Constraint + */ + u_char inhibit_any; /** * x509 constraints and other flags @@ -187,6 +222,53 @@ static const chunk_t ASN1_subjectAltName_oid = chunk_from_chars( ); /** + * Destroy a CertificateDistributionPoint + */ +static void crl_uri_destroy(x509_cdp_t *this) +{ + free(this->uri); + DESTROY_IF(this->issuer); + free(this); +} + +/** + * Destroy a CertificatePolicy + */ +static void cert_policy_destroy(x509_cert_policy_t *this) +{ + free(this->oid.ptr); + free(this->cps_uri); + free(this->unotice_text); + free(this); +} + +/** + * Free policy mapping + */ +static void policy_mapping_destroy(x509_policy_mapping_t *mapping) +{ + free(mapping->issuer.ptr); + free(mapping->subject.ptr); + free(mapping); +} + +/** + * Parse a length constraint from an unwrapped integer + */ +static u_int parse_constraint(chunk_t object) +{ + switch (object.len) + { + case 0: + return 0; + case 1: + return (object.ptr[0] & 0x80) ? X509_NO_CONSTRAINT : object.ptr[0]; + default: + return X509_NO_CONSTRAINT; + } +} + +/** * ASN.1 definition of a basicConstraints extension */ static const asn1Object_t basicConstraintsObjects[] = { @@ -228,15 +310,7 @@ static void parse_basicConstraints(chunk_t blob, int level0, case BASIC_CONSTRAINTS_PATH_LEN: if (isCA) { - if (object.len == 0) - { - this->pathLenConstraint = 0; - } - else if (object.len == 1) - { - this->pathLenConstraint = *object.ptr; - } - /* we ignore path length constraints > 127 */ + this->pathLenConstraint = parse_constraint(object); } break; default: @@ -574,7 +648,7 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0, } break; default: - /* unkown accessMethod, ignoring */ + /* unknown accessMethod, ignoring */ break; } break; @@ -589,6 +663,60 @@ end: } /** + * Extract KeyUsage flags + */ +static void parse_keyUsage(chunk_t blob, private_x509_cert_t *this) +{ + enum { + KU_DIGITAL_SIGNATURE = 0, + KU_NON_REPUDIATION = 1, + KU_KEY_ENCIPHERMENT = 2, + KU_DATA_ENCIPHERMENT = 3, + KU_KEY_AGREEMENT = 4, + KU_KEY_CERT_SIGN = 5, + KU_CRL_SIGN = 6, + KU_ENCIPHER_ONLY = 7, + KU_DECIPHER_ONLY = 8, + }; + + if (asn1_unwrap(&blob, &blob) == ASN1_BIT_STRING && blob.len) + { + int bit, byte, unused = blob.ptr[0]; + + blob = chunk_skip(blob, 1); + for (byte = 0; byte < blob.len; byte++) + { + for (bit = 0; bit < 8; bit++) + { + if (byte == blob.len - 1 && bit > (7 - unused)) + { + break; + } + if (blob.ptr[byte] & 1 << (7 - bit)) + { + switch (byte * 8 + bit) + { + case KU_CRL_SIGN: + this->flags |= X509_CRL_SIGN; + break; + case KU_KEY_CERT_SIGN: + /* we use the caBasicConstraint, MUST be set */ + case KU_DIGITAL_SIGNATURE: + case KU_NON_REPUDIATION: + case KU_KEY_ENCIPHERMENT: + case KU_DATA_ENCIPHERMENT: + case KU_KEY_AGREEMENT: + case KU_ENCIPHER_ONLY: + case KU_DECIPHER_ONLY: + break; + } + } + } + } + } +} + +/** * ASN.1 definition of a extendedKeyUsage extension */ static const asn1Object_t extendedKeyUsageObjects[] = { @@ -600,7 +728,7 @@ static const asn1Object_t extendedKeyUsageObjects[] = { #define EXT_KEY_USAGE_PURPOSE_ID 1 /** - * Extracts extendedKeyUsage OIDs - currently only OCSP_SIGING is returned + * Extracts extendedKeyUsage OIDs */ static void parse_extendedKeyUsage(chunk_t blob, int level0, private_x509_cert_t *this) @@ -649,51 +777,328 @@ static const asn1Object_t crlDistributionPointsObjects[] = { { 2, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ { 2, "reasons", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_BODY }, /* 8 */ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 9 */ - { 2, "crlIssuer", ASN1_CONTEXT_C_2, ASN1_OPT|ASN1_BODY }, /* 10 */ + { 2, "crlIssuer", ASN1_CONTEXT_C_2, ASN1_OPT|ASN1_OBJ }, /* 10 */ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 11 */ { 0, "end loop", ASN1_EOC, ASN1_END }, /* 12 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; +#define CRL_DIST_POINTS 1 #define CRL_DIST_POINTS_FULLNAME 3 +#define CRL_DIST_POINTS_ISSUER 10 + +/** + * Add entry to the list of each pairing of URI and Issuer + */ +static void add_cdps(linked_list_t *list, linked_list_t *uris, + linked_list_t *issuers) +{ + identification_t *issuer, *id; + enumerator_t *enumerator; + x509_cdp_t *cdp; + char *uri; + + while (uris->remove_last(uris, (void**)&id) == SUCCESS) + { + if (asprintf(&uri, "%Y", id) > 0) + { + if (issuers->get_count(issuers)) + { + enumerator = issuers->create_enumerator(issuers); + while (enumerator->enumerate(enumerator, &issuer)) + { + INIT(cdp, + .uri = strdup(uri), + .issuer = issuer->clone(issuer), + ); + list->insert_last(list, cdp); + } + enumerator->destroy(enumerator); + free(uri); + } + else + { + INIT(cdp, + .uri = uri, + ); + list->insert_last(list, cdp); + } + } + id->destroy(id); + } + while (issuers->remove_last(issuers, (void**)&id) == SUCCESS) + { + id->destroy(id); + } +} /** * Extracts one or several crlDistributionPoints into a list */ -static void parse_crlDistributionPoints(chunk_t blob, int level0, - private_x509_cert_t *this) +void x509_parse_crlDistributionPoints(chunk_t blob, int level0, + linked_list_t *list) { + linked_list_t *uris, *issuers; asn1_parser_t *parser; chunk_t object; int objectID; - linked_list_t *list = linked_list_create(); + uris = linked_list_create(); + issuers = linked_list_create(); parser = asn1_parser_create(crlDistributionPointsObjects, blob); parser->set_top_level(parser, level0); while (parser->iterate(parser, &objectID, &object)) { - if (objectID == CRL_DIST_POINTS_FULLNAME) + switch (objectID) { - identification_t *id; + case CRL_DIST_POINTS: + add_cdps(list, uris, issuers); + break; + case CRL_DIST_POINTS_FULLNAME: + x509_parse_generalNames(object, parser->get_level(parser) + 1, + TRUE, uris); + break; + case CRL_DIST_POINTS_ISSUER: + x509_parse_generalNames(object, parser->get_level(parser) + 1, + TRUE, issuers); + break; + default: + break; + } + } + parser->destroy(parser); - /* append extracted generalNames to existing chained list */ - x509_parse_generalNames(object, parser->get_level(parser)+1, - TRUE, list); + add_cdps(list, uris, issuers); - while (list->remove_last(list, (void**)&id) == SUCCESS) - { - char *uri; + uris->destroy(uris); + issuers->destroy(issuers); +} + +/** + * ASN.1 definition of nameConstraints + */ +static const asn1Object_t nameConstraintsObjects[] = { + { 0, "nameConstraints", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "permittedSubtrees", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_LOOP }, /* 1 */ + { 2, "generalSubtree", ASN1_SEQUENCE, ASN1_BODY }, /* 2 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 3 */ + { 1, "excludedSubtrees", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_LOOP }, /* 4 */ + { 2, "generalSubtree", ASN1_SEQUENCE, ASN1_BODY }, /* 5 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 6 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 7 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define NAME_CONSTRAINT_PERMITTED 2 +#define NAME_CONSTRAINT_EXCLUDED 5 + +/** + * Parse permitted/excluded nameConstraints + */ +static void parse_nameConstraints(chunk_t blob, int level0, + private_x509_cert_t *this) +{ + asn1_parser_t *parser; + identification_t *id; + chunk_t object; + int objectID; + + parser = asn1_parser_create(nameConstraintsObjects, blob); + parser->set_top_level(parser, level0); - if (asprintf(&uri, "%Y", id) > 0) + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case NAME_CONSTRAINT_PERMITTED: + id = parse_generalName(object, parser->get_level(parser) + 1); + if (id) { - this->crl_uris->insert_last(this->crl_uris, uri); + this->permitted_names->insert_last(this->permitted_names, id); } - id->destroy(id); - } + break; + case NAME_CONSTRAINT_EXCLUDED: + id = parse_generalName(object, parser->get_level(parser) + 1); + if (id) + { + this->excluded_names->insert_last(this->excluded_names, id); + } + break; + default: + break; + } + } + parser->destroy(parser); +} + +/** + * ASN.1 definition of a certificatePolicies extension + */ +static const asn1Object_t certificatePoliciesObject[] = { + { 0, "certificatePolicies", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "policyInformation", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "policyId", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "qualifier", ASN1_SEQUENCE, ASN1_OPT|ASN1_BODY }, /* 3 */ + { 3, "qualifierInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 4 */ + { 4, "qualifierId", ASN1_OID, ASN1_BODY }, /* 5 */ + { 4, "cPSuri", ASN1_IA5STRING, ASN1_OPT|ASN1_BODY }, /* 6 */ + { 4, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ + { 4, "userNotice", ASN1_SEQUENCE, ASN1_OPT|ASN1_NONE }, /* 8 */ + { 5, "explicitText", ASN1_EOC, ASN1_RAW }, /* 9 */ + { 4, "end choice", ASN1_EOC, ASN1_END }, /* 10 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 13 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define CERT_POLICY_ID 2 +#define CERT_POLICY_QUALIFIER_ID 5 +#define CERT_POLICY_CPS_URI 6 +#define CERT_POLICY_EXPLICIT_TEXT 9 + +/** + * Parse certificatePolicies + */ +static void parse_certificatePolicies(chunk_t blob, int level0, + private_x509_cert_t *this) +{ + x509_cert_policy_t *policy = NULL; + asn1_parser_t *parser; + chunk_t object; + int objectID, qualifier = OID_UNKNOWN; + + parser = asn1_parser_create(certificatePoliciesObject, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case CERT_POLICY_ID: + INIT(policy, + .oid = chunk_clone(object), + ); + this->cert_policies->insert_last(this->cert_policies, policy); + break; + case CERT_POLICY_QUALIFIER_ID: + qualifier = asn1_known_oid(object); + break; + case CERT_POLICY_CPS_URI: + if (policy && !policy->cps_uri && object.len && + qualifier == OID_POLICY_QUALIFIER_CPS && + chunk_printable(object, NULL, 0)) + { + policy->cps_uri = strndup(object.ptr, object.len); + } + break; + case CERT_POLICY_EXPLICIT_TEXT: + /* TODO */ + break; + default: + break; + } + } + parser->destroy(parser); +} + +/** + * ASN.1 definition of a policyMappings extension + */ +static const asn1Object_t policyMappingsObjects[] = { + { 0, "policyMappings", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "policyMapping", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "issuerPolicy", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "subjectPolicy", ASN1_OID, ASN1_BODY }, /* 3 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 4 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define POLICY_MAPPING 1 +#define POLICY_MAPPING_ISSUER 2 +#define POLICY_MAPPING_SUBJECT 3 + +/** + * Parse policyMappings + */ +static void parse_policyMappings(chunk_t blob, int level0, + private_x509_cert_t *this) +{ + x509_policy_mapping_t *map = NULL; + asn1_parser_t *parser; + chunk_t object; + int objectID; + + parser = asn1_parser_create(policyMappingsObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case POLICY_MAPPING: + INIT(map); + this->policy_mappings->insert_last(this->policy_mappings, map); + break; + case POLICY_MAPPING_ISSUER: + if (map && !map->issuer.len) + { + map->issuer = chunk_clone(object); + } + break; + case POLICY_MAPPING_SUBJECT: + if (map && !map->subject.len) + { + map->subject = chunk_clone(object); + } + break; + default: + break; + } + } + parser->destroy(parser); +} + +/** + * ASN.1 definition of a policyConstraints extension + */ +static const asn1Object_t policyConstraintsObjects[] = { + { 0, "policyConstraints", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "requireExplicitPolicy", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_NONE }, /* 1 */ + { 2, "SkipCerts", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ + { 1, "inhibitPolicyMapping", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_NONE }, /* 4 */ + { 2, "SkipCerts", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 6 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define POLICY_CONSTRAINT_EXPLICIT 2 +#define POLICY_CONSTRAINT_INHIBIT 5 + +/** + * Parse policyConstraints + */ +static void parse_policyConstraints(chunk_t blob, int level0, + private_x509_cert_t *this) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + + parser = asn1_parser_create(policyConstraintsObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case POLICY_CONSTRAINT_EXPLICIT: + this->require_explicit = parse_constraint(object); + break; + case POLICY_CONSTRAINT_INHIBIT: + this->inhibit_mapping = parse_constraint(object); + break; + default: + break; } } parser->destroy(parser); - list->destroy(list); } /** @@ -888,11 +1293,6 @@ static const asn1Object_t certObjects[] = { #define X509_OBJ_SIGNATURE 25 /** - * forward declaration - */ -static bool issued_by(private_x509_cert_t *this, certificate_t *issuer); - -/** * Parses an X.509v3 certificate */ static bool parse_certificate(private_x509_cert_t *this) @@ -992,7 +1392,8 @@ static bool parse_certificate(private_x509_cert_t *this) parse_basicConstraints(object, level, this); break; case OID_CRL_DISTRIBUTION_POINTS: - parse_crlDistributionPoints(object, level, this); + x509_parse_crlDistributionPoints(object, level, + this->crl_uris); break; case OID_AUTHORITY_KEY_ID: this->authKeyIdentifier = x509_parse_authorityKeyIdentifier(object, @@ -1002,7 +1403,7 @@ static bool parse_certificate(private_x509_cert_t *this) parse_authorityInfoAccess(object, level, this); break; case OID_KEY_USAGE: - /* TODO parse the flags */ + parse_keyUsage(object, this); break; case OID_EXTENDED_KEY_USAGE: parse_extendedKeyUsage(object, level, this); @@ -1010,6 +1411,26 @@ static bool parse_certificate(private_x509_cert_t *this) case OID_IP_ADDR_BLOCKS: parse_ipAddrBlocks(object, level, this); break; + case OID_NAME_CONSTRAINTS: + parse_nameConstraints(object, level, this); + break; + case OID_CERTIFICATE_POLICIES: + parse_certificatePolicies(object, level, this); + break; + case OID_POLICY_MAPPINGS: + parse_policyMappings(object, level, this); + break; + case OID_POLICY_CONSTRAINTS: + parse_policyConstraints(object, level, this); + break; + case OID_INHIBIT_ANY_POLICY: + if (!asn1_parse_simple_object(&object, ASN1_INTEGER, + level, "inhibitAnyPolicy")) + { + goto end; + } + this->inhibit_any = parse_constraint(object); + break; case OID_NS_REVOCATION_URL: case OID_NS_CA_REVOCATION_URL: case OID_NS_CA_POLICY_URL: @@ -1022,9 +1443,9 @@ static bool parse_certificate(private_x509_cert_t *this) break; default: if (critical && lib->settings->get_bool(lib->settings, - "libstrongswan.plugins.x509.enforce_critical", FALSE)) + "libstrongswan.x509.enforce_critical", TRUE)) { - DBG1(DBG_LIB, "critical %s extension not supported", + DBG1(DBG_LIB, "critical '%s' extension not supported", (extn_oid == OID_UNKNOWN) ? "unknown" : (char*)oid_names[extn_oid].name); goto end; @@ -1057,7 +1478,9 @@ end: hasher_t *hasher; /* check if the certificate is self-signed */ - if (issued_by(this, &this->public.interface.interface)) + if (this->public.interface.interface.issued_by( + &this->public.interface.interface, + &this->public.interface.interface)) { this->flags |= X509_SELF_SIGNED; } @@ -1074,34 +1497,26 @@ end: return success; } -/** - * Implementation of certificate_t.get_type - */ -static certificate_type_t get_type(private_x509_cert_t *this) +METHOD(certificate_t, get_type, certificate_type_t, + private_x509_cert_t *this) { return CERT_X509; } -/** - * Implementation of certificate_t.get_subject - */ -static identification_t* get_subject(private_x509_cert_t *this) +METHOD(certificate_t, get_subject, identification_t*, + private_x509_cert_t *this) { return this->subject; } -/** - * Implementation of certificate_t.get_issuer - */ -static identification_t* get_issuer(private_x509_cert_t *this) +METHOD(certificate_t, get_issuer, identification_t*, + private_x509_cert_t *this) { return this->issuer; } -/** - * Implementation of certificate_t.has_subject. - */ -static id_match_t has_subject(private_x509_cert_t *this, identification_t *subject) +METHOD(certificate_t, has_subject, id_match_t, + private_x509_cert_t *this, identification_t *subject) { identification_t *current; enumerator_t *enumerator; @@ -1142,19 +1557,15 @@ static id_match_t has_subject(private_x509_cert_t *this, identification_t *subje return best; } -/** - * Implementation of certificate_t.has_issuer. - */ -static id_match_t has_issuer(private_x509_cert_t *this, identification_t *issuer) +METHOD(certificate_t, has_issuer, id_match_t, + private_x509_cert_t *this, identification_t *issuer) { /* issuerAltNames currently not supported */ return this->issuer->matches(this->issuer, issuer); } -/** - * Implementation of certificate_t.issued_by. - */ -static bool issued_by(private_x509_cert_t *this, certificate_t *issuer) +METHOD(certificate_t, issued_by, bool, + private_x509_cert_t *this, certificate_t *issuer) { public_key_t *key; signature_scheme_t scheme; @@ -1201,37 +1612,23 @@ static bool issued_by(private_x509_cert_t *this, certificate_t *issuer) return valid; } -/** - * Implementation of certificate_t.get_public_key - */ -static public_key_t* get_public_key(private_x509_cert_t *this) +METHOD(certificate_t, get_public_key, public_key_t*, + private_x509_cert_t *this) { this->public_key->get_ref(this->public_key); return this->public_key; } -/** - * Implementation of certificate_t.get_ref - */ -static private_x509_cert_t* get_ref(private_x509_cert_t *this) +METHOD(certificate_t, get_ref, certificate_t*, + private_x509_cert_t *this) { ref_get(&this->ref); - return this; + return &this->public.interface.interface; } -/** - * Implementation of x509_cert_t.get_flags. - */ -static x509_flag_t get_flags(private_x509_cert_t *this) -{ - return this->flags; -} - -/** - * Implementation of x509_cert_t.get_validity. - */ -static bool get_validity(private_x509_cert_t *this, time_t *when, - time_t *not_before, time_t *not_after) +METHOD(certificate_t, get_validity, bool, + private_x509_cert_t *this, time_t *when, time_t *not_before, + time_t *not_after) { time_t t = when ? *when : time(NULL); @@ -1246,11 +1643,8 @@ static bool get_validity(private_x509_cert_t *this, time_t *when, return (t >= this->notBefore && t <= this->notAfter); } -/** - * Implementation of certificate_t.get_encoding. - */ -static bool get_encoding(private_x509_cert_t *this, cred_encoding_type_t type, - chunk_t *encoding) +METHOD(certificate_t, get_encoding, bool, + private_x509_cert_t *this, cred_encoding_type_t type, chunk_t *encoding) { if (type == CERT_ASN1_DER) { @@ -1261,10 +1655,8 @@ static bool get_encoding(private_x509_cert_t *this, cred_encoding_type_t type, CRED_PART_X509_ASN1_DER, this->encoding, CRED_PART_END); } -/** - * Implementation of certificate_t.equals. - */ -static bool equals(private_x509_cert_t *this, certificate_t *other) +METHOD(certificate_t, equals, bool, + private_x509_cert_t *this, certificate_t *other) { chunk_t encoding; bool equal; @@ -1290,18 +1682,20 @@ static bool equals(private_x509_cert_t *this, certificate_t *other) return equal; } -/** - * Implementation of x509_t.get_serial. - */ -static chunk_t get_serial(private_x509_cert_t *this) +METHOD(x509_t, get_flags, x509_flag_t, + private_x509_cert_t *this) +{ + return this->flags; +} + +METHOD(x509_t, get_serial, chunk_t, + private_x509_cert_t *this) { return this->serialNumber; } -/** - * Implementation of x509_t.get_subjectKeyIdentifier. - */ -static chunk_t get_subjectKeyIdentifier(private_x509_cert_t *this) +METHOD(x509_t, get_subjectKeyIdentifier, chunk_t, + private_x509_cert_t *this) { if (this->subjectKeyIdentifier.ptr) { @@ -1323,66 +1717,95 @@ static chunk_t get_subjectKeyIdentifier(private_x509_cert_t *this) } } -/** - * Implementation of x509_t.get_authKeyIdentifier. - */ -static chunk_t get_authKeyIdentifier(private_x509_cert_t *this) +METHOD(x509_t, get_authKeyIdentifier, chunk_t, + private_x509_cert_t *this) { return this->authKeyIdentifier; } -/** - * Implementation of x509_t.get_pathLenConstraint. - */ -static int get_pathLenConstraint(private_x509_cert_t *this) +METHOD(x509_t, get_constraint, u_int, + private_x509_cert_t *this, x509_constraint_t type) { - return this->pathLenConstraint; + switch (type) + { + case X509_PATH_LEN: + return this->pathLenConstraint; + case X509_REQUIRE_EXPLICIT_POLICY: + return this->require_explicit; + case X509_INHIBIT_POLICY_MAPPING: + return this->inhibit_mapping; + case X509_INHIBIT_ANY_POLICY: + return this->inhibit_any; + default: + return X509_NO_CONSTRAINT; + } } -/** - * Implementation of x509_cert_t.create_subjectAltName_enumerator. - */ -static enumerator_t* create_subjectAltName_enumerator(private_x509_cert_t *this) +METHOD(x509_t, create_subjectAltName_enumerator, enumerator_t*, + private_x509_cert_t *this) { return this->subjectAltNames->create_enumerator(this->subjectAltNames); } -/** - * Implementation of x509_cert_t.create_ocsp_uri_enumerator. - */ -static enumerator_t* create_ocsp_uri_enumerator(private_x509_cert_t *this) +METHOD(x509_t, create_ocsp_uri_enumerator, enumerator_t*, + private_x509_cert_t *this) { return this->ocsp_uris->create_enumerator(this->ocsp_uris); } -/** - * Implementation of x509_cert_t.create_crl_uri_enumerator. - */ -static enumerator_t* create_crl_uri_enumerator(private_x509_cert_t *this) +METHOD(x509_t, create_crl_uri_enumerator, enumerator_t*, + private_x509_cert_t *this) { return this->crl_uris->create_enumerator(this->crl_uris); } -/** - * Implementation of x509_cert_t.create_ipAddrBlock_enumerator. - */ -static enumerator_t* create_ipAddrBlock_enumerator(private_x509_cert_t *this) +METHOD(x509_t, create_ipAddrBlock_enumerator, enumerator_t*, + private_x509_cert_t *this) { return this->ipAddrBlocks->create_enumerator(this->ipAddrBlocks); } -/** - * Implementation of certificate_t.destroy. - */ -static void destroy(private_x509_cert_t *this) +METHOD(x509_t, create_name_constraint_enumerator, enumerator_t*, + private_x509_cert_t *this, bool perm) +{ + if (perm) + { + return this->permitted_names->create_enumerator(this->permitted_names); + } + return this->excluded_names->create_enumerator(this->excluded_names); +} + +METHOD(x509_t, create_cert_policy_enumerator, enumerator_t*, + private_x509_cert_t *this) +{ + return this->cert_policies->create_enumerator(this->cert_policies); +} + +METHOD(x509_t, create_policy_mapping_enumerator, enumerator_t*, + private_x509_cert_t *this) +{ + return this->policy_mappings->create_enumerator(this->policy_mappings); +} + +METHOD(certificate_t, destroy, void, + private_x509_cert_t *this) { if (ref_put(&this->ref)) { this->subjectAltNames->destroy_offset(this->subjectAltNames, offsetof(identification_t, destroy)); - this->crl_uris->destroy_function(this->crl_uris, free); + this->crl_uris->destroy_function(this->crl_uris, (void*)crl_uri_destroy); this->ocsp_uris->destroy_function(this->ocsp_uris, free); - this->ipAddrBlocks->destroy_offset(this->ipAddrBlocks, offsetof(traffic_selector_t, destroy)); + this->ipAddrBlocks->destroy_offset(this->ipAddrBlocks, + offsetof(traffic_selector_t, destroy)); + this->permitted_names->destroy_offset(this->permitted_names, + offsetof(identification_t, destroy)); + this->excluded_names->destroy_offset(this->excluded_names, + offsetof(identification_t, destroy)); + this->cert_policies->destroy_function(this->cert_policies, + (void*)cert_policy_destroy); + this->policy_mappings->destroy_function(this->policy_mappings, + (void*)policy_mapping_destroy); DESTROY_IF(this->issuer); DESTROY_IF(this->subject); DESTROY_IF(this->public_key); @@ -1404,63 +1827,93 @@ static void destroy(private_x509_cert_t *this) */ static private_x509_cert_t* create_empty(void) { - private_x509_cert_t *this = malloc_thing(private_x509_cert_t); - - this->public.interface.interface.get_type = (certificate_type_t (*) (certificate_t*))get_type; - this->public.interface.interface.get_subject = (identification_t* (*) (certificate_t*))get_subject; - this->public.interface.interface.get_issuer = (identification_t* (*) (certificate_t*))get_issuer; - this->public.interface.interface.has_subject = (id_match_t (*) (certificate_t*, identification_t*))has_subject; - this->public.interface.interface.has_issuer = (id_match_t (*) (certificate_t*, identification_t*))has_issuer; - this->public.interface.interface.issued_by = (bool (*) (certificate_t*, certificate_t*))issued_by; - this->public.interface.interface.get_public_key = (public_key_t* (*) (certificate_t*))get_public_key; - this->public.interface.interface.get_validity = (bool (*) (certificate_t*, time_t*, time_t*, time_t*))get_validity; - this->public.interface.interface.get_encoding = (bool (*) (certificate_t*,cred_encoding_type_t,chunk_t*))get_encoding; - this->public.interface.interface.equals = (bool (*)(certificate_t*, certificate_t*))equals; - this->public.interface.interface.get_ref = (certificate_t* (*)(certificate_t*))get_ref; - this->public.interface.interface.destroy = (void (*)(certificate_t*))destroy; - this->public.interface.get_flags = (x509_flag_t (*)(x509_t*))get_flags; - this->public.interface.get_serial = (chunk_t (*)(x509_t*))get_serial; - this->public.interface.get_subjectKeyIdentifier = (chunk_t (*)(x509_t*))get_subjectKeyIdentifier; - this->public.interface.get_authKeyIdentifier = (chunk_t (*)(x509_t*))get_authKeyIdentifier; - this->public.interface.get_pathLenConstraint = (int (*)(x509_t*))get_pathLenConstraint; - this->public.interface.create_subjectAltName_enumerator = (enumerator_t* (*)(x509_t*))create_subjectAltName_enumerator; - this->public.interface.create_crl_uri_enumerator = (enumerator_t* (*)(x509_t*))create_crl_uri_enumerator; - this->public.interface.create_ocsp_uri_enumerator = (enumerator_t* (*)(x509_t*))create_ocsp_uri_enumerator; - this->public.interface.create_ipAddrBlock_enumerator = (enumerator_t* (*)(x509_t*))create_ipAddrBlock_enumerator; - - this->encoding = chunk_empty; - this->encoding_hash = chunk_empty; - this->tbsCertificate = chunk_empty; - this->version = 1; - this->serialNumber = chunk_empty; - this->notBefore = 0; - this->notAfter = 0; - this->public_key = NULL; - this->subject = NULL; - this->issuer = NULL; - this->subjectAltNames = linked_list_create(); - this->crl_uris = linked_list_create(); - this->ocsp_uris = linked_list_create(); - this->ipAddrBlocks = linked_list_create(); - this->subjectKeyIdentifier = chunk_empty; - this->authKeyIdentifier = chunk_empty; - this->authKeySerialNumber = chunk_empty; - this->pathLenConstraint = X509_NO_PATH_LEN_CONSTRAINT; - this->algorithm = 0; - this->signature = chunk_empty; - this->flags = 0; - this->ref = 1; - this->parsed = FALSE; - + private_x509_cert_t *this; + + INIT(this, + .public = { + .interface = { + .interface = { + .get_type = _get_type, + .get_subject = _get_subject, + .get_issuer = _get_issuer, + .has_subject = _has_subject, + .has_issuer = _has_issuer, + .issued_by = _issued_by, + .get_public_key = _get_public_key, + .get_validity = _get_validity, + .get_encoding = _get_encoding, + .equals = _equals, + .get_ref = _get_ref, + .destroy = _destroy, + }, + .get_flags = _get_flags, + .get_serial = _get_serial, + .get_subjectKeyIdentifier = _get_subjectKeyIdentifier, + .get_authKeyIdentifier = _get_authKeyIdentifier, + .get_constraint = _get_constraint, + .create_subjectAltName_enumerator = _create_subjectAltName_enumerator, + .create_crl_uri_enumerator = _create_crl_uri_enumerator, + .create_ocsp_uri_enumerator = _create_ocsp_uri_enumerator, + .create_ipAddrBlock_enumerator = _create_ipAddrBlock_enumerator, + .create_name_constraint_enumerator = _create_name_constraint_enumerator, + .create_cert_policy_enumerator = _create_cert_policy_enumerator, + .create_policy_mapping_enumerator = _create_policy_mapping_enumerator, + }, + }, + .version = 1, + .subjectAltNames = linked_list_create(), + .crl_uris = linked_list_create(), + .ocsp_uris = linked_list_create(), + .ipAddrBlocks = linked_list_create(), + .permitted_names = linked_list_create(), + .excluded_names = linked_list_create(), + .cert_policies = linked_list_create(), + .policy_mappings = linked_list_create(), + .pathLenConstraint = X509_NO_CONSTRAINT, + .require_explicit = X509_NO_CONSTRAINT, + .inhibit_mapping = X509_NO_CONSTRAINT, + .inhibit_any = X509_NO_CONSTRAINT, + .ref = 1, + ); return this; } /** + * Build a generalName from an id + */ +chunk_t build_generalName(identification_t *id) +{ + int context; + + switch (id->get_type(id)) + { + case ID_RFC822_ADDR: + context = ASN1_CONTEXT_S_1; + break; + case ID_FQDN: + context = ASN1_CONTEXT_S_2; + break; + case ID_DER_ASN1_DN: + context = ASN1_CONTEXT_C_4; + break; + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + context = ASN1_CONTEXT_S_7; + break; + default: + DBG1(DBG_LIB, "encoding %N as generalName not supported", + id_type_names, id->get_type(id)); + return chunk_empty; + } + return asn1_wrap(context, "c", id->get_encoding(id)); +} + +/** * Encode a linked list of subjectAltNames */ chunk_t x509_build_subjectAltNames(linked_list_t *list) { - chunk_t subjectAltNames = chunk_empty; + chunk_t subjectAltNames = chunk_empty, name; enumerator_t *enumerator; identification_t *id; @@ -1472,29 +1925,7 @@ chunk_t x509_build_subjectAltNames(linked_list_t *list) enumerator = list->create_enumerator(list); while (enumerator->enumerate(enumerator, &id)) { - int context; - chunk_t name; - - switch (id->get_type(id)) - { - case ID_RFC822_ADDR: - context = ASN1_CONTEXT_S_1; - break; - case ID_FQDN: - context = ASN1_CONTEXT_S_2; - break; - case ID_IPV4_ADDR: - case ID_IPV6_ADDR: - context = ASN1_CONTEXT_S_7; - break; - default: - DBG1(DBG_LIB, "encoding %N as subjectAltName not supported", - id_type_names, id->get_type(id)); - enumerator->destroy(enumerator); - free(subjectAltNames.ptr); - return chunk_empty; - } - name = asn1_wrap(context, "c", id->get_encoding(id)); + name = build_generalName(id); subjectAltNames = chunk_cat("mm", subjectAltNames, name); } enumerator->destroy(enumerator); @@ -1508,6 +1939,47 @@ chunk_t x509_build_subjectAltNames(linked_list_t *list) } /** + * Encode CRL distribution points extension from a x509_cdp_t list + */ +chunk_t x509_build_crlDistributionPoints(linked_list_t *list, int extn) +{ + chunk_t crlDistributionPoints = chunk_empty; + enumerator_t *enumerator; + x509_cdp_t *cdp; + + if (list->get_count(list) == 0) + { + return chunk_empty; + } + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &cdp)) + { + chunk_t distributionPoint, crlIssuer = chunk_empty; + + if (cdp->issuer) + { + crlIssuer = asn1_wrap(ASN1_CONTEXT_C_2, "m", + build_generalName(cdp->issuer)); + } + distributionPoint = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_CONTEXT_C_0, "m", + asn1_wrap(ASN1_CONTEXT_C_0, "m", + asn1_wrap(ASN1_CONTEXT_S_6, "c", + chunk_create(cdp->uri, strlen(cdp->uri))))), + crlIssuer); + crlDistributionPoints = chunk_cat("mm", crlDistributionPoints, + distributionPoint); + } + enumerator->destroy(enumerator); + + return asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(extn), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_wrap(ASN1_SEQUENCE, "m", crlDistributionPoints))); +} + +/** * Generate and sign a new certificate */ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, @@ -1515,12 +1987,13 @@ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, { chunk_t extensions = chunk_empty, extendedKeyUsage = chunk_empty; chunk_t serverAuth = chunk_empty, clientAuth = chunk_empty; - chunk_t ocspSigning = chunk_empty; - chunk_t basicConstraints = chunk_empty; - chunk_t keyUsage = chunk_empty; - chunk_t subjectAltNames = chunk_empty; + chunk_t ocspSigning = chunk_empty, certPolicies = chunk_empty; + chunk_t basicConstraints = chunk_empty, nameConstraints = chunk_empty; + chunk_t keyUsage = chunk_empty, keyUsageBits = chunk_empty; + chunk_t subjectAltNames = chunk_empty, policyMappings = chunk_empty; chunk_t subjectKeyIdentifier = chunk_empty, authKeyIdentifier = chunk_empty; chunk_t crlDistributionPoints = chunk_empty, authorityInfoAccess = chunk_empty; + chunk_t policyConstraints = chunk_empty, inhibitAnyPolicy = chunk_empty; identification_t *issuer, *subject; chunk_t key_info; signature_scheme_t scheme; @@ -1574,29 +2047,8 @@ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, /* encode subjectAltNames */ subjectAltNames = x509_build_subjectAltNames(cert->subjectAltNames); - /* encode CRL distribution points extension */ - enumerator = cert->crl_uris->create_enumerator(cert->crl_uris); - while (enumerator->enumerate(enumerator, &uri)) - { - chunk_t distributionPoint; - - distributionPoint = asn1_wrap(ASN1_SEQUENCE, "m", - asn1_wrap(ASN1_CONTEXT_C_0, "m", - asn1_wrap(ASN1_CONTEXT_C_0, "m", - asn1_wrap(ASN1_CONTEXT_S_6, "c", - chunk_create(uri, strlen(uri)))))); - - crlDistributionPoints = chunk_cat("mm", crlDistributionPoints, - distributionPoint); - } - enumerator->destroy(enumerator); - if (crlDistributionPoints.ptr) - { - crlDistributionPoints = asn1_wrap(ASN1_SEQUENCE, "mm", - asn1_build_known_oid(OID_CRL_DISTRIBUTION_POINTS), - asn1_wrap(ASN1_OCTET_STRING, "m", - asn1_wrap(ASN1_SEQUENCE, "m", crlDistributionPoints))); - } + crlDistributionPoints = x509_build_crlDistributionPoints(cert->crl_uris, + OID_CRL_DISTRIBUTION_POINTS); /* encode OCSP URIs in authorityInfoAccess extension */ enumerator = cert->ocsp_uris->create_enumerator(cert->ocsp_uris); @@ -1625,11 +2077,10 @@ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, { chunk_t pathLenConstraint = chunk_empty; - if (cert->pathLenConstraint != X509_NO_PATH_LEN_CONSTRAINT) + if (cert->pathLenConstraint != X509_NO_CONSTRAINT) { - char pathlen = (char)cert->pathLenConstraint; - - pathLenConstraint = asn1_integer("c", chunk_from_thing(pathlen)); + pathLenConstraint = asn1_integer("c", + chunk_from_thing(cert->pathLenConstraint)); } basicConstraints = asn1_wrap(ASN1_SEQUENCE, "mmm", asn1_build_known_oid(OID_BASIC_CONSTRAINTS), @@ -1640,13 +2091,20 @@ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, asn1_wrap(ASN1_BOOLEAN, "c", chunk_from_chars(0xFF)), pathLenConstraint))); + /* set CertificateSign and implicitly CRLsign */ + keyUsageBits = chunk_from_chars(0x01, 0x06); + } + else if (cert->flags & X509_CRL_SIGN) + { + keyUsageBits = chunk_from_chars(0x01, 0x02); + } + if (keyUsageBits.len) + { keyUsage = asn1_wrap(ASN1_SEQUENCE, "mmm", - asn1_build_known_oid(OID_KEY_USAGE), - asn1_wrap(ASN1_BOOLEAN, "c", - chunk_from_chars(0xFF)), - asn1_wrap(ASN1_OCTET_STRING, "m", - asn1_wrap(ASN1_BIT_STRING, "c", - chunk_from_chars(0x01, 0x06)))); + asn1_build_known_oid(OID_KEY_USAGE), + asn1_wrap(ASN1_BOOLEAN, "c", chunk_from_chars(0xFF)), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_wrap(ASN1_BIT_STRING, "c", keyUsageBits))); } /* add serverAuth extendedKeyUsage flag */ @@ -1675,7 +2133,7 @@ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, } /* add subjectKeyIdentifier to CA and OCSP signer certificates */ - if (cert->flags & (X509_CA | X509_OCSP_SIGNER)) + if (cert->flags & (X509_CA | X509_OCSP_SIGNER | X509_CRL_SIGN)) { chunk_t keyid; @@ -1703,15 +2161,153 @@ static bool generate(private_x509_cert_t *cert, certificate_t *sign_cert, asn1_wrap(ASN1_CONTEXT_S_0, "c", keyid)))); } } + + if (cert->permitted_names->get_count(cert->permitted_names) || + cert->excluded_names->get_count(cert->excluded_names)) + { + chunk_t permitted = chunk_empty, excluded = chunk_empty, subtree; + identification_t *id; + + enumerator = create_name_constraint_enumerator(cert, TRUE); + while (enumerator->enumerate(enumerator, &id)) + { + subtree = asn1_wrap(ASN1_SEQUENCE, "m", build_generalName(id)); + permitted = chunk_cat("mm", permitted, subtree); + } + enumerator->destroy(enumerator); + if (permitted.ptr) + { + permitted = asn1_wrap(ASN1_CONTEXT_C_0, "m", permitted); + } + + enumerator = create_name_constraint_enumerator(cert, FALSE); + while (enumerator->enumerate(enumerator, &id)) + { + subtree = asn1_wrap(ASN1_SEQUENCE, "m", build_generalName(id)); + excluded = chunk_cat("mm", excluded, subtree); + } + enumerator->destroy(enumerator); + if (excluded.ptr) + { + excluded = asn1_wrap(ASN1_CONTEXT_C_1, "m", excluded); + } + + nameConstraints = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_NAME_CONSTRAINTS), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + permitted, excluded))); + } + + if (cert->cert_policies->get_count(cert->cert_policies)) + { + x509_cert_policy_t *policy; + + enumerator = create_cert_policy_enumerator(cert); + while (enumerator->enumerate(enumerator, &policy)) + { + chunk_t chunk = chunk_empty, cps = chunk_empty, notice = chunk_empty; + + if (policy->cps_uri) + { + cps = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_POLICY_QUALIFIER_CPS), + asn1_wrap(ASN1_IA5STRING, "c", + chunk_create(policy->cps_uri, + strlen(policy->cps_uri)))); + } + if (policy->unotice_text) + { + notice = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_POLICY_QUALIFIER_UNOTICE), + asn1_wrap(ASN1_SEQUENCE, "m", + asn1_wrap(ASN1_VISIBLESTRING, "c", + chunk_create(policy->unotice_text, + strlen(policy->unotice_text))))); + } + if (cps.len || notice.len) + { + chunk = asn1_wrap(ASN1_SEQUENCE, "mm", cps, notice); + } + chunk = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_OID, "c", policy->oid), chunk); + certPolicies = chunk_cat("mm", certPolicies, chunk); + } + enumerator->destroy(enumerator); + + certPolicies = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_CERTIFICATE_POLICIES), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_wrap(ASN1_SEQUENCE, "m", certPolicies))); + } + + if (cert->policy_mappings->get_count(cert->policy_mappings)) + { + x509_policy_mapping_t *mapping; + + enumerator = create_policy_mapping_enumerator(cert); + while (enumerator->enumerate(enumerator, &mapping)) + { + chunk_t chunk; + + chunk = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_OID, "c", mapping->issuer), + asn1_wrap(ASN1_OID, "c", mapping->subject)); + policyMappings = chunk_cat("mm", policyMappings, chunk); + } + enumerator->destroy(enumerator); + + policyMappings = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_build_known_oid(OID_POLICY_MAPPINGS), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_wrap(ASN1_SEQUENCE, "m", policyMappings))); + } + + if (cert->inhibit_mapping != X509_NO_CONSTRAINT || + cert->require_explicit != X509_NO_CONSTRAINT) + { + chunk_t inhibit = chunk_empty, explicit = chunk_empty; + + if (cert->require_explicit != X509_NO_CONSTRAINT) + { + explicit = asn1_wrap(ASN1_CONTEXT_C_0, "m", + asn1_integer("c", + chunk_from_thing(cert->require_explicit))); + } + if (cert->inhibit_mapping != X509_NO_CONSTRAINT) + { + inhibit = asn1_wrap(ASN1_CONTEXT_C_1, "m", + asn1_integer("c", + chunk_from_thing(cert->inhibit_mapping))); + } + policyConstraints = asn1_wrap(ASN1_SEQUENCE, "mmm", + asn1_build_known_oid(OID_POLICY_CONSTRAINTS), + asn1_wrap(ASN1_BOOLEAN, "c", chunk_from_chars(0xFF)), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + explicit, inhibit))); + } + + if (cert->inhibit_any != X509_NO_CONSTRAINT) + { + inhibitAnyPolicy = asn1_wrap(ASN1_SEQUENCE, "mmm", + asn1_build_known_oid(OID_INHIBIT_ANY_POLICY), + asn1_wrap(ASN1_BOOLEAN, "c", chunk_from_chars(0xFF)), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_integer("c", + chunk_from_thing(cert->inhibit_any)))); + } + if (basicConstraints.ptr || subjectAltNames.ptr || authKeyIdentifier.ptr || - crlDistributionPoints.ptr) + crlDistributionPoints.ptr || nameConstraints.ptr) { extensions = asn1_wrap(ASN1_CONTEXT_C_3, "m", - asn1_wrap(ASN1_SEQUENCE, "mmmmmmmm", + asn1_wrap(ASN1_SEQUENCE, "mmmmmmmmmmmmm", basicConstraints, keyUsage, subjectKeyIdentifier, authKeyIdentifier, subjectAltNames, extendedKeyUsage, crlDistributionPoints, - authorityInfoAccess)); + authorityInfoAccess, nameConstraints, certPolicies, + policyMappings, policyConstraints, inhibitAnyPolicy)); } cert->tbsCertificate = asn1_wrap(ASN1_SEQUENCE, "mmmcmcmm", @@ -1794,6 +2390,7 @@ x509_cert_t *x509_cert_gen(certificate_type_t type, va_list args) certificate_t *sign_cert = NULL; private_key_t *sign_key = NULL; hash_algorithm_t digest_alg = HASH_SHA1; + u_int constraint; cert = create_empty(); while (TRUE) @@ -1837,13 +2434,17 @@ x509_cert_t *x509_cert_gen(certificate_type_t type, va_list args) { enumerator_t *enumerator; linked_list_t *list; - char *uri; + x509_cdp_t *in, *cdp; list = va_arg(args, linked_list_t*); enumerator = list->create_enumerator(list); - while (enumerator->enumerate(enumerator, &uri)) + while (enumerator->enumerate(enumerator, &in)) { - cert->crl_uris->insert_last(cert->crl_uris, strdup(uri)); + INIT(cdp, + .uri = strdup(in->uri), + .issuer = in->issuer ? in->issuer->clone(in->issuer) : NULL, + ); + cert->crl_uris->insert_last(cert->crl_uris, cdp); } enumerator->destroy(enumerator); continue; @@ -1864,11 +2465,96 @@ x509_cert_t *x509_cert_gen(certificate_type_t type, va_list args) continue; } case BUILD_PATHLEN: - cert->pathLenConstraint = va_arg(args, int); - if (cert->pathLenConstraint < 0 || cert->pathLenConstraint > 127) + constraint = va_arg(args, u_int); + cert->pathLenConstraint = (constraint < 128) ? + constraint : X509_NO_CONSTRAINT; + continue; + case BUILD_PERMITTED_NAME_CONSTRAINTS: + { + enumerator_t *enumerator; + linked_list_t *list; + identification_t *constraint; + + list = va_arg(args, linked_list_t*); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &constraint)) + { + cert->permitted_names->insert_last(cert->permitted_names, + constraint->clone(constraint)); + } + enumerator->destroy(enumerator); + continue; + } + case BUILD_EXCLUDED_NAME_CONSTRAINTS: + { + enumerator_t *enumerator; + linked_list_t *list; + identification_t *constraint; + + list = va_arg(args, linked_list_t*); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &constraint)) + { + cert->excluded_names->insert_last(cert->excluded_names, + constraint->clone(constraint)); + } + enumerator->destroy(enumerator); + continue; + } + case BUILD_CERTIFICATE_POLICIES: + { + enumerator_t *enumerator; + linked_list_t *list; + x509_cert_policy_t *policy, *in; + + list = va_arg(args, linked_list_t*); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &in)) + { + INIT(policy, + .oid = chunk_clone(in->oid), + .cps_uri = strdupnull(in->cps_uri), + .unotice_text = strdupnull(in->unotice_text), + ); + cert->cert_policies->insert_last(cert->cert_policies, policy); + } + enumerator->destroy(enumerator); + continue; + } + case BUILD_POLICY_MAPPINGS: + { + enumerator_t *enumerator; + linked_list_t *list; + x509_policy_mapping_t* mapping, *in; + + list = va_arg(args, linked_list_t*); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &in)) { - cert->pathLenConstraint = X509_NO_PATH_LEN_CONSTRAINT; + INIT(mapping, + .issuer = chunk_clone(in->issuer), + .subject = chunk_clone(in->subject), + ); + cert->policy_mappings->insert_last(cert->policy_mappings, + mapping); } + enumerator->destroy(enumerator); + continue; + } + case BUILD_POLICY_REQUIRE_EXPLICIT: + constraint = va_arg(args, u_int); + cert->require_explicit = (constraint < 128) ? + constraint : X509_NO_CONSTRAINT; + continue; + case BUILD_POLICY_INHIBIT_MAPPING: + constraint = va_arg(args, u_int); + cert->inhibit_mapping = (constraint < 128) ? + constraint : X509_NO_CONSTRAINT; + continue; + case BUILD_POLICY_INHIBIT_ANY: + constraint = va_arg(args, u_int); + cert->inhibit_any = (constraint < 128) ? + constraint : X509_NO_CONSTRAINT; continue; case BUILD_NOT_BEFORE_TIME: cert->notBefore = va_arg(args, time_t); diff --git a/src/libstrongswan/plugins/x509/x509_crl.c b/src/libstrongswan/plugins/x509/x509_crl.c index 4bd0470d3..758505ab5 100644 --- a/src/libstrongswan/plugins/x509/x509_crl.c +++ b/src/libstrongswan/plugins/x509/x509_crl.c @@ -100,6 +100,11 @@ struct private_x509_crl_t { linked_list_t *revoked; /** + * List of Freshest CRL distribution points + */ + linked_list_t *crl_uris; + + /** * Authority Key Identifier */ chunk_t authKeyIdentifier; @@ -110,6 +115,11 @@ struct private_x509_crl_t { chunk_t authKeySerialNumber; /** + * Number of BaseCRL, if a delta CRL + */ + chunk_t baseCrlNumber; + + /** * Signature algorithm */ int algorithm; @@ -133,9 +143,19 @@ struct private_x509_crl_t { /** * from x509_cert */ -extern chunk_t x509_parse_authorityKeyIdentifier( - chunk_t blob, int level0, - chunk_t *authKeySerialNumber); +extern chunk_t x509_parse_authorityKeyIdentifier(chunk_t blob, int level0, + chunk_t *authKeySerialNumber); + +/** + * from x509_cert + */ +extern void x509_parse_crlDistributionPoints(chunk_t blob, int level0, + linked_list_t *list); + +/** + * from x509_cert + */ +extern chunk_t x509_build_crlDistributionPoints(linked_list_t *list, int extn); /** * ASN.1 definition of an X.509 certificate revocation list @@ -206,7 +226,7 @@ static bool parse(private_x509_crl_t *this) int objectID; int sig_alg = OID_UNKNOWN; bool success = FALSE; - bool critical; + bool critical = FALSE; revoked_t *revoked = NULL; parser = asn1_parser_create(crlObjects, this->encoding); @@ -258,35 +278,61 @@ static bool parse(private_x509_crl_t *this) break; case CRL_OBJ_CRL_ENTRY_EXTN_VALUE: case CRL_OBJ_EXTN_VALUE: - { - int extn_oid = asn1_known_oid(extnID); + { + int extn_oid = asn1_known_oid(extnID); - if (revoked && extn_oid == OID_CRL_REASON_CODE) - { - if (*object.ptr == ASN1_ENUMERATED && - asn1_length(&object) == 1) + switch (extn_oid) + { + case OID_CRL_REASON_CODE: + if (revoked) { - revoked->reason = *object.ptr; + if (object.len && *object.ptr == ASN1_ENUMERATED && + asn1_length(&object) == 1) + { + revoked->reason = *object.ptr; + } + DBG2(DBG_LIB, " '%N'", crl_reason_names, + revoked->reason); } - DBG2(DBG_LIB, " '%N'", crl_reason_names, - revoked->reason); - } - else if (extn_oid == OID_AUTHORITY_KEY_ID) - { - this->authKeyIdentifier = x509_parse_authorityKeyIdentifier(object, - level, &this->authKeySerialNumber); - } - else if (extn_oid == OID_CRL_NUMBER) - { + break; + case OID_AUTHORITY_KEY_ID: + this->authKeyIdentifier = + x509_parse_authorityKeyIdentifier( + object, level, &this->authKeySerialNumber); + break; + case OID_CRL_NUMBER: if (!asn1_parse_simple_object(&object, ASN1_INTEGER, level, "crlNumber")) { goto end; } this->crlNumber = object; - } + break; + case OID_FRESHEST_CRL: + x509_parse_crlDistributionPoints(object, level, + this->crl_uris); + break; + case OID_DELTA_CRL_INDICATOR: + if (!asn1_parse_simple_object(&object, ASN1_INTEGER, + level, "deltaCrlIndicator")) + { + goto end; + } + this->baseCrlNumber = object; + break; + default: + if (critical && lib->settings->get_bool(lib->settings, + "libstrongswan.x509.enforce_critical", TRUE)) + { + DBG1(DBG_LIB, "critical '%s' extension not supported", + (extn_oid == OID_UNKNOWN) ? "unknown" : + (char*)oid_names[extn_oid].name); + goto end; + } + break; } break; + } case CRL_OBJ_ALGORITHM: { this->algorithm = asn1_parse_algorithmIdentifier(object, level, NULL); @@ -344,6 +390,26 @@ METHOD(crl_t, get_authKeyIdentifier, chunk_t, return this->authKeyIdentifier; } +METHOD(crl_t, is_delta_crl, bool, + private_x509_crl_t *this, chunk_t *base_crl) +{ + if (this->baseCrlNumber.len) + { + if (base_crl) + { + *base_crl = this->baseCrlNumber; + } + return TRUE; + } + return FALSE; +} + +METHOD(crl_t, create_delta_crl_uri_enumerator, enumerator_t*, + private_x509_crl_t *this) +{ + return this->crl_uris->create_enumerator(this->crl_uris); +} + METHOD(crl_t, create_enumerator, enumerator_t*, private_x509_crl_t *this) { @@ -388,7 +454,7 @@ METHOD(certificate_t, issued_by, bool, { return FALSE; } - if (!(x509->get_flags(x509) & X509_CA)) + if (!(x509->get_flags(x509) & (X509_CA | X509_CRL_SIGN))) { return FALSE; } @@ -501,18 +567,30 @@ static void revoked_destroy(revoked_t *revoked) free(revoked); } +/** + * Destroy a CDP entry + */ +static void cdp_destroy(x509_cdp_t *this) +{ + free(this->uri); + DESTROY_IF(this->issuer); + free(this); +} + METHOD(certificate_t, destroy, void, private_x509_crl_t *this) { if (ref_put(&this->ref)) { this->revoked->destroy_function(this->revoked, (void*)revoked_destroy); + this->crl_uris->destroy_function(this->crl_uris, (void*)cdp_destroy); DESTROY_IF(this->issuer); free(this->authKeyIdentifier.ptr); free(this->encoding.ptr); if (this->generated) { free(this->crlNumber.ptr); + free(this->baseCrlNumber.ptr); free(this->signature.ptr); free(this->tbsCertList.ptr); } @@ -546,10 +624,13 @@ static private_x509_crl_t* create_empty(void) }, .get_serial = _get_serial, .get_authKeyIdentifier = _get_authKeyIdentifier, + .is_delta_crl = _is_delta_crl, + .create_delta_crl_uri_enumerator = _create_delta_crl_uri_enumerator, .create_enumerator = _create_enumerator, }, }, .revoked = linked_list_create(), + .crl_uris = linked_list_create(), .ref = 1, ); return this; @@ -618,6 +699,7 @@ static bool generate(private_x509_crl_t *this, certificate_t *cert, private_key_t *key, hash_algorithm_t digest_alg) { chunk_t extensions = chunk_empty, certList = chunk_empty, serial; + chunk_t crlDistributionPoints = chunk_empty, baseCrlNumber = chunk_empty; enumerator_t *enumerator; crl_reason_t reason; time_t date; @@ -625,7 +707,7 @@ static bool generate(private_x509_crl_t *this, certificate_t *cert, x509 = (x509_t*)cert; - this->issuer = cert->get_issuer(cert); + this->issuer = cert->get_subject(cert); this->issuer = this->issuer->clone(this->issuer); this->authKeyIdentifier = chunk_clone(x509->get_subjectKeyIdentifier(x509)); @@ -660,8 +742,21 @@ static bool generate(private_x509_crl_t *this, certificate_t *cert, } enumerator->destroy(enumerator); + crlDistributionPoints = x509_build_crlDistributionPoints(this->crl_uris, + OID_FRESHEST_CRL); + + if (this->baseCrlNumber.len) + { + baseCrlNumber = asn1_wrap(ASN1_SEQUENCE, "mmm", + asn1_build_known_oid(OID_DELTA_CRL_INDICATOR), + asn1_wrap(ASN1_BOOLEAN, "c", + chunk_from_chars(0xFF)), + asn1_wrap(ASN1_OCTET_STRING, "m", + asn1_integer("c", this->baseCrlNumber))); + } + extensions = asn1_wrap(ASN1_CONTEXT_C_0, "m", - asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_SEQUENCE, "mmmm", asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_AUTHORITY_KEY_ID), asn1_wrap(ASN1_OCTET_STRING, "m", @@ -671,9 +766,8 @@ static bool generate(private_x509_crl_t *this, certificate_t *cert, asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_CRL_NUMBER), asn1_wrap(ASN1_OCTET_STRING, "m", - asn1_integer("c", this->crlNumber)) - ) - )); + asn1_integer("c", this->crlNumber))), + crlDistributionPoints, baseCrlNumber)); this->tbsCertList = asn1_wrap(ASN1_SEQUENCE, "cmcmmmm", ASN1_INTEGER_1, @@ -736,6 +830,29 @@ x509_crl_t *x509_crl_gen(certificate_type_t type, va_list args) case BUILD_REVOKED_ENUMERATOR: read_revoked(crl, va_arg(args, enumerator_t*)); continue; + case BUILD_BASE_CRL: + crl->baseCrlNumber = va_arg(args, chunk_t); + crl->baseCrlNumber = chunk_clone(crl->baseCrlNumber); + break; + case BUILD_CRL_DISTRIBUTION_POINTS: + { + enumerator_t *enumerator; + linked_list_t *list; + x509_cdp_t *in, *cdp; + + list = va_arg(args, linked_list_t*); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &in)) + { + INIT(cdp, + .uri = strdup(in->uri), + .issuer = in->issuer ? in->issuer->clone(in->issuer) : NULL, + ); + crl->crl_uris->insert_last(crl->crl_uris, cdp); + } + enumerator->destroy(enumerator); + continue; + } case BUILD_END: break; default: diff --git a/src/libstrongswan/plugins/x509/x509_plugin.c b/src/libstrongswan/plugins/x509/x509_plugin.c index 11a7f023c..d40cc3567 100644 --- a/src/libstrongswan/plugins/x509/x509_plugin.c +++ b/src/libstrongswan/plugins/x509/x509_plugin.c @@ -36,10 +36,8 @@ struct private_x509_plugin_t { x509_plugin_t public; }; -/** - * Implementation of x509_plugin_t.x509troy - */ -static void destroy(private_x509_plugin_t *this) +METHOD(plugin_t, destroy, void, + private_x509_plugin_t *this) { lib->creds->remove_builder(lib->creds, (builder_function_t)x509_cert_gen); @@ -69,9 +67,15 @@ static void destroy(private_x509_plugin_t *this) */ plugin_t *x509_plugin_create() { - private_x509_plugin_t *this = malloc_thing(private_x509_plugin_t); + private_x509_plugin_t *this; - this->public.plugin.destroy = (void(*)(plugin_t*))destroy; + INIT(this, + .public = { + .plugin = { + .destroy = _destroy, + }, + }, + ); lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509, FALSE, (builder_function_t)x509_cert_gen); |