/** * @file x509.c * * @brief Implementation of x509_t. * */ /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include #include #include #include "x509.h" #include "hashers/hasher.h" #include #include #include #include #include #include #include #define CERT_WARNING_INTERVAL 30 /* days */ /** * Different kinds of generalNames */ typedef enum generalNames_t generalNames_t; enum generalNames_t { GN_OTHER_NAME = 0, GN_RFC822_NAME = 1, GN_DNS_NAME = 2, GN_X400_ADDRESS = 3, GN_DIRECTORY_NAME = 4, GN_EDI_PARTY_NAME = 5, GN_URI = 6, GN_IP_ADDRESS = 7, GN_REGISTERED_ID = 8, }; typedef struct private_x509_t private_x509_t; /** * Private data of a x509_t object. */ struct private_x509_t { /** * Public interface for this certificate. */ x509_t public; /** * Time when certificate was installed */ time_t installed; /** * Time until certificate can be trusted */ time_t until; /** * Certificate status */ cert_status_t status; /** * Authority flags */ u_int authority_flags; /** * X.509 Certificate in DER format */ chunk_t certificate; /** * X.509 certificate body over which signature is computed */ chunk_t tbsCertificate; /** * Version of the X.509 certificate */ u_int version; /** * Serial number of the X.509 certificate */ chunk_t serialNumber; /** * Signature algorithm */ int sigAlg; /** * ID representing the certificate issuer */ identification_t *issuer; /** * Start time of certificate validity */ time_t notBefore; /** * End time of certificate validity */ time_t notAfter; /** * ID representing the certificate subject */ identification_t *subject; /** * List of identification_t's representing subjectAltNames */ linked_list_t *subjectAltNames; /** * List of identification_t's representing crlDistributionPoints */ linked_list_t *crlDistributionPoints; /** * List of identification_t's representing ocspAccessLocations */ linked_list_t *ocspAccessLocations; /** * Subject public key */ chunk_t subjectPublicKey; /** * Subject RSA public key, if subjectPublicKeyAlgorithm == RSA */ rsa_public_key_t *public_key; /** * Subject Key Identifier */ chunk_t subjectKeyID; /** * Authority Key Identifier */ chunk_t authKeyID; /** * Authority Key Serial Number */ chunk_t authKeySerialNumber; /** * CA basic constraints flag */ bool isCA; /** * OCSPSigner extended key usage flag */ bool isOcspSigner; /** * Signature algorithm (must be identical to sigAlg) */ int algorithm; /** * Signature */ chunk_t signature; }; /** * ASN.1 definition of generalName */ static const asn1Object_t generalNameObjects[] = { { 0, "otherName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_BODY }, /* 0 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 1 */ { 0, "rfc822Name", ASN1_CONTEXT_S_1, ASN1_OPT|ASN1_BODY }, /* 2 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 3 */ { 0, "dnsName", ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 4 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ { 0, "x400Address", ASN1_CONTEXT_S_3, ASN1_OPT|ASN1_BODY }, /* 6 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ { 0, "directoryName", ASN1_CONTEXT_C_4, ASN1_OPT|ASN1_BODY }, /* 8 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ { 0, "ediPartyName", ASN1_CONTEXT_C_5, ASN1_OPT|ASN1_BODY }, /* 10 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 11 */ { 0, "URI", ASN1_CONTEXT_S_6, ASN1_OPT|ASN1_BODY }, /* 12 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 13 */ { 0, "ipAddress", ASN1_CONTEXT_S_7, ASN1_OPT|ASN1_BODY }, /* 14 */ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 15 */ { 0, "registeredID", ASN1_CONTEXT_S_8, ASN1_OPT|ASN1_BODY }, /* 16 */ { 0, "end choice", ASN1_EOC, ASN1_END } /* 17 */ }; #define GN_OBJ_OTHER_NAME 0 #define GN_OBJ_RFC822_NAME 2 #define GN_OBJ_DNS_NAME 4 #define GN_OBJ_X400_ADDRESS 6 #define GN_OBJ_DIRECTORY_NAME 8 #define GN_OBJ_EDI_PARTY_NAME 10 #define GN_OBJ_URI 12 #define GN_OBJ_IP_ADDRESS 14 #define GN_OBJ_REGISTERED_ID 16 #define GN_OBJ_ROOF 18 /** * ASN.1 definition of otherName */ static const asn1Object_t otherNameObjects[] = { {0, "type-id", ASN1_OID, ASN1_BODY }, /* 0 */ {0, "value", ASN1_CONTEXT_C_0, ASN1_BODY } /* 1 */ }; #define ON_OBJ_ID_TYPE 0 #define ON_OBJ_VALUE 1 #define ON_OBJ_ROOF 2 /** * ASN.1 definition of a basicConstraints extension */ static const asn1Object_t basicConstraintsObjects[] = { { 0, "basicConstraints", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ { 1, "CA", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 1 */ { 1, "pathLenConstraint", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 2 */ { 1, "end opt", ASN1_EOC, ASN1_END } /* 3 */ }; #define BASIC_CONSTRAINTS_CA 1 #define BASIC_CONSTRAINTS_ROOF 4 /** * ASN.1 definition of time */ static const asn1Object_t timeObjects[] = { { 0, "utcTime", ASN1_UTCTIME, ASN1_OPT|ASN1_BODY }, /* 0 */ { 0, "end opt", ASN1_EOC, ASN1_END }, /* 1 */ { 0, "generalizeTime",ASN1_GENERALIZEDTIME, ASN1_OPT|ASN1_BODY }, /* 2 */ { 0, "end opt", ASN1_EOC, ASN1_END } /* 3 */ }; #define TIME_UTC 0 #define TIME_GENERALIZED 2 #define TIME_ROOF 4 /** * ASN.1 definition of a keyIdentifier */ static const asn1Object_t keyIdentifierObjects[] = { { 0, "keyIdentifier", ASN1_OCTET_STRING, ASN1_BODY } /* 0 */ }; /** * ASN.1 definition of a authorityKeyIdentifier extension */ static const asn1Object_t authorityKeyIdentifierObjects[] = { { 0, "authorityKeyIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ { 1, "keyIdentifier", ASN1_CONTEXT_S_0, ASN1_OPT|ASN1_OBJ }, /* 1 */ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ { 1, "authorityCertIssuer", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_OBJ }, /* 3 */ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ { 1, "authorityCertSerialNumber",ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 5 */ { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */ }; #define AUTH_KEY_ID_KEY_ID 1 #define AUTH_KEY_ID_CERT_ISSUER 3 #define AUTH_KEY_ID_CERT_SERIAL 5 #define AUTH_KEY_ID_ROOF 7 /** * ASN.1 definition of a authorityInfoAccess extension */ static const asn1Object_t authorityInfoAccessObjects[] = { { 0, "authorityInfoAccess", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ { 1, "accessDescription", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ { 2, "accessMethod", ASN1_OID, ASN1_BODY }, /* 2 */ { 2, "accessLocation", ASN1_EOC, ASN1_RAW }, /* 3 */ { 0, "end loop", ASN1_EOC, ASN1_END } /* 4 */ }; #define AUTH_INFO_ACCESS_METHOD 2 #define AUTH_INFO_ACCESS_LOCATION 3 #define AUTH_INFO_ACCESS_ROOF 5 /** * ASN.1 definition of a extendedKeyUsage extension */ static const asn1Object_t extendedKeyUsageObjects[] = { { 0, "extendedKeyUsage", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ { 1, "keyPurposeID", ASN1_OID, ASN1_BODY }, /* 1 */ { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */ }; #define EXT_KEY_USAGE_PURPOSE_ID 1 #define EXT_KEY_USAGE_ROOF 3 /** * ASN.1 definition of generalNames */ static const asn1Object_t generalNamesObjects[] = { { 0, "generalNames", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ { 1, "generalName", ASN1_EOC, ASN1_RAW }, /* 1 */ { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */ }; #define GENERAL_NAMES_GN 1 #define GENERAL_NAMES_ROOF 3 /** * ASN.1 definition of crlDistributionPoints */ static const asn1Object_t crlDistributionPointsObjects[] = { { 0, "crlDistributionPoints", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ { 1, "DistributionPoint", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ { 2, "distributionPoint", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_LOOP }, /* 2 */ { 3, "fullName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_OBJ }, /* 3 */ { 3, "end choice", ASN1_EOC, ASN1_END }, /* 4 */ { 3, "nameRelToCRLIssuer",ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_BODY }, /* 5 */ { 3, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ { 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, "end opt", ASN1_EOC, ASN1_END }, /* 11 */ { 0, "end loop", ASN1_EOC, ASN1_END }, /* 12 */ }; #define CRL_DIST_POINTS_FULLNAME 3 #define CRL_DIST_POINTS_ROOF 13 /** * ASN.1 definition of an X.509v3 x509 */ static const asn1Object_t certObjects[] = { { 0, "x509", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ { 1, "tbsCertificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ { 2, "DEFAULT v1", ASN1_CONTEXT_C_0, ASN1_DEF }, /* 2 */ { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 4 */ { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 5 */ { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */ { 2, "validity", ASN1_SEQUENCE, ASN1_NONE }, /* 7 */ { 3, "notBefore", ASN1_EOC, ASN1_RAW }, /* 8 */ { 3, "notAfter", ASN1_EOC, ASN1_RAW }, /* 9 */ { 2, "subject", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */ { 2, "subjectPublicKeyInfo",ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ { 3, "algorithm", ASN1_EOC, ASN1_RAW }, /* 12 */ { 3, "subjectPublicKey", ASN1_BIT_STRING, ASN1_NONE }, /* 13 */ { 4, "RSAPublicKey", ASN1_SEQUENCE, ASN1_RAW }, /* 14 */ { 2, "issuerUniqueID", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 15 */ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 16 */ { 2, "subjectUniqueID", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 17 */ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 18 */ { 2, "optional extensions", ASN1_CONTEXT_C_3, ASN1_OPT }, /* 19 */ { 3, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */ { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */ { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */ { 5, "critical", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 23 */ { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */ { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */ { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */ }; #define X509_OBJ_CERTIFICATE 0 #define X509_OBJ_TBS_CERTIFICATE 1 #define X509_OBJ_VERSION 3 #define X509_OBJ_SERIAL_NUMBER 4 #define X509_OBJ_SIG_ALG 5 #define X509_OBJ_ISSUER 6 #define X509_OBJ_NOT_BEFORE 8 #define X509_OBJ_NOT_AFTER 9 #define X509_OBJ_SUBJECT 10 #define X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM 12 #define X509_OBJ_SUBJECT_PUBLIC_KEY 13 #define X509_OBJ_RSA_PUBLIC_KEY 14 #define X509_OBJ_EXTN_ID 22 #define X509_OBJ_CRITICAL 23 #define X509_OBJ_EXTN_VALUE 24 #define X509_OBJ_ALGORITHM 27 #define X509_OBJ_SIGNATURE 28 #define X509_OBJ_ROOF 29 static u_char ASN1_subjectAltName_oid_str[] = { 0x06, 0x03, 0x55, 0x1D, 0x11 }; static const chunk_t ASN1_subjectAltName_oid = chunk_from_buf(ASN1_subjectAltName_oid_str); /** * compare two X.509 x509s by comparing their signatures */ static bool equals(const private_x509_t *this, const private_x509_t *other) { return chunk_equals(this->signature, other->signature); } /** * extracts the basicConstraints extension */ static bool parse_basicConstraints(chunk_t blob, int level0) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; bool isCA = FALSE; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < BASIC_CONSTRAINTS_ROOF) { if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx)) { break; } if (objectID == BASIC_CONSTRAINTS_CA) { isCA = object.len && *object.ptr; DBG2(" %s", isCA ? "TRUE" : "FALSE"); } objectID++; } return isCA; } /* * extracts an otherName */ static bool parse_otherName(chunk_t blob, int level0) { asn1_ctx_t ctx; chunk_t object; int objectID = 0; u_int level; int oid = OID_UNKNOWN; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < ON_OBJ_ROOF) { if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx)) return FALSE; switch (objectID) { case ON_OBJ_ID_TYPE: oid = known_oid(object); break; case ON_OBJ_VALUE: if (oid == OID_XMPP_ADDR) { if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING, level + 1, "xmppAddr")) return FALSE; } break; default: break; } objectID++; } return TRUE; } /* * extracts a generalName */ static identification_t *parse_generalName(chunk_t blob, int level0) { asn1_ctx_t ctx; chunk_t object; int objectID = 0; u_int level; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < GN_OBJ_ROOF) { id_type_t id_type = ID_ANY; if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx)) return NULL; switch (objectID) { case GN_OBJ_RFC822_NAME: id_type = ID_RFC822_ADDR; break; case GN_OBJ_DNS_NAME: id_type = ID_FQDN; break; case GN_OBJ_URI: id_type = ID_DER_ASN1_GN_URI; break; case GN_OBJ_DIRECTORY_NAME: id_type = ID_DER_ASN1_DN; break; case GN_OBJ_IP_ADDRESS: id_type = ID_IPV4_ADDR; break; case GN_OBJ_OTHER_NAME: if (!parse_otherName(object, level + 1)) return NULL; break; case GN_OBJ_X400_ADDRESS: case GN_OBJ_EDI_PARTY_NAME: case GN_OBJ_REGISTERED_ID: break; default: break; } if (id_type != ID_ANY) { identification_t *gn = identification_create_from_encoding(id_type, object); DBG2(" '%D'", gn); return gn; } objectID++; } return NULL; } /** * extracts one or several GNs and puts them into a chained list */ static void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; asn1_init(&ctx, blob, level0, implicit, FALSE); while (objectID < GENERAL_NAMES_ROOF) { if (!extract_object(generalNamesObjects, &objectID, &object, &level, &ctx)) return; if (objectID == GENERAL_NAMES_GN) { identification_t *gn = parse_generalName(object, level+1); if (gn != NULL) list->insert_last(list, (void *)gn); } objectID++; } return; } /** * extracts and converts a UTCTIME or GENERALIZEDTIME object */ time_t parse_time(chunk_t blob, int level0) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < TIME_ROOF) { if (!extract_object(timeObjects, &objectID, &object, &level, &ctx)) return 0; if (objectID == TIME_UTC || objectID == TIME_GENERALIZED) { return asn1totime(&object, (objectID == TIME_UTC) ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME); } objectID++; } return 0; } /** * extracts a keyIdentifier */ static chunk_t parse_keyIdentifier(chunk_t blob, int level0, bool implicit) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; asn1_init(&ctx, blob, level0, implicit, FALSE); extract_object(keyIdentifierObjects, &objectID, &object, &level, &ctx); return object; } /** * extracts an authoritykeyIdentifier */ void parse_authorityKeyIdentifier(chunk_t blob, int level0 , chunk_t *authKeyID, chunk_t *authKeySerialNumber) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < AUTH_KEY_ID_ROOF) { if (!extract_object(authorityKeyIdentifierObjects, &objectID, &object, &level, &ctx)) { return; } switch (objectID) { case AUTH_KEY_ID_KEY_ID: *authKeyID = parse_keyIdentifier(object, level+1, TRUE); break; case AUTH_KEY_ID_CERT_ISSUER: { /* TODO: parse_generalNames(object, level+1, TRUE); */ break; } case AUTH_KEY_ID_CERT_SERIAL: *authKeySerialNumber = object; break; default: break; } objectID++; } } /** * extracts an authorityInfoAcess location */ static void parse_authorityInfoAccess(chunk_t blob, int level0, linked_list_t *list) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; u_int accessMethod = OID_UNKNOWN; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < AUTH_INFO_ACCESS_ROOF) { if (!extract_object(authorityInfoAccessObjects, &objectID, &object, &level, &ctx)) { return; } switch (objectID) { case AUTH_INFO_ACCESS_METHOD: accessMethod = known_oid(object); break; case AUTH_INFO_ACCESS_LOCATION: { switch (accessMethod) { case OID_OCSP: if (*object.ptr == ASN1_CONTEXT_S_6) { identification_t *accessLocation; if (asn1_length(&object) == ASN1_INVALID_LENGTH) return; DBG2(" '%.*s'",(int)object.len, object.ptr); accessLocation = identification_create_from_encoding(ID_DER_ASN1_GN_URI, object); list->insert_last(list, (void *)accessLocation); } break; default: /* unkown accessMethod, ignoring */ break; } break; } default: break; } objectID++; } } /** * extracts extendedKeyUsage OIDs */ static bool parse_extendedKeyUsage(chunk_t blob, int level0) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < EXT_KEY_USAGE_ROOF) { if (!extract_object(extendedKeyUsageObjects, &objectID, &object, &level, &ctx)) { return FALSE; } if (objectID == EXT_KEY_USAGE_PURPOSE_ID && known_oid(object) == OID_OCSP_SIGNING) { return TRUE; } objectID++; } return FALSE; } /** * extracts one or several crlDistributionPoints and puts them into * a chained list */ static void parse_crlDistributionPoints(chunk_t blob, int level0, linked_list_t *list) { asn1_ctx_t ctx; chunk_t object; u_int level; int objectID = 0; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < CRL_DIST_POINTS_ROOF) { if (!extract_object(crlDistributionPointsObjects, &objectID, &object, &level, &ctx)) { return; } if (objectID == CRL_DIST_POINTS_FULLNAME) { /* append extracted generalNames to existing chained list */ parse_generalNames(object, level+1, TRUE, list); } objectID++; } } /** * Parses an X.509v3 certificate */ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *cert) { asn1_ctx_t ctx; bool critical; chunk_t object; u_int level; u_int extn_oid = OID_UNKNOWN; int objectID = 0; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < X509_OBJ_ROOF) { if (!extract_object(certObjects, &objectID, &object, &level, &ctx)) { return FALSE; } /* those objects which will parsed further need the next higher level */ level++; switch (objectID) { case X509_OBJ_CERTIFICATE: cert->certificate = object; break; case X509_OBJ_TBS_CERTIFICATE: cert->tbsCertificate = object; break; case X509_OBJ_VERSION: cert->version = (object.len) ? (1+(u_int)*object.ptr) : 1; DBG2(" v%d", cert->version); break; case X509_OBJ_SERIAL_NUMBER: cert->serialNumber = object; break; case X509_OBJ_SIG_ALG: cert->sigAlg = parse_algorithmIdentifier(object, level, NULL); break; case X509_OBJ_ISSUER: cert->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); DBG2(" '%D'", cert->issuer); break; case X509_OBJ_NOT_BEFORE: cert->notBefore = parse_time(object, level); break; case X509_OBJ_NOT_AFTER: cert->notAfter = parse_time(object, level); break; case X509_OBJ_SUBJECT: cert->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object); DBG2(" '%D'", cert->subject); break; case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM: if (parse_algorithmIdentifier(object, level, NULL) != OID_RSA_ENCRYPTION) { DBG2(" unsupported public key algorithm"); return FALSE; } break; case X509_OBJ_SUBJECT_PUBLIC_KEY: if (ctx.blobs[4].len > 0 && *ctx.blobs[4].ptr == 0x00) { /* skip initial bit string octet defining 0 unused bits */ ctx.blobs[4].ptr++; ctx.blobs[4].len--; } else { DBG2(" invalid RSA public key format"); return FALSE; } break; case X509_OBJ_RSA_PUBLIC_KEY: cert->subjectPublicKey = object; break; case X509_OBJ_EXTN_ID: extn_oid = known_oid(object); break; case X509_OBJ_CRITICAL: critical = object.len && *object.ptr; DBG2(" %s", critical ? "TRUE" : "FALSE"); break; case X509_OBJ_EXTN_VALUE: { switch (extn_oid) { case OID_SUBJECT_KEY_ID: cert->subjectKeyID = chunk_clone(parse_keyIdentifier(object, level, FALSE)); break; case OID_SUBJECT_ALT_NAME: parse_generalNames(object, level, FALSE, cert->subjectAltNames); break; case OID_BASIC_CONSTRAINTS: cert->isCA = parse_basicConstraints(object, level); break; case OID_CRL_DISTRIBUTION_POINTS: parse_crlDistributionPoints(object, level, cert->crlDistributionPoints); break; case OID_AUTHORITY_KEY_ID: parse_authorityKeyIdentifier(object, level , &cert->authKeyID, &cert->authKeySerialNumber); break; case OID_AUTHORITY_INFO_ACCESS: parse_authorityInfoAccess(object, level, cert->ocspAccessLocations); break; case OID_EXTENDED_KEY_USAGE: cert->isOcspSigner = parse_extendedKeyUsage(object, level); break; case OID_NS_REVOCATION_URL: case OID_NS_CA_REVOCATION_URL: case OID_NS_CA_POLICY_URL: case OID_NS_COMMENT: if (!parse_asn1_simple_object(&object, ASN1_IA5STRING , level, oid_names[extn_oid].name)) return FALSE; break; default: break; } break; } case X509_OBJ_ALGORITHM: cert->algorithm = parse_algorithmIdentifier(object, level, NULL); break; case X509_OBJ_SIGNATURE: cert->signature = object; break; default: break; } objectID++; } if (cert->subjectKeyID.ptr == NULL) { hasher_t *hasher = hasher_create(HASH_SHA1); hasher->allocate_hash(hasher, cert->subjectPublicKey, &cert->subjectKeyID); hasher->destroy(hasher); } time(&cert->installed); return TRUE; } /** * Implements x509_t.is_valid */ static err_t is_valid(const private_x509_t *this, time_t *until) { time_t current_time = time(NULL); DBG2(" not before : %T", &this->notBefore); DBG2(" current time: %T", ¤t_time); DBG2(" not after : %T", &this->notAfter); if (until != NULL && (*until == UNDEFINED_TIME || this->notAfter < *until)) { *until = this->notAfter; } if (current_time < this->notBefore) { return "is not valid yet"; } if (current_time > this->notAfter) { return "has expired"; } DBG2(" certificate is valid"); return NULL; } /** * Implements x509_t.is_ca */ static bool is_ca(const private_x509_t *this) { return this->isCA; } /** * Implements x509_t.is_ocsp_signer */ static bool is_ocsp_signer(const private_x509_t *this) { return this->isOcspSigner; } /** * Implements x509_t.is_self_signed */ static bool is_self_signed(const private_x509_t *this) { return this->subject->equals(this->subject, this->issuer); } /** * Implements x509_t.equals_subjectAltName */ static bool equals_subjectAltName(const private_x509_t *this, identification_t *id) { bool found = FALSE; identification_t *subjectAltName; iterator_t *iterator; iterator = this->subjectAltNames->create_iterator(this->subjectAltNames, TRUE); while (iterator->iterate(iterator, (void**)&subjectAltName)) { if (id->equals(id, subjectAltName)) { found = TRUE; break; } } iterator->destroy(iterator); return found; } /** * Implements x509_t.is_issuer */ static bool is_issuer(const private_x509_t *this, const private_x509_t *issuer) { return (this->authKeyID.ptr) ? chunk_equals(this->authKeyID, issuer->subjectKeyID) : (this->issuer->equals(this->issuer, issuer->subject) && chunk_equals_or_null(this->authKeySerialNumber, issuer->serialNumber)); } /** * Implements x509_t.get_certificate */ static chunk_t get_certificate(const private_x509_t *this) { return this->certificate; } /** * Implements x509_t.get_public_key */ static rsa_public_key_t *get_public_key(const private_x509_t *this) { return this->public_key; } /** * Implements x509_t.get_serialNumber */ static chunk_t get_serialNumber(const private_x509_t *this) { return this->serialNumber; } /** * Implements x509_t.get_subjectKeyID */ static chunk_t get_subjectKeyID(const private_x509_t *this) { return this->subjectKeyID; } /** * Implements x509_t.get_keyid */ static chunk_t get_keyid(const private_x509_t *this) { return this->public_key->get_keyid(this->public_key); } /** * Implements x509_t.get_issuer */ static identification_t *get_issuer(const private_x509_t *this) { return this->issuer; } /** * Implements x509_t.get_subject */ static identification_t *get_subject(const private_x509_t *this) { return this->subject; } /** * Implements x509_t.set_until */ static void set_until(private_x509_t *this, time_t until) { this->until = until; } /** * Implements x509_t.get_until */ static time_t get_until(const private_x509_t *this) { return this->until; } /** * Implements x509_t.set_status */ static void set_status(private_x509_t *this, cert_status_t status) { this->status = status; } /** * Implements x509_t.get_status */ static cert_status_t get_status(const private_x509_t *this) { return this->status; } /** * Implements x509_t.add_authority_flags */ static void add_authority_flags(private_x509_t *this, u_int flags) { this->authority_flags |= flags; } /** * Implements x509_t.add_authority_flags */ static u_int get_authority_flags(private_x509_t *this) { return this->authority_flags; } /** * Implements x509_t.has_authority_flag */ static bool has_authority_flag(private_x509_t *this, u_int flags) { return (this->authority_flags & flags) != AUTH_NONE; } /** * Implements x509_t.create_crluri_iterator */ static iterator_t *create_crluri_iterator(const private_x509_t *this) { return this->crlDistributionPoints->create_iterator(this->crlDistributionPoints, TRUE); } /** * Implements x509_t.create_crluri_iterator */ static iterator_t *create_ocspuri_iterator(const private_x509_t *this) { return this->ocspAccessLocations->create_iterator(this->ocspAccessLocations, TRUE); } /** * Implements x509_t.verify */ static bool verify(const private_x509_t *this, const rsa_public_key_t *signer) { return signer->verify_emsa_pkcs1_signature(signer, this->tbsCertificate, this->signature) == SUCCESS; } /** * output handler in printf() */ static int print(FILE *stream, const struct printf_info *info, const void *const *args) { private_x509_t *this = *((private_x509_t**)(args[0])); iterator_t *iterator; bool utc = TRUE; int written = 0; if (info->alt) { utc = *((bool*)(args[1])); } if (this == NULL) { return fprintf(stream, "(null)"); } /* determine the current time */ time_t now = time(NULL); written += fprintf(stream, "%#T\n", &this->installed, utc); if (this->subjectAltNames->get_count(this->subjectAltNames)) { identification_t *subjectAltName; bool first = TRUE; written += fprintf(stream, " altNames: "); iterator = this->subjectAltNames->create_iterator(this->subjectAltNames, TRUE); while (iterator->iterate(iterator, (void**)&subjectAltName)) { if (first) { first = FALSE; } else { written += fprintf(stream, ", "); } written += fprintf(stream, "'%D'", subjectAltName); } iterator->destroy(iterator); written += fprintf(stream, "\n"); } written += fprintf(stream, " subject: '%D'\n", this->subject); written += fprintf(stream, " issuer: '%D'\n", this->issuer); written += fprintf(stream, " serial: %#B\n", &this->serialNumber); written += fprintf(stream, " validity: not before %#T, ", &this->notBefore, utc); if (now < this->notBefore) { written += fprintf(stream, "not valid yet (valid in %V)\n", &now, &this->notBefore); } else { written += fprintf(stream, "ok\n"); } written += fprintf(stream, " not after %#T, ", &this->notAfter, utc); if (now > this->notAfter) { written += fprintf(stream, "expired (%V ago)\n", &now, &this->notAfter); } else { written += fprintf(stream, "ok"); if (now > this->notAfter - CERT_WARNING_INTERVAL * 60 * 60 * 24) { written += fprintf(stream, " (expires in %V)", &now, &this->notAfter); } written += fprintf(stream, " \n"); } { chunk_t keyid = this->public_key->get_keyid(this->public_key); written += fprintf(stream, " keyid: %#B\n", &keyid); } if (this->subjectKeyID.ptr) { written += fprintf(stream, " subjkey: %#B\n", &this->subjectKeyID); } if (this->authKeyID.ptr) { written += fprintf(stream, " authkey: %#B\n", &this->authKeyID); } if (this->authKeySerialNumber.ptr) { written += fprintf(stream, " aserial: %#B\n", &this->authKeySerialNumber); } written += fprintf(stream, " pubkey: RSA %d bits", BITS_PER_BYTE * this->public_key->get_keysize(this->public_key)); written += fprintf(stream, ", status %N", cert_status_names, this->status); switch (this->status) { case CERT_GOOD: written += fprintf(stream, " until %#T", &this->until, utc); break; case CERT_REVOKED: written += fprintf(stream, " on %#T", &this->until, utc); break; case CERT_UNKNOWN: case CERT_UNDEFINED: case CERT_UNTRUSTED: default: break; } return written; } /** * register printf() handlers */ static void __attribute__ ((constructor))print_register() { register_printf_function(PRINTF_X509, print, arginfo_ptr_alt_ptr_int); } /** * Implements x509_t.destroy */ static void destroy(private_x509_t *this) { this->subjectAltNames->destroy_offset(this->subjectAltNames, offsetof(identification_t, destroy)); this->crlDistributionPoints->destroy_offset(this->crlDistributionPoints, offsetof(identification_t, destroy)); this->ocspAccessLocations->destroy_offset(this->ocspAccessLocations, offsetof(identification_t, destroy)); DESTROY_IF(this->issuer); DESTROY_IF(this->subject); DESTROY_IF(this->public_key); free(this->subjectKeyID.ptr); free(this->certificate.ptr); free(this); } /* * Described in header. */ x509_t *x509_create_from_chunk(chunk_t chunk, u_int level) { private_x509_t *this = malloc_thing(private_x509_t); /* initialize */ this->subjectPublicKey = chunk_empty; this->public_key = NULL; this->subject = NULL; this->issuer = NULL; this->subjectAltNames = linked_list_create(); this->crlDistributionPoints = linked_list_create(); this->ocspAccessLocations = linked_list_create(); this->subjectKeyID = chunk_empty; this->authKeyID = chunk_empty; this->authKeySerialNumber = chunk_empty; this->authority_flags = AUTH_NONE; /* public functions */ this->public.equals = (bool (*) (const x509_t*,const x509_t*))equals; this->public.equals_subjectAltName = (bool (*) (const x509_t*,identification_t*))equals_subjectAltName; this->public.is_issuer = (bool (*) (const x509_t*,const x509_t*))is_issuer; this->public.is_valid = (err_t (*) (const x509_t*,time_t*))is_valid; this->public.is_ca = (bool (*) (const x509_t*))is_ca; this->public.is_self_signed = (bool (*) (const x509_t*))is_self_signed; this->public.is_ocsp_signer = (bool (*) (const x509_t*))is_ocsp_signer; this->public.get_certificate = (chunk_t (*) (const x509_t*))get_certificate; this->public.get_public_key = (rsa_public_key_t* (*) (const x509_t*))get_public_key; this->public.get_serialNumber = (chunk_t (*) (const x509_t*))get_serialNumber; this->public.get_subjectKeyID = (chunk_t (*) (const x509_t*))get_subjectKeyID; this->public.get_keyid = (chunk_t (*) (const x509_t*))get_keyid; this->public.get_issuer = (identification_t* (*) (const x509_t*))get_issuer; this->public.get_subject = (identification_t* (*) (const x509_t*))get_subject; this->public.set_until = (void (*) (x509_t*,time_t))set_until; this->public.get_until = (time_t (*) (const x509_t*))get_until; this->public.set_status = (void (*) (x509_t*,cert_status_t))set_status; this->public.get_status = (cert_status_t (*) (const x509_t*))get_status; this->public.add_authority_flags = (void (*) (x509_t*,u_int))add_authority_flags; this->public.get_authority_flags = (u_int (*) (x509_t*))get_authority_flags; this->public.has_authority_flag = (bool (*) (x509_t*,u_int))has_authority_flag; this->public.create_crluri_iterator = (iterator_t* (*) (const x509_t*))create_crluri_iterator; this->public.create_ocspuri_iterator = (iterator_t* (*) (const x509_t*))create_ocspuri_iterator; this->public.verify = (bool (*) (const x509_t*,const rsa_public_key_t*))verify; this->public.destroy = (void (*) (x509_t*))destroy; if (!parse_certificate(chunk, level, this)) { destroy(this); return NULL; } /* extract public key from certificate */ this->public_key = rsa_public_key_create_from_chunk(this->subjectPublicKey); if (this->public_key == NULL) { destroy(this); return NULL; } /* set trusted lifetime of public key to notAfter */ this->status = is_self_signed(this)? CERT_GOOD:CERT_UNDEFINED; this->until = this->notAfter; return &this->public; } /* * Described in header. */ x509_t *x509_create_from_file(const char *filename, const char *label) { bool pgp = FALSE; chunk_t chunk = chunk_empty; x509_t *cert = NULL; char cert_label[BUF_LEN]; snprintf(cert_label, BUF_LEN, "%s certificate", label); if (!pem_asn1_load_file(filename, NULL, cert_label, &chunk, &pgp)) return NULL; cert = x509_create_from_chunk(chunk, 0); if (cert == NULL) free(chunk.ptr); return cert; }