1/* ocsp_ht.c */ 2/* 3 * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project 4 * 2006. 5 */ 6/* ==================================================================== 7 * Copyright (c) 2006 The OpenSSL Project. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. All advertising materials mentioning features or use of this 22 * software must display the following acknowledgment: 23 * "This product includes software developed by the OpenSSL Project 24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25 * 26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For written permission, please contact 29 * licensing@OpenSSL.org. 30 * 31 * 5. Products derived from this software may not be called "OpenSSL" 32 * nor may "OpenSSL" appear in their names without prior written 33 * permission of the OpenSSL Project. 34 * 35 * 6. Redistributions of any form whatsoever must retain the following 36 * acknowledgment: 37 * "This product includes software developed by the OpenSSL Project 38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 51 * OF THE POSSIBILITY OF SUCH DAMAGE. 52 * ==================================================================== 53 * 54 * This product includes cryptographic software written by Eric Young 55 * (eay@cryptsoft.com). This product includes software written by Tim 56 * Hudson (tjh@cryptsoft.com). 57 * 58 */ 59 60#include <stdio.h> 61#include <stdlib.h> 62#include <ctype.h> 63#include <string.h> 64#include "e_os.h" 65#include <openssl/asn1.h> 66#include <openssl/ocsp.h> 67#include <openssl/err.h> 68#include <openssl/buffer.h> 69#ifdef OPENSSL_SYS_SUNOS 70# define strtoul (unsigned long)strtol 71#endif /* OPENSSL_SYS_SUNOS */ 72 73/* Stateful OCSP request code, supporting non-blocking I/O */ 74 75/* Opaque OCSP request status structure */ 76 77struct ocsp_req_ctx_st { 78 int state; /* Current I/O state */ 79 unsigned char *iobuf; /* Line buffer */ 80 int iobuflen; /* Line buffer length */ 81 BIO *io; /* BIO to perform I/O with */ 82 BIO *mem; /* Memory BIO response is built into */ 83 unsigned long asn1_len; /* ASN1 length of response */ 84}; 85 86#define OCSP_MAX_REQUEST_LENGTH (100 * 1024) 87#define OCSP_MAX_LINE_LEN 4096; 88 89/* OCSP states */ 90 91/* If set no reading should be performed */ 92#define OHS_NOREAD 0x1000 93/* Error condition */ 94#define OHS_ERROR (0 | OHS_NOREAD) 95/* First line being read */ 96#define OHS_FIRSTLINE 1 97/* MIME headers being read */ 98#define OHS_HEADERS 2 99/* OCSP initial header (tag + length) being read */ 100#define OHS_ASN1_HEADER 3 101/* OCSP content octets being read */ 102#define OHS_ASN1_CONTENT 4 103/* Request being sent */ 104#define OHS_ASN1_WRITE (6 | OHS_NOREAD) 105/* Request being flushed */ 106#define OHS_ASN1_FLUSH (7 | OHS_NOREAD) 107/* Completed */ 108#define OHS_DONE (8 | OHS_NOREAD) 109 110static int parse_http_line1(char *line); 111 112void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) 113{ 114 if (rctx->mem) 115 BIO_free(rctx->mem); 116 if (rctx->iobuf) 117 OPENSSL_free(rctx->iobuf); 118 OPENSSL_free(rctx); 119} 120 121int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req) 122{ 123 static const char req_hdr[] = 124 "Content-Type: application/ocsp-request\r\n" 125 "Content-Length: %d\r\n\r\n"; 126 if (BIO_printf(rctx->mem, req_hdr, i2d_OCSP_REQUEST(req, NULL)) <= 0) 127 return 0; 128 if (i2d_OCSP_REQUEST_bio(rctx->mem, req) <= 0) 129 return 0; 130 rctx->state = OHS_ASN1_WRITE; 131 rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL); 132 return 1; 133} 134 135int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, 136 const char *name, const char *value) 137{ 138 if (!name) 139 return 0; 140 if (BIO_puts(rctx->mem, name) <= 0) 141 return 0; 142 if (value) { 143 if (BIO_write(rctx->mem, ": ", 2) != 2) 144 return 0; 145 if (BIO_puts(rctx->mem, value) <= 0) 146 return 0; 147 } 148 if (BIO_write(rctx->mem, "\r\n", 2) != 2) 149 return 0; 150 return 1; 151} 152 153OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, char *path, OCSP_REQUEST *req, 154 int maxline) 155{ 156 static const char post_hdr[] = "POST %s HTTP/1.0\r\n"; 157 158 OCSP_REQ_CTX *rctx; 159 rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX)); 160 if (!rctx) 161 return NULL; 162 rctx->state = OHS_ERROR; 163 rctx->mem = BIO_new(BIO_s_mem()); 164 rctx->io = io; 165 rctx->asn1_len = 0; 166 if (maxline > 0) 167 rctx->iobuflen = maxline; 168 else 169 rctx->iobuflen = OCSP_MAX_LINE_LEN; 170 rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); 171 if (!rctx->mem || !rctx->iobuf) 172 goto err; 173 if (!path) 174 path = "/"; 175 176 if (BIO_printf(rctx->mem, post_hdr, path) <= 0) 177 goto err; 178 179 if (req && !OCSP_REQ_CTX_set1_req(rctx, req)) 180 goto err; 181 182 return rctx; 183 err: 184 OCSP_REQ_CTX_free(rctx); 185 return NULL; 186} 187 188/* 189 * Parse the HTTP response. This will look like this: "HTTP/1.0 200 OK". We 190 * need to obtain the numeric code and (optional) informational message. 191 */ 192 193static int parse_http_line1(char *line) 194{ 195 int retcode; 196 char *p, *q, *r; 197 /* Skip to first white space (passed protocol info) */ 198 199 for (p = line; *p && !isspace((unsigned char)*p); p++) 200 continue; 201 if (!*p) { 202 OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR); 203 return 0; 204 } 205 206 /* Skip past white space to start of response code */ 207 while (*p && isspace((unsigned char)*p)) 208 p++; 209 210 if (!*p) { 211 OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR); 212 return 0; 213 } 214 215 /* Find end of response code: first whitespace after start of code */ 216 for (q = p; *q && !isspace((unsigned char)*q); q++) 217 continue; 218 219 if (!*q) { 220 OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR); 221 return 0; 222 } 223 224 /* Set end of response code and start of message */ 225 *q++ = 0; 226 227 /* Attempt to parse numeric code */ 228 retcode = strtoul(p, &r, 10); 229 230 if (*r) 231 return 0; 232 233 /* Skip over any leading white space in message */ 234 while (*q && isspace((unsigned char)*q)) 235 q++; 236 237 if (*q) { 238 /* 239 * Finally zap any trailing white space in message (include CRLF) 240 */ 241 242 /* We know q has a non white space character so this is OK */ 243 for (r = q + strlen(q) - 1; isspace((unsigned char)*r); r--) 244 *r = 0; 245 } 246 if (retcode != 200) { 247 OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR); 248 if (!*q) 249 ERR_add_error_data(2, "Code=", p); 250 else 251 ERR_add_error_data(4, "Code=", p, ",Reason=", q); 252 return 0; 253 } 254 255 return 1; 256 257} 258 259int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) 260{ 261 int i, n; 262 const unsigned char *p; 263 next_io: 264 if (!(rctx->state & OHS_NOREAD)) { 265 n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen); 266 267 if (n <= 0) { 268 if (BIO_should_retry(rctx->io)) 269 return -1; 270 return 0; 271 } 272 273 /* Write data to memory BIO */ 274 275 if (BIO_write(rctx->mem, rctx->iobuf, n) != n) 276 return 0; 277 } 278 279 switch (rctx->state) { 280 281 case OHS_ASN1_WRITE: 282 n = BIO_get_mem_data(rctx->mem, &p); 283 284 i = BIO_write(rctx->io, p + (n - rctx->asn1_len), rctx->asn1_len); 285 286 if (i <= 0) { 287 if (BIO_should_retry(rctx->io)) 288 return -1; 289 rctx->state = OHS_ERROR; 290 return 0; 291 } 292 293 rctx->asn1_len -= i; 294 295 if (rctx->asn1_len > 0) 296 goto next_io; 297 298 rctx->state = OHS_ASN1_FLUSH; 299 300 (void)BIO_reset(rctx->mem); 301 302 case OHS_ASN1_FLUSH: 303 304 i = BIO_flush(rctx->io); 305 306 if (i > 0) { 307 rctx->state = OHS_FIRSTLINE; 308 goto next_io; 309 } 310 311 if (BIO_should_retry(rctx->io)) 312 return -1; 313 314 rctx->state = OHS_ERROR; 315 return 0; 316 317 case OHS_ERROR: 318 return 0; 319 320 case OHS_FIRSTLINE: 321 case OHS_HEADERS: 322 323 /* Attempt to read a line in */ 324 325 next_line: 326 /* 327 * Due to &%^*$" memory BIO behaviour with BIO_gets we have to check 328 * there's a complete line in there before calling BIO_gets or we'll 329 * just get a partial read. 330 */ 331 n = BIO_get_mem_data(rctx->mem, &p); 332 if ((n <= 0) || !memchr(p, '\n', n)) { 333 if (n >= rctx->iobuflen) { 334 rctx->state = OHS_ERROR; 335 return 0; 336 } 337 goto next_io; 338 } 339 n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen); 340 341 if (n <= 0) { 342 if (BIO_should_retry(rctx->mem)) 343 goto next_io; 344 rctx->state = OHS_ERROR; 345 return 0; 346 } 347 348 /* Don't allow excessive lines */ 349 if (n == rctx->iobuflen) { 350 rctx->state = OHS_ERROR; 351 return 0; 352 } 353 354 /* First line */ 355 if (rctx->state == OHS_FIRSTLINE) { 356 if (parse_http_line1((char *)rctx->iobuf)) { 357 rctx->state = OHS_HEADERS; 358 goto next_line; 359 } else { 360 rctx->state = OHS_ERROR; 361 return 0; 362 } 363 } else { 364 /* Look for blank line: end of headers */ 365 for (p = rctx->iobuf; *p; p++) { 366 if ((*p != '\r') && (*p != '\n')) 367 break; 368 } 369 if (*p) 370 goto next_line; 371 372 rctx->state = OHS_ASN1_HEADER; 373 374 } 375 376 /* Fall thru */ 377 378 case OHS_ASN1_HEADER: 379 /* 380 * Now reading ASN1 header: can read at least 2 bytes which is enough 381 * for ASN1 SEQUENCE header and either length field or at least the 382 * length of the length field. 383 */ 384 n = BIO_get_mem_data(rctx->mem, &p); 385 if (n < 2) 386 goto next_io; 387 388 /* Check it is an ASN1 SEQUENCE */ 389 if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) { 390 rctx->state = OHS_ERROR; 391 return 0; 392 } 393 394 /* Check out length field */ 395 if (*p & 0x80) { 396 /* 397 * If MSB set on initial length octet we can now always read 6 398 * octets: make sure we have them. 399 */ 400 if (n < 6) 401 goto next_io; 402 n = *p & 0x7F; 403 /* Not NDEF or excessive length */ 404 if (!n || (n > 4)) { 405 rctx->state = OHS_ERROR; 406 return 0; 407 } 408 p++; 409 rctx->asn1_len = 0; 410 for (i = 0; i < n; i++) { 411 rctx->asn1_len <<= 8; 412 rctx->asn1_len |= *p++; 413 } 414 415 if (rctx->asn1_len > OCSP_MAX_REQUEST_LENGTH) { 416 rctx->state = OHS_ERROR; 417 return 0; 418 } 419 420 rctx->asn1_len += n + 2; 421 } else 422 rctx->asn1_len = *p + 2; 423 424 rctx->state = OHS_ASN1_CONTENT; 425 426 /* Fall thru */ 427 428 case OHS_ASN1_CONTENT: 429 n = BIO_get_mem_data(rctx->mem, &p); 430 if (n < (int)rctx->asn1_len) 431 goto next_io; 432 433 *presp = d2i_OCSP_RESPONSE(NULL, &p, rctx->asn1_len); 434 if (*presp) { 435 rctx->state = OHS_DONE; 436 return 1; 437 } 438 439 rctx->state = OHS_ERROR; 440 return 0; 441 442 break; 443 444 case OHS_DONE: 445 return 1; 446 447 } 448 449 return 0; 450 451} 452 453/* Blocking OCSP request handler: now a special case of non-blocking I/O */ 454 455OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req) 456{ 457 OCSP_RESPONSE *resp = NULL; 458 OCSP_REQ_CTX *ctx; 459 int rv; 460 461 ctx = OCSP_sendreq_new(b, path, req, -1); 462 463 if (!ctx) 464 return NULL; 465 466 do { 467 rv = OCSP_sendreq_nbio(&resp, ctx); 468 } while ((rv == -1) && BIO_should_retry(b)); 469 470 OCSP_REQ_CTX_free(ctx); 471 472 if (rv) 473 return resp; 474 475 return NULL; 476} 477