GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/acme-client/http.c Lines: 0 334 0.0 %
Date: 2017-11-13 Branches: 0 170 0.0 %

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