From 6d76b2f247f3d2a7137bff5b7ec937d00eb9bb1e Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 31 May 2012 17:29:32 -0700 Subject: [PATCH] dynamic allocation of OCSP responses, response signature check --- ctaocrypt/src/asn.c | 218 +++++++++++++++------ cyassl/ctaocrypt/asn.h | 55 +++--- cyassl/internal.h | 4 +- src/ocsp.c | 420 +++++++++++++++++++++++++++-------------- 4 files changed, 470 insertions(+), 227 deletions(-) diff --git a/ctaocrypt/src/asn.c b/ctaocrypt/src/asn.c index f32ce5bb9..546ca4f70 100644 --- a/ctaocrypt/src/asn.c +++ b/ctaocrypt/src/asn.c @@ -1916,7 +1916,7 @@ word32 EncodeSignature(byte* out, const byte* digest, word32 digSz, int hashOID) return encDigSz + algoSz + seqSz; } - +#include /* return true (1) for Confirmation */ static int ConfirmSignature(const byte* buf, word32 bufSz, const byte* key, word32 keySz, word32 keyOID, @@ -2029,7 +2029,7 @@ static int ConfirmSignature(const byte* buf, word32 bufSz, { int x; printf("cyassl encodedSig:\n"); - for (x = 0; x < sigSz; x++) { + for (x = 0; x < encodedSigSz; x++) { printf("%02x ", encodedSig[x]); if ( (x % 16) == 15) printf("\n"); @@ -4122,6 +4122,8 @@ static int GetEnumerated(const byte* input, word32* inOutIdx, int *value) word32 idx = *inOutIdx; word32 len; + CYASSL_ENTER("GetEnumerated"); + *value = 0; if (input[idx++] != ASN_ENUMERATED) @@ -4145,22 +4147,19 @@ static int DecodeSingleResponse(byte* source, word32* ioIndex, OcspResponse* resp, word32 size) { word32 index = *ioIndex, prevIndex, oid; - int length; - CertStatus* cs = NULL; + int length, wrapperSz; + CertStatus* cs = resp->status; + + CYASSL_ENTER("DecodeSingleResponse"); /* Outer wrapper of the SEQUENCE OF Single Responses. */ - if (GetSequence(source, &index, &length, size) < 0) + if (GetSequence(source, &index, &wrapperSz, size) < 0) return ASN_PARSE_E; + prevIndex = index; + /* When making a request, we only request one status on one certificate * at a time. There should only be one SingleResponse */ - cs = resp->status;/*XMALLOC(sizeof(CertStatus), NULL, DYNAMIC_TYPE_CERT_STATUS); - if (cs == NULL) - { - CYASSL_MSG("\tAlloc Cert Status failed"); - return MEMORY_E; - } - cs->next = NULL; */ /* Wrapper around the Single Response */ if (GetSequence(source, &index, &length, size) < 0) @@ -4172,17 +4171,19 @@ static int DecodeSingleResponse(byte* source, /* Skip the hash algorithm */ if (GetAlgoId(source, &index, &oid, size) < 0) return ASN_PARSE_E; - /* Skip the hash of CN */ + /* Save reference to the hash of CN */ if (source[index++] != ASN_OCTET_STRING) return ASN_PARSE_E; if (GetLength(source, &index, &length, size) < 0) return ASN_PARSE_E; + resp->issuerHash = source + index; index += length; - /* Skip the hash of the issuer public key */ + /* Save reference to the hash of the issuer public key */ if (source[index++] != ASN_OCTET_STRING) return ASN_PARSE_E; if (GetLength(source, &index, &length, size) < 0) return ASN_PARSE_E; + resp->issuerKeyHash = source + index; index += length; /* Read the serial number, it is handled as a string, not as a @@ -4231,8 +4232,12 @@ static int DecodeSingleResponse(byte* source, if (GetBasicDate(source, &index, cs->thisDate, &cs->thisDateFormat, size) < 0) return ASN_PARSE_E; - - if (source[index] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + + /* The following items are optional. Only check for them if there is more + * unprocessed data in the singleResponse wrapper. */ + + if ((index - prevIndex < wrapperSz) && + (source[index] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0))) { index++; if (GetLength(source, &index, &length, size) < 0) @@ -4241,7 +4246,8 @@ static int DecodeSingleResponse(byte* source, &cs->nextDateFormat, size) < 0) return ASN_PARSE_E; } - if (source[index] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) + if ((index - prevIndex < wrapperSz) && + (source[index] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1))) { index++; if (GetLength(source, &index, &length, size) < 0) @@ -4318,15 +4324,18 @@ static int DecodeOcspRespExtensions(byte* source, static int DecodeResponseData(byte* source, word32* ioIndex, OcspResponse* resp, word32 size) { - word32 idx = *ioIndex; + word32 idx = *ioIndex, prev_idx; int length; int version; word32 responderId = 0; + CYASSL_ENTER("DecodeResponseData"); + + resp->response = source + idx; + prev_idx = idx; if (GetSequence(source, &idx, &length, size) < 0) return ASN_PARSE_E; - resp->respBegin = idx; - resp->respLength = length; + resp->responseSz = length + idx - prev_idx; /* Get version. It is an EXPLICIT[0] DEFAULT(0) value. If this * item isn't an EXPLICIT[0], then set version to zero and move @@ -4375,7 +4384,7 @@ static int DecodeCerts(byte* source, { word32 idx = *ioIndex; - (void)resp; + CYASSL_ENTER("DecodeCerts"); if (source[idx++] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { @@ -4383,6 +4392,13 @@ static int DecodeCerts(byte* source, if (GetLength(source, &idx, &length, size) < 0) return ASN_PARSE_E; + + if (GetSequence(source, &idx, &length, size) < 0) + return ASN_PARSE_E; + + resp->cert = source + idx; + resp->certSz = length; + idx += length; } *ioIndex = idx; @@ -4396,6 +4412,8 @@ static int DecodeBasicOcspResponse(byte* source, word32 idx = *ioIndex; word32 end_index; + CYASSL_ENTER("DecodeBasicOcspResponse"); + if (GetSequence(source, &idx, &length, size) < 0) return ASN_PARSE_E; @@ -4416,8 +4434,8 @@ static int DecodeBasicOcspResponse(byte* source, int sigLength = 0; if (GetLength(source, &idx, &sigLength, size) < 0) return ASN_PARSE_E; - resp->sigLength = sigLength; - resp->sigIndex = idx; + resp->sigSz = sigLength; + resp->sig = source + idx; idx += sigLength; } @@ -4426,25 +4444,55 @@ static int DecodeBasicOcspResponse(byte* source, * see if there are certificates, they are optional. */ if (idx < end_index) - return DecodeCerts(source, &idx, resp, size); + { + DecodedCert cert; + int ret; + + if (DecodeCerts(source, &idx, resp, size) < 0) + return ASN_PARSE_E; + + InitDecodedCert(&cert, resp->cert, resp->certSz, 0); + ret = ParseCertRelative(&cert, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) + return ret; + + ret = ConfirmSignature(resp->response, resp->responseSz, + cert.publicKey, cert.pubKeySize, cert.keyOID, + resp->sig, resp->sigSz, resp->sigOID, NULL); + FreeDecodedCert(&cert); + + if (ret == 0) + { + CYASSL_MSG("\tConfirm signature failed"); + return ASN_SIG_CONFIRM_E; + } + } *ioIndex = idx; return 0; } -void InitOcspResponse(OcspResponse* resp, byte* source, word32 inSz, void* heap) +void InitOcspResponse(OcspResponse* resp, CertStatus* status, + byte* source, word32 inSz) { - XMEMSET(resp, 0, sizeof(*resp)); + CYASSL_ENTER("InitOcspResponse"); + + resp->responseStatus = -1; + resp->response = NULL; + resp->responseSz = 0; + resp->producedAt = NULL; + resp->producedAtFormat = 0; + resp->issuerHash = NULL; + resp->issuerKeyHash = NULL; + resp->sig = NULL; + resp->sigSz = 0; + resp->sigOID = 0; + resp->status = status; + resp->nonce = NULL; + resp->nonceSz = 0; resp->source = source; resp->maxIdx = inSz; - resp->heap = heap; -} - - -void FreeOcspResponse(OcspResponse* resp) -{ - (void)resp; } @@ -4456,6 +4504,8 @@ int OcspResponseDecode(OcspResponse* resp) word32 size = resp->maxIdx; word32 oid; + CYASSL_ENTER("OcspResponseDecode"); + /* peel the outer SEQUENCE wrapper */ if (GetSequence(source, &idx, &length, size) < 0) return ASN_PARSE_E; @@ -4501,6 +4551,8 @@ static int SetSerialNumber(const byte* sn, word32 snSz, byte* output) { int result = 0; + CYASSL_ENTER("SetSerialNumber"); + if (snSz <= EXTERNAL_SERIAL_SIZE) { output[0] = ASN_INTEGER; output[1] = snSz + 1; @@ -4520,6 +4572,8 @@ static word32 SetOcspReqExtensions(word32 extSz, byte* output, byte seqArray[5][MAX_SEQ_SZ]; word32 seqSz[5], totalSz; + CYASSL_ENTER("SetOcspReqExtensions"); + if (nonce == NULL || nonceSz == 0) return 0; seqArray[0][0] = ASN_OCTET_STRING; @@ -4563,7 +4617,7 @@ static word32 SetOcspReqExtensions(word32 extSz, byte* output, } -int EncodeOcspRequest(DecodedCert* cert, byte* output, word32 outputSz) +int EncodeOcspRequest(OcspRequest* req) { byte seqArray[5][MAX_SEQ_SZ]; /* The ASN.1 of the OCSP Request is an onion of sequences */ @@ -4572,29 +4626,34 @@ int EncodeOcspRequest(DecodedCert* cert, byte* output, word32 outputSz) byte issuerKeyArray[MAX_ENCODED_DIG_SZ]; byte snArray[MAX_SN_SZ]; byte extArray[MAX_OCSP_EXT_SZ]; - byte nonceArray[MAX_OCSP_NONCE_SZ]; + byte* output = req->dest; + word32 outputSz = req->destSz; RNG rng; - word32 seqSz[5], algoSz, issuerSz, issuerKeySz, snSz, nonceSz, - extSz, totalSz; + word32 seqSz[5], algoSz, issuerSz, issuerKeySz, snSz, extSz, totalSz; int i; - (void)outputSz; - CYASSL_ENTER("EncodeOcspRequest"); + algoSz = SetAlgoID(SHAh, algoArray, hashType); - issuerSz = SetDigest(cert->issuerHash, SHA_SIZE, issuerArray); - issuerKeySz = SetDigest(cert->issuerKeyHash, SHA_SIZE, issuerKeyArray); - snSz = SetSerialNumber(cert->serial, cert->serialSz, snArray); + + req->issuerHash = req->cert->issuerHash; + issuerSz = SetDigest(req->cert->issuerHash, SHA_SIZE, issuerArray); + + req->issuerKeyHash = req->cert->issuerKeyHash; + issuerKeySz = SetDigest(req->cert->issuerKeyHash, SHA_SIZE, issuerKeyArray); + + req->serial = req->cert->serial; + req->serialSz = req->cert->serialSz; + snSz = SetSerialNumber(req->cert->serial, req->cert->serialSz, snArray); if (InitRng(&rng) != 0) { CYASSL_MSG("\tCannot initialize RNG. Skipping the OSCP Nonce."); - nonceSz = 0; extSz = 0; } else { - nonceSz = MAX_OCSP_NONCE_SZ; - RNG_GenerateBlock(&rng, nonceArray, nonceSz); + req->nonceSz = MAX_OCSP_NONCE_SZ; + RNG_GenerateBlock(&rng, req->nonce, req->nonceSz); extSz = SetOcspReqExtensions(MAX_OCSP_EXT_SZ, extArray, - nonceArray, nonceSz); + req->nonce, req->nonceSz); } totalSz = algoSz + issuerSz + issuerKeySz + snSz; @@ -4626,12 +4685,18 @@ int EncodeOcspRequest(DecodedCert* cert, byte* output, word32 outputSz) } -void InitOcspRequest(OcspRequest* req, byte* dest, word32 destSz, void* heap) +void InitOcspRequest(OcspRequest* req, DecodedCert* cert, + byte* dest, word32 destSz) { - XMEMSET(req, 0, sizeof(*req)); + CYASSL_ENTER("InitOcspRequest"); + + req->cert = cert; + req->nonceSz = 0; + req->issuerHash = NULL; + req->issuerKeyHash = NULL; + req->serial = NULL; req->dest = dest; req->destSz = destSz; - req->heap = heap; } @@ -4639,14 +4704,61 @@ int CompareOcspReqResp(OcspRequest* req, OcspResponse* resp) { int cmp; - if (req == NULL) return -1; - if (resp == NULL) return 1; + CYASSL_ENTER("CompareOcspReqResp"); + + if (req == NULL) + { + CYASSL_MSG("\tReq missing"); + return -1; + } + + if (resp == NULL) + { + CYASSL_MSG("\tResp missing"); + return 1; + } cmp = req->nonceSz - resp->nonceSz; - if (cmp != 0) return cmp; + if (cmp != 0) + { + CYASSL_MSG("\tnonceSz mismatch"); + return cmp; + } cmp = XMEMCMP(req->nonce, resp->nonce, req->nonceSz); - if (cmp != 0) return cmp; + if (cmp != 0) + { + CYASSL_MSG("\tnonce mismatch"); + return cmp; + } + + cmp = XMEMCMP(req->issuerHash, resp->issuerHash, SHA_DIGEST_SIZE); + if (cmp != 0) + { + CYASSL_MSG("\tissuerHash mismatch"); + return cmp; + } + + cmp = XMEMCMP(req->issuerKeyHash, resp->issuerKeyHash, SHA_DIGEST_SIZE); + if (cmp != 0) + { + CYASSL_MSG("\tissuerKeyHash mismatch"); + return cmp; + } + + cmp = req->serialSz - resp->status->serialSz; + if (cmp != 0) + { + CYASSL_MSG("\tserialSz mismatch"); + return cmp; + } + + cmp = XMEMCMP(req->serial, resp->status->serial, req->serialSz); + if (cmp != 0) + { + CYASSL_MSG("\tserial mismatch"); + return cmp; + } return 0; } diff --git a/cyassl/ctaocrypt/asn.h b/cyassl/ctaocrypt/asn.h index cb1e050f0..7db5f7d1f 100644 --- a/cyassl/ctaocrypt/asn.h +++ b/cyassl/ctaocrypt/asn.h @@ -358,68 +358,69 @@ typedef struct OcspResponse OcspResponse; struct CertStatus { - CertStatus* next; + CertStatus* next; - byte serial[EXTERNAL_SERIAL_SIZE]; - int serialSz; + byte serial[EXTERNAL_SERIAL_SIZE]; + int serialSz; int status; - byte thisDate[MAX_DATE_SIZE]; - byte nextDate[MAX_DATE_SIZE]; - byte thisDateFormat; - byte nextDateFormat; + byte thisDate[MAX_DATE_SIZE]; + byte nextDate[MAX_DATE_SIZE]; + byte thisDateFormat; + byte nextDateFormat; }; struct OcspResponse { int responseStatus; /* return code from Responder */ - word32 respBegin; /* index to beginning of OCSP Response */ - word32 respLength; /* length of the OCSP Response */ - - int version; /* Response version number */ + byte* response; /* Pointer to beginning of OCSP Response */ + word32 responseSz; /* length of the OCSP Response */ byte* producedAt; /* Time at which this response was signed */ - byte producedAtFormat;/* format of the producedAt date */ + byte producedAtFormat;/* format of the producedAt date */ + byte* issuerHash; + byte* issuerKeyHash; - word32 sigIndex; /* Index into source for start of sig */ - word32 sigLength; /* Length in octets for the sig */ + byte* cert; + word32 certSz; + + byte* sig; /* Pointer to sig in source */ + word32 sigSz; /* Length in octets for the sig */ word32 sigOID; /* OID for hash used for sig */ - CertStatus status[1]; /* list of certificate status */ + CertStatus* status; /* certificate status to fill out */ byte* nonce; /* pointer to nonce inside ASN.1 response */ int nonceSz; /* length of the nonce string */ byte* source; /* pointer to source buffer, not owned */ word32 maxIdx; /* max offset based on init size */ - void* heap; /* for user memory overrides */ }; struct OcspRequest { - byte* nonce; + DecodedCert* cert; + + byte nonce[MAX_OCSP_NONCE_SZ]; int nonceSz; - byte* issuerHash; /* pointer to issuerHash in source cert */ - byte* issuerKeyHash; /* pointer to issuerKeyHash in source cert */ - byte* serial; /* pointer to serial number in source cert */ - int serialSz; /* length of the serial number */ + byte* issuerHash; /* pointer to issuerHash in source cert */ + byte* issuerKeyHash; /* pointer to issuerKeyHash in source cert */ + byte* serial; /* pointer to serial number in source cert */ + int serialSz; /* length of the serial number */ byte* dest; /* pointer to the destination ASN.1 buffer */ word32 destSz; /* length of the destination buffer */ - void* heap; }; -CYASSL_LOCAL void InitOcspResponse(OcspResponse*, byte*, word32, void*); -CYASSL_LOCAL void FreeOcspResponse(OcspResponse*); +CYASSL_LOCAL void InitOcspResponse(OcspResponse*, CertStatus*, byte*, word32); CYASSL_LOCAL int OcspResponseDecode(OcspResponse*); -CYASSL_LOCAL void InitOcspRequest(OcspRequest*, byte*, word32, void*); -CYASSL_LOCAL void FreeOcspRequest(OcspRequest*); -CYASSL_LOCAL int EncodeOcspRequest(DecodedCert*, byte*, word32); +CYASSL_LOCAL void InitOcspRequest(OcspRequest*, DecodedCert*, byte*, word32); +CYASSL_LOCAL int EncodeOcspRequest(OcspRequest*); CYASSL_LOCAL int CompareOcspReqResp(OcspRequest*, OcspResponse*); diff --git a/cyassl/internal.h b/cyassl/internal.h index 0baecd6c2..fe7685d2b 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -630,7 +630,7 @@ struct OCSP_Entry { OCSP_Entry* next; /* next entry */ byte issuerHash[SHA_DIGEST_SIZE]; /* issuer hash */ byte issuerKeyHash[SHA_DIGEST_SIZE]; /* issuer public key hash */ - CertStatus status[1]; /* OCSP response list */ + CertStatus* status; /* OCSP response list */ int totalStatus; /* number on list */ }; @@ -642,7 +642,7 @@ struct CYASSL_OCSP { char overrideName[80]; char overridePath[80]; int overridePort; - OCSP_Entry ocspList[1]; + OCSP_Entry* ocspList; }; diff --git a/src/ocsp.c b/src/ocsp.c index 62cf628f0..ed33a7b6c 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -51,29 +51,6 @@ typedef struct sockaddr_in SOCKADDR_IN_T; #define SOCKET_T unsigned int -int ocsp_test(unsigned char* buf, int sz) -{ - CYASSL_OCSP ocsp; - OcspResponse resp; - int result; - - CyaSSL_OCSP_Init(&ocsp); - InitOcspResponse(&resp, buf, sz, NULL); - - ocsp.enabled = 1; - ocsp.useOverrideUrl = 1; - CyaSSL_OCSP_set_override_url(&ocsp, "http://ocsp.example.com:8080/bob"); - CyaSSL_OCSP_Lookup_Cert(&ocsp, NULL); - - result = OcspResponseDecode(&resp); - - FreeOcspResponse(&resp); - CyaSSL_OCSP_Cleanup(&ocsp); - - return result; -} - - int CyaSSL_OCSP_Init(CYASSL_OCSP* ocsp) { if (ocsp != NULL) { @@ -85,83 +62,105 @@ int CyaSSL_OCSP_Init(CYASSL_OCSP* ocsp) } +static void FreeOCSP_Entry(OCSP_Entry* ocspe) +{ + CertStatus* tmp = ocspe->status; + + CYASSL_ENTER("FreeOCSP_Entry"); + + while (tmp) { + CertStatus* next = tmp->next; + XFREE(tmp, NULL, DYNAMIC_TYPE_OCSP_STATUS); + tmp = next; + } +} + + void CyaSSL_OCSP_Cleanup(CYASSL_OCSP* ocsp) { + OCSP_Entry* tmp = ocsp->ocspList; + ocsp->enabled = 0; + while (tmp) { + OCSP_Entry* next = tmp->next; + FreeOCSP_Entry(tmp); + XFREE(tmp, NULL, DYNAMIC_TYPE_OCSP_ENTRY); + tmp = next; + } } static int decode_url(const char* url, int urlSz, - char* outName, char* outPath, int* outPort) + char* outName, char* outPath, int* outPort) { - if (outName != NULL && outPath != NULL && outPort != NULL) - { - if (url == NULL || urlSz == 0) - { - *outName = 0; - *outPath = 0; - *outPort = 0; - } - else - { - int i, cur; - - /* need to break the url down into scheme, address, and port */ - /* "http://example.com:8080/" */ - if (XSTRNCMP(url, "http://", 7) == 0) { - cur = 7; - } else cur = 0; - - i = 0; - while (url[cur] != 0 && url[cur] != ':' && url[cur] != '/') { - outName[i++] = url[cur++]; - } - outName[i] = 0; - /* Need to pick out the path after the domain name */ - - if (cur < urlSz && url[cur] == ':') { - char port[6]; - int j; - i = 0; - cur++; - while (cur < urlSz && url[cur] != 0 && url[cur] != '/' && - i < 6) { - port[i++] = url[cur++]; - } - - *outPort = 0; - for (j = 0; j < i; j++) { - if (port[j] < '0' || port[j] > '9') return -1; - *outPort = (*outPort * 10) + (port[j] - '0'); - } - } - else - *outPort = 80; - - if (cur < urlSz && url[cur] == '/') { - i = 0; - while (cur < urlSz && url[cur] != 0 && i < 80) { - outPath[i++] = url[cur++]; - } - outPath[i] = 0; - } - else { - outPath[0] = '/'; - outPath[1] = 0; - } - } - } + if (outName != NULL && outPath != NULL && outPort != NULL) + { + if (url == NULL || urlSz == 0) + { + *outName = 0; + *outPath = 0; + *outPort = 0; + } + else + { + int i, cur; + + /* need to break the url down into scheme, address, and port */ + /* "http://example.com:8080/" */ + if (XSTRNCMP(url, "http://", 7) == 0) { + cur = 7; + } else cur = 0; + + i = 0; + while (url[cur] != 0 && url[cur] != ':' && url[cur] != '/') { + outName[i++] = url[cur++]; + } + outName[i] = 0; + /* Need to pick out the path after the domain name */ + + if (cur < urlSz && url[cur] == ':') { + char port[6]; + int j; + i = 0; + cur++; + while (cur < urlSz && url[cur] != 0 && url[cur] != '/' && + i < 6) { + port[i++] = url[cur++]; + } + + *outPort = 0; + for (j = 0; j < i; j++) { + if (port[j] < '0' || port[j] > '9') return -1; + *outPort = (*outPort * 10) + (port[j] - '0'); + } + } + else + *outPort = 80; + + if (cur < urlSz && url[cur] == '/') { + i = 0; + while (cur < urlSz && url[cur] != 0 && i < 80) { + outPath[i++] = url[cur++]; + } + outPath[i] = 0; + } + else { + outPath[0] = '/'; + outPath[1] = 0; + } + } + } - return 0; + return 0; } int CyaSSL_OCSP_set_override_url(CYASSL_OCSP* ocsp, const char* url) { if (ocsp != NULL) { - int urlSz = strlen(url); - decode_url(url, urlSz, - ocsp->overrideName, ocsp->overridePath, &ocsp->overridePort); + int urlSz = strlen(url); + decode_url(url, urlSz, + ocsp->overrideName, ocsp->overridePath, &ocsp->overridePort); return 1; } @@ -214,7 +213,7 @@ static INLINE void tcp_connect(SOCKET_T* sockfd, const char* ip, word16 port) static int build_http_request(const char* domainName, const char* path, - int ocspReqSz, byte* buf, int bufSize) + int ocspReqSz, byte* buf, int bufSize) { return snprintf((char*)buf, bufSize, "POST %s HTTP/1.1\r\n" @@ -226,22 +225,23 @@ static int build_http_request(const char* domainName, const char* path, } -static byte* decode_http_response(byte* httpBuf, int httpBufSz, int* ocspRespSz) +static int decode_http_response(byte* httpBuf, int httpBufSz, byte** dst) { int idx = 0; int stop = 0; + int len = 0; byte* contentType = NULL; byte* contentLength = NULL; char* buf = (char*)httpBuf; /* kludge so I'm not constantly casting */ if (strncasecmp(buf, "HTTP/1", 6) != 0) - return NULL; + return 0; idx = 9; /* sets to the first byte after "HTTP/1.X ", which should be the * HTTP result code */ if (strncasecmp(&buf[idx], "200 OK", 6) != 0) - return NULL; + return 0; idx += 8; @@ -256,18 +256,16 @@ static byte* decode_http_response(byte* httpBuf, int httpBufSz, int* ocspRespSz) idx += 13; if (buf[idx] == ' ') idx++; if (strncasecmp(&buf[idx], "application/ocsp-response", 25) != 0) - return NULL; + return 0; idx += 27; } else if (contentLength == NULL && strncasecmp(&buf[idx], "Content-Length:", 15) == 0) { - int len = 0; idx += 15; if (buf[idx] == ' ') idx++; while (buf[idx] >= '0' && buf[idx] <= '9' && idx < httpBufSz) { len = (len * 10) + (buf[idx] - '0'); idx++; } - *ocspRespSz = len; idx += 2; /* skip the crlf */ } else { /* Advance idx past the next \r\n */ @@ -277,52 +275,128 @@ static byte* decode_http_response(byte* httpBuf, int httpBufSz, int* ocspRespSz) } } } - return &httpBuf[idx]; + + if (len > 0) { + *dst = (byte*)XMALLOC(len, NULL, DYNAMIC_TYPE_IN_BUFFER); + XMEMCPY(*dst, httpBuf + idx, len); + } + + return len; +} + + +static int InitOCSP_Entry(OCSP_Entry* ocspe, DecodedCert* cert) +{ + CYASSL_ENTER("InitOCSP_Entry"); + + ocspe->next = NULL; + XMEMCPY(ocspe->issuerHash, cert->issuerHash, SHA_DIGEST_SIZE); + XMEMCPY(ocspe->issuerKeyHash, cert->issuerKeyHash, SHA_DIGEST_SIZE); + ocspe->status = NULL; + ocspe->totalStatus = 0; + + return 0; +} + + +static OCSP_Entry* find_ocsp_entry(CYASSL_OCSP* ocsp, DecodedCert* cert) +{ + OCSP_Entry* entry = ocsp->ocspList; + + while (entry) + { + if (XMEMCMP(entry->issuerHash, cert->issuerHash, SHA_DIGEST_SIZE) == 0 + && XMEMCMP(entry->issuerKeyHash, cert->issuerKeyHash, + SHA_DIGEST_SIZE) == 0) + { + CYASSL_MSG("Found OCSP responder"); + break; + } + else + { + entry = entry->next; + } + } + + if (entry == NULL) + { + CYASSL_MSG("Add a new OCSP entry"); + entry = (OCSP_Entry*)XMALLOC(sizeof(OCSP_Entry), + NULL, DYNAMIC_TYPE_OCSP_ENTRY); + if (entry != NULL) + { + InitOCSP_Entry(entry, cert); + entry->next = ocsp->ocspList; + ocsp->ocspList = entry; + } + } + + return entry; +} + + +static CertStatus* find_cert_status(OCSP_Entry* ocspe, DecodedCert* cert) +{ + CertStatus* stat = ocspe->status; + + while (stat) + { + if(stat->serialSz == cert->serialSz && + (XMEMCMP(stat->serial, cert->serial, cert->serialSz) == 0)) + { + break; + } + else + { + stat = stat->next; + } + } + if (stat == NULL) + { + stat = (CertStatus*)XMALLOC(sizeof(CertStatus), + NULL, DYNAMIC_TYPE_OCSP_STATUS); + if (stat != NULL) + { + XMEMCPY(stat->serial, cert->serial, cert->serialSz); + stat->serialSz = cert->serialSz; + stat->status = -1; + ocspe->totalStatus++; + + stat->next = ocspe->status; + ocspe->status = stat; + } + } + + return stat; } #define SCRATCH_BUFFER_SIZE 2048 -int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) +static int http_ocsp_transaction(CYASSL_OCSP* ocsp, DecodedCert* cert, + byte* ocspReqBuf, int ocspReqSz, byte** ocspRespBuf) { SOCKET_T sfd = -1; - byte buf[SCRATCH_BUFFER_SIZE]; - byte* httpBuf = &buf[0]; - int httpBufSz = SCRATCH_BUFFER_SIZE/4; - byte* ocspReqBuf = &buf[httpBufSz]; - int ocspReqSz = SCRATCH_BUFFER_SIZE - httpBufSz; - OcspResponse ocspResponse; - int result = 0; - char domainName[80], path[80]; - int port; - - /* If OCSP lookups are disabled, return success. */ - if (!ocsp->enabled) { - CYASSL_MSG("OCSP lookup disabled, assuming CERT_GOOD"); - return 0; - } + byte httpBuf[SCRATCH_BUFFER_SIZE]; + int httpBufSz = SCRATCH_BUFFER_SIZE; + char domainName[80], path[80]; + int port, ocspRespSz; if (ocsp->useOverrideUrl || cert->extAuthInfo == NULL) { - if (ocsp->overrideName != NULL) { - XMEMCPY(domainName, ocsp->overrideName, 80); - XMEMCPY(path, ocsp->overridePath, 80); - port = ocsp->overridePort; - } else - return OCSP_NEED_URL; - } else { - if (!decode_url((const char*)cert->extAuthInfo, cert->extAuthInfoSz, - domainName, path, &port)) - return OCSP_NEED_URL; - } + if (ocsp->overrideName != NULL) { + XMEMCPY(domainName, ocsp->overrideName, 80); + XMEMCPY(path, ocsp->overridePath, 80); + port = ocsp->overridePort; + } else + return OCSP_NEED_URL; + } else { + if (!decode_url((const char*)cert->extAuthInfo, cert->extAuthInfoSz, + domainName, path, &port)) + return OCSP_NEED_URL; + } - XMEMCPY(ocsp->ocspList->issuerHash, cert->issuerHash, SHA_SIZE); - XMEMCPY(ocsp->ocspList->issuerKeyHash, cert->issuerKeyHash, SHA_SIZE); - XMEMCPY(ocsp->ocspList->status->serial, cert->serial, cert->serialSz); - ocsp->ocspList->status->serialSz = cert->serialSz; - - ocspReqSz = EncodeOcspRequest(cert, ocspReqBuf, ocspReqSz); httpBufSz = build_http_request(domainName, path, ocspReqSz, - httpBuf, httpBufSz); + httpBuf, httpBufSz); tcp_connect(&sfd, domainName, port); if (sfd > 0) { @@ -331,15 +405,15 @@ int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) if (written == httpBufSz) { written = write(sfd, ocspReqBuf, ocspReqSz); if (written == ocspReqSz) { - httpBufSz = read(sfd, buf, SCRATCH_BUFFER_SIZE); + httpBufSz = read(sfd, httpBuf, SCRATCH_BUFFER_SIZE); if (httpBufSz > 0) { - ocspReqBuf = decode_http_response(buf, httpBufSz, - &ocspReqSz); + ocspRespSz = decode_http_response(httpBuf, httpBufSz, + ocspRespBuf); } } } close(sfd); - if (ocspReqBuf == NULL) { + if (ocspRespSz == 0) { CYASSL_MSG("HTTP response was not OK, no OCSP response"); return OCSP_LOOKUP_FAIL; } @@ -348,26 +422,82 @@ int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) return OCSP_LOOKUP_FAIL; } - InitOcspResponse(&ocspResponse, ocspReqBuf, ocspReqSz, NULL); + return ocspRespSz; +} + + +int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) +{ + byte ocspReqBuf[SCRATCH_BUFFER_SIZE]; + int ocspReqSz = SCRATCH_BUFFER_SIZE; + byte* ocspRespBuf = NULL; + int ocspRespSz; + OcspRequest ocspRequest; + OcspResponse ocspResponse; + int result = 0; + OCSP_Entry* ocspe; + CertStatus* certStatus; + + /* If OCSP lookups are disabled, return success. */ + if (!ocsp->enabled) { + CYASSL_MSG("OCSP lookup disabled, assuming CERT_GOOD"); + return 0; + } + + ocspe = find_ocsp_entry(ocsp, cert); + if (ocspe == NULL) { + CYASSL_MSG("alloc OCSP entry failed"); + return MEMORY_ERROR; + } + + certStatus = find_cert_status(ocspe, cert); + if (certStatus == NULL) + { + CYASSL_MSG("alloc OCSP cert status failed"); + return MEMORY_ERROR; + } + + if (certStatus->status != -1) + { + } + + InitOcspRequest(&ocspRequest, cert, ocspReqBuf, ocspReqSz); + ocspReqSz = EncodeOcspRequest(&ocspRequest); + result = http_ocsp_transaction(ocsp, cert, + ocspReqBuf, ocspReqSz, &ocspRespBuf); + if (result < 0) return result; + /* If the transaction failed, return that result. */ + + InitOcspResponse(&ocspResponse, certStatus, ocspRespBuf, ocspRespSz); OcspResponseDecode(&ocspResponse); if (ocspResponse.responseStatus != OCSP_SUCCESSFUL) { CYASSL_MSG("OCSP Responder failure"); - result = OCSP_LOOKUP_FAIL; + result = OCSP_LOOKUP_FAIL; } else { - switch (ocspResponse.status[0].status) { - case CERT_GOOD: - result = 0; - break; - case CERT_REVOKED: - result = OCSP_CERT_REVOKED; - break; - default: - result = OCSP_CERT_UNKNOWN; - break; - } + if (CompareOcspReqResp(&ocspRequest, &ocspResponse) == 0) + { + switch (ocspResponse.status[0].status) { + case CERT_GOOD: + result = 0; + break; + case CERT_REVOKED: + result = OCSP_CERT_REVOKED; + break; + default: + result = OCSP_CERT_UNKNOWN; + break; + } + } + else + { + CYASSL_MSG("OCSP Response incorrect for Request"); + result = OCSP_LOOKUP_FAIL; + } + } + if (ocspReqBuf != NULL) { + XFREE(ocspRespBuf, NULL, DYNAMIC_TYPE_IN_BUFFER); } - FreeOcspResponse(&ocspResponse); return result; }