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