summaryrefslogtreecommitdiff
path: root/src/usr.sbin/ocspcheck/http.c
diff options
context:
space:
mode:
authorbeck <>2017-01-24 08:50:57 +0000
committerbeck <>2017-01-24 08:50:57 +0000
commitf748422c1c8c084effc6cacbef92cb4ee75c13d6 (patch)
treea8f074f9e36fc17e90b2be5b61a6676a5a58757c /src/usr.sbin/ocspcheck/http.c
parent461ec96400c0a98b56a42533ecf86de58bc3c16f (diff)
downloadopenbsd-f748422c1c8c084effc6cacbef92cb4ee75c13d6.tar.gz
openbsd-f748422c1c8c084effc6cacbef92cb4ee75c13d6.tar.bz2
openbsd-f748422c1c8c084effc6cacbef92cb4ee75c13d6.zip
New ocspcheck utility to validate a certificate against its ocsp responder
and save the reply for stapling ok deraadt@ jsing@
Diffstat (limited to 'src/usr.sbin/ocspcheck/http.c')
-rw-r--r--src/usr.sbin/ocspcheck/http.c782
1 files changed, 782 insertions, 0 deletions
diff --git a/src/usr.sbin/ocspcheck/http.c b/src/usr.sbin/ocspcheck/http.c
new file mode 100644
index 0000000000..3c0f404c31
--- /dev/null
+++ b/src/usr.sbin/ocspcheck/http.c
@@ -0,0 +1,782 @@
1/* $Id: http.c,v 1.1 2017/01/24 08:50:57 beck Exp $ */
2/*
3 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/socket.h>
19#include <sys/param.h>
20#include <arpa/inet.h>
21
22#include <ctype.h>
23#include <err.h>
24#include <limits.h>
25#include <netdb.h>
26#include <stdio.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <string.h>
30#include <tls.h>
31#include <unistd.h>
32
33#include "http.h"
34#include <tls.h>
35
36#define DEFAULT_CA_FILE "/etc/ssl/cert.pem"
37
38/*
39 * A buffer for transferring HTTP/S data.
40 */
41struct httpxfer {
42 char *hbuf; /* header transfer buffer */
43 size_t hbufsz; /* header buffer size */
44 int headok; /* header has been parsed */
45 char *bbuf; /* body transfer buffer */
46 size_t bbufsz; /* body buffer size */
47 int bodyok; /* body has been parsed */
48 char *headbuf; /* lookaside buffer for headers */
49 struct httphead *head; /* parsed headers */
50 size_t headsz; /* number of headers */
51};
52
53/*
54 * An HTTP/S connection object.
55 */
56struct http {
57 int fd; /* connected socket */
58 short port; /* port number */
59 struct source src; /* endpoint (raw) host */
60 char *path; /* path to request */
61 char *host; /* name of endpoint host */
62 struct tls *ctx; /* if TLS */
63 writefp writer; /* write function */
64 readfp reader; /* read function */
65};
66
67struct tls_config *tlscfg;
68
69static ssize_t
70dosysread(char *buf, size_t sz, const struct http *http)
71{
72 ssize_t rc;
73
74 rc = read(http->fd, buf, sz);
75 if (rc < 0)
76 warn("%s: read", http->src.ip);
77 return (rc);
78}
79
80static ssize_t
81dosyswrite(const void *buf, size_t sz, const struct http *http)
82{
83 ssize_t rc;
84
85 rc = write(http->fd, buf, sz);
86 if (rc < 0)
87 warn("%s: write", http->src.ip);
88 return (rc);
89}
90
91static ssize_t
92dotlsread(char *buf, size_t sz, const struct http *http)
93{
94 ssize_t rc;
95
96 do {
97 rc = tls_read(http->ctx, buf, sz);
98 } while (TLS_WANT_POLLIN == rc || TLS_WANT_POLLOUT == rc);
99
100 if (rc < 0)
101 warnx("%s: tls_read: %s", http->src.ip,
102 tls_error(http->ctx));
103 return (rc);
104}
105
106static ssize_t
107dotlswrite(const void *buf, size_t sz, const struct http *http)
108{
109 ssize_t rc;
110
111 do {
112 rc = tls_write(http->ctx, buf, sz);
113 } while (TLS_WANT_POLLIN == rc || TLS_WANT_POLLOUT == rc);
114
115 if (rc < 0)
116 warnx("%s: tls_write: %s", http->src.ip,
117 tls_error(http->ctx));
118 return (rc);
119}
120
121int
122http_init()
123{
124 if (NULL != tlscfg)
125 return (0);
126
127 if (-1 == tls_init()) {
128 warn("tls_init");
129 goto err;
130 }
131
132 tlscfg = tls_config_new();
133 if (NULL == tlscfg) {
134 warn("tls_config_new");
135 goto err;
136 }
137
138 if (-1 == tls_config_set_ca_file(tlscfg, DEFAULT_CA_FILE)) {
139 warn("tls_config_set_ca_file: %s", tls_config_error(tlscfg));
140 goto err;
141 }
142
143 return (0);
144
145 err:
146 tls_config_free(tlscfg);
147 tlscfg = NULL;
148
149 return (-1);
150}
151
152static ssize_t
153http_read(char *buf, size_t sz, const struct http *http)
154{
155 ssize_t ssz, xfer;
156
157 xfer = 0;
158 do {
159 if ((ssz = http->reader(buf, sz, http)) < 0)
160 return (-1);
161 if (0 == ssz)
162 break;
163 xfer += ssz;
164 sz -= ssz;
165 buf += ssz;
166 } while (ssz > 0 && sz > 0);
167
168 return (xfer);
169}
170
171static int
172http_write(const char *buf, size_t sz, const struct http *http)
173{
174 ssize_t ssz, xfer;
175
176 xfer = sz;
177 while (sz > 0) {
178 if ((ssz = http->writer(buf, sz, http)) < 0)
179 return (-1);
180 sz -= ssz;
181 buf += (size_t)ssz;
182 }
183 return (xfer);
184}
185
186void
187http_disconnect(struct http *http)
188{
189 int rc;
190
191 if (NULL != http->ctx) {
192 /* TLS connection. */
193 do {
194 rc = tls_close(http->ctx);
195 } while (TLS_WANT_POLLIN == rc || TLS_WANT_POLLOUT == rc);
196
197 if (rc < 0)
198 warnx("%s: tls_close: %s", http->src.ip,
199 tls_error(http->ctx));
200
201 tls_free(http->ctx);
202 }
203 if (-1 != http->fd) {
204 if (-1 == close(http->fd))
205 warn("%s: close", http->src.ip);
206 }
207
208 http->fd = -1;
209 http->ctx = NULL;
210}
211
212void
213http_free(struct http *http)
214{
215
216 if (NULL == http)
217 return;
218 http_disconnect(http);
219 free(http->host);
220 free(http->path);
221 free(http->src.ip);
222 free(http);
223}
224
225struct http *
226http_alloc(const struct source *addrs, size_t addrsz,
227 const char *host, short port, const char *path)
228{
229 struct sockaddr_storage ss;
230 int family, fd, c;
231 socklen_t len;
232 size_t cur, i = 0;
233 struct http *http;
234
235 /* Do this while we still have addresses to connect. */
236again:
237 if (i == addrsz)
238 return (NULL);
239 cur = i++;
240
241 /* Convert to PF_INET or PF_INET6 address from string. */
242
243 memset(&ss, 0, sizeof(struct sockaddr_storage));
244
245 if (4 == addrs[cur].family) {
246 family = PF_INET;
247 ((struct sockaddr_in *)&ss)->sin_family = AF_INET;
248 ((struct sockaddr_in *)&ss)->sin_port = htons(port);
249 c = inet_pton(AF_INET, addrs[cur].ip,
250 &((struct sockaddr_in *)&ss)->sin_addr);
251 len = sizeof(struct sockaddr_in);
252 } else if (6 == addrs[cur].family) {
253 family = PF_INET6;
254 ((struct sockaddr_in6 *)&ss)->sin6_family = AF_INET6;
255 ((struct sockaddr_in6 *)&ss)->sin6_port = htons(port);
256 c = inet_pton(AF_INET6, addrs[cur].ip,
257 &((struct sockaddr_in6 *)&ss)->sin6_addr);
258 len = sizeof(struct sockaddr_in6);
259 } else {
260 warnx("%s: unknown family", addrs[cur].ip);
261 goto again;
262 }
263
264 if (c < 0) {
265 warn("%s: inet_ntop", addrs[cur].ip);
266 goto again;
267 } else if (0 == c) {
268 warnx("%s: inet_ntop", addrs[cur].ip);
269 goto again;
270 }
271
272 /* Create socket and connect. */
273
274 fd = socket(family, SOCK_STREAM, 0);
275 if (-1 == fd) {
276 warn("%s: socket", addrs[cur].ip);
277 goto again;
278 } else if (-1 == connect(fd, (struct sockaddr *)&ss, len)) {
279 warn("%s: connect", addrs[cur].ip);
280 close(fd);
281 goto again;
282 }
283
284 /* Allocate the communicator. */
285
286 http = calloc(1, sizeof(struct http));
287 if (NULL == http) {
288 warn("calloc");
289 close(fd);
290 return (NULL);
291 }
292 http->fd = fd;
293 http->port = port;
294 http->src.family = addrs[cur].family;
295 http->src.ip = strdup(addrs[cur].ip);
296 http->host = strdup(host);
297 http->path = strdup(path);
298 if (NULL == http->src.ip || NULL == http->host || NULL == http->path) {
299 warn("strdup");
300 goto err;
301 }
302
303 /* If necessary, do our TLS setup. */
304
305 if (443 != port) {
306 http->writer = dosyswrite;
307 http->reader = dosysread;
308 return (http);
309 }
310
311 http->writer = dotlswrite;
312 http->reader = dotlsread;
313
314 if (NULL == (http->ctx = tls_client())) {
315 warn("tls_client");
316 goto err;
317 } else if (-1 == tls_configure(http->ctx, tlscfg)) {
318 warnx("%s: tls_configure: %s",
319 http->src.ip, tls_error(http->ctx));
320 goto err;
321 }
322
323 if (0 != tls_connect_socket(http->ctx, http->fd, http->host)) {
324 warnx("%s: tls_connect_socket: %s, %s", http->src.ip,
325 http->host, tls_error(http->ctx));
326 goto err;
327 }
328
329 return (http);
330err:
331 http_free(http);
332 return (NULL);
333}
334
335struct httpxfer *
336http_open(const struct http *http, const void *p, size_t psz)
337{
338 char *req;
339 int c;
340 struct httpxfer *trans;
341
342 if (NULL == p) {
343 c = asprintf(&req,
344 "GET %s HTTP/1.0\r\n"
345 "Host: %s\r\n"
346 "\r\n",
347 http->path, http->host);
348 } else {
349 c = asprintf(&req,
350 "POST %s HTTP/1.0\r\n"
351 "Host: %s\r\n"
352 "Content-Type: application/ocsp-request\r\n"
353 "Accept: application/ocsp-response\r\n"
354 "Content-Length: %zu\r\n"
355 "\r\n",
356 http->path, http->host, psz);
357 }
358 if (-1 == c) {
359 warn("asprintf");
360 return (NULL);
361 } else if (!http_write(req, c, http)) {
362 free(req);
363 return (NULL);
364 } else if (NULL != p && ! http_write(p, psz, http)) {
365 free(req);
366 return (NULL);
367 }
368
369 free(req);
370
371 trans = calloc(1, sizeof(struct httpxfer));
372 if (NULL == trans)
373 warn("calloc");
374 return (trans);
375}
376
377void
378http_close(struct httpxfer *x)
379{
380
381 if (NULL == x)
382 return;
383 free(x->hbuf);
384 free(x->bbuf);
385 free(x->headbuf);
386 free(x->head);
387 free(x);
388}
389
390/*
391 * Read the HTTP body from the wire.
392 * If invoked multiple times, this will return the same pointer with the
393 * same data (or NULL, if the original invocation returned NULL).
394 * Returns NULL if read or allocation errors occur.
395 * You must not free the returned pointer.
396 */
397char *
398http_body_read(const struct http *http, struct httpxfer *trans, size_t *sz)
399{
400 char buf[BUFSIZ];
401 ssize_t ssz;
402 void *pp;
403 size_t szp;
404
405 if (NULL == sz)
406 sz = &szp;
407
408 /* Have we already parsed this? */
409
410 if (trans->bodyok > 0) {
411 *sz = trans->bbufsz;
412 return (trans->bbuf);
413 } else if (trans->bodyok < 0)
414 return (NULL);
415
416 *sz = 0;
417 trans->bodyok = -1;
418
419 do {
420 /* If less than sizeof(buf), at EOF. */
421 if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
422 return (NULL);
423 else if (0 == ssz)
424 break;
425 pp = realloc(trans->bbuf, trans->bbufsz + ssz);
426 if (NULL == pp) {
427 warn("realloc");
428 return (NULL);
429 }
430 trans->bbuf = pp;
431 memcpy(trans->bbuf + trans->bbufsz, buf, ssz);
432 trans->bbufsz += ssz;
433 } while (sizeof(buf) == ssz);
434
435 trans->bodyok = 1;
436 *sz = trans->bbufsz;
437 return (trans->bbuf);
438}
439
440struct httphead *
441http_head_get(const char *v, struct httphead *h, size_t hsz)
442{
443 size_t i;
444
445 for (i = 0; i < hsz; i++) {
446 if (strcmp(h[i].key, v))
447 continue;
448 return (&h[i]);
449 }
450 return (NULL);
451}
452
453/*
454 * Look through the headers and determine our HTTP code.
455 * This will return -1 on failure, otherwise the code.
456 */
457int
458http_head_status(const struct http *http, struct httphead *h, size_t sz)
459{
460 int rc;
461 unsigned int code;
462 struct httphead *st;
463
464 if (NULL == (st = http_head_get("Status", h, sz))) {
465 warnx("%s: no status header", http->src.ip);
466 return (-1);
467 }
468
469 rc = sscanf(st->val, "%*s %u %*s", &code);
470 if (rc < 0) {
471 warn("sscanf");
472 return (-1);
473 } else if (1 != rc) {
474 warnx("%s: cannot convert status header", http->src.ip);
475 return (-1);
476 }
477 return (code);
478}
479
480/*
481 * Parse headers from the transfer.
482 * Malformed headers are skipped.
483 * A special "Status" header is added for the HTTP status line.
484 * This can only happen once http_head_read has been called with
485 * success.
486 * This can be invoked multiple times: it will only parse the headers
487 * once and after that it will just return the cache.
488 * You must not free the returned pointer.
489 * If the original header parse failed, or if memory allocation fails
490 * internally, this returns NULL.
491 */
492struct httphead *
493http_head_parse(const struct http *http, struct httpxfer *trans, size_t *sz)
494{
495 size_t hsz, szp;
496 struct httphead *h;
497 char *cp, *ep, *ccp, *buf;
498
499 if (NULL == sz)
500 sz = &szp;
501
502 /*
503 * If we've already parsed the headers, return the
504 * previously-parsed buffer now.
505 * If we have errors on the stream, return NULL now.
506 */
507
508 if (NULL != trans->head) {
509 *sz = trans->headsz;
510 return (trans->head);
511 } else if (trans->headok <= 0)
512 return (NULL);
513
514 if (NULL == (buf = strdup(trans->hbuf))) {
515 warn("strdup");
516 return (NULL);
517 }
518 hsz = 0;
519 cp = buf;
520
521 do {
522 if (NULL != (cp = strstr(cp, "\r\n")))
523 cp += 2;
524 hsz++;
525 } while (NULL != cp);
526
527 /*
528 * Allocate headers, then step through the data buffer, parsing
529 * out headers as we have them.
530 * We know at this point that the buffer is nil-terminated in
531 * the usual way.
532 */
533
534 h = calloc(hsz, sizeof(struct httphead));
535 if (NULL == h) {
536 warn("calloc");
537 free(buf);
538 return (NULL);
539 }
540
541 *sz = hsz;
542 hsz = 0;
543 cp = buf;
544
545 do {
546 if (NULL != (ep = strstr(cp, "\r\n"))) {
547 *ep = '\0';
548 ep += 2;
549 }
550 if (0 == hsz) {
551 h[hsz].key = "Status";
552 h[hsz++].val = cp;
553 continue;
554 }
555
556 /* Skip bad headers. */
557 if (NULL == (ccp = strchr(cp, ':'))) {
558 warnx("%s: header without separator", http->src.ip);
559 continue;
560 }
561
562 *ccp++ = '\0';
563 while (isspace((int)*ccp))
564 ccp++;
565 h[hsz].key = cp;
566 h[hsz++].val = ccp;
567 } while (NULL != (cp = ep));
568
569 trans->headbuf = buf;
570 trans->head = h;
571 trans->headsz = hsz;
572 return (h);
573}
574
575/*
576 * Read the HTTP headers from the wire.
577 * If invoked multiple times, this will return the same pointer with the
578 * same data (or NULL, if the original invocation returned NULL).
579 * Returns NULL if read or allocation errors occur.
580 * You must not free the returned pointer.
581 */
582char *
583http_head_read(const struct http *http, struct httpxfer *trans, size_t *sz)
584{
585 char buf[BUFSIZ];
586 ssize_t ssz;
587 char *ep;
588 void *pp;
589 size_t szp;
590
591 if (NULL == sz)
592 sz = &szp;
593
594 /* Have we already parsed this? */
595
596 if (trans->headok > 0) {
597 *sz = trans->hbufsz;
598 return (trans->hbuf);
599 } else if (trans->headok < 0)
600 return (NULL);
601
602 *sz = 0;
603 ep = NULL;
604 trans->headok = -1;
605
606 /*
607 * Begin by reading by BUFSIZ blocks until we reach the header
608 * termination marker (two CRLFs).
609 * We might read into our body, but that's ok: we'll copy out
610 * the body parts into our body buffer afterward.
611 */
612
613 do {
614 /* If less than sizeof(buf), at EOF. */
615 if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
616 return (NULL);
617 else if (0 == ssz)
618 break;
619 pp = realloc(trans->hbuf, trans->hbufsz + ssz);
620 if (NULL == pp) {
621 warn("realloc");
622 return (NULL);
623 }
624 trans->hbuf = pp;
625 memcpy(trans->hbuf + trans->hbufsz, buf, ssz);
626 trans->hbufsz += ssz;
627 /* Search for end of headers marker. */
628 ep = memmem(trans->hbuf, trans->hbufsz, "\r\n\r\n", 4);
629 } while (NULL == ep && sizeof(buf) == ssz);
630
631 if (NULL == ep) {
632 warnx("%s: partial transfer", http->src.ip);
633 return (NULL);
634 }
635 *ep = '\0';
636
637 /*
638 * The header data is invalid if it has any binary characters in
639 * it: check that now.
640 * This is important because we want to guarantee that all
641 * header keys and pairs are properly nil-terminated.
642 */
643
644 if (strlen(trans->hbuf) != (uintptr_t)(ep - trans->hbuf)) {
645 warnx("%s: binary data in header", http->src.ip);
646 return (NULL);
647 }
648
649 /*
650 * Copy remaining buffer into body buffer.
651 */
652
653 ep += 4;
654 trans->bbufsz = (trans->hbuf + trans->hbufsz) - ep;
655 trans->bbuf = malloc(trans->bbufsz);
656 if (NULL == trans->bbuf) {
657 warn("malloc");
658 return (NULL);
659 }
660 memcpy(trans->bbuf, ep, trans->bbufsz);
661
662 trans->headok = 1;
663 *sz = trans->hbufsz;
664 return (trans->hbuf);
665}
666
667void
668http_get_free(struct httpget *g)
669{
670
671 if (NULL == g)
672 return;
673 http_close(g->xfer);
674 http_free(g->http);
675 free(g);
676}
677
678struct httpget *
679http_get(const struct source *addrs, size_t addrsz, const char *domain,
680 short port, const char *path, const void *post, size_t postsz)
681{
682 struct http *h;
683 struct httpxfer *x;
684 struct httpget *g;
685 struct httphead *head;
686 size_t headsz, bodsz, headrsz;
687 int code;
688 char *bod, *headr;
689
690 h = http_alloc(addrs, addrsz, domain, port, path);
691 if (NULL == h)
692 return (NULL);
693
694 if (NULL == (x = http_open(h, post, postsz))) {
695 http_free(h);
696 return (NULL);
697 } else if (NULL == (headr = http_head_read(h, x, &headrsz))) {
698 http_close(x);
699 http_free(h);
700 return (NULL);
701 } else if (NULL == (bod = http_body_read(h, x, &bodsz))) {
702 http_close(x);
703 http_free(h);
704 return (NULL);
705 }
706
707 http_disconnect(h);
708
709 if (NULL == (head = http_head_parse(h, x, &headsz))) {
710 http_close(x);
711 http_free(h);
712 return (NULL);
713 } else if ((code = http_head_status(h, head, headsz)) < 0) {
714 http_close(x);
715 http_free(h);
716 return (NULL);
717 }
718
719 if (NULL == (g = calloc(1, sizeof(struct httpget)))) {
720 warn("calloc");
721 http_close(x);
722 http_free(h);
723 return (NULL);
724 }
725
726 g->headpart = headr;
727 g->headpartsz = headrsz;
728 g->bodypart = bod;
729 g->bodypartsz = bodsz;
730 g->head = head;
731 g->headsz = headsz;
732 g->code = code;
733 g->xfer = x;
734 g->http = h;
735 return (g);
736}
737
738#if 0
739int
740main(void)
741{
742 struct httpget *g;
743 struct httphead *httph;
744 size_t i, httphsz;
745 struct source addrs[2];
746 size_t addrsz;
747
748#if 0
749 addrs[0].ip = "127.0.0.1";
750 addrs[0].family = 4;
751 addrsz = 1;
752#else
753 addrs[0].ip = "2a00:1450:400a:806::2004";
754 addrs[0].family = 6;
755 addrs[1].ip = "193.135.3.123";
756 addrs[1].family = 4;
757 addrsz = 2;
758#endif
759
760 if (http_init() == -1)
761 errx(EXIT_FAILURE, "http_init");
762
763#if 0
764 g = http_get(addrs, addrsz, "localhost", 80, "/index.html");
765#else
766 g = http_get(addrs, addrsz, "www.google.ch", 80, "/index.html",
767 NULL, 0);
768#endif
769
770 if (NULL == g)
771 errx(EXIT_FAILURE, "http_get");
772
773 httph = http_head_parse(g->http, g->xfer, &httphsz);
774 warnx("code: %d", g->code);
775
776 for (i = 0; i < httphsz; i++)
777 warnx("head: [%s]=[%s]", httph[i].key, httph[i].val);
778
779 http_get_free(g);
780 return (EXIT_SUCCESS);
781}
782#endif