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