parsing basic ocsp response
This commit is contained in:
+269
-26
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
@@ -85,32 +86,32 @@ struct private_ocsp_t {
|
||||
chunk_t authNameID;
|
||||
};
|
||||
|
||||
static const char *const response_status_names[] = {
|
||||
"successful",
|
||||
"malformed request",
|
||||
"internal error",
|
||||
"try later",
|
||||
"signature required",
|
||||
"unauthorized"
|
||||
};
|
||||
ENUM(response_status_names, STATUS_SUCCESSFUL, STATUS_UNAUTHORIZED,
|
||||
"successful",
|
||||
"malformed request",
|
||||
"internal error",
|
||||
"try later",
|
||||
"signature required",
|
||||
"unauthorized"
|
||||
);
|
||||
|
||||
/* response container */
|
||||
typedef struct response_t response_t;
|
||||
|
||||
struct response_t {
|
||||
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;
|
||||
identification_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 , /* responder_id_name */
|
||||
{ NULL, 0 } , /* responder_id_key */
|
||||
UNDEFINED_TIME, /* produced_at */
|
||||
{ NULL, 0 } , /* single_response */
|
||||
@@ -218,7 +219,7 @@ static const asn1Object_t basicResponseObjects[] = {
|
||||
{ 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 */
|
||||
{ 3, "certificate", ASN1_SEQUENCE, ASN1_RAW }, /* 24 */
|
||||
{ 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */
|
||||
{ 1, "end opt", ASN1_EOC, ASN1_END } /* 26 */
|
||||
};
|
||||
@@ -416,6 +417,7 @@ static chunk_t build_tbs_request(private_ocsp_t *this, bool has_requestor_cert)
|
||||
*/
|
||||
static chunk_t build_signature(private_ocsp_t *this, chunk_t tbsRequest)
|
||||
{
|
||||
/* TODO */
|
||||
return chunk_empty;
|
||||
}
|
||||
|
||||
@@ -435,28 +437,267 @@ static chunk_t ocsp_build_request(private_ocsp_t *this)
|
||||
/* looks for requestor cert and matching private key */
|
||||
has_requestor_cert = FALSE;
|
||||
|
||||
/* has_requestor_cert = get_ocsp_requestor_cert(location); */
|
||||
/* TODO has_requestor_cert = get_ocsp_requestor_cert(location); */
|
||||
|
||||
/* build content */
|
||||
/* build content */
|
||||
tbsRequest = build_tbs_request(this, has_requestor_cert);
|
||||
|
||||
/* sign tbsReuqest */
|
||||
/* sign tbsReuqest */
|
||||
signature = (has_requestor_cert)? build_signature(this, tbsRequest): chunk_empty;
|
||||
|
||||
return asn1_wrap(ASN1_SEQUENCE, "mm",
|
||||
return asn1_wrap(ASN1_SEQUENCE, "mm",
|
||||
tbsRequest,
|
||||
signature);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OCSP response has a valid signature
|
||||
*/
|
||||
static bool ocsp_valid_response(response_t *res)
|
||||
{
|
||||
/* TODO */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse a basic OCSP response
|
||||
*/
|
||||
static bool ocsp_parse_basic_response(chunk_t blob, int level0, response_t *res)
|
||||
{
|
||||
u_int level, version;
|
||||
u_int extn_oid = OID_UNKNOWN;
|
||||
u_char buf[BUF_LEN];
|
||||
asn1_ctx_t ctx;
|
||||
bool critical;
|
||||
chunk_t object;
|
||||
int objectID = 0;
|
||||
|
||||
asn1_init(&ctx, blob, level0, FALSE, FALSE);
|
||||
|
||||
while (objectID < BASIC_RESPONSE_ROOF)
|
||||
{
|
||||
if (!extract_object(basicResponseObjects, &objectID, &object, &level, &ctx))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (objectID)
|
||||
{
|
||||
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)
|
||||
{
|
||||
DBG1("wrong ocsp basic response version (version= %i)", version);
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case BASIC_RESPONSE_ID_BY_NAME:
|
||||
res->responder_id_name = identification_create_from_encoding(ID_DER_ASN1_DN, object);
|
||||
DBG2(" '%D'", res->responder_id_name);
|
||||
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;
|
||||
DBG2(" %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 = chunk_clone(object);
|
||||
x509_t *cert = x509_create_from_chunk(blob, level+1);
|
||||
|
||||
if (cert == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (cert->is_ocsp_signer(cert))
|
||||
{
|
||||
DBG2("received OCSP signer certificate");
|
||||
cert->destroy(cert);
|
||||
/* TODO trust_authcert_candidate(cert, NULL))
|
||||
add_authcert(cert, AUTH_OCSP); */
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1("embedded ocsp certificate rejected");
|
||||
cert->destroy(cert);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
objectID++;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse an ocsp response and return the result as a response_t struct
|
||||
*/
|
||||
static response_status ocsp_parse_response(chunk_t blob, response_t * res)
|
||||
{
|
||||
asn1_ctx_t ctx;
|
||||
chunk_t object;
|
||||
u_int level;
|
||||
int objectID = 0;
|
||||
|
||||
response_status rStatus = STATUS_INTERNALERROR;
|
||||
u_int ocspResponseType = OID_UNKNOWN;
|
||||
|
||||
asn1_init(&ctx, blob, 0, FALSE, FALSE);
|
||||
|
||||
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;
|
||||
DBG2(" '%N'", response_status_names, rStatus);
|
||||
|
||||
switch (rStatus)
|
||||
{
|
||||
case STATUS_SUCCESSFUL:
|
||||
break;
|
||||
case STATUS_MALFORMEDREQUEST:
|
||||
case STATUS_INTERNALERROR:
|
||||
case STATUS_TRYLATER:
|
||||
case STATUS_SIGREQUIRED:
|
||||
case STATUS_UNAUTHORIZED:
|
||||
DBG1("unsuccessful ocsp response: server said '%N'",
|
||||
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 (!ocsp_parse_basic_response(object, level+1, res))
|
||||
{
|
||||
return STATUS_INTERNALERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG1("ocsp response is not of type BASIC");
|
||||
DBG1("ocsp response OID: %#B", &object);
|
||||
return STATUS_INTERNALERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
objectID++;
|
||||
}
|
||||
return rStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* verify and process ocsp response and update the ocsp cache
|
||||
*/
|
||||
void ocsp_process_response(private_ocsp_t *this, chunk_t reply)
|
||||
{
|
||||
response_t res = empty_response;
|
||||
|
||||
/* parse the ocsp response without looking at the single responses yet */
|
||||
response_status status = ocsp_parse_response(reply, &res);
|
||||
|
||||
if (status != STATUS_SUCCESSFUL)
|
||||
{
|
||||
DBG1("error in ocsp response");
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if there was a nonce in the request */
|
||||
if (this->nonce.ptr != NULL && res.nonce.ptr == NULL)
|
||||
{
|
||||
DBG1("ocsp response contains no nonce, replay attack possible");
|
||||
}
|
||||
|
||||
/* check if the nonces are identical */
|
||||
if (res.nonce.ptr != NULL && !chunk_equals(res.nonce, this->nonce))
|
||||
{
|
||||
DBG1("invalid nonce in ocsp response");
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if the response is signed by a trusted key */
|
||||
if (!ocsp_valid_response(&res))
|
||||
{
|
||||
DBG1("invalid ocsp response");
|
||||
return;
|
||||
}
|
||||
DBG2("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;
|
||||
|
||||
asn1_init(&ctx, res.responses, 0, FALSE, FALSE);
|
||||
|
||||
while (objectID < RESPONSES_ROOF)
|
||||
{
|
||||
if (!extract_object(responsesObjects, &objectID, &object, &level, &ctx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (objectID == RESPONSES_SINGLE_RESPONSE)
|
||||
{
|
||||
single_response_t sres = empty_single_response;
|
||||
|
||||
if (parse_ocsp_single_response(object, level+1, &sres))
|
||||
{
|
||||
process_single_response(this, &sres);
|
||||
}
|
||||
}
|
||||
objectID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ocsp_t.fetch.
|
||||
*/
|
||||
static void fetch(private_ocsp_t *this, certinfo_t *certinfo)
|
||||
{
|
||||
chunk_t request;
|
||||
chunk_t reply;
|
||||
chunk_t response;
|
||||
bool fetched = FALSE;
|
||||
|
||||
if (this->uris->get_count(this->uris) == 0)
|
||||
@@ -480,9 +721,9 @@ static void fetch(private_ocsp_t *this, certinfo_t *certinfo)
|
||||
snprintf(uri_string, BUF_LEN, "%.*s", uri_chunk.len, uri_chunk.ptr);
|
||||
fetcher = fetcher_create(uri_string);
|
||||
|
||||
reply = fetcher->post(fetcher, "application/ocsp-request", request);
|
||||
response = fetcher->post(fetcher, "application/ocsp-request", request);
|
||||
fetcher->destroy(fetcher);
|
||||
if (reply.ptr != NULL)
|
||||
if (response.ptr != NULL)
|
||||
{
|
||||
fetched = TRUE;
|
||||
break;
|
||||
@@ -496,7 +737,9 @@ static void fetch(private_ocsp_t *this, certinfo_t *certinfo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DBG3("ocsp reply: %B", &reply);
|
||||
DBG3("ocsp response: %B", &response);
|
||||
ocsp_process_response(this, response);
|
||||
free(response.ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user