Line data Source code
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 :
77 : struct 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 : unsigned long max_resp_len; /* Maximum length of response */
85 : };
86 :
87 : #define OCSP_MAX_RESP_LENGTH (100 * 1024)
88 : #define OCSP_MAX_LINE_LEN 4096;
89 :
90 : /* OCSP states */
91 :
92 : /* If set no reading should be performed */
93 : #define OHS_NOREAD 0x1000
94 : /* Error condition */
95 : #define OHS_ERROR (0 | OHS_NOREAD)
96 : /* First line being read */
97 : #define OHS_FIRSTLINE 1
98 : /* MIME headers being read */
99 : #define OHS_HEADERS 2
100 : /* OCSP initial header (tag + length) being read */
101 : #define OHS_ASN1_HEADER 3
102 : /* OCSP content octets being read */
103 : #define OHS_ASN1_CONTENT 4
104 : /* First call: ready to start I/O */
105 : #define OHS_ASN1_WRITE_INIT (5 | OHS_NOREAD)
106 : /* Request being sent */
107 : #define OHS_ASN1_WRITE (6 | OHS_NOREAD)
108 : /* Request being flushed */
109 : #define OHS_ASN1_FLUSH (7 | OHS_NOREAD)
110 : /* Completed */
111 : #define OHS_DONE (8 | OHS_NOREAD)
112 : /* Headers set, no final \r\n included */
113 : #define OHS_HTTP_HEADER (9 | OHS_NOREAD)
114 :
115 : static int parse_http_line1(char *line);
116 :
117 0 : OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline)
118 : {
119 : OCSP_REQ_CTX *rctx;
120 0 : rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX));
121 0 : if (!rctx)
122 : return NULL;
123 0 : rctx->state = OHS_ERROR;
124 0 : rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
125 0 : rctx->mem = BIO_new(BIO_s_mem());
126 0 : rctx->io = io;
127 0 : rctx->asn1_len = 0;
128 0 : if (maxline > 0)
129 0 : rctx->iobuflen = maxline;
130 : else
131 0 : rctx->iobuflen = OCSP_MAX_LINE_LEN;
132 0 : rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
133 0 : if (!rctx->iobuf || !rctx->mem) {
134 0 : OCSP_REQ_CTX_free(rctx);
135 0 : return NULL;
136 : }
137 : return rctx;
138 : }
139 :
140 0 : void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx)
141 : {
142 0 : if (rctx->mem)
143 0 : BIO_free(rctx->mem);
144 0 : if (rctx->iobuf)
145 0 : OPENSSL_free(rctx->iobuf);
146 0 : OPENSSL_free(rctx);
147 0 : }
148 :
149 0 : BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx)
150 : {
151 0 : return rctx->mem;
152 : }
153 :
154 0 : void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len)
155 : {
156 0 : if (len == 0)
157 0 : rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
158 : else
159 0 : rctx->max_resp_len = len;
160 0 : }
161 :
162 0 : int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val)
163 : {
164 : static const char req_hdr[] =
165 : "Content-Type: application/ocsp-request\r\n"
166 : "Content-Length: %d\r\n\r\n";
167 0 : int reqlen = ASN1_item_i2d(val, NULL, it);
168 0 : if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0)
169 : return 0;
170 0 : if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0)
171 : return 0;
172 0 : rctx->state = OHS_ASN1_WRITE_INIT;
173 0 : return 1;
174 : }
175 :
176 0 : int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx,
177 : ASN1_VALUE **pval, const ASN1_ITEM *it)
178 : {
179 : int rv, len;
180 : const unsigned char *p;
181 :
182 0 : rv = OCSP_REQ_CTX_nbio(rctx);
183 0 : if (rv != 1)
184 : return rv;
185 :
186 0 : len = BIO_get_mem_data(rctx->mem, &p);
187 0 : *pval = ASN1_item_d2i(NULL, &p, len, it);
188 0 : if (*pval == NULL) {
189 0 : rctx->state = OHS_ERROR;
190 0 : return 0;
191 : }
192 : return 1;
193 : }
194 :
195 0 : int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path)
196 : {
197 : static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
198 :
199 0 : if (!path)
200 : path = "/";
201 :
202 0 : if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0)
203 : return 0;
204 0 : rctx->state = OHS_HTTP_HEADER;
205 0 : return 1;
206 : }
207 :
208 0 : int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
209 : {
210 0 : return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
211 : (ASN1_VALUE *)req);
212 : }
213 :
214 0 : int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
215 : const char *name, const char *value)
216 : {
217 0 : if (!name)
218 : return 0;
219 0 : if (BIO_puts(rctx->mem, name) <= 0)
220 : return 0;
221 0 : if (value) {
222 0 : if (BIO_write(rctx->mem, ": ", 2) != 2)
223 : return 0;
224 0 : if (BIO_puts(rctx->mem, value) <= 0)
225 : return 0;
226 : }
227 0 : if (BIO_write(rctx->mem, "\r\n", 2) != 2)
228 : return 0;
229 0 : rctx->state = OHS_HTTP_HEADER;
230 0 : return 1;
231 : }
232 :
233 0 : OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req,
234 : int maxline)
235 : {
236 :
237 : OCSP_REQ_CTX *rctx = NULL;
238 0 : rctx = OCSP_REQ_CTX_new(io, maxline);
239 0 : if (!rctx)
240 : return NULL;
241 :
242 0 : if (!OCSP_REQ_CTX_http(rctx, "POST", path))
243 : goto err;
244 :
245 0 : if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
246 : goto err;
247 :
248 0 : return rctx;
249 :
250 : err:
251 0 : OCSP_REQ_CTX_free(rctx);
252 0 : return NULL;
253 : }
254 :
255 : /*
256 : * Parse the HTTP response. This will look like this: "HTTP/1.0 200 OK". We
257 : * need to obtain the numeric code and (optional) informational message.
258 : */
259 :
260 0 : static int parse_http_line1(char *line)
261 : {
262 : int retcode;
263 : char *p, *q, *r;
264 : /* Skip to first white space (passed protocol info) */
265 :
266 0 : for (p = line; *p && !isspace((unsigned char)*p); p++)
267 0 : continue;
268 0 : if (!*p) {
269 0 : OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
270 0 : return 0;
271 : }
272 :
273 : /* Skip past white space to start of response code */
274 0 : while (*p && isspace((unsigned char)*p))
275 0 : p++;
276 :
277 0 : if (!*p) {
278 0 : OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
279 0 : return 0;
280 : }
281 :
282 : /* Find end of response code: first whitespace after start of code */
283 0 : for (q = p; *q && !isspace((unsigned char)*q); q++)
284 0 : continue;
285 :
286 0 : if (!*q) {
287 0 : OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
288 0 : return 0;
289 : }
290 :
291 : /* Set end of response code and start of message */
292 0 : *q++ = 0;
293 :
294 : /* Attempt to parse numeric code */
295 0 : retcode = strtoul(p, &r, 10);
296 :
297 0 : if (*r)
298 : return 0;
299 :
300 : /* Skip over any leading white space in message */
301 0 : while (*q && isspace((unsigned char)*q))
302 0 : q++;
303 :
304 0 : if (*q) {
305 : /*
306 : * Finally zap any trailing white space in message (include CRLF)
307 : */
308 :
309 : /* We know q has a non white space character so this is OK */
310 0 : for (r = q + strlen(q) - 1; isspace((unsigned char)*r); r--)
311 0 : *r = 0;
312 : }
313 0 : if (retcode != 200) {
314 0 : OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR);
315 0 : if (!*q)
316 0 : ERR_add_error_data(2, "Code=", p);
317 : else
318 0 : ERR_add_error_data(4, "Code=", p, ",Reason=", q);
319 : return 0;
320 : }
321 :
322 : return 1;
323 :
324 : }
325 :
326 0 : int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx)
327 : {
328 : int i, n;
329 : const unsigned char *p;
330 : next_io:
331 0 : if (!(rctx->state & OHS_NOREAD)) {
332 0 : n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
333 :
334 0 : if (n <= 0) {
335 0 : if (BIO_should_retry(rctx->io))
336 : return -1;
337 0 : return 0;
338 : }
339 :
340 : /* Write data to memory BIO */
341 :
342 0 : if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
343 : return 0;
344 : }
345 :
346 0 : switch (rctx->state) {
347 : case OHS_HTTP_HEADER:
348 : /* Last operation was adding headers: need a final \r\n */
349 0 : if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
350 0 : rctx->state = OHS_ERROR;
351 0 : return 0;
352 : }
353 0 : rctx->state = OHS_ASN1_WRITE_INIT;
354 :
355 : case OHS_ASN1_WRITE_INIT:
356 0 : rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL);
357 0 : rctx->state = OHS_ASN1_WRITE;
358 :
359 : case OHS_ASN1_WRITE:
360 0 : n = BIO_get_mem_data(rctx->mem, &p);
361 :
362 0 : i = BIO_write(rctx->io, p + (n - rctx->asn1_len), rctx->asn1_len);
363 :
364 0 : if (i <= 0) {
365 0 : if (BIO_should_retry(rctx->io))
366 : return -1;
367 0 : rctx->state = OHS_ERROR;
368 0 : return 0;
369 : }
370 :
371 0 : rctx->asn1_len -= i;
372 :
373 0 : if (rctx->asn1_len > 0)
374 : goto next_io;
375 :
376 0 : rctx->state = OHS_ASN1_FLUSH;
377 :
378 0 : (void)BIO_reset(rctx->mem);
379 :
380 : case OHS_ASN1_FLUSH:
381 :
382 0 : i = BIO_flush(rctx->io);
383 :
384 0 : if (i > 0) {
385 0 : rctx->state = OHS_FIRSTLINE;
386 0 : goto next_io;
387 : }
388 :
389 0 : if (BIO_should_retry(rctx->io))
390 : return -1;
391 :
392 0 : rctx->state = OHS_ERROR;
393 0 : return 0;
394 :
395 : case OHS_ERROR:
396 : return 0;
397 :
398 : case OHS_FIRSTLINE:
399 : case OHS_HEADERS:
400 :
401 : /* Attempt to read a line in */
402 :
403 : next_line:
404 : /*
405 : * Due to &%^*$" memory BIO behaviour with BIO_gets we have to check
406 : * there's a complete line in there before calling BIO_gets or we'll
407 : * just get a partial read.
408 : */
409 0 : n = BIO_get_mem_data(rctx->mem, &p);
410 0 : if ((n <= 0) || !memchr(p, '\n', n)) {
411 0 : if (n >= rctx->iobuflen) {
412 0 : rctx->state = OHS_ERROR;
413 0 : return 0;
414 : }
415 : goto next_io;
416 : }
417 0 : n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
418 :
419 0 : if (n <= 0) {
420 0 : if (BIO_should_retry(rctx->mem))
421 : goto next_io;
422 0 : rctx->state = OHS_ERROR;
423 0 : return 0;
424 : }
425 :
426 : /* Don't allow excessive lines */
427 0 : if (n == rctx->iobuflen) {
428 0 : rctx->state = OHS_ERROR;
429 0 : return 0;
430 : }
431 :
432 : /* First line */
433 0 : if (rctx->state == OHS_FIRSTLINE) {
434 0 : if (parse_http_line1((char *)rctx->iobuf)) {
435 0 : rctx->state = OHS_HEADERS;
436 0 : goto next_line;
437 : } else {
438 0 : rctx->state = OHS_ERROR;
439 0 : return 0;
440 : }
441 : } else {
442 : /* Look for blank line: end of headers */
443 0 : for (p = rctx->iobuf; *p; p++) {
444 0 : if ((*p != '\r') && (*p != '\n'))
445 : break;
446 : }
447 0 : if (*p)
448 : goto next_line;
449 :
450 0 : rctx->state = OHS_ASN1_HEADER;
451 :
452 : }
453 :
454 : /* Fall thru */
455 :
456 : case OHS_ASN1_HEADER:
457 : /*
458 : * Now reading ASN1 header: can read at least 2 bytes which is enough
459 : * for ASN1 SEQUENCE header and either length field or at least the
460 : * length of the length field.
461 : */
462 0 : n = BIO_get_mem_data(rctx->mem, &p);
463 0 : if (n < 2)
464 : goto next_io;
465 :
466 : /* Check it is an ASN1 SEQUENCE */
467 0 : if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) {
468 0 : rctx->state = OHS_ERROR;
469 0 : return 0;
470 : }
471 :
472 : /* Check out length field */
473 0 : if (*p & 0x80) {
474 : /*
475 : * If MSB set on initial length octet we can now always read 6
476 : * octets: make sure we have them.
477 : */
478 0 : if (n < 6)
479 : goto next_io;
480 0 : n = *p & 0x7F;
481 : /* Not NDEF or excessive length */
482 0 : if (!n || (n > 4)) {
483 0 : rctx->state = OHS_ERROR;
484 0 : return 0;
485 : }
486 0 : p++;
487 0 : rctx->asn1_len = 0;
488 0 : for (i = 0; i < n; i++) {
489 0 : rctx->asn1_len <<= 8;
490 0 : rctx->asn1_len |= *p++;
491 : }
492 :
493 0 : if (rctx->asn1_len > rctx->max_resp_len) {
494 0 : rctx->state = OHS_ERROR;
495 0 : return 0;
496 : }
497 :
498 0 : rctx->asn1_len += n + 2;
499 : } else
500 0 : rctx->asn1_len = *p + 2;
501 :
502 0 : rctx->state = OHS_ASN1_CONTENT;
503 :
504 : /* Fall thru */
505 :
506 : case OHS_ASN1_CONTENT:
507 0 : n = BIO_get_mem_data(rctx->mem, NULL);
508 0 : if (n < (int)rctx->asn1_len)
509 : goto next_io;
510 :
511 0 : rctx->state = OHS_DONE;
512 0 : return 1;
513 :
514 : break;
515 :
516 : case OHS_DONE:
517 0 : return 1;
518 :
519 : }
520 :
521 : return 0;
522 :
523 : }
524 :
525 0 : int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
526 : {
527 0 : return OCSP_REQ_CTX_nbio_d2i(rctx,
528 : (ASN1_VALUE **)presp,
529 : ASN1_ITEM_rptr(OCSP_RESPONSE));
530 : }
531 :
532 : /* Blocking OCSP request handler: now a special case of non-blocking I/O */
533 :
534 0 : OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req)
535 : {
536 0 : OCSP_RESPONSE *resp = NULL;
537 : OCSP_REQ_CTX *ctx;
538 : int rv;
539 :
540 0 : ctx = OCSP_sendreq_new(b, path, req, -1);
541 :
542 0 : if (!ctx)
543 : return NULL;
544 :
545 : do {
546 : rv = OCSP_sendreq_nbio(&resp, ctx);
547 0 : } while ((rv == -1) && BIO_should_retry(b));
548 :
549 0 : OCSP_REQ_CTX_free(ctx);
550 :
551 0 : if (rv)
552 0 : return resp;
553 :
554 : return NULL;
555 : }
|