diff options
Diffstat (limited to 'src/pluto/ocsp.c')
-rw-r--r-- | src/pluto/ocsp.c | 2396 |
1 files changed, 1188 insertions, 1208 deletions
diff --git a/src/pluto/ocsp.c b/src/pluto/ocsp.c index 74b86bf19..80164fa1d 100644 --- a/src/pluto/ocsp.c +++ b/src/pluto/ocsp.c @@ -11,8 +11,6 @@ * 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. - * - * RCSID $Id: ocsp.c 4827 2009-01-09 01:36:13Z andreas $ */ #include <unistd.h> @@ -24,7 +22,13 @@ #include <fcntl.h> #include <freeswan.h> -#include <ipsec_policy.h> + +#include <library.h> +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> +#include <asn1/oid.h> +#include <crypto/rngs/rng.h> +#include <crypto/hashers/hasher.h> #include "constants.h" #include "defs.h" @@ -32,120 +36,116 @@ #include "x509.h" #include "crl.h" #include "ca.h" -#include "rnd.h" -#include "asn1.h" #include "certs.h" #include "smartcard.h" -#include <asn1/oid.h> #include "whack.h" -#include "pkcs1.h" #include "keys.h" #include "fetch.h" #include "ocsp.h" -#define NONCE_LENGTH 16 +#define NONCE_LENGTH 16 static const char *const cert_status_names[] = { - "good", - "revoked", - "unknown", - "undefined" + "good", + "revoked", + "unknown", + "undefined" }; static const char *const response_status_names[] = { - "successful", - "malformed request", - "internal error", - "try later", - "status #4", - "signature required", - "unauthorized" + "successful", + "malformed request", + "internal error", + "try later", + "status #4", + "signature required", + "unauthorized" }; /* response container */ typedef struct response response_t; struct response { - chunk_t tbs; - chunk_t responder_id_name; - chunk_t responder_id_key; - time_t produced_at; - chunk_t responses; - chunk_t nonce; - int algorithm; - chunk_t signature; + chunk_t tbs; + chunk_t responder_id_name; + chunk_t responder_id_key; + time_t produced_at; + chunk_t responses; + chunk_t nonce; + int algorithm; + chunk_t signature; }; const response_t empty_response = { - { NULL, 0 } , /* tbs */ - { NULL, 0 } , /* responder_id_name */ - { NULL, 0 } , /* responder_id_key */ - UNDEFINED_TIME, /* produced_at */ - { NULL, 0 } , /* single_response */ - { NULL, 0 } , /* nonce */ - OID_UNKNOWN , /* signature_algorithm */ - { NULL, 0 } /* signature */ + { NULL, 0 } , /* tbs */ + { NULL, 0 } , /* responder_id_name */ + { NULL, 0 } , /* responder_id_key */ + UNDEFINED_TIME, /* produced_at */ + { NULL, 0 } , /* single_response */ + { NULL, 0 } , /* nonce */ + OID_UNKNOWN , /* signature_algorithm */ + { NULL, 0 } /* signature */ }; /* single response container */ typedef struct single_response single_response_t; struct single_response { - single_response_t *next; - int hash_algorithm; - chunk_t issuer_name_hash; - chunk_t issuer_key_hash; - chunk_t serialNumber; - cert_status_t status; - time_t revocationTime; - crl_reason_t revocationReason; - time_t thisUpdate; - time_t nextUpdate; + single_response_t *next; + int hash_algorithm; + chunk_t issuer_name_hash; + chunk_t issuer_key_hash; + chunk_t serialNumber; + cert_status_t status; + time_t revocationTime; + crl_reason_t revocationReason; + time_t thisUpdate; + time_t nextUpdate; }; const single_response_t empty_single_response = { - NULL , /* *next */ - OID_UNKNOWN , /* hash_algorithm */ - { NULL, 0 } , /* issuer_name_hash */ - { NULL, 0 } , /* issuer_key_hash */ - { NULL, 0 } , /* serial_number */ - CERT_UNDEFINED , /* status */ - UNDEFINED_TIME , /* revocationTime */ - REASON_UNSPECIFIED, /* revocationReason */ - UNDEFINED_TIME , /* this_update */ - UNDEFINED_TIME /* next_update */ + NULL , /* *next */ + OID_UNKNOWN , /* hash_algorithm */ + { NULL, 0 } , /* issuer_name_hash */ + { NULL, 0 } , /* issuer_key_hash */ + { NULL, 0 } , /* serial_number */ + CERT_UNDEFINED , /* status */ + UNDEFINED_TIME , /* revocationTime */ + REASON_UNSPECIFIED, /* revocationReason */ + UNDEFINED_TIME , /* this_update */ + UNDEFINED_TIME /* next_update */ }; /* list of single requests */ typedef struct request_list request_list_t; struct request_list { - chunk_t request; - request_list_t *next; + chunk_t request; + request_list_t *next; }; /* some OCSP specific prefabricated ASN.1 constants */ static u_char ASN1_nonce_oid_str[] = { - 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02 + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02 }; -static const chunk_t ASN1_nonce_oid = strchunk(ASN1_nonce_oid_str); +static const chunk_t ASN1_nonce_oid = chunk_from_buf(ASN1_nonce_oid_str); static u_char ASN1_response_oid_str[] = { - 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04 + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04 }; -static const chunk_t ASN1_response_oid = strchunk(ASN1_response_oid_str); +static const chunk_t ASN1_response_oid = chunk_from_buf(ASN1_response_oid_str); static u_char ASN1_response_content_str[] = { - 0x04, 0x0D, - 0x30, 0x0B, - 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 + 0x04, 0x0D, + 0x30, 0x0B, + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 }; -static const chunk_t ASN1_response_content = strchunk(ASN1_response_content_str); +static const chunk_t ASN1_response_content = chunk_from_buf(ASN1_response_content_str); /* default OCSP uri */ static chunk_t ocsp_default_uri; @@ -158,57 +158,60 @@ static x509cert_t *ocsp_requestor_cert = NULL; static smartcard_t *ocsp_requestor_sc = NULL; -static const struct RSA_private_key *ocsp_requestor_pri = NULL; - -/* asn.1 definitions for parsing */ +static private_key_t *ocsp_requestor_key = NULL; +/** + * ASN.1 definition of ocspResponse + */ static const asn1Object_t ocspResponseObjects[] = { - { 0, "OCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "responseStatus", ASN1_ENUMERATED, ASN1_BODY }, /* 1 */ - { 1, "responseBytesContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 2 */ - { 2, "responseBytes", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ - { 3, "responseType", ASN1_OID, ASN1_BODY }, /* 4 */ - { 3, "response", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ - { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */ + { 0, "OCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "responseStatus", ASN1_ENUMERATED, ASN1_BODY }, /* 1 */ + { 1, "responseBytesContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 2 */ + { 2, "responseBytes", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ + { 3, "responseType", ASN1_OID, ASN1_BODY }, /* 4 */ + { 3, "response", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 6 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } }; - #define OCSP_RESPONSE_STATUS 1 -#define OCSP_RESPONSE_TYPE 4 -#define OCSP_RESPONSE 5 -#define OCSP_RESPONSE_ROOF 7 +#define OCSP_RESPONSE_TYPE 4 +#define OCSP_RESPONSE 5 +/** + * ASN.1 definition of basicResponse + */ static const asn1Object_t basicResponseObjects[] = { - { 0, "BasicOCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "tbsResponseData", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ - { 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE | - ASN1_DEF }, /* 2 */ - { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ - { 2, "responderIdContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */ - { 3, "responderIdByName", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ - { 2, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ - { 2, "responderIdContext", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 7 */ - { 3, "responderIdByKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 8 */ - { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ - { 2, "producedAt", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */ - { 2, "responses", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ - { 2, "responseExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 12 */ - { 3, "responseExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 13 */ - { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 14 */ - { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 15 */ - { 5, "critical", ASN1_BOOLEAN, ASN1_BODY | - ASN1_DEF }, /* 16 */ - { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 17 */ - { 4, "end loop", ASN1_EOC, ASN1_END }, /* 18 */ - { 2, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ - { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 20 */ - { 1, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 21 */ - { 1, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 22 */ - { 2, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 23 */ - { 3, "certificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 24 */ - { 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ - { 1, "end opt", ASN1_EOC, ASN1_END } /* 26 */ + { 0, "BasicOCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "tbsResponseData", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE | + ASN1_DEF }, /* 2 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 2, "responderIdContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */ + { 3, "responderIdByName", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ + { 2, "responderIdContext", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 7 */ + { 3, "responderIdByKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 8 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 2, "producedAt", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */ + { 2, "responses", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ + { 2, "responseExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 12 */ + { 3, "responseExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 13 */ + { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 14 */ + { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 15 */ + { 5, "critical", ASN1_BOOLEAN, ASN1_BODY | + ASN1_DEF }, /* 16 */ + { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 17 */ + { 4, "end loop", ASN1_EOC, ASN1_END }, /* 18 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 20 */ + { 1, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 21 */ + { 1, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 22 */ + { 2, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 23 */ + { 3, "certificate", ASN1_SEQUENCE, ASN1_RAW }, /* 24 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } }; - #define BASIC_RESPONSE_TBS_DATA 1 #define BASIC_RESPONSE_VERSION 3 #define BASIC_RESPONSE_ID_BY_NAME 5 @@ -221,1349 +224,1326 @@ static const asn1Object_t basicResponseObjects[] = { #define BASIC_RESPONSE_ALGORITHM 20 #define BASIC_RESPONSE_SIGNATURE 21 #define BASIC_RESPONSE_CERTIFICATE 24 -#define BASIC_RESPONSE_ROOF 27 +/** + * ASN.1 definition of responses + */ static const asn1Object_t responsesObjects[] = { - { 0, "responses", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ - { 1, "singleResponse", ASN1_EOC, ASN1_RAW }, /* 1 */ - { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */ + { 0, "responses", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "singleResponse", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } }; +#define RESPONSES_SINGLE_RESPONSE 1 -#define RESPONSES_SINGLE_RESPONSE 1 -#define RESPONSES_ROOF 3 - +/** + * ASN.1 definition of singleResponse + */ static const asn1Object_t singleResponseObjects[] = { - { 0, "singleResponse", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ - { 1, "certID", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ - { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ - { 2, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ - { 2, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */ - { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 5 */ - { 1, "certStatusGood", ASN1_CONTEXT_S_0, ASN1_OPT }, /* 6 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ - { 1, "certStatusRevoked", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 8 */ - { 2, "revocationTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 9 */ - { 2, "revocationReason", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 10 */ - { 3, "crlReason", ASN1_ENUMERATED, ASN1_BODY }, /* 11 */ - { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 13 */ - { 1, "certStatusUnknown", ASN1_CONTEXT_S_2, ASN1_OPT }, /* 14 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 15 */ - { 1, "thisUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */ - { 1, "nextUpdateContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 17 */ - { 2, "nextUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ - { 1, "singleExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 20 */ - { 2, "singleExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 21 */ - { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ - { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 23 */ - { 4, "critical", ASN1_BOOLEAN, ASN1_BODY | - ASN1_DEF }, /* 24 */ - { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 25 */ - { 2, "end loop", ASN1_EOC, ASN1_END }, /* 26 */ - { 1, "end opt", ASN1_EOC, ASN1_END } /* 27 */ + { 0, "singleResponse", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ + { 1, "certID", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 2, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ + { 2, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */ + { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "certStatusGood", ASN1_CONTEXT_S_0, ASN1_OPT }, /* 6 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 1, "certStatusRevoked", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 8 */ + { 2, "revocationTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 9 */ + { 2, "revocationReason", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 10 */ + { 3, "crlReason", ASN1_ENUMERATED, ASN1_BODY }, /* 11 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 13 */ + { 1, "certStatusUnknown", ASN1_CONTEXT_S_2, ASN1_OPT }, /* 14 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 15 */ + { 1, "thisUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */ + { 1, "nextUpdateContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 17 */ + { 2, "nextUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ + { 1, "singleExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 20 */ + { 2, "singleExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 21 */ + { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ + { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 23 */ + { 4, "critical", ASN1_BOOLEAN, ASN1_BODY | + ASN1_DEF }, /* 24 */ + { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 25 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 26 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 27 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } }; - -#define SINGLE_RESPONSE_ALGORITHM 2 -#define SINGLE_RESPONSE_ISSUER_NAME_HASH 3 -#define SINGLE_RESPONSE_ISSUER_KEY_HASH 4 -#define SINGLE_RESPONSE_SERIAL_NUMBER 5 -#define SINGLE_RESPONSE_CERT_STATUS_GOOD 6 -#define SINGLE_RESPONSE_CERT_STATUS_REVOKED 8 +#define SINGLE_RESPONSE_ALGORITHM 2 +#define SINGLE_RESPONSE_ISSUER_NAME_HASH 3 +#define SINGLE_RESPONSE_ISSUER_KEY_HASH 4 +#define SINGLE_RESPONSE_SERIAL_NUMBER 5 +#define SINGLE_RESPONSE_CERT_STATUS_GOOD 6 +#define SINGLE_RESPONSE_CERT_STATUS_REVOKED 8 #define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME 9 #define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON 11 -#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN 14 -#define SINGLE_RESPONSE_THIS_UPDATE 16 -#define SINGLE_RESPONSE_NEXT_UPDATE 18 -#define SINGLE_RESPONSE_EXT_ID 23 -#define SINGLE_RESPONSE_CRITICAL 24 -#define SINGLE_RESPONSE_EXT_VALUE 25 -#define SINGLE_RESPONSE_ROOF 28 - -/* build an ocsp location from certificate information +#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN 14 +#define SINGLE_RESPONSE_THIS_UPDATE 16 +#define SINGLE_RESPONSE_NEXT_UPDATE 18 +#define SINGLE_RESPONSE_EXT_ID 23 +#define SINGLE_RESPONSE_CRITICAL 24 +#define SINGLE_RESPONSE_EXT_VALUE 25 + +/* + * Build an ocsp location from certificate information * without unsharing its contents */ -static bool -build_ocsp_location(const x509cert_t *cert, ocsp_location_t *location) +static bool build_ocsp_location(const x509cert_t *cert, ocsp_location_t *location) { - static u_char digest[SHA1_DIGEST_SIZE]; /* temporary storage */ + hasher_t *hasher; + static u_char digest[HASH_SIZE_SHA1]; /* temporary storage */ + + location->uri = cert->accessLocation; - location->uri = cert->accessLocation; + if (location->uri.ptr == NULL) + { + ca_info_t *ca = get_ca_info(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID); + if (ca != NULL && ca->ocspuri != NULL) + { + location->uri = chunk_create(ca->ocspuri, strlen(ca->ocspuri)); + } + else + { /* abort if no ocsp location uri is defined */ + return FALSE; + } + } + + /* compute authNameID from as SHA-1 hash of issuer DN */ + location->authNameID = chunk_create(digest, HASH_SIZE_SHA1); + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (hasher == NULL) + { + return FALSE; + } + hasher->get_hash(hasher, cert->issuer, digest); + hasher->destroy(hasher); - if (location->uri.ptr == NULL) - { - ca_info_t *ca = get_ca_info(cert->issuer, cert->authKeySerialNumber - , cert->authKeyID); - if (ca != NULL && ca->ocspuri != NULL) - setchunk(location->uri, ca->ocspuri, strlen(ca->ocspuri)) - else - /* abort if no ocsp location uri is defined */ - return FALSE; - } - - setchunk(location->authNameID, digest, SHA1_DIGEST_SIZE); - compute_digest(cert->issuer, OID_SHA1, &location->authNameID); - - location->next = NULL; - location->issuer = cert->issuer; - location->authKeyID = cert->authKeyID; - location->authKeySerialNumber = cert->authKeySerialNumber; - - if (cert->authKeyID.ptr == NULL) - { - x509cert_t *authcert = get_authcert(cert->issuer - , cert->authKeySerialNumber, cert->authKeyID, AUTH_CA); - - if (authcert != NULL) + location->next = NULL; + location->issuer = cert->issuer; + location->authKeyID = cert->authKeyID; + location->authKeySerialNumber = cert->authKeySerialNumber; + + if (cert->authKeyID.ptr == NULL) { - location->authKeyID = authcert->subjectKeyID; - location->authKeySerialNumber = authcert->serialNumber; + x509cert_t *authcert = get_authcert(cert->issuer + , cert->authKeySerialNumber, cert->authKeyID, AUTH_CA); + + if (authcert != NULL) + { + location->authKeyID = authcert->subjectKeyID; + location->authKeySerialNumber = authcert->serialNumber; + } } - } - location->nonce = empty_chunk; - location->certinfo = NULL; + location->nonce = chunk_empty; + location->certinfo = NULL; - return TRUE; + return TRUE; } -/* - * compare two ocsp locations for equality +/** + * Compare two ocsp locations for equality */ -static bool -same_ocsp_location(const ocsp_location_t *a, const ocsp_location_t *b) +static bool same_ocsp_location(const ocsp_location_t *a, const ocsp_location_t *b) { - return ((a->authKeyID.ptr != NULL) - ? same_keyid(a->authKeyID, b->authKeyID) - : (same_dn(a->issuer, b->issuer) - && same_serial(a->authKeySerialNumber, b->authKeySerialNumber))) - && same_chunk(a->uri, b->uri); + return ((a->authKeyID.ptr != NULL) + ? same_keyid(a->authKeyID, b->authKeyID) + : (same_dn(a->issuer, b->issuer) + && same_serial(a->authKeySerialNumber, b->authKeySerialNumber))) + && chunk_equals(a->uri, b->uri); } -/* - * find an existing ocsp location in a chained list +/** + * Find an existing ocsp location in a chained list */ -ocsp_location_t* -get_ocsp_location(const ocsp_location_t * loc, ocsp_location_t *chain) +ocsp_location_t* get_ocsp_location(const ocsp_location_t * loc, ocsp_location_t *chain) { - while (chain != NULL) - { - if (same_ocsp_location(loc, chain)) - return chain; - chain = chain->next; - } - return NULL; + while (chain != NULL) + { + if (same_ocsp_location(loc, chain)) + return chain; + chain = chain->next; + } + return NULL; } - -/* retrieves the status of a cert from the ocsp cache + +/** + * Retrieves the status of a cert from the ocsp cache * returns CERT_UNDEFINED if no status is found */ -static cert_status_t -get_ocsp_status(const ocsp_location_t *loc, chunk_t serialNumber - ,time_t *nextUpdate, time_t *revocationTime, crl_reason_t *revocationReason) +static cert_status_t get_ocsp_status(const ocsp_location_t *loc, + chunk_t serialNumber, + time_t *nextUpdate, time_t *revocationTime, + crl_reason_t *revocationReason) { - ocsp_certinfo_t *certinfo, **certinfop; - int cmp = -1; + ocsp_certinfo_t *certinfo, **certinfop; + int cmp = -1; - /* find location */ - ocsp_location_t *location = get_ocsp_location(loc, ocsp_cache); + /* find location */ + ocsp_location_t *location = get_ocsp_location(loc, ocsp_cache); - if (location == NULL) - return CERT_UNDEFINED; - - /* traverse list of certinfos in increasing order */ - certinfop = &location->certinfo; - certinfo = *certinfop; + if (location == NULL) + return CERT_UNDEFINED; - while (certinfo != NULL) - { - cmp = cmp_chunk(serialNumber, certinfo->serialNumber); - if (cmp <= 0) - break; - certinfop = &certinfo->next; + /* traverse list of certinfos in increasing order */ + certinfop = &location->certinfo; certinfo = *certinfop; - } - if (cmp == 0) - { - *nextUpdate = certinfo->nextUpdate; - *revocationTime = certinfo->revocationTime; - *revocationReason = certinfo->revocationReason; - return certinfo->status; - } + while (certinfo != NULL) + { + cmp = chunk_compare(serialNumber, certinfo->serialNumber); + if (cmp <= 0) + break; + certinfop = &certinfo->next; + certinfo = *certinfop; + } + + if (cmp == 0) + { + *nextUpdate = certinfo->nextUpdate; + *revocationTime = certinfo->revocationTime; + *revocationReason = certinfo->revocationReason; + return certinfo->status; + } - return CERT_UNDEFINED; + return CERT_UNDEFINED; } -/* - * verify the ocsp status of a certificate +/** + * Verify the ocsp status of a certificate */ -cert_status_t -verify_by_ocsp(const x509cert_t *cert, time_t *until -, time_t *revocationDate, crl_reason_t *revocationReason) +cert_status_t verify_by_ocsp(const x509cert_t *cert, time_t *until, + time_t *revocationDate, + crl_reason_t *revocationReason) { - cert_status_t status; - ocsp_location_t location; - time_t nextUpdate = 0; - - *revocationDate = UNDEFINED_TIME; - *revocationReason = REASON_UNSPECIFIED; - - /* is an ocsp location defined? */ - if (!build_ocsp_location(cert, &location)) - return CERT_UNDEFINED; + cert_status_t status; + ocsp_location_t location; + time_t nextUpdate = 0; + + *revocationDate = UNDEFINED_TIME; + *revocationReason = REASON_UNSPECIFIED; + + /* is an ocsp location defined? */ + if (!build_ocsp_location(cert, &location)) + return CERT_UNDEFINED; + + lock_ocsp_cache("verify_by_ocsp"); + status = get_ocsp_status(&location, cert->serialNumber, &nextUpdate + , revocationDate, revocationReason); + unlock_ocsp_cache("verify_by_ocsp"); + + if (status == CERT_UNDEFINED || nextUpdate < time(NULL)) + { + plog("ocsp status is stale or not in cache"); + add_ocsp_fetch_request(&location, cert->serialNumber); - lock_ocsp_cache("verify_by_ocsp"); - status = get_ocsp_status(&location, cert->serialNumber, &nextUpdate - , revocationDate, revocationReason); - unlock_ocsp_cache("verify_by_ocsp"); - - if (status == CERT_UNDEFINED || nextUpdate < time(NULL)) - { - plog("ocsp status is stale or not in cache"); - add_ocsp_fetch_request(&location, cert->serialNumber); - - /* inititate fetching of ocsp status */ - wake_fetch_thread("verify_by_ocsp"); - } - *until = nextUpdate; - return status; + /* inititate fetching of ocsp status */ + wake_fetch_thread("verify_by_ocsp"); + } + *until = nextUpdate; + return status; } -/* - * check if an ocsp status is about to expire +/** + * Check if an ocsp status is about to expire */ -void -check_ocsp(void) +void check_ocsp(void) { - ocsp_location_t *location; - - lock_ocsp_cache("check_ocsp"); - location = ocsp_cache; - - while (location != NULL) - { - char buf[BUF_LEN]; - bool first = TRUE; - ocsp_certinfo_t *certinfo = location->certinfo; + ocsp_location_t *location; - while (certinfo != NULL) + lock_ocsp_cache("check_ocsp"); + location = ocsp_cache; + + while (location != NULL) { - if (!certinfo->once) - { - time_t time_left = certinfo->nextUpdate - time(NULL); + char buf[BUF_LEN]; + bool first = TRUE; + ocsp_certinfo_t *certinfo = location->certinfo; - DBG(DBG_CONTROL, - if (first) - { - dntoa(buf, BUF_LEN, location->issuer); - DBG_log("issuer: '%s'", buf); - if (location->authKeyID.ptr != NULL) + while (certinfo != NULL) + { + if (!certinfo->once) { - datatot(location->authKeyID.ptr, location->authKeyID.len - , ':', buf, BUF_LEN); - DBG_log("authkey: %s", buf); + time_t time_left = certinfo->nextUpdate - time(NULL); + + DBG(DBG_CONTROL, + if (first) + { + dntoa(buf, BUF_LEN, location->issuer); + DBG_log("issuer: '%s'", buf); + if (location->authKeyID.ptr != NULL) + { + datatot(location->authKeyID.ptr, location->authKeyID.len + , ':', buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + first = FALSE; + } + datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len + , ':', buf, BUF_LEN); + DBG_log("serial: %s, %ld seconds left", buf, time_left) + ) + + if (time_left < 2*crl_check_interval) + add_ocsp_fetch_request(location, certinfo->serialNumber); } - first = FALSE; - } - datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len - , ':', buf, BUF_LEN); - DBG_log("serial: %s, %ld seconds left", buf, time_left) - ) - - if (time_left < 2*crl_check_interval) - add_ocsp_fetch_request(location, certinfo->serialNumber); - } - certinfo = certinfo->next; + certinfo = certinfo->next; + } + location = location->next; } - location = location->next; - } - unlock_ocsp_cache("check_ocsp"); + unlock_ocsp_cache("check_ocsp"); } -/* +/** * frees the allocated memory of a certinfo struct */ -static void -free_certinfo(ocsp_certinfo_t *certinfo) +static void free_certinfo(ocsp_certinfo_t *certinfo) { - freeanychunk(certinfo->serialNumber); - pfree(certinfo); + free(certinfo->serialNumber.ptr); + free(certinfo); } -/* +/** * frees all certinfos in a chained list */ -static void -free_certinfos(ocsp_certinfo_t *chain) +static void free_certinfos(ocsp_certinfo_t *chain) { - ocsp_certinfo_t *certinfo; + ocsp_certinfo_t *certinfo; - while (chain != NULL) - { - certinfo = chain; - chain = chain->next; - free_certinfo(certinfo); - } + while (chain != NULL) + { + certinfo = chain; + chain = chain->next; + free_certinfo(certinfo); + } } -/* - * frees the memory allocated to an ocsp location including all certinfos +/** + * Frees the memory allocated to an ocsp location including all certinfos */ -static void -free_ocsp_location(ocsp_location_t* location) +static void free_ocsp_location(ocsp_location_t* location) { - freeanychunk(location->issuer); - freeanychunk(location->authNameID); - freeanychunk(location->authKeyID); - freeanychunk(location->authKeySerialNumber); - freeanychunk(location->uri); - free_certinfos(location->certinfo); - pfree(location); + free(location->issuer.ptr); + free(location->authNameID.ptr); + free(location->authKeyID.ptr); + free(location->authKeySerialNumber.ptr); + free(location->uri.ptr); + free_certinfos(location->certinfo); + free(location); } /* - * free a chained list of ocsp locations + * Free a chained list of ocsp locations */ -void -free_ocsp_locations(ocsp_location_t **chain) +void free_ocsp_locations(ocsp_location_t **chain) { - while (*chain != NULL) - { - ocsp_location_t *location = *chain; - *chain = location->next; - free_ocsp_location(location); - } + while (*chain != NULL) + { + ocsp_location_t *location = *chain; + *chain = location->next; + free_ocsp_location(location); + } } -/* - * free the ocsp cache +/** + * Free the ocsp cache */ -void -free_ocsp_cache(void) +void free_ocsp_cache(void) { - lock_ocsp_cache("free_ocsp_cache"); - free_ocsp_locations(&ocsp_cache); - unlock_ocsp_cache("free_ocsp_cache"); + lock_ocsp_cache("free_ocsp_cache"); + free_ocsp_locations(&ocsp_cache); + unlock_ocsp_cache("free_ocsp_cache"); } -/* - * frees the ocsp cache and global variables +/** + * Frees the ocsp cache and global variables */ -void -free_ocsp(void) +void free_ocsp(void) { - pfreeany(ocsp_default_uri.ptr); - free_ocsp_cache(); + free(ocsp_default_uri.ptr); + free_ocsp_cache(); } -/* - * list a chained list of ocsp_locations +/** + * List a chained list of ocsp_locations */ -void -list_ocsp_locations(ocsp_location_t *location, bool requests, bool utc -, bool strict) +void list_ocsp_locations(ocsp_location_t *location, bool requests, + bool utc, bool strict) { - bool first = TRUE; - - while (location != NULL) - { - ocsp_certinfo_t *certinfo = location->certinfo; + bool first = TRUE; - if (certinfo != NULL) + while (location != NULL) { - u_char buf[BUF_LEN]; - - if (first) - { - whack_log(RC_COMMENT, " "); - whack_log(RC_COMMENT, "List of OCSP %s:", requests? - "fetch requests":"responses"); - first = FALSE; - } - whack_log(RC_COMMENT, " "); - if (location->issuer.ptr != NULL) - { - dntoa(buf, BUF_LEN, location->issuer); - whack_log(RC_COMMENT, " issuer: '%s'", buf); - } - whack_log(RC_COMMENT, " uri: '%.*s'", (int)location->uri.len - , location->uri.ptr); - if (location->authNameID.ptr != NULL) - { - datatot(location->authNameID.ptr, location->authNameID.len, ':' - , buf, BUF_LEN); - whack_log(RC_COMMENT, " authname: %s", buf); - } - if (location->authKeyID.ptr != NULL) - { - datatot(location->authKeyID.ptr, location->authKeyID.len, ':' - , buf, BUF_LEN); - whack_log(RC_COMMENT, " authkey: %s", buf); - } - if (location->authKeySerialNumber.ptr != NULL) - { - datatot(location->authKeySerialNumber.ptr - , location->authKeySerialNumber.len, ':', buf, BUF_LEN); - whack_log(RC_COMMENT, " aserial: %s", buf); - } - while (certinfo != NULL) - { - char thisUpdate[TIMETOA_BUF]; - - strcpy(thisUpdate, timetoa(&certinfo->thisUpdate, utc)); - - if (requests) - { - whack_log(RC_COMMENT, "%s, trials: %d", thisUpdate - , certinfo->trials); - } - else if (certinfo->once) - { - whack_log(RC_COMMENT, "%s, onetime use%s", thisUpdate - , (certinfo->nextUpdate < time(NULL))? " (expired)": ""); - } - else + ocsp_certinfo_t *certinfo = location->certinfo; + + if (certinfo != NULL) { - whack_log(RC_COMMENT, "%s, until %s %s", thisUpdate - , timetoa(&certinfo->nextUpdate, utc) - , check_expiry(certinfo->nextUpdate, OCSP_WARNING_INTERVAL, strict)); + u_char buf[BUF_LEN]; + + if (first) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of OCSP %s:", requests? + "fetch requests":"responses"); + first = FALSE; + } + whack_log(RC_COMMENT, " "); + if (location->issuer.ptr != NULL) + { + dntoa(buf, BUF_LEN, location->issuer); + whack_log(RC_COMMENT, " issuer: '%s'", buf); + } + whack_log(RC_COMMENT, " uri: '%.*s'", (int)location->uri.len + , location->uri.ptr); + if (location->authNameID.ptr != NULL) + { + datatot(location->authNameID.ptr, location->authNameID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authname: %s", buf); + } + if (location->authKeyID.ptr != NULL) + { + datatot(location->authKeyID.ptr, location->authKeyID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authkey: %s", buf); + } + if (location->authKeySerialNumber.ptr != NULL) + { + datatot(location->authKeySerialNumber.ptr + , location->authKeySerialNumber.len, ':', buf, BUF_LEN); + whack_log(RC_COMMENT, " aserial: %s", buf); + } + while (certinfo != NULL) + { + char thisUpdate[BUF_LEN]; + + snprintf(thisUpdate, BUF_LEN, "%T", &certinfo->thisUpdate, utc); + + if (requests) + { + whack_log(RC_COMMENT, "%s, trials: %d", thisUpdate + , certinfo->trials); + } + else if (certinfo->once) + { + whack_log(RC_COMMENT, "%s, onetime use%s", thisUpdate + , (certinfo->nextUpdate < time(NULL))? " (expired)": ""); + } + else + { + whack_log(RC_COMMENT, "%s, until %T %s", thisUpdate + , &certinfo->nextUpdate, utc + , check_expiry(certinfo->nextUpdate, OCSP_WARNING_INTERVAL, strict)); + } + datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " serial: %s, %s", buf + , cert_status_names[certinfo->status]); + certinfo = certinfo->next; + } } - datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len, ':' - , buf, BUF_LEN); - whack_log(RC_COMMENT, " serial: %s, %s", buf - , cert_status_names[certinfo->status]); - certinfo = certinfo->next; - } + location = location->next; } - location = location->next; - } } -/* - * list the ocsp cache +/** + * List the ocsp cache */ -void -list_ocsp_cache(bool utc, bool strict) +void list_ocsp_cache(bool utc, bool strict) { - lock_ocsp_cache("list_ocsp_cache"); - list_ocsp_locations(ocsp_cache, FALSE, utc, strict); - unlock_ocsp_cache("list_ocsp_cache"); + lock_ocsp_cache("list_ocsp_cache"); + list_ocsp_locations(ocsp_cache, FALSE, utc, strict); + unlock_ocsp_cache("list_ocsp_cache"); } -static bool -get_ocsp_requestor_cert(ocsp_location_t *location) +static bool get_ocsp_requestor_cert(ocsp_location_t *location) { - x509cert_t *cert = NULL; + x509cert_t *cert = NULL; - /* initialize temporary static storage */ - ocsp_requestor_cert = NULL; - ocsp_requestor_sc = NULL; - ocsp_requestor_pri = NULL; + /* initialize temporary static storage */ + ocsp_requestor_cert = NULL; + ocsp_requestor_sc = NULL; + ocsp_requestor_key = NULL; - for (;;) - { - char buf[BUF_LEN]; - - /* looking for a certificate from the same issuer */ - cert = get_x509cert(location->issuer, location->authKeySerialNumber - ,location->authKeyID, cert); - if (cert == NULL) - break; - - DBG(DBG_CONTROL, - dntoa(buf, BUF_LEN, cert->subject); - DBG_log("candidate: '%s'", buf); - ) - - if (cert->smartcard) + for (;;) { - /* look for a matching private key on a smartcard */ - smartcard_t *sc = scx_get(cert); + char buf[BUF_LEN]; + + /* looking for a certificate from the same issuer */ + cert = get_x509cert(location->issuer, location->authKeySerialNumber + ,location->authKeyID, cert); + if (cert == NULL) + break; - if (sc != NULL) - { DBG(DBG_CONTROL, - DBG_log("matching smartcard found") + dntoa(buf, BUF_LEN, cert->subject); + DBG_log("candidate: '%s'", buf); ) - if (sc->valid) + + if (cert->smartcard) { - ocsp_requestor_cert = cert; - ocsp_requestor_sc = sc; - return TRUE; + /* look for a matching private key on a smartcard */ + smartcard_t *sc = scx_get(cert); + + if (sc != NULL) + { + DBG(DBG_CONTROL, + DBG_log("matching smartcard found") + ) + if (sc->valid) + { + ocsp_requestor_cert = cert; + ocsp_requestor_sc = sc; + return TRUE; + } + plog("unable to sign ocsp request without PIN"); + } } - plog("unable to sign ocsp request without PIN"); - } - } - else - { - /* look for a matching private key in the chained list */ - const struct RSA_private_key *pri = get_x509_private_key(cert); + else + { + /* look for a matching private key in the chained list */ + private_key_t *private = get_x509_private_key(cert); - if (pri != NULL) - { - DBG(DBG_CONTROL, - DBG_log("matching private key found") - ) - ocsp_requestor_cert = cert; - ocsp_requestor_pri = pri; - return TRUE; - } + if (private != NULL) + { + DBG(DBG_CONTROL, + DBG_log("matching private key found") + ) + ocsp_requestor_cert = cert; + ocsp_requestor_key = private; + return TRUE; + } + } } - } - return FALSE; + return FALSE; } -static chunk_t -generate_signature(chunk_t digest, smartcard_t *sc - , const RSA_private_key_t *pri) +static chunk_t sc_build_sha1_signature(chunk_t tbs, smartcard_t *sc) { - chunk_t sigdata; - u_char *pos; - size_t siglen = 0; - - if (sc != NULL) - { - /* RSA signature is done on smartcard */ + hasher_t *hasher; + u_char *pos; + u_char digest_buf[HASH_SIZE_SHA1]; + chunk_t digest = chunk_from_buf(digest_buf); + chunk_t digest_info, sigdata; + size_t siglen = 0; if (!scx_establish_context(sc) || !scx_login(sc)) { - scx_release_context(sc); - return empty_chunk; + scx_release_context(sc); + return chunk_empty; } siglen = scx_get_keylength(sc); if (siglen == 0) { - plog("failed to get keylength from smartcard"); - scx_release_context(sc); - return empty_chunk; + plog("failed to get keylength from smartcard"); + scx_release_context(sc); + return chunk_empty; } DBG(DBG_CONTROL | DBG_CRYPT, - DBG_log("signing hash with RSA key from smartcard (slot: %d, id: %s)" - , (int)sc->slot, sc->id) + DBG_log("signing hash with RSA key from smartcard (slot: %d, id: %s)" + , (int)sc->slot, sc->id) ) - pos = build_asn1_object(&sigdata, ASN1_BIT_STRING, 1 + siglen); + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (hasher == NULL) + { + return chunk_empty; + } + hasher->get_hash(hasher, tbs, digest_buf); + hasher->destroy(hasher); + + /* according to PKCS#1 v2.1 digest must be packaged into + * an ASN.1 structure for encryption + */ + digest_info = asn1_wrap(ASN1_SEQUENCE, "cm" + , asn1_algorithmIdentifier(OID_SHA1) + , asn1_simple_object(ASN1_OCTET_STRING, digest)); + + pos = asn1_build_object(&sigdata, ASN1_BIT_STRING, 1 + siglen); *pos++ = 0x00; - scx_sign_hash(sc, digest.ptr, digest.len, pos, siglen); + scx_sign_hash(sc, digest_info.ptr, digest_info.len, pos, siglen); + free(digest_info.ptr); + if (!pkcs11_keep_state) - scx_release_context(sc); - } - else - { - /* RSA signature is done in software */ - siglen = pri->pub.k; - pos = build_asn1_object(&sigdata, ASN1_BIT_STRING, 1 + siglen); - *pos++ = 0x00; - sign_hash(pri, digest.ptr, digest.len, pos, siglen); - } - return sigdata; + { + scx_release_context(sc); + } + return sigdata; } -/* - * build signature into ocsp request - * gets built only if a request cert with - * a corresponding private key is found +/** + * build signature into ocsp request gets built only if a request cert + * with a corresponding private key is found */ -static chunk_t -build_signature(chunk_t tbsRequest) +static chunk_t build_signature(chunk_t tbsRequest) { - chunk_t sigdata, certs; - chunk_t digest_info; - - u_char digest_buf[MAX_DIGEST_LEN]; - chunk_t digest_raw = { digest_buf, MAX_DIGEST_LEN }; - - if (!compute_digest(tbsRequest, OID_SHA1, &digest_raw)) - return empty_chunk; - - /* according to PKCS#1 v2.1 digest must be packaged into - * an ASN.1 structure for encryption - */ - digest_info = asn1_wrap(ASN1_SEQUENCE, "cm" - , ASN1_sha1_id - , asn1_simple_object(ASN1_OCTET_STRING, digest_raw)); - - /* generate the RSA signature */ - sigdata = generate_signature(digest_info - , ocsp_requestor_sc - , ocsp_requestor_pri); - freeanychunk(digest_info); - - /* has the RSA signature generation been successful? */ - if (sigdata.ptr == NULL) - return empty_chunk; - - /* include our certificate */ - certs = asn1_wrap(ASN1_CONTEXT_C_0, "m" - , asn1_simple_object(ASN1_SEQUENCE - , ocsp_requestor_cert->certificate - ) - ); - - /* build signature comprising algorithm, signature and cert */ - return asn1_wrap(ASN1_CONTEXT_C_0, "m" - , asn1_wrap(ASN1_SEQUENCE, "cmm" - , ASN1_sha1WithRSA_id - , sigdata - , certs - ) - ); + chunk_t sigdata, certs; + + if (ocsp_requestor_sc != NULL) + { + /* RSA signature is done on smartcard */ + sigdata = sc_build_sha1_signature(tbsRequest, ocsp_requestor_sc); + } + else + { + /* RSA signature is done in software */ + sigdata = x509_build_signature(tbsRequest, OID_SHA1, ocsp_requestor_key, + TRUE); + } + if (sigdata.ptr == NULL) + { + return chunk_empty; + } + + /* include our certificate */ + certs = asn1_wrap(ASN1_CONTEXT_C_0, "m" + , asn1_simple_object(ASN1_SEQUENCE + , ocsp_requestor_cert->certificate + ) + ); + + /* build signature comprising algorithm, signature and cert */ + return asn1_wrap(ASN1_CONTEXT_C_0, "m" + , asn1_wrap(ASN1_SEQUENCE, "cmm" + , asn1_algorithmIdentifier(OID_SHA1_WITH_RSA) + , sigdata + , certs + ) + ); } -/* build request (into requestList) +/** + * Build request (into requestList) * no singleRequestExtensions used */ -static chunk_t -build_request(ocsp_location_t *location, ocsp_certinfo_t *certinfo) +static chunk_t build_request(ocsp_location_t *location, ocsp_certinfo_t *certinfo) { - chunk_t reqCert = asn1_wrap(ASN1_SEQUENCE, "cmmm" - , ASN1_sha1_id - , asn1_simple_object(ASN1_OCTET_STRING, location->authNameID) - , asn1_simple_object(ASN1_OCTET_STRING, location->authKeyID) - , asn1_simple_object(ASN1_INTEGER, certinfo->serialNumber)); + chunk_t reqCert = asn1_wrap(ASN1_SEQUENCE, "cmmm" + , asn1_algorithmIdentifier(OID_SHA1) + , asn1_simple_object(ASN1_OCTET_STRING, location->authNameID) + , asn1_simple_object(ASN1_OCTET_STRING, location->authKeyID) + , asn1_simple_object(ASN1_INTEGER, certinfo->serialNumber)); - return asn1_wrap(ASN1_SEQUENCE, "m", reqCert); + return asn1_wrap(ASN1_SEQUENCE, "m", reqCert); } -/* +/** * build requestList (into TBSRequest) */ -static chunk_t -build_request_list(ocsp_location_t *location) +static chunk_t build_request_list(ocsp_location_t *location) { - chunk_t requestList; - request_list_t *reqs = NULL; - ocsp_certinfo_t *certinfo = location->certinfo; - u_char *pos; - - size_t datalen = 0; - - /* build content */ - while (certinfo != NULL) - { - /* build request for every certificate in list - * and store them in a chained list - */ - request_list_t *req = alloc_thing(request_list_t, "ocsp request"); + chunk_t requestList; + request_list_t *reqs = NULL; + ocsp_certinfo_t *certinfo = location->certinfo; + u_char *pos; - req->request = build_request(location, certinfo); - req->next = reqs; - reqs = req; + size_t datalen = 0; - datalen += req->request.len; - certinfo = certinfo->next; - } + /* build content */ + while (certinfo != NULL) + { + /* build request for every certificate in list + * and store them in a chained list + */ + request_list_t *req = malloc_thing(request_list_t); + + req->request = build_request(location, certinfo); + req->next = reqs; + reqs = req; + + datalen += req->request.len; + certinfo = certinfo->next; + } - pos = build_asn1_object(&requestList, ASN1_SEQUENCE - , datalen); + pos = asn1_build_object(&requestList, ASN1_SEQUENCE, datalen); - /* copy all in chained list, free list afterwards */ - while (reqs != NULL) - { - request_list_t *req = reqs; + /* copy all in chained list, free list afterwards */ + while (reqs != NULL) + { + request_list_t *req = reqs; - mv_chunk(&pos, req->request); - reqs = reqs->next; - pfree(req); - } + mv_chunk(&pos, req->request); + reqs = reqs->next; + free(req); + } - return requestList; + return requestList; } -/* - * build requestorName (into TBSRequest) +/** + * Build requestorName (into TBSRequest) */ -static chunk_t -build_requestor_name(void) +static chunk_t build_requestor_name(void) { - return asn1_wrap(ASN1_CONTEXT_C_1, "m" - , asn1_simple_object(ASN1_CONTEXT_C_4 - , ocsp_requestor_cert->subject)); + return asn1_wrap(ASN1_CONTEXT_C_1, "m" + , asn1_simple_object(ASN1_CONTEXT_C_4 + , ocsp_requestor_cert->subject)); } -/* +/** * build nonce extension (into requestExtensions) */ -static chunk_t -build_nonce_extension(ocsp_location_t *location) +static chunk_t build_nonce_extension(ocsp_location_t *location) { - /* generate a random nonce */ - location->nonce.ptr = alloc_bytes(NONCE_LENGTH, "ocsp nonce"), - location->nonce.len = NONCE_LENGTH; - get_rnd_bytes(location->nonce.ptr, NONCE_LENGTH); - - return asn1_wrap(ASN1_SEQUENCE, "cm" - , ASN1_nonce_oid - , asn1_simple_object(ASN1_OCTET_STRING, location->nonce)); + rng_t *rng; + + /* generate a random nonce */ + location->nonce.ptr = malloc(NONCE_LENGTH), + location->nonce.len = NONCE_LENGTH; + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + rng->get_bytes(rng, location->nonce.len, location->nonce.ptr); + rng->destroy(rng); + + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_nonce_oid + , asn1_simple_object(ASN1_OCTET_STRING, location->nonce)); } -/* - * build requestExtensions (into TBSRequest) +/** + * Build requestExtensions (into TBSRequest) */ -static chunk_t -build_request_ext(ocsp_location_t *location) +static chunk_t build_request_ext(ocsp_location_t *location) { - return asn1_wrap(ASN1_CONTEXT_C_2, "m" - , asn1_wrap(ASN1_SEQUENCE, "mm" - , build_nonce_extension(location) - , asn1_wrap(ASN1_SEQUENCE, "cc" - , ASN1_response_oid - , ASN1_response_content - ) - ) - ); + return asn1_wrap(ASN1_CONTEXT_C_2, "m" + , asn1_wrap(ASN1_SEQUENCE, "mm" + , build_nonce_extension(location) + , asn1_wrap(ASN1_SEQUENCE, "cc" + , ASN1_response_oid + , ASN1_response_content + ) + ) + ); } -/* - * build TBSRequest (into OCSPRequest) +/** + * Build TBSRequest (into OCSPRequest) */ -static chunk_t -build_tbs_request(ocsp_location_t *location, bool has_requestor_cert) +static chunk_t build_tbs_request(ocsp_location_t *location, bool has_requestor_cert) { - /* version is skipped since the default is ok */ - return asn1_wrap(ASN1_SEQUENCE, "mmm" - , (has_requestor_cert) - ? build_requestor_name() - : empty_chunk - , build_request_list(location) - , build_request_ext(location)); + /* version is skipped since the default is ok */ + return asn1_wrap(ASN1_SEQUENCE, "mmm" + , (has_requestor_cert) + ? build_requestor_name() + : chunk_empty + , build_request_list(location) + , build_request_ext(location)); } -/* assembles an ocsp request to given location +/** + * Assembles an ocsp request to given location * and sets nonce field in location to the sent nonce */ -chunk_t -build_ocsp_request(ocsp_location_t *location) +chunk_t build_ocsp_request(ocsp_location_t *location) { - bool has_requestor_cert; - chunk_t tbsRequest, signature; - char buf[BUF_LEN]; - - DBG(DBG_CONTROL, - DBG_log("assembling ocsp request"); - dntoa(buf, BUF_LEN, location->issuer); - DBG_log("issuer: '%s'", buf); - if (location->authKeyID.ptr != NULL) - { - datatot(location->authKeyID.ptr, location->authKeyID.len, ':' - , buf, BUF_LEN); - DBG_log("authkey: %s", buf); - } - ) - lock_certs_and_keys("build_ocsp_request"); + bool has_requestor_cert; + chunk_t tbsRequest, signature; + char buf[BUF_LEN]; - /* looks for requestor cert and matching private key */ - has_requestor_cert = get_ocsp_requestor_cert(location); + DBG(DBG_CONTROL, + DBG_log("assembling ocsp request"); + dntoa(buf, BUF_LEN, location->issuer); + DBG_log("issuer: '%s'", buf); + if (location->authKeyID.ptr != NULL) + { + datatot(location->authKeyID.ptr, location->authKeyID.len, ':' + , buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + ) + lock_certs_and_keys("build_ocsp_request"); + + /* looks for requestor cert and matching private key */ + has_requestor_cert = get_ocsp_requestor_cert(location); - /* build content */ - tbsRequest = build_tbs_request(location, has_requestor_cert); + /* build content */ + tbsRequest = build_tbs_request(location, has_requestor_cert); - /* sign tbsReuqest */ - signature = (has_requestor_cert)? build_signature(tbsRequest) - : empty_chunk; + /* sign tbsReuqest */ + signature = (has_requestor_cert)? build_signature(tbsRequest) + : chunk_empty; - unlock_certs_and_keys("build_ocsp_request"); + unlock_certs_and_keys("build_ocsp_request"); - return asn1_wrap(ASN1_SEQUENCE, "mm" - , tbsRequest - , signature); + return asn1_wrap(ASN1_SEQUENCE, "mm" + , tbsRequest + , signature); } -/* - * check if the OCSP response has a valid signature +/** + * Check if the OCSP response has a valid signature */ -static bool -valid_ocsp_response(response_t *res) +static bool valid_ocsp_response(response_t *res) { - int pathlen; - x509cert_t *authcert; + int pathlen; + x509cert_t *authcert; - lock_authcert_list("valid_ocsp_response"); + lock_authcert_list("valid_ocsp_response"); - authcert = get_authcert(res->responder_id_name, empty_chunk - , res->responder_id_key, AUTH_OCSP | AUTH_CA); - - if (authcert == NULL) - { - plog("no matching ocsp signer cert found"); - unlock_authcert_list("valid_ocsp_response"); - return FALSE; - } - DBG(DBG_CONTROL, - DBG_log("ocsp signer cert found") - ) - - if (!check_signature(res->tbs, res->signature, res->algorithm - , res->algorithm, authcert)) - { - plog("signature of ocsp response is invalid"); - unlock_authcert_list("valid_ocsp_response"); - return FALSE; - } - DBG(DBG_CONTROL, - DBG_log("signature of ocsp response is valid") - ) - - - for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) - { - u_char buf[BUF_LEN]; - err_t ugh = NULL; - time_t until; - - x509cert_t *cert = authcert; - - DBG(DBG_CONTROL, - dntoa(buf, BUF_LEN, cert->subject); - DBG_log("subject: '%s'",buf); - dntoa(buf, BUF_LEN, cert->issuer); - DBG_log("issuer: '%s'",buf); - if (cert->authKeyID.ptr != NULL) - { - datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':' - , buf, BUF_LEN); - DBG_log("authkey: %s", buf); - } - ) - - ugh = check_validity(authcert, &until); - - if (ugh != NULL) - { - plog("%s", ugh); - unlock_authcert_list("valid_ocsp_response"); - return FALSE; - } - - DBG(DBG_CONTROL, - DBG_log("certificate is valid") - ) - - authcert = get_authcert(cert->issuer, cert->authKeySerialNumber - , cert->authKeyID, AUTH_CA); + authcert = get_authcert(res->responder_id_name, chunk_empty + , res->responder_id_key, AUTH_OCSP | AUTH_CA); if (authcert == NULL) { - plog("issuer cacert not found"); - unlock_authcert_list("valid_ocsp_response"); - return FALSE; + plog("no matching ocsp signer cert found"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; } DBG(DBG_CONTROL, - DBG_log("issuer cacert found") + DBG_log("ocsp signer cert found") ) - if (!check_signature(cert->tbsCertificate, cert->signature - , cert->algorithm, cert->algorithm, authcert)) + if (!x509_check_signature(res->tbs, res->signature, res->algorithm, authcert)) { - plog("certificate signature is invalid"); - unlock_authcert_list("valid_ocsp_response"); - return FALSE; + plog("signature of ocsp response is invalid"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; } DBG(DBG_CONTROL, - DBG_log("certificate signature is valid") + DBG_log("signature of ocsp response is valid") ) - /* check if cert is self-signed */ - if (same_dn(cert->issuer, cert->subject)) + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) { - DBG(DBG_CONTROL, - DBG_log("reached self-signed root ca") - ) - unlock_authcert_list("valid_ocsp_response"); - return TRUE; + u_char buf[BUF_LEN]; + err_t ugh = NULL; + time_t until; + + x509cert_t *cert = authcert; + + DBG(DBG_CONTROL, + dntoa(buf, BUF_LEN, cert->subject); + DBG_log("subject: '%s'",buf); + dntoa(buf, BUF_LEN, cert->issuer); + DBG_log("issuer: '%s'",buf); + if (cert->authKeyID.ptr != NULL) + { + datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':' + , buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + ) + + ugh = check_validity(authcert, &until); + + if (ugh != NULL) + { + plog("%s", ugh); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + + DBG(DBG_CONTROL, + DBG_log("certificate is valid") + ) + + authcert = get_authcert(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID, AUTH_CA); + + if (authcert == NULL) + { + plog("issuer cacert not found"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("issuer cacert found") + ) + + if (!x509_check_signature(cert->tbsCertificate, cert->signature, + cert->algorithm, authcert)) + { + plog("certificate signature is invalid"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("certificate signature is valid") + ) + + /* check if cert is self-signed */ + if (same_dn(cert->issuer, cert->subject)) + { + DBG(DBG_CONTROL, + DBG_log("reached self-signed root ca") + ) + unlock_authcert_list("valid_ocsp_response"); + return TRUE; + } } - } - plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); - unlock_authcert_list("valid_ocsp_response"); - return FALSE; + plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; } -/* - * parse a basic OCSP response +/** + * Parse a basic OCSP response */ -static bool -parse_basic_ocsp_response(chunk_t blob, int level0, response_t *res) +static bool parse_basic_ocsp_response(chunk_t blob, int level0, response_t *res) { - asn1_ctx_t ctx; - bool critical; - chunk_t object; - u_int level, version; - u_char buf[BUF_LEN]; - int objectID = 0; - int extn_oid = OID_UNKNOWN; - - asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); - - while (objectID < BASIC_RESPONSE_ROOF) - { - if (!extract_object(basicResponseObjects, &objectID, &object, &level, &ctx)) - return FALSE; - - switch (objectID) + asn1_parser_t *parser; + chunk_t object; + u_int version; + u_char buf[BUF_LEN]; + int objectID; + int extn_oid = OID_UNKNOWN; + bool success = FALSE; + bool critical; + + parser = asn1_parser_create(basicResponseObjects, blob); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) { - case BASIC_RESPONSE_TBS_DATA: - res->tbs = object; - break; - case BASIC_RESPONSE_VERSION: - version = (object.len)? (1 + (u_int)*object.ptr) : 1; - if (version != OCSP_BASIC_RESPONSE_VERSION) - { - plog("wrong ocsp basic response version (version= %i)", version); - return FALSE; - } - break; - case BASIC_RESPONSE_ID_BY_NAME: - res->responder_id_name = object; - DBG(DBG_PARSING, - dntoa(buf, BUF_LEN, object); - DBG_log(" '%s'",buf) - ) - break; - case BASIC_RESPONSE_ID_BY_KEY: - res->responder_id_key = object; - break; - case BASIC_RESPONSE_PRODUCED_AT: - res->produced_at = asn1totime(&object, ASN1_GENERALIZEDTIME); - break; - case BASIC_RESPONSE_RESPONSES: - res->responses = object; - break; - case BASIC_RESPONSE_EXT_ID: - extn_oid = known_oid(object); - break; - case BASIC_RESPONSE_CRITICAL: - critical = object.len && *object.ptr; - DBG(DBG_PARSING, - DBG_log(" %s",(critical)?"TRUE":"FALSE"); - ) - break; - case BASIC_RESPONSE_EXT_VALUE: - if (extn_oid == OID_NONCE) - res->nonce = object; - break; - case BASIC_RESPONSE_ALGORITHM: - res->algorithm = parse_algorithmIdentifier(object, level+1, NULL); - break; - case BASIC_RESPONSE_SIGNATURE: - res->signature = object; - break; - case BASIC_RESPONSE_CERTIFICATE: - { - chunk_t blob; - x509cert_t *cert = alloc_thing(x509cert_t, "ocspcert"); - - clonetochunk(blob, object.ptr, object.len, "ocspcert blob"); - *cert = empty_x509cert; - - if (parse_x509cert(blob, level+1, cert) - && cert->isOcspSigner - && trust_authcert_candidate(cert, NULL)) + switch (objectID) { - add_authcert(cert, AUTH_OCSP); - } - else - { - DBG(DBG_CONTROL | DBG_PARSING, - DBG_log("embedded ocsp certificate rejected") - ) - free_x509cert(cert); + case BASIC_RESPONSE_TBS_DATA: + res->tbs = object; + break; + case BASIC_RESPONSE_VERSION: + version = (object.len)? (1 + (u_int)*object.ptr) : 1; + if (version != OCSP_BASIC_RESPONSE_VERSION) + { + plog("wrong ocsp basic response version (version= %i)", version); + goto end; + } + break; + case BASIC_RESPONSE_ID_BY_NAME: + res->responder_id_name = object; + DBG(DBG_PARSING, + dntoa(buf, BUF_LEN, object); + DBG_log(" '%s'",buf) + ) + break; + case BASIC_RESPONSE_ID_BY_KEY: + res->responder_id_key = object; + break; + case BASIC_RESPONSE_PRODUCED_AT: + res->produced_at = asn1_to_time(&object, ASN1_GENERALIZEDTIME); + break; + case BASIC_RESPONSE_RESPONSES: + res->responses = object; + break; + case BASIC_RESPONSE_EXT_ID: + extn_oid = asn1_known_oid(object); + break; + case BASIC_RESPONSE_CRITICAL: + critical = object.len && *object.ptr; + DBG(DBG_PARSING, + DBG_log(" %s",(critical)?"TRUE":"FALSE"); + ) + break; + case BASIC_RESPONSE_EXT_VALUE: + if (extn_oid == OID_NONCE) + res->nonce = object; + break; + case BASIC_RESPONSE_ALGORITHM: + res->algorithm = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser)+1, NULL); + break; + case BASIC_RESPONSE_SIGNATURE: + res->signature = object; + break; + case BASIC_RESPONSE_CERTIFICATE: + { + chunk_t blob = chunk_clone(object); + x509cert_t *cert = malloc_thing(x509cert_t); + + *cert = empty_x509cert; + + if (parse_x509cert(blob, parser->get_level(parser)+1, cert) + && cert->isOcspSigner + && trust_authcert_candidate(cert, NULL)) + { + add_authcert(cert, AUTH_OCSP); + } + else + { + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log("embedded ocsp certificate rejected") + ) + free_x509cert(cert); + } + } + break; } - } - break; } - objectID++; - } - return TRUE; + success = parser->success(parser); + +end: + parser->destroy(parser); + return success; + } -/* - * parse an ocsp response and return the result as a response_t struct +/** + * Parse an ocsp response and return the result as a response_t struct */ -static response_status -parse_ocsp_response(chunk_t blob, response_t * res) +static response_status parse_ocsp_response(chunk_t blob, response_t * res) { - asn1_ctx_t ctx; - chunk_t object; - u_int level; - int objectID = 0; - int ocspResponseType = OID_UNKNOWN; - response_status rStatus = STATUS_INTERNALERROR; - - asn1_init(&ctx, blob, 0, FALSE, DBG_RAW); - - while (objectID < OCSP_RESPONSE_ROOF) - { - if (!extract_object(ocspResponseObjects, &objectID, &object, &level, &ctx)) - return STATUS_INTERNALERROR; - - switch (objectID) { - case OCSP_RESPONSE_STATUS: - rStatus = (response_status) *object.ptr; - - switch (rStatus) - { - case STATUS_SUCCESSFUL: - break; - case STATUS_MALFORMEDREQUEST: - case STATUS_INTERNALERROR: - case STATUS_TRYLATER: - case STATUS_SIGREQUIRED: - case STATUS_UNAUTHORIZED: - plog("ocsp response: server said '%s'" - , response_status_names[rStatus]); - return rStatus; - default: - return STATUS_INTERNALERROR; - } - break; - case OCSP_RESPONSE_TYPE: - ocspResponseType = known_oid(object); - break; - case OCSP_RESPONSE: - { - switch (ocspResponseType) { - case OID_BASIC: - if (!parse_basic_ocsp_response(object, level+1, res)) - return STATUS_INTERNALERROR; - break; - default: - DBG(DBG_CONTROL, - DBG_log("ocsp response is not of type BASIC"); - DBG_dump_chunk("ocsp response OID: ", object); - ) - return STATUS_INTERNALERROR; + asn1_parser_t *parser; + chunk_t object; + int objectID; + int ocspResponseType = OID_UNKNOWN; + bool success = FALSE; + response_status rStatus = STATUS_INTERNALERROR; + + parser = asn1_parser_create(ocspResponseObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) { + case OCSP_RESPONSE_STATUS: + rStatus = (response_status) *object.ptr; + + switch (rStatus) + { + case STATUS_SUCCESSFUL: + break; + case STATUS_MALFORMEDREQUEST: + case STATUS_INTERNALERROR: + case STATUS_TRYLATER: + case STATUS_SIGREQUIRED: + case STATUS_UNAUTHORIZED: + plog("ocsp response: server said '%s'" + , response_status_names[rStatus]); + goto end; + default: + goto end; + } + break; + case OCSP_RESPONSE_TYPE: + ocspResponseType = asn1_known_oid(object); + break; + case OCSP_RESPONSE: + { + switch (ocspResponseType) { + case OID_BASIC: + success = parse_basic_ocsp_response(object, + parser->get_level(parser)+1, res); + break; + default: + DBG(DBG_CONTROL, + DBG_log("ocsp response is not of type BASIC"); + DBG_dump_chunk("ocsp response OID: ", object); + ) + goto end; + } + } + break; } - } - break; } - objectID++; - } - return rStatus; + success &= parser->success(parser); + +end: + parser->destroy(parser); + return rStatus; } -/* - * parse a basic OCSP response +/** + * Parse a basic OCSP response */ -static bool -parse_ocsp_single_response(chunk_t blob, int level0, single_response_t *sres) +static bool parse_ocsp_single_response(chunk_t blob, int level0, + single_response_t *sres) { - u_int level, extn_oid; - asn1_ctx_t ctx; - bool critical; - chunk_t object; - int objectID = 0; - - asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + asn1_parser_t *parser; + chunk_t object; + u_int extn_oid; + int objectID; + bool critical; + bool success = FALSE; - while (objectID < SINGLE_RESPONSE_ROOF) - { - if (!extract_object(singleResponseObjects, &objectID, &object, &level, &ctx)) - return FALSE; + parser = asn1_parser_create(singleResponseObjects, blob); + parser->set_top_level(parser, level0); - switch (objectID) + while (parser->iterate(parser, &objectID, &object)) { - case SINGLE_RESPONSE_ALGORITHM: - sres->hash_algorithm = parse_algorithmIdentifier(object, level+1, NULL); - break; - case SINGLE_RESPONSE_ISSUER_NAME_HASH: - sres->issuer_name_hash = object; - break; - case SINGLE_RESPONSE_ISSUER_KEY_HASH: - sres->issuer_key_hash = object; - break; - case SINGLE_RESPONSE_SERIAL_NUMBER: - sres->serialNumber = object; - break; - case SINGLE_RESPONSE_CERT_STATUS_GOOD: - sres->status = CERT_GOOD; - break; - case SINGLE_RESPONSE_CERT_STATUS_REVOKED: - sres->status = CERT_REVOKED; - break; - case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME: - sres->revocationTime = asn1totime(&object, ASN1_GENERALIZEDTIME); - break; - case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON: - sres->revocationReason = (object.len == 1) - ? *object.ptr : REASON_UNSPECIFIED; - break; - case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN: - sres->status = CERT_UNKNOWN; - break; - case SINGLE_RESPONSE_THIS_UPDATE: - sres->thisUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME); - break; - case SINGLE_RESPONSE_NEXT_UPDATE: - sres->nextUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME); - break; - case SINGLE_RESPONSE_EXT_ID: - extn_oid = known_oid(object); - break; - case SINGLE_RESPONSE_CRITICAL: - critical = object.len && *object.ptr; - DBG(DBG_PARSING, - DBG_log(" %s",(critical)?"TRUE":"FALSE"); - ) - case SINGLE_RESPONSE_EXT_VALUE: - break; + switch (objectID) + { + case SINGLE_RESPONSE_ALGORITHM: + sres->hash_algorithm = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser)+1, NULL); + break; + case SINGLE_RESPONSE_ISSUER_NAME_HASH: + sres->issuer_name_hash = object; + break; + case SINGLE_RESPONSE_ISSUER_KEY_HASH: + sres->issuer_key_hash = object; + break; + case SINGLE_RESPONSE_SERIAL_NUMBER: + sres->serialNumber = object; + break; + case SINGLE_RESPONSE_CERT_STATUS_GOOD: + sres->status = CERT_GOOD; + break; + case SINGLE_RESPONSE_CERT_STATUS_REVOKED: + sres->status = CERT_REVOKED; + break; + case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME: + sres->revocationTime = asn1_to_time(&object, ASN1_GENERALIZEDTIME); + break; + case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON: + sres->revocationReason = (object.len == 1) + ? *object.ptr : REASON_UNSPECIFIED; + break; + case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN: + sres->status = CERT_UNKNOWN; + break; + case SINGLE_RESPONSE_THIS_UPDATE: + sres->thisUpdate = asn1_to_time(&object, ASN1_GENERALIZEDTIME); + break; + case SINGLE_RESPONSE_NEXT_UPDATE: + sres->nextUpdate = asn1_to_time(&object, ASN1_GENERALIZEDTIME); + break; + case SINGLE_RESPONSE_EXT_ID: + extn_oid = asn1_known_oid(object); + break; + case SINGLE_RESPONSE_CRITICAL: + critical = object.len && *object.ptr; + DBG(DBG_PARSING, + DBG_log(" %s",(critical)?"TRUE":"FALSE"); + ) + case SINGLE_RESPONSE_EXT_VALUE: + break; + } } - objectID++; - } - return TRUE; + success = parser->success(parser); + parser->destroy(parser); + return success; } -/* - * add an ocsp location to a chained list +/** + * Add an ocsp location to a chained list */ -ocsp_location_t* -add_ocsp_location(const ocsp_location_t *loc, ocsp_location_t **chain) +ocsp_location_t* add_ocsp_location(const ocsp_location_t *loc, + ocsp_location_t **chain) { - ocsp_location_t *location = alloc_thing(ocsp_location_t, "ocsp location"); - - /* unshare location fields */ - clonetochunk(location->issuer - , loc->issuer.ptr, loc->issuer.len - , "ocsp issuer"); - - clonetochunk(location->authNameID - , loc->authNameID.ptr, loc->authNameID.len - , "ocsp authNameID"); - - if (loc->authKeyID.ptr == NULL) - location->authKeyID = empty_chunk; - else - clonetochunk(location->authKeyID - , loc->authKeyID.ptr, loc->authKeyID.len - , "ocsp authKeyID"); - - if (loc->authKeySerialNumber.ptr == NULL) - location->authKeySerialNumber = empty_chunk; - else - clonetochunk(location->authKeySerialNumber - , loc->authKeySerialNumber.ptr, loc->authKeySerialNumber.len - , "ocsp authKeySerialNumber"); - - clonetochunk(location->uri - , loc->uri.ptr, loc->uri.len - , "ocsp uri"); - - location->certinfo = NULL; - - /* insert new ocsp location in front of chain */ - location->next = *chain; - *chain = location; - - DBG(DBG_CONTROL, - DBG_log("new ocsp location added") - ) - - return location; + ocsp_location_t *location = malloc_thing(ocsp_location_t); + + /* unshare location fields */ + location->issuer = chunk_clone(loc->issuer); + location->authNameID = chunk_clone(loc->authNameID); + location->authKeyID = chunk_clone(loc->authKeyID); + location->authKeySerialNumber = chunk_clone(loc->authKeySerialNumber); + location->uri = chunk_clone(loc->uri); + location->certinfo = NULL; + + /* insert new ocsp location in front of chain */ + location->next = *chain; + *chain = location; + + DBG(DBG_CONTROL, + DBG_log("new ocsp location added") + ) + + return location; } -/* +/** * add a certinfo struct to a chained list */ -void -add_certinfo(ocsp_location_t *loc, ocsp_certinfo_t *info, ocsp_location_t **chain - , bool request) +void add_certinfo(ocsp_location_t *loc, ocsp_certinfo_t *info, + ocsp_location_t **chain, bool request) { - ocsp_location_t *location; - ocsp_certinfo_t *certinfo, **certinfop; - char buf[BUF_LEN]; - time_t now; - int cmp = -1; - - location = get_ocsp_location(loc, *chain); - if (location == NULL) - location = add_ocsp_location(loc, chain); - - /* traverse list of certinfos in increasing order */ - certinfop = &location->certinfo; - certinfo = *certinfop; - - while (certinfo != NULL) - { - cmp = cmp_chunk(info->serialNumber, certinfo->serialNumber); - if (cmp <= 0) - break; - certinfop = &certinfo->next; + ocsp_location_t *location; + ocsp_certinfo_t *certinfo, **certinfop; + char buf[BUF_LEN]; + time_t now; + int cmp = -1; + + location = get_ocsp_location(loc, *chain); + if (location == NULL) + { + location = add_ocsp_location(loc, chain); + } + + /* traverse list of certinfos in increasing order */ + certinfop = &location->certinfo; certinfo = *certinfop; - } - - if (cmp != 0) - { - /* add a new certinfo entry */ - ocsp_certinfo_t *cnew = alloc_thing(ocsp_certinfo_t, "ocsp certinfo"); - clonetochunk(cnew->serialNumber, info->serialNumber.ptr - , info->serialNumber.len, "serialNumber"); - cnew->next = certinfo; - *certinfop = cnew; - certinfo = cnew; - } - - DBG(DBG_CONTROL, - datatot(info->serialNumber.ptr, info->serialNumber.len, ':' - , buf, BUF_LEN); - DBG_log("ocsp %s for serial %s %s" - , request?"fetch request":"certinfo" - , buf - , (cmp == 0)? (request?"already exists":"updated"):"added") - ) - - time(&now); - - if (request) - { - certinfo->status = CERT_UNDEFINED; - + + while (certinfo != NULL) + { + cmp = chunk_compare(info->serialNumber, certinfo->serialNumber); + if (cmp <= 0) + break; + certinfop = &certinfo->next; + certinfo = *certinfop; + } + if (cmp != 0) - certinfo->thisUpdate = now; - - certinfo->nextUpdate = UNDEFINED_TIME; - } - else - { - certinfo->status = info->status; - certinfo->revocationTime = info->revocationTime; - certinfo->revocationReason = info->revocationReason; - - certinfo->thisUpdate = (info->thisUpdate != UNDEFINED_TIME)? - info->thisUpdate : now; + { + /* add a new certinfo entry */ + ocsp_certinfo_t *cnew = malloc_thing(ocsp_certinfo_t); + + cnew->serialNumber = chunk_clone(info->serialNumber); + cnew->next = certinfo; + *certinfop = cnew; + certinfo = cnew; + } + + DBG(DBG_CONTROL, + datatot(info->serialNumber.ptr, info->serialNumber.len, ':' + , buf, BUF_LEN); + DBG_log("ocsp %s for serial %s %s" + , request?"fetch request":"certinfo" + , buf + , (cmp == 0)? (request?"already exists":"updated"):"added") + ) + + time(&now); + + if (request) + { + certinfo->status = CERT_UNDEFINED; + + if (cmp != 0) + { + certinfo->thisUpdate = now; + } + certinfo->nextUpdate = UNDEFINED_TIME; + } + else + { + certinfo->status = info->status; + certinfo->revocationTime = info->revocationTime; + certinfo->revocationReason = info->revocationReason; + + certinfo->thisUpdate = (info->thisUpdate != UNDEFINED_TIME)? + info->thisUpdate : now; - certinfo->once = (info->nextUpdate == UNDEFINED_TIME); + certinfo->once = (info->nextUpdate == UNDEFINED_TIME); - certinfo->nextUpdate = (certinfo->once)? - (now + OCSP_DEFAULT_VALID_TIME) : info->nextUpdate; - } + certinfo->nextUpdate = (certinfo->once)? + (now + OCSP_DEFAULT_VALID_TIME) : info->nextUpdate; + } } -/* - * process received ocsp single response and add it to ocsp cache +/** + * Process received ocsp single response and add it to ocsp cache */ -static void -process_single_response(ocsp_location_t *location, single_response_t *sres) +static void process_single_response(ocsp_location_t *location, + single_response_t *sres) { - ocsp_certinfo_t *certinfo, **certinfop; - int cmp = -1; - - if (sres->hash_algorithm != OID_SHA1) - { - plog("only SHA-1 hash supported in OCSP single response"); - return; - } - if (!(same_chunk(sres->issuer_name_hash, location->authNameID) - && same_chunk(sres->issuer_key_hash, location->authKeyID))) - { - plog("ocsp single response has wrong issuer"); - return; - } - - /* traverse list of certinfos in increasing order */ - certinfop = &location->certinfo; - certinfo = *certinfop; - - while (certinfo != NULL) - { - cmp = cmp_chunk(sres->serialNumber, certinfo->serialNumber); - if (cmp <= 0) - break; - certinfop = &certinfo->next; + ocsp_certinfo_t *certinfo, **certinfop; + int cmp = -1; + + if (sres->hash_algorithm != OID_SHA1) + { + plog("only SHA-1 hash supported in OCSP single response"); + return; + } + if (!(chunk_equals(sres->issuer_name_hash, location->authNameID) + && chunk_equals(sres->issuer_key_hash, location->authKeyID))) + { + plog("ocsp single response has wrong issuer"); + return; + } + + /* traverse list of certinfos in increasing order */ + certinfop = &location->certinfo; certinfo = *certinfop; - } - - if (cmp != 0) - { - plog("received unrequested cert status from ocsp server"); - return; - } - - /* unlink cert from ocsp fetch request list */ - *certinfop = certinfo->next; - - /* update certinfo using the single response information */ - certinfo->thisUpdate = sres->thisUpdate; - certinfo->nextUpdate = sres->nextUpdate; - certinfo->status = sres->status; - certinfo->revocationTime = sres->revocationTime; - certinfo->revocationReason = sres->revocationReason; - - /* add or update certinfo in ocsp cache */ - lock_ocsp_cache("process_single_response"); - add_certinfo(location, certinfo, &ocsp_cache, FALSE); - unlock_ocsp_cache("process_single_response"); - - /* free certinfo unlinked from ocsp fetch request list */ - free_certinfo(certinfo); + while (certinfo != NULL) + { + cmp = chunk_compare(sres->serialNumber, certinfo->serialNumber); + if (cmp <= 0) + break; + certinfop = &certinfo->next; + certinfo = *certinfop; + } + + if (cmp != 0) + { + plog("received unrequested cert status from ocsp server"); + return; + } + + /* unlink cert from ocsp fetch request list */ + *certinfop = certinfo->next; + + /* update certinfo using the single response information */ + certinfo->thisUpdate = sres->thisUpdate; + certinfo->nextUpdate = sres->nextUpdate; + certinfo->status = sres->status; + certinfo->revocationTime = sres->revocationTime; + certinfo->revocationReason = sres->revocationReason; + + /* add or update certinfo in ocsp cache */ + lock_ocsp_cache("process_single_response"); + add_certinfo(location, certinfo, &ocsp_cache, FALSE); + unlock_ocsp_cache("process_single_response"); + + /* free certinfo unlinked from ocsp fetch request list */ + free_certinfo(certinfo); } -/* - * parse and verify ocsp response and update the ocsp cache +/** + * Parse and verify ocsp response and update the ocsp cache */ -void -parse_ocsp(ocsp_location_t *location, chunk_t blob) +void parse_ocsp(ocsp_location_t *location, chunk_t blob) { - response_t res = empty_response; - - /* parse the ocsp response without looking at the single responses yet */ - response_status status = parse_ocsp_response(blob, &res); - - if (status != STATUS_SUCCESSFUL) - { - plog("error in ocsp response"); - return; - } - /* check if there was a nonce in the request */ - if (location->nonce.ptr != NULL && res.nonce.ptr == NULL) - { - plog("ocsp response contains no nonce, replay attack possible"); - } - /* check if the nonce is identical */ - if (res.nonce.ptr != NULL && !same_chunk(res.nonce, location->nonce)) - { - plog("invalid nonce in ocsp response"); - return; - } - /* check if the response is signed by a trusted key */ - if (!valid_ocsp_response(&res)) - { - plog("invalid ocsp response"); - return; - } - DBG(DBG_CONTROL, - DBG_log("valid ocsp response") - ) - - /* now parse the single responses one at a time */ - { - u_int level; - asn1_ctx_t ctx; - chunk_t object; - int objectID = 0; + response_t res = empty_response; - asn1_init(&ctx, res.responses, 0, FALSE, DBG_RAW); + /* parse the ocsp response without looking at the single responses yet */ + response_status status = parse_ocsp_response(blob, &res); - while (objectID < RESPONSES_ROOF) + if (status != STATUS_SUCCESSFUL) + { + plog("error in ocsp response"); + return; + } + /* check if there was a nonce in the request */ + if (location->nonce.ptr != NULL && res.nonce.ptr == NULL) + { + plog("ocsp response contains no nonce, replay attack possible"); + } + /* check if the nonce is identical */ + if (res.nonce.ptr != NULL && !chunk_equals(res.nonce, location->nonce)) + { + plog("invalid nonce in ocsp response"); + return; + } + /* check if the response is signed by a trusted key */ + if (!valid_ocsp_response(&res)) { - if (!extract_object(responsesObjects, &objectID, &object, &level, &ctx)) + plog("invalid ocsp response"); return; - - if (objectID == RESPONSES_SINGLE_RESPONSE) - { - single_response_t sres = empty_single_response; + } + DBG(DBG_CONTROL, + DBG_log("valid ocsp response") + ) - if (parse_ocsp_single_response(object, level+1, &sres)) + /* now parse the single responses one at a time */ + { + asn1_parser_t *parser; + chunk_t object; + int objectID; + + parser = asn1_parser_create(responsesObjects, res.responses); + + while (parser->iterate(parser, &objectID, &object)) { - process_single_response(location, &sres); + if (objectID == RESPONSES_SINGLE_RESPONSE) + { + single_response_t sres = empty_single_response; + + if (!parse_ocsp_single_response(object, + parser->get_level(parser)+1, &sres)) + { + goto end; + } + process_single_response(location, &sres); + } } - } - objectID++; +end: + parser->destroy(parser); } - } } |