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    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
115static int parse_http_line1(char *line);
116
117OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline)
118{
119    OCSP_REQ_CTX *rctx;
120    rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX));
121    if (!rctx)
122        return NULL;
123    rctx->state = OHS_ERROR;
124    rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
125    rctx->mem = BIO_new(BIO_s_mem());
126    rctx->io = io;
127    rctx->asn1_len = 0;
128    if (maxline > 0)
129        rctx->iobuflen = maxline;
130    else
131        rctx->iobuflen = OCSP_MAX_LINE_LEN;
132    rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
133    if (!rctx->iobuf || !rctx->mem) {
134        OCSP_REQ_CTX_free(rctx);
135        return NULL;
136    }
137    return rctx;
138}
139
140void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx)
141{
142    if (rctx->mem)
143        BIO_free(rctx->mem);
144    if (rctx->iobuf)
145        OPENSSL_free(rctx->iobuf);
146    OPENSSL_free(rctx);
147}
148
149BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx)
150{
151    return rctx->mem;
152}
153
154void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len)
155{
156    if (len == 0)
157        rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
158    else
159        rctx->max_resp_len = len;
160}
161
162int 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    int reqlen = ASN1_item_i2d(val, NULL, it);
168    if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0)
169        return 0;
170    if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0)
171        return 0;
172    rctx->state = OHS_ASN1_WRITE_INIT;
173    return 1;
174}
175
176int 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    rv = OCSP_REQ_CTX_nbio(rctx);
183    if (rv != 1)
184        return rv;
185
186    len = BIO_get_mem_data(rctx->mem, &p);
187    *pval = ASN1_item_d2i(NULL, &p, len, it);
188    if (*pval == NULL) {
189        rctx->state = OHS_ERROR;
190        return 0;
191    }
192    return 1;
193}
194
195int 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    if (!path)
200        path = "/";
201
202    if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0)
203        return 0;
204    rctx->state = OHS_HTTP_HEADER;
205    return 1;
206}
207
208int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
209{
210    return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
211                            (ASN1_VALUE *)req);
212}
213
214int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
215                             const char *name, const char *value)
216{
217    if (!name)
218        return 0;
219    if (BIO_puts(rctx->mem, name) <= 0)
220        return 0;
221    if (value) {
222        if (BIO_write(rctx->mem, ": ", 2) != 2)
223            return 0;
224        if (BIO_puts(rctx->mem, value) <= 0)
225            return 0;
226    }
227    if (BIO_write(rctx->mem, "\r\n", 2) != 2)
228        return 0;
229    rctx->state = OHS_HTTP_HEADER;
230    return 1;
231}
232
233OCSP_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    rctx = OCSP_REQ_CTX_new(io, maxline);
239    if (!rctx)
240        return NULL;
241
242    if (!OCSP_REQ_CTX_http(rctx, "POST", path))
243        goto err;
244
245    if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
246        goto err;
247
248    return rctx;
249
250 err:
251    OCSP_REQ_CTX_free(rctx);
252    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
260static 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    for (p = line; *p && !isspace((unsigned char)*p); p++)
267        continue;
268    if (!*p) {
269        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
270        return 0;
271    }
272
273    /* Skip past white space to start of response code */
274    while (*p && isspace((unsigned char)*p))
275        p++;
276
277    if (!*p) {
278        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
279        return 0;
280    }
281
282    /* Find end of response code: first whitespace after start of code */
283    for (q = p; *q && !isspace((unsigned char)*q); q++)
284        continue;
285
286    if (!*q) {
287        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
288        return 0;
289    }
290
291    /* Set end of response code and start of message */
292    *q++ = 0;
293
294    /* Attempt to parse numeric code */
295    retcode = strtoul(p, &r, 10);
296
297    if (*r)
298        return 0;
299
300    /* Skip over any leading white space in message */
301    while (*q && isspace((unsigned char)*q))
302        q++;
303
304    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        for (r = q + strlen(q) - 1; isspace((unsigned char)*r); r--)
311            *r = 0;
312    }
313    if (retcode != 200) {
314        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR);
315        if (!*q)
316            ERR_add_error_data(2, "Code=", p);
317        else
318            ERR_add_error_data(4, "Code=", p, ",Reason=", q);
319        return 0;
320    }
321
322    return 1;
323
324}
325
326int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx)
327{
328    int i, n;
329    const unsigned char *p;
330 next_io:
331    if (!(rctx->state & OHS_NOREAD)) {
332        n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
333
334        if (n <= 0) {
335            if (BIO_should_retry(rctx->io))
336                return -1;
337            return 0;
338        }
339
340        /* Write data to memory BIO */
341
342        if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
343            return 0;
344    }
345
346    switch (rctx->state) {
347    case OHS_HTTP_HEADER:
348        /* Last operation was adding headers: need a final \r\n */
349        if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
350            rctx->state = OHS_ERROR;
351            return 0;
352        }
353        rctx->state = OHS_ASN1_WRITE_INIT;
354
355    case OHS_ASN1_WRITE_INIT:
356        rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL);
357        rctx->state = OHS_ASN1_WRITE;
358
359    case OHS_ASN1_WRITE:
360        n = BIO_get_mem_data(rctx->mem, &p);
361
362        i = BIO_write(rctx->io, p + (n - rctx->asn1_len), rctx->asn1_len);
363
364        if (i <= 0) {
365            if (BIO_should_retry(rctx->io))
366                return -1;
367            rctx->state = OHS_ERROR;
368            return 0;
369        }
370
371        rctx->asn1_len -= i;
372
373        if (rctx->asn1_len > 0)
374            goto next_io;
375
376        rctx->state = OHS_ASN1_FLUSH;
377
378        (void)BIO_reset(rctx->mem);
379
380    case OHS_ASN1_FLUSH:
381
382        i = BIO_flush(rctx->io);
383
384        if (i > 0) {
385            rctx->state = OHS_FIRSTLINE;
386            goto next_io;
387        }
388
389        if (BIO_should_retry(rctx->io))
390            return -1;
391
392        rctx->state = OHS_ERROR;
393        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        n = BIO_get_mem_data(rctx->mem, &p);
410        if ((n <= 0) || !memchr(p, '\n', n)) {
411            if (n >= rctx->iobuflen) {
412                rctx->state = OHS_ERROR;
413                return 0;
414            }
415            goto next_io;
416        }
417        n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
418
419        if (n <= 0) {
420            if (BIO_should_retry(rctx->mem))
421                goto next_io;
422            rctx->state = OHS_ERROR;
423            return 0;
424        }
425
426        /* Don't allow excessive lines */
427        if (n == rctx->iobuflen) {
428            rctx->state = OHS_ERROR;
429            return 0;
430        }
431
432        /* First line */
433        if (rctx->state == OHS_FIRSTLINE) {
434            if (parse_http_line1((char *)rctx->iobuf)) {
435                rctx->state = OHS_HEADERS;
436                goto next_line;
437            } else {
438                rctx->state = OHS_ERROR;
439                return 0;
440            }
441        } else {
442            /* Look for blank line: end of headers */
443            for (p = rctx->iobuf; *p; p++) {
444                if ((*p != '\r') && (*p != '\n'))
445                    break;
446            }
447            if (*p)
448                goto next_line;
449
450            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        n = BIO_get_mem_data(rctx->mem, &p);
463        if (n < 2)
464            goto next_io;
465
466        /* Check it is an ASN1 SEQUENCE */
467        if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) {
468            rctx->state = OHS_ERROR;
469            return 0;
470        }
471
472        /* Check out length field */
473        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            if (n < 6)
479                goto next_io;
480            n = *p & 0x7F;
481            /* Not NDEF or excessive length */
482            if (!n || (n > 4)) {
483                rctx->state = OHS_ERROR;
484                return 0;
485            }
486            p++;
487            rctx->asn1_len = 0;
488            for (i = 0; i < n; i++) {
489                rctx->asn1_len <<= 8;
490                rctx->asn1_len |= *p++;
491            }
492
493            if (rctx->asn1_len > rctx->max_resp_len) {
494                rctx->state = OHS_ERROR;
495                return 0;
496            }
497
498            rctx->asn1_len += n + 2;
499        } else
500            rctx->asn1_len = *p + 2;
501
502        rctx->state = OHS_ASN1_CONTENT;
503
504        /* Fall thru */
505
506    case OHS_ASN1_CONTENT:
507        n = BIO_get_mem_data(rctx->mem, NULL);
508        if (n < (int)rctx->asn1_len)
509            goto next_io;
510
511        rctx->state = OHS_DONE;
512        return 1;
513
514        break;
515
516    case OHS_DONE:
517        return 1;
518
519    }
520
521    return 0;
522
523}
524
525int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
526{
527    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
534OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req)
535{
536    OCSP_RESPONSE *resp = NULL;
537    OCSP_REQ_CTX *ctx;
538    int rv;
539
540    ctx = OCSP_sendreq_new(b, path, req, -1);
541
542    if (!ctx)
543        return NULL;
544
545    do {
546        rv = OCSP_sendreq_nbio(&resp, ctx);
547    } while ((rv == -1) && BIO_should_retry(b));
548
549    OCSP_REQ_CTX_free(ctx);
550
551    if (rv)
552        return resp;
553
554    return NULL;
555}
556