Import OpenSSL-1.0.0b.
[dragonfly.git] / crypto / openssl / crypto / ocsp / ocsp_ht.c
CommitLineData
56276539 1/* ocsp_ht.c */
730b1645 2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
2e6ca3d0 3 * project 2006.
56276539
SS
4 */
5/* ====================================================================
2e6ca3d0 6 * Copyright (c) 2006 The OpenSSL Project. All rights reserved.
56276539
SS
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
56276539
SS
59#include <stdio.h>
60#include <stdlib.h>
61#include <ctype.h>
62#include <string.h>
730b1645
PA
63#include "e_os.h"
64#include <openssl/asn1.h>
56276539
SS
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
2e6ca3d0 72/* Stateful OCSP request code, supporting non-blocking I/O */
56276539 73
2e6ca3d0
PA
74/* Opaque OCSP request status structure */
75
76struct 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
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);
56276539 119 }
2e6ca3d0 120
919b01cc 121int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
2e6ca3d0 122 {
919b01cc 123 static const char req_hdr[] =
2e6ca3d0
PA
124 "Content-Type: application/ocsp-request\r\n"
125 "Content-Length: %d\r\n\r\n";
919b01cc
PA
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 {
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
154OCSP_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";
2e6ca3d0
PA
158
159 OCSP_REQ_CTX *rctx;
160 rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX));
919b01cc 161 rctx->state = OHS_ERROR;
2e6ca3d0
PA
162 rctx->mem = BIO_new(BIO_s_mem());
163 rctx->io = io;
919b01cc 164 rctx->asn1_len = 0;
2e6ca3d0
PA
165 if (maxline > 0)
166 rctx->iobuflen = maxline;
167 else
168 rctx->iobuflen = OCSP_MAX_LINE_LEN;
169 rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
919b01cc
PA
170 if (!rctx->iobuf)
171 return 0;
2e6ca3d0
PA
172 if (!path)
173 path = "/";
174
919b01cc 175 if (BIO_printf(rctx->mem, post_hdr, path) <= 0)
2e6ca3d0 176 return 0;
919b01cc
PA
177
178 if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
2e6ca3d0 179 return 0;
2e6ca3d0
PA
180
181 return rctx;
56276539 182 }
56276539 183
2e6ca3d0
PA
184/* Parse the HTTP response. This will look like this:
185 * "HTTP/1.0 200 OK". We need to obtain the numeric code and
186 * (optional) informational message.
187 */
188
189static int parse_http_line1(char *line)
190 {
191 int retcode;
192 char *p, *q, *r;
56276539 193 /* Skip to first white space (passed protocol info) */
2e6ca3d0
PA
194
195 for(p = line; *p && !isspace((unsigned char)*p); p++)
196 continue;
197 if(!*p)
198 {
199 OCSPerr(OCSP_F_PARSE_HTTP_LINE1,
200 OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
201 return 0;
202 }
203
56276539 204 /* Skip past white space to start of response code */
2e6ca3d0
PA
205 while(*p && isspace((unsigned char)*p))
206 p++;
207
208 if(!*p)
209 {
210 OCSPerr(OCSP_F_PARSE_HTTP_LINE1,
211 OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
212 return 0;
213 }
214
56276539 215 /* Find end of response code: first whitespace after start of code */
2e6ca3d0
PA
216 for(q = p; *q && !isspace((unsigned char)*q); q++)
217 continue;
218
219 if(!*q)
220 {
221 OCSPerr(OCSP_F_PARSE_HTTP_LINE1,
222 OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
223 return 0;
224 }
225
56276539
SS
226 /* Set end of response code and start of message */
227 *q++ = 0;
2e6ca3d0 228
56276539
SS
229 /* Attempt to parse numeric code */
230 retcode = strtoul(p, &r, 10);
2e6ca3d0
PA
231
232 if(*r)
233 return 0;
234
56276539 235 /* Skip over any leading white space in message */
2e6ca3d0
PA
236 while(*q && isspace((unsigned char)*q))
237 q++;
238
239 if(*q)
240 {
241 /* Finally zap any trailing white space in message (include
242 * CRLF) */
243
244 /* We know q has a non white space character so this is OK */
245 for(r = q + strlen(q) - 1; isspace((unsigned char)*r); r--)
246 *r = 0;
56276539 247 }
2e6ca3d0
PA
248 if(retcode != 200)
249 {
250 OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR);
251 if(!*q)
252 ERR_add_error_data(2, "Code=", p);
253 else
56276539 254 ERR_add_error_data(4, "Code=", p, ",Reason=", q);
2e6ca3d0 255 return 0;
56276539 256 }
2e6ca3d0
PA
257
258
259 return 1;
260
56276539 261 }
2e6ca3d0
PA
262
263int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
56276539 264 {
2e6ca3d0
PA
265 int i, n;
266 const unsigned char *p;
267 next_io:
268 if (!(rctx->state & OHS_NOREAD))
269 {
270 n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
271
272 if (n <= 0)
273 {
274 if (BIO_should_retry(rctx->io))
275 return -1;
276 return 0;
277 }
278
279 /* Write data to memory BIO */
280
281 if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
282 return 0;
283 }
284
285 switch(rctx->state)
286 {
287
288 case OHS_ASN1_WRITE:
289 n = BIO_get_mem_data(rctx->mem, &p);
290
291 i = BIO_write(rctx->io,
292 p + (n - rctx->asn1_len), rctx->asn1_len);
293
294 if (i <= 0)
295 {
296 if (BIO_should_retry(rctx->io))
297 return -1;
298 rctx->state = OHS_ERROR;
299 return 0;
300 }
301
302 rctx->asn1_len -= i;
303
304 if (rctx->asn1_len > 0)
305 goto next_io;
306
307 rctx->state = OHS_ASN1_FLUSH;
308
309 (void)BIO_reset(rctx->mem);
310
311 case OHS_ASN1_FLUSH:
312
313 i = BIO_flush(rctx->io);
314
315 if (i > 0)
316 {
317 rctx->state = OHS_FIRSTLINE;
318 goto next_io;
319 }
320
321 if (BIO_should_retry(rctx->io))
322 return -1;
323
324 rctx->state = OHS_ERROR;
325 return 0;
326
327 case OHS_ERROR:
328 return 0;
329
330 case OHS_FIRSTLINE:
331 case OHS_HEADERS:
332
333 /* Attempt to read a line in */
334
335 next_line:
336 /* Due to &%^*$" memory BIO behaviour with BIO_gets we
337 * have to check there's a complete line in there before
338 * calling BIO_gets or we'll just get a partial read.
339 */
340 n = BIO_get_mem_data(rctx->mem, &p);
341 if ((n <= 0) || !memchr(p, '\n', n))
342 {
343 if (n >= rctx->iobuflen)
344 {
345 rctx->state = OHS_ERROR;
346 return 0;
347 }
348 goto next_io;
349 }
350 n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
351
352 if (n <= 0)
353 {
354 if (BIO_should_retry(rctx->mem))
355 goto next_io;
356 rctx->state = OHS_ERROR;
357 return 0;
358 }
359
360 /* Don't allow excessive lines */
361 if (n == rctx->iobuflen)
362 {
363 rctx->state = OHS_ERROR;
364 return 0;
365 }
366
367 /* First line */
368 if (rctx->state == OHS_FIRSTLINE)
369 {
370 if (parse_http_line1((char *)rctx->iobuf))
371 {
372 rctx->state = OHS_HEADERS;
373 goto next_line;
374 }
375 else
376 {
377 rctx->state = OHS_ERROR;
378 return 0;
379 }
380 }
381 else
382 {
383 /* Look for blank line: end of headers */
384 for (p = rctx->iobuf; *p; p++)
385 {
386 if ((*p != '\r') && (*p != '\n'))
387 break;
388 }
389 if (*p)
390 goto next_line;
391
392 rctx->state = OHS_ASN1_HEADER;
393
394 }
395
396 /* Fall thru */
397
398
399 case OHS_ASN1_HEADER:
9f1859dd
PA
400 /* Now reading ASN1 header: can read at least 2 bytes which
401 * is enough for ASN1 SEQUENCE header and either length field
402 * or at least the length of the length field.
2e6ca3d0
PA
403 */
404 n = BIO_get_mem_data(rctx->mem, &p);
9f1859dd 405 if (n < 2)
2e6ca3d0
PA
406 goto next_io;
407
408 /* Check it is an ASN1 SEQUENCE */
409 if (*p++ != (V_ASN1_SEQUENCE|V_ASN1_CONSTRUCTED))
410 {
411 rctx->state = OHS_ERROR;
412 return 0;
413 }
414
415 /* Check out length field */
416 if (*p & 0x80)
417 {
9f1859dd
PA
418 /* If MSB set on initial length octet we can now
419 * always read 6 octets: make sure we have them.
420 */
421 if (n < 6)
422 goto next_io;
2e6ca3d0
PA
423 n = *p & 0x7F;
424 /* Not NDEF or excessive length */
425 if (!n || (n > 4))
426 {
427 rctx->state = OHS_ERROR;
428 return 0;
429 }
430 p++;
431 rctx->asn1_len = 0;
432 for (i = 0; i < n; i++)
433 {
434 rctx->asn1_len <<= 8;
435 rctx->asn1_len |= *p++;
436 }
437
438 if (rctx->asn1_len > OCSP_MAX_REQUEST_LENGTH)
439 {
440 rctx->state = OHS_ERROR;
441 return 0;
442 }
443
444 rctx->asn1_len += n + 2;
445 }
446 else
447 rctx->asn1_len = *p + 2;
448
449 rctx->state = OHS_ASN1_CONTENT;
450
451 /* Fall thru */
452
453 case OHS_ASN1_CONTENT:
454 n = BIO_get_mem_data(rctx->mem, &p);
455 if (n < (int)rctx->asn1_len)
456 goto next_io;
457
458
459 *presp = d2i_OCSP_RESPONSE(NULL, &p, rctx->asn1_len);
460 if (*presp)
461 {
462 rctx->state = OHS_DONE;
463 return 1;
464 }
465
466 rctx->state = OHS_ERROR;
467 return 0;
468
469 break;
470
471 case OHS_DONE:
472 return 1;
473
474 }
475
476
477
478 return 0;
479
480
56276539 481 }
2e6ca3d0
PA
482
483/* Blocking OCSP request handler: now a special case of non-blocking I/O */
484
485OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req)
486 {
487 OCSP_RESPONSE *resp = NULL;
488 OCSP_REQ_CTX *ctx;
489 int rv;
490
491 ctx = OCSP_sendreq_new(b, path, req, -1);
492
493 do
494 {
495 rv = OCSP_sendreq_nbio(&resp, ctx);
496 } while ((rv == -1) && BIO_should_retry(b));
497
498 OCSP_REQ_CTX_free(ctx);
499
500 if (rv)
501 return resp;
502
503 return NULL;
56276539 504 }