From 0c34ecb451629a0560510a085e5fcbd5c1dd1295 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 20 Jun 2013 11:07:54 -0700 Subject: [PATCH] OCSP Updates 1. Add option to example server and client to check the OCSP responder. 2. Add option to example server and client to override the URL to use when checking the OCSP responder. 3. Copy the certificate serial number correctly into OCSP request. Add leading zero only if MS bit is set. 4. Fix responder address used when Auth Info extension is present. 5. Update EmbedOcspLookup callback to better handle the HTTP response and obtain the complete OCSP response. --- ctaocrypt/src/asn.c | 18 +++- examples/client/client.c | 34 ++++++- examples/server/server.c | 33 ++++++- src/io.c | 192 +++++++++++++++++++++++++-------------- src/ocsp.c | 2 +- 5 files changed, 202 insertions(+), 77 deletions(-) diff --git a/ctaocrypt/src/asn.c b/ctaocrypt/src/asn.c index fd050cfb7..21d3b9c80 100644 --- a/ctaocrypt/src/asn.c +++ b/ctaocrypt/src/asn.c @@ -4847,10 +4847,20 @@ static int SetSerialNumber(const byte* sn, word32 snSz, byte* output) if (snSz <= EXTERNAL_SERIAL_SIZE) { output[0] = ASN_INTEGER; - output[1] = snSz + 1; - output[2] = 0; - XMEMCPY(&output[3], sn, snSz); - result = snSz + 3; + /* The serial number is always positive. When encoding the + * INTEGER, if the MSB is 1, add a padding zero to keep the + * number positive. */ + if (sn[0] & 0x80) { + output[1] = snSz + 1; + output[2] = 0; + XMEMCPY(&output[3], sn, snSz); + result = snSz + 3; + } + else { + output[1] = snSz; + XMEMCPY(&output[2], sn, snSz); + result = snSz + 2; + } } return result; } diff --git a/examples/client/client.c b/examples/client/client.c index 200d05f74..632d872f8 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -131,6 +131,10 @@ static void Usage(void) printf("-z Print structure sizes\n"); #endif printf("-S Use Host Name Indication\n"); +#ifdef HAVE_OCSP + printf("-o Perform OCSP lookup on peer certificate\n"); + printf("-O Perform OCSP lookup using as responder\n"); +#endif } #ifdef CYASSL_MDK_SHELL @@ -186,6 +190,11 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) char* sniHostName = NULL; #endif +#ifdef HAVE_OCSP + int useOcsp = 0; + char* ocspUrl = NULL; +#endif + int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; @@ -203,7 +212,8 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) StackTrap(); - while ((ch = mygetopt(argc, argv, "?gdusmNrtfxh:p:v:l:A:c:k:b:zS:")) != -1){ + while ((ch = mygetopt(argc, argv, + "?gdusmNrtfxh:p:v:l:A:c:k:b:zS:oO:")) != -1) { switch (ch) { case '?' : Usage(); @@ -308,6 +318,19 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) #endif break; + case 'o' : + #ifdef HAVE_OCSP + useOcsp = 1; + #endif + break; + + case 'O' : + #ifdef HAVE_OCSP + useOcsp = 1; + ocspUrl = myoptarg; + #endif + break; + default: Usage(); exit(MY_EX_USAGE); @@ -429,6 +452,15 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) } #endif +#ifdef HAVE_OCSP + if (useOcsp) { + CyaSSL_CTX_OCSP_set_options(ctx, + CYASSL_OCSP_ENABLE | CYASSL_OCSP_NO_NONCE); + if (ocspUrl != NULL) + CyaSSL_CTX_OCSP_set_override_url(ctx, ocspUrl); + } +#endif + #ifdef USER_CA_CB CyaSSL_CTX_SetCACb(ctx, CaCb); #endif diff --git a/examples/server/server.c b/examples/server/server.c index c0eac3e51..032ff7a1e 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -123,6 +123,10 @@ static void Usage(void) printf("-f Fewer packets/group messages\n"); printf("-N Use Non-blocking sockets\n"); printf("-S Use Host Name Indication\n"); +#ifdef HAVE_OCSP + printf("-o Perform OCSP lookup on peer certificate\n"); + printf("-O Perform OCSP lookup using as responder\n"); +#endif } #ifdef CYASSL_MDK_SHELL @@ -164,6 +168,11 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) char* sniHostName = NULL; #endif +#ifdef HAVE_OCSP + int useOcsp = 0; + char* ocspUrl = NULL; +#endif + ((func_args*)args)->return_code = -1; /* error state */ #ifdef NO_RSA @@ -173,7 +182,7 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #endif (void)trackMemory; - while ((ch = mygetopt(argc, argv, "?dbstnNufp:v:l:A:c:k:S:")) != -1) { + while ((ch = mygetopt(argc, argv, "?dbstnNufp:v:l:A:c:k:S:oO:")) != -1) { switch (ch) { case '?' : Usage(); @@ -251,6 +260,19 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #endif break; + case 'o' : + #ifdef HAVE_OCSP + useOcsp = 1; + #endif + break; + + case 'O' : + #ifdef HAVE_OCSP + useOcsp = 1; + ocspUrl = myoptarg; + #endif + break; + default: Usage(); exit(MY_EX_USAGE); @@ -424,6 +446,15 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) CYASSL_CRL_START_MON); CyaSSL_SetCRL_Cb(ssl, CRL_CallBack); #endif +#ifdef HAVE_OCSP + if (useOcsp) { + CyaSSL_CTX_OCSP_set_options(ctx, + CYASSL_OCSP_ENABLE | CYASSL_OCSP_NO_NONCE); + if (ocspUrl != NULL) + CyaSSL_CTX_OCSP_set_override_url(ctx, ocspUrl); + } +#endif + tcp_accept(&sockfd, &clientfd, (func_args*)args, port, useAnyAddr, doDTLS); if (!doDTLS) CloseSocket(sockfd); diff --git a/src/io.c b/src/io.c index 15320fac2..64e19a682 100644 --- a/src/io.c +++ b/src/io.c @@ -562,69 +562,6 @@ static int build_http_request(const char* domainName, const char* path, } -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 (XSTRNCASECMP(buf, "HTTP/1", 6) != 0) - return 0; - - idx = 9; /* sets to the first byte after "HTTP/1.X ", which should be the - * HTTP result code */ - - if (XSTRNCASECMP(&buf[idx], "200 OK", 6) != 0) - return 0; - - idx += 8; - - while (idx < httpBufSz && !stop) { - if (buf[idx] == '\r' && buf[idx+1] == '\n') { - stop = 1; - idx += 2; - } - else { - if (contentType == NULL && - XSTRNCASECMP(&buf[idx], "Content-Type:", 13) == 0) { - idx += 13; - if (buf[idx] == ' ') idx++; - if (XSTRNCASECMP(&buf[idx], - "application/ocsp-response", 25) != 0) { - return 0; - } - idx += 27; - } - else if (contentLength == NULL && - XSTRNCASECMP(&buf[idx], "Content-Length:", 15) == 0) { - idx += 15; - if (buf[idx] == ' ') idx++; - while (buf[idx] >= '0' && buf[idx] <= '9' && idx < httpBufSz) { - len = (len * 10) + (buf[idx] - '0'); - idx++; - } - idx += 2; /* skip the crlf */ - } - else { - /* Advance idx past the next \r\n */ - char* end = XSTRSTR(&buf[idx], "\r\n"); - idx = (int)(end - buf + 2); - } - } - } - - if (len > 0) { - *dst = (byte*)XMALLOC(len, NULL, DYNAMIC_TYPE_IN_BUFFER); - XMEMCPY(*dst, httpBuf + idx, len); - } - - return len; -} - - static int decode_url(const char* url, int urlSz, char* outName, char* outPath, int* outPort) { @@ -694,7 +631,124 @@ static int decode_url(const char* url, int urlSz, } -#define SCRATCH_BUFFER_SIZE 2048 +/* return: >0 OCSP Response Size + * -1 error */ +static int process_http_response(int sfd, byte** respBuf, + byte* httpBuf, int httpBufSz) +{ + int result; + int len = 0; + char *start, *end; + byte *recvBuf = NULL; + int recvBufSz = 0; + enum phr_state { phr_init, phr_http_start, phr_have_length, + phr_have_type, phr_wait_end, phr_http_end + } state = phr_init; + + start = end = NULL; + do { + if (end == NULL) { + result = (int)recv(sfd, httpBuf+len, httpBufSz-len-1, 0); + if (result > 0) { + len += result; + start = (char*)httpBuf; + start[len+1] = 0; + } + else { + CYASSL_MSG("process_http_response recv http from peer failed"); + return -1; + } + } + end = XSTRSTR(start, "\r\n"); + + if (end == NULL) { + if (len != 0) + XMEMMOVE(httpBuf, start, len); + start = end = NULL; + } + else if (end == start) { + if (state == phr_wait_end) { + state = phr_http_end; + len -= 2; + start += 2; + } + else { + CYASSL_MSG("process_http_response header ended early"); + return -1; + } + } + else { + *end = 0; + len -= end - start + 2; + + if (XSTRNCASECMP(start, "HTTP/1", 6) == 0) { + start += 9; + if (XSTRNCASECMP(start, "200 OK", 6) != 0 || + state != phr_init) { + CYASSL_MSG("process_http_response not OK"); + return -1; + } + state = phr_http_start; + } + else if (XSTRNCASECMP(start, "Content-Type:", 13) == 0) { + start += 13; + while (*start == ' ' && *start != '\0') start++; + if (XSTRNCASECMP(start, "application/ocsp-response", 25) != 0) { + CYASSL_MSG("process_http_response not ocsp-response"); + return -1; + } + + if (state == phr_http_start) state = phr_have_type; + else if (state == phr_have_length) state = phr_wait_end; + else { + CYASSL_MSG("process_http_response type invalid state"); + return -1; + } + } + else if (XSTRNCASECMP(start, "Content-Length:", 15) == 0) { + start += 15; + while (*start == ' ' && *start != '\0') start++; + recvBufSz = atoi(start); + + if (state == phr_http_start) state = phr_have_length; + else if (state == phr_have_type) state = phr_wait_end; + else { + CYASSL_MSG("process_http_response length invalid state"); + return -1; + } + } + + start = end + 2; + } + } while (state != phr_http_end); + + recvBuf = XMALLOC(recvBufSz, NULL, DYNAMIC_TYPE_IN_BUFFER); + if (recvBuf == NULL) { + CYASSL_MSG("process_http_response couldn't create response buffer"); + return -1; + } + + /* copy the remainder of the httpBuf into the respBuf */ + if (len != 0) + XMEMCPY(recvBuf, start, len); + + /* receive the OCSP response data */ + do { + result = (int)recv(sfd, recvBuf+len, recvBufSz-len, 0); + if (result > 0) + len += result; + else { + CYASSL_MSG("process_http_response recv ocsp from peer failed"); + return -1; + } + } while (len != recvBufSz); + + *respBuf = recvBuf; + return recvBufSz; +} + + +#define SCRATCH_BUFFER_SIZE 512 int EmbedOcspLookup(void* ctx, const char* url, int urlSz, byte* ocspReqBuf, int ocspReqSz, byte** ocspRespBuf) @@ -721,6 +775,8 @@ int EmbedOcspLookup(void* ctx, const char* url, int urlSz, return -1; } + /* Note, the library uses the EmbedOcspRespFree() callback to + * free this buffer. */ httpBufSz = SCRATCH_BUFFER_SIZE; httpBuf = (byte*)XMALLOC(httpBufSz, NULL, DYNAMIC_TYPE_IN_BUFFER); @@ -728,7 +784,6 @@ int EmbedOcspLookup(void* ctx, const char* url, int urlSz, CYASSL_MSG("Unable to create OCSP response buffer"); return -1; } - *ocspRespBuf = httpBuf; httpBufSz = build_http_request(domainName, path, ocspReqSz, httpBuf, httpBufSz); @@ -739,11 +794,8 @@ int EmbedOcspLookup(void* ctx, const char* url, int urlSz, if (written == httpBufSz) { written = (int)send(sfd, ocspReqBuf, ocspReqSz, 0); if (written == ocspReqSz) { - httpBufSz = (int)recv(sfd, httpBuf, SCRATCH_BUFFER_SIZE, 0); - if (httpBufSz > 0) { - ocspRespSz = decode_http_response(httpBuf, httpBufSz, - ocspRespBuf); - } + ocspRespSz = process_http_response(sfd, ocspRespBuf, + httpBuf, SCRATCH_BUFFER_SIZE); } } close(sfd); diff --git a/src/ocsp.c b/src/ocsp.c index 64d082216..6933e9748 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -283,7 +283,7 @@ int CyaSSL_OCSP_Lookup_Cert(CYASSL_OCSP* ocsp, DecodedCert* cert) else return OCSP_NEED_URL; } - else if (cert->extAuthInfoSz == 0 || cert->extAuthInfo == NULL) { + else if (cert->extAuthInfoSz != 0 && cert->extAuthInfo != NULL) { url = (const char *)cert->extAuthInfo; urlSz = cert->extAuthInfoSz; }