1109998Smarkm/* ocsp_ht.c */ 2194206Ssimon/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 3194206Ssimon * project 2006. 4109998Smarkm */ 5109998Smarkm/* ==================================================================== 6194206Ssimon * Copyright (c) 2006 The OpenSSL Project. All rights reserved. 7109998Smarkm * 8109998Smarkm * Redistribution and use in source and binary forms, with or without 9109998Smarkm * modification, are permitted provided that the following conditions 10109998Smarkm * are met: 11109998Smarkm * 12109998Smarkm * 1. Redistributions of source code must retain the above copyright 13109998Smarkm * notice, this list of conditions and the following disclaimer. 14109998Smarkm * 15109998Smarkm * 2. Redistributions in binary form must reproduce the above copyright 16109998Smarkm * notice, this list of conditions and the following disclaimer in 17109998Smarkm * the documentation and/or other materials provided with the 18109998Smarkm * distribution. 19109998Smarkm * 20109998Smarkm * 3. All advertising materials mentioning features or use of this 21109998Smarkm * software must display the following acknowledgment: 22109998Smarkm * "This product includes software developed by the OpenSSL Project 23109998Smarkm * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24109998Smarkm * 25109998Smarkm * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26109998Smarkm * endorse or promote products derived from this software without 27109998Smarkm * prior written permission. For written permission, please contact 28109998Smarkm * licensing@OpenSSL.org. 29109998Smarkm * 30109998Smarkm * 5. Products derived from this software may not be called "OpenSSL" 31109998Smarkm * nor may "OpenSSL" appear in their names without prior written 32109998Smarkm * permission of the OpenSSL Project. 33109998Smarkm * 34109998Smarkm * 6. Redistributions of any form whatsoever must retain the following 35109998Smarkm * acknowledgment: 36109998Smarkm * "This product includes software developed by the OpenSSL Project 37109998Smarkm * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38109998Smarkm * 39109998Smarkm * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40109998Smarkm * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41109998Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42109998Smarkm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43109998Smarkm * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44109998Smarkm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45109998Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46109998Smarkm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47109998Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48109998Smarkm * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49109998Smarkm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50109998Smarkm * OF THE POSSIBILITY OF SUCH DAMAGE. 51109998Smarkm * ==================================================================== 52109998Smarkm * 53109998Smarkm * This product includes cryptographic software written by Eric Young 54109998Smarkm * (eay@cryptsoft.com). This product includes software written by Tim 55109998Smarkm * Hudson (tjh@cryptsoft.com). 56109998Smarkm * 57109998Smarkm */ 58109998Smarkm 59109998Smarkm#include <stdio.h> 60109998Smarkm#include <stdlib.h> 61109998Smarkm#include <ctype.h> 62109998Smarkm#include <string.h> 63194206Ssimon#include "e_os.h" 64194206Ssimon#include <openssl/asn1.h> 65109998Smarkm#include <openssl/ocsp.h> 66109998Smarkm#include <openssl/err.h> 67109998Smarkm#include <openssl/buffer.h> 68109998Smarkm#ifdef OPENSSL_SYS_SUNOS 69109998Smarkm#define strtoul (unsigned long)strtol 70109998Smarkm#endif /* OPENSSL_SYS_SUNOS */ 71109998Smarkm 72194206Ssimon/* Stateful OCSP request code, supporting non-blocking I/O */ 73109998Smarkm 74194206Ssimon/* Opaque OCSP request status structure */ 75109998Smarkm 76194206Ssimonstruct ocsp_req_ctx_st { 77194206Ssimon int state; /* Current I/O state */ 78194206Ssimon unsigned char *iobuf; /* Line buffer */ 79194206Ssimon int iobuflen; /* Line buffer length */ 80194206Ssimon BIO *io; /* BIO to perform I/O with */ 81194206Ssimon BIO *mem; /* Memory BIO response is built into */ 82194206Ssimon unsigned long asn1_len; /* ASN1 length of response */ 83194206Ssimon }; 84194206Ssimon 85194206Ssimon#define OCSP_MAX_REQUEST_LENGTH (100 * 1024) 86194206Ssimon#define OCSP_MAX_LINE_LEN 4096; 87194206Ssimon 88194206Ssimon/* OCSP states */ 89194206Ssimon 90194206Ssimon/* If set no reading should be performed */ 91194206Ssimon#define OHS_NOREAD 0x1000 92194206Ssimon/* Error condition */ 93194206Ssimon#define OHS_ERROR (0 | OHS_NOREAD) 94194206Ssimon/* First line being read */ 95194206Ssimon#define OHS_FIRSTLINE 1 96194206Ssimon/* MIME headers being read */ 97194206Ssimon#define OHS_HEADERS 2 98194206Ssimon/* OCSP initial header (tag + length) being read */ 99194206Ssimon#define OHS_ASN1_HEADER 3 100194206Ssimon/* OCSP content octets being read */ 101194206Ssimon#define OHS_ASN1_CONTENT 4 102194206Ssimon/* Request being sent */ 103194206Ssimon#define OHS_ASN1_WRITE (6 | OHS_NOREAD) 104194206Ssimon/* Request being flushed */ 105194206Ssimon#define OHS_ASN1_FLUSH (7 | OHS_NOREAD) 106194206Ssimon/* Completed */ 107194206Ssimon#define OHS_DONE (8 | OHS_NOREAD) 108194206Ssimon 109194206Ssimon 110194206Ssimonstatic int parse_http_line1(char *line); 111194206Ssimon 112194206Ssimonvoid OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) 113194206Ssimon { 114194206Ssimon if (rctx->mem) 115194206Ssimon BIO_free(rctx->mem); 116194206Ssimon if (rctx->iobuf) 117194206Ssimon OPENSSL_free(rctx->iobuf); 118194206Ssimon OPENSSL_free(rctx); 119109998Smarkm } 120194206Ssimon 121238405Sjkimint OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req) 122194206Ssimon { 123238405Sjkim static const char req_hdr[] = 124194206Ssimon "Content-Type: application/ocsp-request\r\n" 125194206Ssimon "Content-Length: %d\r\n\r\n"; 126238405Sjkim if (BIO_printf(rctx->mem, req_hdr, i2d_OCSP_REQUEST(req, NULL)) <= 0) 127238405Sjkim return 0; 128238405Sjkim if (i2d_OCSP_REQUEST_bio(rctx->mem, req) <= 0) 129238405Sjkim return 0; 130238405Sjkim rctx->state = OHS_ASN1_WRITE; 131238405Sjkim rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL); 132238405Sjkim return 1; 133238405Sjkim } 134194206Ssimon 135238405Sjkimint OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, 136238405Sjkim const char *name, const char *value) 137238405Sjkim { 138238405Sjkim if (!name) 139238405Sjkim return 0; 140238405Sjkim if (BIO_puts(rctx->mem, name) <= 0) 141238405Sjkim return 0; 142238405Sjkim if (value) 143238405Sjkim { 144238405Sjkim if (BIO_write(rctx->mem, ": ", 2) != 2) 145238405Sjkim return 0; 146238405Sjkim if (BIO_puts(rctx->mem, value) <= 0) 147238405Sjkim return 0; 148238405Sjkim } 149238405Sjkim if (BIO_write(rctx->mem, "\r\n", 2) != 2) 150238405Sjkim return 0; 151238405Sjkim return 1; 152238405Sjkim } 153238405Sjkim 154238405SjkimOCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, char *path, OCSP_REQUEST *req, 155238405Sjkim int maxline) 156238405Sjkim { 157238405Sjkim static const char post_hdr[] = "POST %s HTTP/1.0\r\n"; 158238405Sjkim 159194206Ssimon OCSP_REQ_CTX *rctx; 160194206Ssimon rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX)); 161279264Sdelphij if (!rctx) 162279264Sdelphij return NULL; 163238405Sjkim rctx->state = OHS_ERROR; 164194206Ssimon rctx->mem = BIO_new(BIO_s_mem()); 165194206Ssimon rctx->io = io; 166238405Sjkim rctx->asn1_len = 0; 167194206Ssimon if (maxline > 0) 168194206Ssimon rctx->iobuflen = maxline; 169194206Ssimon else 170194206Ssimon rctx->iobuflen = OCSP_MAX_LINE_LEN; 171194206Ssimon rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); 172279264Sdelphij if (!rctx->mem || !rctx->iobuf) 173279264Sdelphij goto err; 174194206Ssimon if (!path) 175194206Ssimon path = "/"; 176194206Ssimon 177238405Sjkim if (BIO_printf(rctx->mem, post_hdr, path) <= 0) 178279264Sdelphij goto err; 179238405Sjkim 180238405Sjkim if (req && !OCSP_REQ_CTX_set1_req(rctx, req)) 181279264Sdelphij goto err; 182194206Ssimon 183194206Ssimon return rctx; 184279264Sdelphij err: 185279264Sdelphij OCSP_REQ_CTX_free(rctx); 186279264Sdelphij return NULL; 187109998Smarkm } 188109998Smarkm 189194206Ssimon/* Parse the HTTP response. This will look like this: 190194206Ssimon * "HTTP/1.0 200 OK". We need to obtain the numeric code and 191194206Ssimon * (optional) informational message. 192194206Ssimon */ 193194206Ssimon 194194206Ssimonstatic int parse_http_line1(char *line) 195194206Ssimon { 196194206Ssimon int retcode; 197194206Ssimon char *p, *q, *r; 198109998Smarkm /* Skip to first white space (passed protocol info) */ 199194206Ssimon 200194206Ssimon for(p = line; *p && !isspace((unsigned char)*p); p++) 201194206Ssimon continue; 202194206Ssimon if(!*p) 203194206Ssimon { 204194206Ssimon OCSPerr(OCSP_F_PARSE_HTTP_LINE1, 205194206Ssimon OCSP_R_SERVER_RESPONSE_PARSE_ERROR); 206194206Ssimon return 0; 207194206Ssimon } 208194206Ssimon 209109998Smarkm /* Skip past white space to start of response code */ 210194206Ssimon while(*p && isspace((unsigned char)*p)) 211194206Ssimon p++; 212194206Ssimon 213194206Ssimon if(!*p) 214194206Ssimon { 215194206Ssimon OCSPerr(OCSP_F_PARSE_HTTP_LINE1, 216194206Ssimon OCSP_R_SERVER_RESPONSE_PARSE_ERROR); 217194206Ssimon return 0; 218194206Ssimon } 219194206Ssimon 220109998Smarkm /* Find end of response code: first whitespace after start of code */ 221194206Ssimon for(q = p; *q && !isspace((unsigned char)*q); q++) 222194206Ssimon continue; 223194206Ssimon 224194206Ssimon if(!*q) 225194206Ssimon { 226194206Ssimon OCSPerr(OCSP_F_PARSE_HTTP_LINE1, 227194206Ssimon OCSP_R_SERVER_RESPONSE_PARSE_ERROR); 228194206Ssimon return 0; 229194206Ssimon } 230194206Ssimon 231109998Smarkm /* Set end of response code and start of message */ 232109998Smarkm *q++ = 0; 233194206Ssimon 234109998Smarkm /* Attempt to parse numeric code */ 235109998Smarkm retcode = strtoul(p, &r, 10); 236194206Ssimon 237194206Ssimon if(*r) 238194206Ssimon return 0; 239194206Ssimon 240109998Smarkm /* Skip over any leading white space in message */ 241194206Ssimon while(*q && isspace((unsigned char)*q)) 242194206Ssimon q++; 243194206Ssimon 244194206Ssimon if(*q) 245194206Ssimon { 246194206Ssimon /* Finally zap any trailing white space in message (include 247194206Ssimon * CRLF) */ 248194206Ssimon 249194206Ssimon /* We know q has a non white space character so this is OK */ 250194206Ssimon for(r = q + strlen(q) - 1; isspace((unsigned char)*r); r--) 251194206Ssimon *r = 0; 252194206Ssimon } 253194206Ssimon if(retcode != 200) 254194206Ssimon { 255194206Ssimon OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR); 256194206Ssimon if(!*q) 257120631Snectar ERR_add_error_data(2, "Code=", p); 258194206Ssimon else 259120631Snectar ERR_add_error_data(4, "Code=", p, ",Reason=", q); 260194206Ssimon return 0; 261120631Snectar } 262194206Ssimon 263194206Ssimon 264194206Ssimon return 1; 265194206Ssimon 266109998Smarkm } 267194206Ssimon 268194206Ssimonint OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) 269109998Smarkm { 270194206Ssimon int i, n; 271194206Ssimon const unsigned char *p; 272194206Ssimon next_io: 273194206Ssimon if (!(rctx->state & OHS_NOREAD)) 274194206Ssimon { 275194206Ssimon n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen); 276194206Ssimon 277194206Ssimon if (n <= 0) 278194206Ssimon { 279194206Ssimon if (BIO_should_retry(rctx->io)) 280194206Ssimon return -1; 281194206Ssimon return 0; 282194206Ssimon } 283194206Ssimon 284194206Ssimon /* Write data to memory BIO */ 285194206Ssimon 286194206Ssimon if (BIO_write(rctx->mem, rctx->iobuf, n) != n) 287194206Ssimon return 0; 288194206Ssimon } 289194206Ssimon 290194206Ssimon switch(rctx->state) 291194206Ssimon { 292194206Ssimon 293194206Ssimon case OHS_ASN1_WRITE: 294194206Ssimon n = BIO_get_mem_data(rctx->mem, &p); 295194206Ssimon 296194206Ssimon i = BIO_write(rctx->io, 297194206Ssimon p + (n - rctx->asn1_len), rctx->asn1_len); 298194206Ssimon 299194206Ssimon if (i <= 0) 300194206Ssimon { 301194206Ssimon if (BIO_should_retry(rctx->io)) 302194206Ssimon return -1; 303194206Ssimon rctx->state = OHS_ERROR; 304194206Ssimon return 0; 305194206Ssimon } 306194206Ssimon 307194206Ssimon rctx->asn1_len -= i; 308194206Ssimon 309194206Ssimon if (rctx->asn1_len > 0) 310194206Ssimon goto next_io; 311194206Ssimon 312194206Ssimon rctx->state = OHS_ASN1_FLUSH; 313194206Ssimon 314194206Ssimon (void)BIO_reset(rctx->mem); 315194206Ssimon 316194206Ssimon case OHS_ASN1_FLUSH: 317194206Ssimon 318194206Ssimon i = BIO_flush(rctx->io); 319194206Ssimon 320194206Ssimon if (i > 0) 321194206Ssimon { 322194206Ssimon rctx->state = OHS_FIRSTLINE; 323194206Ssimon goto next_io; 324194206Ssimon } 325194206Ssimon 326194206Ssimon if (BIO_should_retry(rctx->io)) 327194206Ssimon return -1; 328194206Ssimon 329194206Ssimon rctx->state = OHS_ERROR; 330194206Ssimon return 0; 331194206Ssimon 332194206Ssimon case OHS_ERROR: 333194206Ssimon return 0; 334194206Ssimon 335194206Ssimon case OHS_FIRSTLINE: 336194206Ssimon case OHS_HEADERS: 337194206Ssimon 338194206Ssimon /* Attempt to read a line in */ 339194206Ssimon 340194206Ssimon next_line: 341194206Ssimon /* Due to &%^*$" memory BIO behaviour with BIO_gets we 342194206Ssimon * have to check there's a complete line in there before 343194206Ssimon * calling BIO_gets or we'll just get a partial read. 344194206Ssimon */ 345194206Ssimon n = BIO_get_mem_data(rctx->mem, &p); 346194206Ssimon if ((n <= 0) || !memchr(p, '\n', n)) 347194206Ssimon { 348194206Ssimon if (n >= rctx->iobuflen) 349194206Ssimon { 350194206Ssimon rctx->state = OHS_ERROR; 351194206Ssimon return 0; 352194206Ssimon } 353194206Ssimon goto next_io; 354194206Ssimon } 355194206Ssimon n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen); 356194206Ssimon 357194206Ssimon if (n <= 0) 358194206Ssimon { 359194206Ssimon if (BIO_should_retry(rctx->mem)) 360194206Ssimon goto next_io; 361194206Ssimon rctx->state = OHS_ERROR; 362194206Ssimon return 0; 363194206Ssimon } 364194206Ssimon 365194206Ssimon /* Don't allow excessive lines */ 366194206Ssimon if (n == rctx->iobuflen) 367194206Ssimon { 368194206Ssimon rctx->state = OHS_ERROR; 369194206Ssimon return 0; 370194206Ssimon } 371194206Ssimon 372194206Ssimon /* First line */ 373194206Ssimon if (rctx->state == OHS_FIRSTLINE) 374194206Ssimon { 375194206Ssimon if (parse_http_line1((char *)rctx->iobuf)) 376194206Ssimon { 377194206Ssimon rctx->state = OHS_HEADERS; 378194206Ssimon goto next_line; 379194206Ssimon } 380194206Ssimon else 381194206Ssimon { 382194206Ssimon rctx->state = OHS_ERROR; 383194206Ssimon return 0; 384194206Ssimon } 385194206Ssimon } 386194206Ssimon else 387194206Ssimon { 388194206Ssimon /* Look for blank line: end of headers */ 389194206Ssimon for (p = rctx->iobuf; *p; p++) 390194206Ssimon { 391194206Ssimon if ((*p != '\r') && (*p != '\n')) 392194206Ssimon break; 393194206Ssimon } 394194206Ssimon if (*p) 395194206Ssimon goto next_line; 396194206Ssimon 397194206Ssimon rctx->state = OHS_ASN1_HEADER; 398194206Ssimon 399194206Ssimon } 400194206Ssimon 401194206Ssimon /* Fall thru */ 402194206Ssimon 403194206Ssimon 404194206Ssimon case OHS_ASN1_HEADER: 405215697Ssimon /* Now reading ASN1 header: can read at least 2 bytes which 406215697Ssimon * is enough for ASN1 SEQUENCE header and either length field 407215697Ssimon * or at least the length of the length field. 408194206Ssimon */ 409194206Ssimon n = BIO_get_mem_data(rctx->mem, &p); 410215697Ssimon if (n < 2) 411194206Ssimon goto next_io; 412194206Ssimon 413194206Ssimon /* Check it is an ASN1 SEQUENCE */ 414194206Ssimon if (*p++ != (V_ASN1_SEQUENCE|V_ASN1_CONSTRUCTED)) 415194206Ssimon { 416194206Ssimon rctx->state = OHS_ERROR; 417194206Ssimon return 0; 418194206Ssimon } 419194206Ssimon 420194206Ssimon /* Check out length field */ 421194206Ssimon if (*p & 0x80) 422194206Ssimon { 423215697Ssimon /* If MSB set on initial length octet we can now 424215697Ssimon * always read 6 octets: make sure we have them. 425215697Ssimon */ 426215697Ssimon if (n < 6) 427215697Ssimon goto next_io; 428194206Ssimon n = *p & 0x7F; 429194206Ssimon /* Not NDEF or excessive length */ 430194206Ssimon if (!n || (n > 4)) 431194206Ssimon { 432194206Ssimon rctx->state = OHS_ERROR; 433194206Ssimon return 0; 434194206Ssimon } 435194206Ssimon p++; 436194206Ssimon rctx->asn1_len = 0; 437194206Ssimon for (i = 0; i < n; i++) 438194206Ssimon { 439194206Ssimon rctx->asn1_len <<= 8; 440194206Ssimon rctx->asn1_len |= *p++; 441194206Ssimon } 442194206Ssimon 443194206Ssimon if (rctx->asn1_len > OCSP_MAX_REQUEST_LENGTH) 444194206Ssimon { 445194206Ssimon rctx->state = OHS_ERROR; 446194206Ssimon return 0; 447194206Ssimon } 448194206Ssimon 449194206Ssimon rctx->asn1_len += n + 2; 450194206Ssimon } 451194206Ssimon else 452194206Ssimon rctx->asn1_len = *p + 2; 453194206Ssimon 454194206Ssimon rctx->state = OHS_ASN1_CONTENT; 455194206Ssimon 456194206Ssimon /* Fall thru */ 457194206Ssimon 458194206Ssimon case OHS_ASN1_CONTENT: 459194206Ssimon n = BIO_get_mem_data(rctx->mem, &p); 460194206Ssimon if (n < (int)rctx->asn1_len) 461194206Ssimon goto next_io; 462194206Ssimon 463194206Ssimon 464194206Ssimon *presp = d2i_OCSP_RESPONSE(NULL, &p, rctx->asn1_len); 465194206Ssimon if (*presp) 466194206Ssimon { 467194206Ssimon rctx->state = OHS_DONE; 468194206Ssimon return 1; 469194206Ssimon } 470194206Ssimon 471194206Ssimon rctx->state = OHS_ERROR; 472194206Ssimon return 0; 473194206Ssimon 474194206Ssimon break; 475194206Ssimon 476194206Ssimon case OHS_DONE: 477194206Ssimon return 1; 478194206Ssimon 479194206Ssimon } 480194206Ssimon 481194206Ssimon 482194206Ssimon 483194206Ssimon return 0; 484194206Ssimon 485194206Ssimon 486109998Smarkm } 487194206Ssimon 488194206Ssimon/* Blocking OCSP request handler: now a special case of non-blocking I/O */ 489194206Ssimon 490194206SsimonOCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req) 491194206Ssimon { 492194206Ssimon OCSP_RESPONSE *resp = NULL; 493194206Ssimon OCSP_REQ_CTX *ctx; 494194206Ssimon int rv; 495194206Ssimon 496194206Ssimon ctx = OCSP_sendreq_new(b, path, req, -1); 497194206Ssimon 498279264Sdelphij if (!ctx) 499279264Sdelphij return NULL; 500279264Sdelphij 501194206Ssimon do 502194206Ssimon { 503194206Ssimon rv = OCSP_sendreq_nbio(&resp, ctx); 504194206Ssimon } while ((rv == -1) && BIO_should_retry(b)); 505194206Ssimon 506194206Ssimon OCSP_REQ_CTX_free(ctx); 507194206Ssimon 508194206Ssimon if (rv) 509194206Ssimon return resp; 510194206Ssimon 511194206Ssimon return NULL; 512109998Smarkm } 513