Coverage Report

Created: 2022-11-30 06:20

/src/openssl/crypto/ocsp/ocsp_ht.c
Line
Count
Source (jump to first uncovered line)
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
0
#define OCSP_MAX_RESP_LENGTH    (100 * 1024)
88
0
#define OCSP_MAX_LINE_LEN       4096;
89
90
/* OCSP states */
91
92
/* If set no reading should be performed */
93
0
#define OHS_NOREAD              0x1000
94
/* Error condition */
95
0
#define OHS_ERROR               (0 | OHS_NOREAD)
96
/* First line being read */
97
0
#define OHS_FIRSTLINE           1
98
/* MIME headers being read */
99
0
#define OHS_HEADERS             2
100
/* OCSP initial header (tag + length) being read */
101
0
#define OHS_ASN1_HEADER         3
102
/* OCSP content octets being read */
103
0
#define OHS_ASN1_CONTENT        4
104
/* First call: ready to start I/O */
105
0
#define OHS_ASN1_WRITE_INIT     (5 | OHS_NOREAD)
106
/* Request being sent */
107
0
#define OHS_ASN1_WRITE          (6 | OHS_NOREAD)
108
/* Request being flushed */
109
0
#define OHS_ASN1_FLUSH          (7 | OHS_NOREAD)
110
/* Completed */
111
0
#define OHS_DONE                (8 | OHS_NOREAD)
112
/* Headers set, no final \r\n included */
113
0
#define OHS_HTTP_HEADER         (9 | OHS_NOREAD)
114
115
static int parse_http_line1(char *line);
116
117
OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline)
118
0
{
119
0
    OCSP_REQ_CTX *rctx;
120
0
    rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX));
121
0
    if (!rctx)
122
0
        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
0
    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
0
    }
137
0
    return rctx;
138
0
}
139
140
void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx)
141
0
{
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
BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx)
150
0
{
151
0
    return rctx->mem;
152
0
}
153
154
void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len)
155
0
{
156
0
    if (len == 0)
157
0
        rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
158
0
    else
159
0
        rctx->max_resp_len = len;
160
0
}
161
162
int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val)
163
0
{
164
0
    static const char req_hdr[] =
165
0
        "Content-Type: application/ocsp-request\r\n"
166
0
        "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
0
        return 0;
170
0
    if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0)
171
0
        return 0;
172
0
    rctx->state = OHS_ASN1_WRITE_INIT;
173
0
    return 1;
174
0
}
175
176
int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx,
177
                          ASN1_VALUE **pval, const ASN1_ITEM *it)
178
0
{
179
0
    int rv, len;
180
0
    const unsigned char *p;
181
182
0
    rv = OCSP_REQ_CTX_nbio(rctx);
183
0
    if (rv != 1)
184
0
        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
0
    }
192
0
    return 1;
193
0
}
194
195
int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path)
196
0
{
197
0
    static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
198
199
0
    if (!path)
200
0
        path = "/";
201
202
0
    if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0)
203
0
        return 0;
204
0
    rctx->state = OHS_HTTP_HEADER;
205
0
    return 1;
206
0
}
207
208
int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
209
0
{
210
0
    return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
211
0
                            (ASN1_VALUE *)req);
212
0
}
213
214
int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
215
                             const char *name, const char *value)
216
0
{
217
0
    if (!name)
218
0
        return 0;
219
0
    if (BIO_puts(rctx->mem, name) <= 0)
220
0
        return 0;
221
0
    if (value) {
222
0
        if (BIO_write(rctx->mem, ": ", 2) != 2)
223
0
            return 0;
224
0
        if (BIO_puts(rctx->mem, value) <= 0)
225
0
            return 0;
226
0
    }
227
0
    if (BIO_write(rctx->mem, "\r\n", 2) != 2)
228
0
        return 0;
229
0
    rctx->state = OHS_HTTP_HEADER;
230
0
    return 1;
231
0
}
232
233
OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req,
234
                               int maxline)
235
0
{
236
237
0
    OCSP_REQ_CTX *rctx = NULL;
238
0
    rctx = OCSP_REQ_CTX_new(io, maxline);
239
0
    if (!rctx)
240
0
        return NULL;
241
242
0
    if (!OCSP_REQ_CTX_http(rctx, "POST", path))
243
0
        goto err;
244
245
0
    if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
246
0
        goto err;
247
248
0
    return rctx;
249
250
0
 err:
251
0
    OCSP_REQ_CTX_free(rctx);
252
0
    return NULL;
253
0
}
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
static int parse_http_line1(char *line)
261
0
{
262
0
    int retcode;
263
0
    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
0
    }
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
0
    }
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
0
    }
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
0
        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
0
    }
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
0
        else
318
0
            ERR_add_error_data(4, "Code=", p, ",Reason=", q);
319
0
        return 0;
320
0
    }
321
322
0
    return 1;
323
324
0
}
325
326
int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx)
327
0
{
328
0
    int i, n;
329
0
    const unsigned char *p;
330
0
 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
0
                return -1;
337
0
            return 0;
338
0
        }
339
340
        /* Write data to memory BIO */
341
342
0
        if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
343
0
            return 0;
344
0
    }
345
346
0
    switch (rctx->state) {
347
0
    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
0
        }
353
0
        rctx->state = OHS_ASN1_WRITE_INIT;
354
355
0
    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
0
    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
0
                return -1;
367
0
            rctx->state = OHS_ERROR;
368
0
            return 0;
369
0
        }
370
371
0
        rctx->asn1_len -= i;
372
373
0
        if (rctx->asn1_len > 0)
374
0
            goto next_io;
375
376
0
        rctx->state = OHS_ASN1_FLUSH;
377
378
0
        (void)BIO_reset(rctx->mem);
379
380
0
    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
0
        }
388
389
0
        if (BIO_should_retry(rctx->io))
390
0
            return -1;
391
392
0
        rctx->state = OHS_ERROR;
393
0
        return 0;
394
395
0
    case OHS_ERROR:
396
0
        return 0;
397
398
0
    case OHS_FIRSTLINE:
399
0
    case OHS_HEADERS:
400
401
        /* Attempt to read a line in */
402
403
0
 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
0
            }
415
0
            goto next_io;
416
0
        }
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
0
                goto next_io;
422
0
            rctx->state = OHS_ERROR;
423
0
            return 0;
424
0
        }
425
426
        /* Don't allow excessive lines */
427
0
        if (n == rctx->iobuflen) {
428
0
            rctx->state = OHS_ERROR;
429
0
            return 0;
430
0
        }
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
0
            } else {
438
0
                rctx->state = OHS_ERROR;
439
0
                return 0;
440
0
            }
441
0
        } 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
0
                    break;
446
0
            }
447
0
            if (*p)
448
0
                goto next_line;
449
450
0
            rctx->state = OHS_ASN1_HEADER;
451
452
0
        }
453
454
        /* Fall thru */
455
456
0
    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
0
            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
0
        }
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
0
                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
0
            }
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
0
            }
492
493
0
            if (rctx->asn1_len > rctx->max_resp_len) {
494
0
                rctx->state = OHS_ERROR;
495
0
                return 0;
496
0
            }
497
498
0
            rctx->asn1_len += n + 2;
499
0
        } else
500
0
            rctx->asn1_len = *p + 2;
501
502
0
        rctx->state = OHS_ASN1_CONTENT;
503
504
        /* Fall thru */
505
506
0
    case OHS_ASN1_CONTENT:
507
0
        n = BIO_get_mem_data(rctx->mem, NULL);
508
0
        if (n < (int)rctx->asn1_len)
509
0
            goto next_io;
510
511
0
        rctx->state = OHS_DONE;
512
0
        return 1;
513
514
0
        break;
515
516
0
    case OHS_DONE:
517
0
        return 1;
518
519
0
    }
520
521
0
    return 0;
522
523
0
}
524
525
int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
526
0
{
527
0
    return OCSP_REQ_CTX_nbio_d2i(rctx,
528
0
                                 (ASN1_VALUE **)presp,
529
0
                                 ASN1_ITEM_rptr(OCSP_RESPONSE));
530
0
}
531
532
/* Blocking OCSP request handler: now a special case of non-blocking I/O */
533
534
OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req)
535
0
{
536
0
    OCSP_RESPONSE *resp = NULL;
537
0
    OCSP_REQ_CTX *ctx;
538
0
    int rv;
539
540
0
    ctx = OCSP_sendreq_new(b, path, req, -1);
541
542
0
    if (!ctx)
543
0
        return NULL;
544
545
0
    do {
546
0
        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
0
    return NULL;
555
0
}