Annotation of src/usr.bin/ftp/http.c, Revision 1.8
1.8 ! jca 1: /* $OpenBSD: http.c,v 1.7 2019/05/14 02:30:00 sunil Exp $ */
1.5 jasper 2:
1.1 deraadt 3: /*
1.4 kmos 4: * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org>
5: * Copyright (c) 2012 - 2015 Reyk Floeter <reyk@openbsd.org>
1.1 deraadt 6: *
1.4 kmos 7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
1.1 deraadt 10: *
1.4 kmos 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 deraadt 18: */
19:
20: #include <err.h>
1.4 kmos 21: #include <fcntl.h>
22: #include <libgen.h>
23: #include <limits.h>
1.1 deraadt 24: #include <stdio.h>
1.4 kmos 25: #include <stdint.h>
1.1 deraadt 26: #include <stdlib.h>
27: #include <string.h>
1.4 kmos 28: #include <strings.h>
1.1 deraadt 29: #include <unistd.h>
1.4 kmos 30: #ifndef NOSSL
31: #include <tls.h>
32: #endif
33:
34: #include "ftp.h"
35: #include "xmalloc.h"
36:
37: #define MAX_REDIRECTS 10
38:
39: #ifndef NOSSL
40: #define MINBUF 128
41:
42: static struct tls_config *tls_config;
43: static struct tls *ctx;
44: static int tls_session_fd = -1;
45: static char * const tls_verify_opts[] = {
46: #define HTTP_TLS_CAFILE 0
47: "cafile",
48: #define HTTP_TLS_CAPATH 1
49: "capath",
50: #define HTTP_TLS_CIPHERS 2
51: "ciphers",
52: #define HTTP_TLS_DONTVERIFY 3
53: "dont",
54: #define HTTP_TLS_VERIFYDEPTH 4
55: "depth",
56: #define HTTP_TLS_MUSTSTAPLE 5
57: "muststaple",
58: #define HTTP_TLS_NOVERIFYTIME 6
59: "noverifytime",
60: #define HTTP_TLS_SESSION 7
61: "session",
62: #define HTTP_TLS_DOVERIFY 8
63: "do",
64: NULL
65: };
66: #endif /* NOSSL */
1.1 deraadt 67:
68: /*
1.4 kmos 69: * HTTP status codes based on IANA assignments (2014-06-11 version):
70: * https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
71: * plus legacy (306) and non-standard (420).
1.1 deraadt 72: */
1.4 kmos 73: static struct http_status {
74: int code;
75: const char *name;
76: } http_status[] = {
77: { 100, "Continue" },
78: { 101, "Switching Protocols" },
79: { 102, "Processing" },
80: /* 103-199 unassigned */
81: { 200, "OK" },
82: { 201, "Created" },
83: { 202, "Accepted" },
84: { 203, "Non-Authoritative Information" },
85: { 204, "No Content" },
86: { 205, "Reset Content" },
87: { 206, "Partial Content" },
88: { 207, "Multi-Status" },
89: { 208, "Already Reported" },
90: /* 209-225 unassigned */
91: { 226, "IM Used" },
92: /* 227-299 unassigned */
93: { 300, "Multiple Choices" },
94: { 301, "Moved Permanently" },
95: { 302, "Found" },
96: { 303, "See Other" },
97: { 304, "Not Modified" },
98: { 305, "Use Proxy" },
99: { 306, "Switch Proxy" },
100: { 307, "Temporary Redirect" },
101: { 308, "Permanent Redirect" },
102: /* 309-399 unassigned */
103: { 400, "Bad Request" },
104: { 401, "Unauthorized" },
105: { 402, "Payment Required" },
106: { 403, "Forbidden" },
107: { 404, "Not Found" },
108: { 405, "Method Not Allowed" },
109: { 406, "Not Acceptable" },
110: { 407, "Proxy Authentication Required" },
111: { 408, "Request Timeout" },
112: { 409, "Conflict" },
113: { 410, "Gone" },
114: { 411, "Length Required" },
115: { 412, "Precondition Failed" },
116: { 413, "Payload Too Large" },
117: { 414, "URI Too Long" },
118: { 415, "Unsupported Media Type" },
119: { 416, "Range Not Satisfiable" },
120: { 417, "Expectation Failed" },
121: { 418, "I'm a teapot" },
122: /* 419-421 unassigned */
123: { 420, "Enhance Your Calm" },
124: { 422, "Unprocessable Entity" },
125: { 423, "Locked" },
126: { 424, "Failed Dependency" },
127: /* 425 unassigned */
128: { 426, "Upgrade Required" },
129: /* 427 unassigned */
130: { 428, "Precondition Required" },
131: { 429, "Too Many Requests" },
132: /* 430 unassigned */
133: { 431, "Request Header Fields Too Large" },
134: /* 432-450 unassigned */
135: { 451, "Unavailable For Legal Reasons" },
136: /* 452-499 unassigned */
137: { 500, "Internal Server Error" },
138: { 501, "Not Implemented" },
139: { 502, "Bad Gateway" },
140: { 503, "Service Unavailable" },
141: { 504, "Gateway Timeout" },
142: { 505, "HTTP Version Not Supported" },
143: { 506, "Variant Also Negotiates" },
144: { 507, "Insufficient Storage" },
145: { 508, "Loop Detected" },
146: /* 509 unassigned */
147: { 510, "Not Extended" },
148: { 511, "Network Authentication Required" },
149: /* 512-599 unassigned */
150: { 0, NULL },
151: };
152:
153: struct http_headers {
154: char *location;
155: off_t content_length;
156: int chunked;
157: };
158:
159: static void decode_chunk(int, uint, FILE *);
160: static char *header_lookup(const char *, const char *);
161: static const char *http_error(int);
162: static void http_headers_free(struct http_headers *);
163: static ssize_t http_getline(int, char **, size_t *);
164: static size_t http_read(int, char *, size_t);
165: static struct url *http_redirect(struct url *, char *);
166: static void http_save_chunks(struct url *, FILE *, off_t *);
167: static int http_status_cmp(const void *, const void *);
168: static int http_request(int, const char *,
169: struct http_headers **);
170: static char *relative_path_resolve(const char *, const char *);
171:
172: #ifndef NOSSL
173: static void tls_copy_file(struct url *, FILE *, off_t *);
174: static ssize_t tls_getline(char **, size_t *, struct tls *);
175: #endif
176:
177: static FILE *fp;
178:
179: void
180: http_connect(struct url *url, struct url *proxy, int timeout)
181: {
182: const char *host, *port;
183: int sock;
184:
185: host = proxy ? proxy->host : url->host;
186: port = proxy ? proxy->port : url->port;
187: if ((sock = tcp_connect(host, port, timeout)) == -1)
188: exit(1);
189:
190: if ((fp = fdopen(sock, "r+")) == NULL)
191: err(1, "%s: fdopen", __func__);
192:
193: #ifndef NOSSL
194: struct http_headers *headers;
195: char *auth = NULL, *req;
196: int authlen = 0, code;
197:
198: if (url->scheme != S_HTTPS)
199: return;
200:
201: if (proxy) {
202: if (url->basic_auth)
203: authlen = xasprintf(&auth,
204: "Proxy-Authorization: Basic %s\r\n",
205: url->basic_auth);
206:
207: xasprintf(&req,
208: "CONNECT %s:%s HTTP/1.0\r\n"
209: "User-Agent: %s\r\n"
210: "%s"
211: "\r\n",
212: url->host, url->port,
213: useragent,
214: url->basic_auth ? auth : "");
215:
216: freezero(auth, authlen);
217: if ((code = http_request(S_HTTP, req, &headers)) != 200)
218: errx(1, "%s: failed to CONNECT to %s:%s: %s",
219: __func__, url->host, url->port, http_error(code));
220:
221: free(req);
222: http_headers_free(headers);
223: }
224:
225: if ((ctx = tls_client()) == NULL)
226: errx(1, "failed to create tls client");
227:
228: if (tls_configure(ctx, tls_config) != 0)
229: errx(1, "%s: %s", __func__, tls_error(ctx));
230:
231: if (tls_connect_socket(ctx, sock, url->host) != 0)
232: errx(1, "%s: %s", __func__, tls_error(ctx));
233: #endif /* NOSSL */
234: }
235:
236: struct url *
237: http_get(struct url *url, struct url *proxy, off_t *offset, off_t *sz)
238: {
239: struct http_headers *headers;
240: char *auth = NULL, *path = NULL, *range = NULL, *req;
241: int authlen = 0, code, redirects = 0;
242:
243: redirected:
244: log_request("Requesting", url, proxy);
245: if (*offset)
246: xasprintf(&range, "Range: bytes=%lld-\r\n", *offset);
247:
248: if (url->basic_auth)
249: authlen = xasprintf(&auth, "Authorization: Basic %s\r\n",
250: url->basic_auth);
251:
252: if (proxy && url->scheme != S_HTTPS)
253: path = url_str(url);
254: else if (url->path)
255: path = url_encode(url->path);
256:
257: xasprintf(&req,
258: "GET %s HTTP/1.1\r\n"
259: "Host: %s\r\n"
260: "%s"
261: "%s"
262: "Connection: close\r\n"
263: "User-Agent: %s\r\n"
264: "\r\n",
265: path ? path : "/",
266: url->host,
267: *offset ? range : "",
268: url->basic_auth ? auth : "",
269: useragent);
270: code = http_request(url->scheme, req, &headers);
271: freezero(auth, authlen);
1.6 tb 272: auth = NULL;
273: authlen = 0;
1.4 kmos 274: free(range);
1.6 tb 275: range = NULL;
1.4 kmos 276: free(path);
1.6 tb 277: path = NULL;
1.4 kmos 278: free(req);
1.6 tb 279: req = NULL;
1.4 kmos 280: switch (code) {
281: case 200:
282: if (*offset) {
283: warnx("Server does not support resume.");
284: *offset = 0;
285: }
286: break;
287: case 206:
288: break;
289: case 301:
290: case 302:
291: case 303:
292: case 307:
293: http_close(url);
294: if (++redirects > MAX_REDIRECTS)
295: errx(1, "Too many redirections requested");
296:
297: if (headers->location == NULL)
298: errx(1, "%s: Location header missing", __func__);
299:
300: url = http_redirect(url, headers->location);
301: http_headers_free(headers);
302: log_request("Redirected to", url, proxy);
303: http_connect(url, proxy, 0);
304: goto redirected;
305: case 416:
306: errx(1, "File is already fully retrieved.");
307: break;
308: default:
309: errx(1, "Error retrieving file: %d %s", code, http_error(code));
310: }
311:
312: *sz = headers->content_length + *offset;
313: url->chunked = headers->chunked;
314: http_headers_free(headers);
315: return url;
316: }
317:
318: void
319: http_save(struct url *url, FILE *dst_fp, off_t *offset)
320: {
321: if (url->chunked)
322: http_save_chunks(url, dst_fp, offset);
323: #ifndef NOSSL
324: else if (url->scheme == S_HTTPS)
325: tls_copy_file(url, dst_fp, offset);
326: #endif
327: else
328: copy_file(dst_fp, fp, offset);
329: }
330:
331: static struct url *
332: http_redirect(struct url *old_url, char *location)
333: {
334: struct url *new_url;
335:
336: /* absolute uri reference */
337: if (strncasecmp(location, "http", 4) == 0 ||
338: strncasecmp(location, "https", 5) == 0) {
339: if ((new_url = url_parse(location)) == NULL)
340: exit(1);
341:
342: goto done;
343: }
344:
345: /* relative uri reference */
346: new_url = xcalloc(1, sizeof *new_url);
347: new_url->scheme = old_url->scheme;
348: new_url->host = xstrdup(old_url->host);
349: new_url->port = xstrdup(old_url->port);
350:
351: /* absolute-path reference */
352: if (location[0] == '/')
353: new_url->path = xstrdup(location);
354: else
355: new_url->path = relative_path_resolve(old_url->path, location);
356:
357: done:
358: new_url->fname = xstrdup(old_url->fname);
359: url_free(old_url);
360: return new_url;
361: }
362:
363: static char *
364: relative_path_resolve(const char *base_path, const char *location)
365: {
366: char *new_path, *p;
367:
368: /* trim fragment component from both uri */
369: if ((p = strchr(location, '#')) != NULL)
1.1 deraadt 370: *p = '\0';
1.4 kmos 371: if (base_path && (p = strchr(base_path, '#')) != NULL)
372: *p = '\0';
373:
374: if (base_path == NULL)
375: xasprintf(&new_path, "/%s", location);
376: else if (base_path[strlen(base_path) - 1] == '/')
377: xasprintf(&new_path, "%s%s", base_path, location);
378: else {
379: p = dirname(base_path);
380: xasprintf(&new_path, "%s/%s",
381: strcmp(p, ".") == 0 ? "" : p, location);
1.1 deraadt 382: }
383:
1.4 kmos 384: return new_path;
385: }
386:
387: static void
388: http_save_chunks(struct url *url, FILE *dst_fp, off_t *offset)
389: {
390: char *buf = NULL;
391: size_t n = 0;
392: uint chunk_sz;
393:
394: http_getline(url->scheme, &buf, &n);
395: if (sscanf(buf, "%x", &chunk_sz) != 1)
396: errx(1, "%s: Failed to get chunk size", __func__);
397:
398: while (chunk_sz > 0) {
399: decode_chunk(url->scheme, chunk_sz, dst_fp);
400: *offset += chunk_sz;
401: http_getline(url->scheme, &buf, &n);
402: if (sscanf(buf, "%x", &chunk_sz) != 1)
403: errx(1, "%s: Failed to get chunk size", __func__);
1.1 deraadt 404: }
405:
1.4 kmos 406: free(buf);
407: }
408:
409: static void
410: decode_chunk(int scheme, uint sz, FILE *dst_fp)
411: {
412: size_t bufsz;
413: size_t r;
414: char buf[BUFSIZ], crlf[2];
415:
416: bufsz = sizeof(buf);
417: while (sz > 0) {
418: if (sz < bufsz)
419: bufsz = sz;
420:
421: r = http_read(scheme, buf, bufsz);
422: if (fwrite(buf, 1, r, dst_fp) != r)
423: errx(1, "%s: fwrite", __func__);
424:
425: sz -= r;
426: }
427:
428: /* CRLF terminating the chunk */
429: if (http_read(scheme, crlf, sizeof(crlf)) != sizeof(crlf))
430: errx(1, "%s: Failed to read terminal crlf", __func__);
431:
432: if (crlf[0] != '\r' || crlf[1] != '\n')
433: errx(1, "%s: Invalid chunked encoding", __func__);
434: }
435:
436: void
437: http_close(struct url *url)
438: {
439: #ifndef NOSSL
440: ssize_t r;
441:
442: if (url->scheme == S_HTTPS) {
443: if (tls_session_fd != -1)
444: dprintf(STDERR_FILENO, "tls session resumed: %s\n",
445: tls_conn_session_resumed(ctx) ? "yes" : "no");
446:
447: do {
448: r = tls_close(ctx);
449: } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
450: tls_free(ctx);
451: }
452:
453: #endif
454: fclose(fp);
455: }
456:
457: static int
458: http_request(int scheme, const char *req, struct http_headers **hdrs)
459: {
460: struct http_headers *headers;
461: const char *e;
462: char *buf = NULL, *p;
463: size_t n = 0;
464: ssize_t buflen;
465: uint code;
466: #ifndef NOSSL
1.7 sunil 467: size_t len;
1.4 kmos 468: ssize_t nw;
469: #endif
470:
471: if (io_debug)
472: fprintf(stderr, "<<< %s", req);
473:
474: switch (scheme) {
475: #ifndef NOSSL
476: case S_HTTPS:
1.7 sunil 477: len = strlen(req);
478: while (len > 0) {
479: nw = tls_write(ctx, req, len);
480: if (nw == TLS_WANT_POLLIN || nw == TLS_WANT_POLLOUT)
481: continue;
482: if (nw < 0)
483: errx(1, "tls_write: %s", tls_error(ctx));
484: req += nw;
485: len -= nw;
486: }
1.4 kmos 487: break;
488: #endif
489: case S_FTP:
490: case S_HTTP:
491: if (fprintf(fp, "%s", req) < 0)
492: errx(1, "%s: fprintf", __func__);
493: (void)fflush(fp);
494: break;
495: }
496:
497: http_getline(scheme, &buf, &n);
498: if (io_debug)
499: fprintf(stderr, ">>> %s", buf);
500:
501: if (sscanf(buf, "%*s %u %*s", &code) != 1)
502: errx(1, "%s: failed to extract status code", __func__);
503:
504: if (code < 100 || code > 511)
505: errx(1, "%s: invalid status code %d", __func__, code);
506:
507: headers = xcalloc(1, sizeof *headers);
508: for (;;) {
509: buflen = http_getline(scheme, &buf, &n);
510: buflen -= 1;
511: if (buflen > 0 && buf[buflen - 1] == '\r')
512: buflen -= 1;
513: buf[buflen] = '\0';
514:
515: if (io_debug)
516: fprintf(stderr, ">>> %s\n", buf);
517:
518: if (buflen == 0)
519: break; /* end of headers */
520:
521: if ((p = header_lookup(buf, "Content-Length:")) != NULL) {
522: headers->content_length = strtonum(p, 0, INT64_MAX, &e);
523: if (e)
524: err(1, "%s: Content-Length is %s: %lld",
525: __func__, e, headers->content_length);
526: }
527:
528: if ((p = header_lookup(buf, "Location:")) != NULL)
529: headers->location = xstrdup(p);
530:
531: if ((p = header_lookup(buf, "Transfer-Encoding:")) != NULL)
532: if (strcasestr(p, "chunked") != NULL)
533: headers->chunked = 1;
534:
1.1 deraadt 535: }
536:
1.4 kmos 537: *hdrs = headers;
538: free(buf);
539: return code;
540: }
541:
542: static void
543: http_headers_free(struct http_headers *headers)
544: {
545: if (headers == NULL)
546: return;
547:
548: free(headers->location);
549: free(headers);
550: }
551:
552: static char *
553: header_lookup(const char *buf, const char *key)
554: {
555: char *p;
556:
557: if (strncasecmp(buf, key, strlen(key)) == 0) {
558: if ((p = strchr(buf, ' ')) == NULL)
559: errx(1, "Failed to parse %s", key);
560: return ++p;
1.1 deraadt 561: }
562:
1.4 kmos 563: return NULL;
564: }
565:
566: static const char *
567: http_error(int code)
568: {
569: struct http_status error, *res;
570:
571: /* Set up key */
572: error.code = code;
573:
574: if ((res = bsearch(&error, http_status,
575: sizeof(http_status) / sizeof(http_status[0]) - 1,
576: sizeof(http_status[0]), http_status_cmp)) != NULL)
577: return (res->name);
578:
579: return (NULL);
580: }
581:
582: static int
583: http_status_cmp(const void *a, const void *b)
584: {
585: const struct http_status *ea = a;
586: const struct http_status *eb = b;
587:
588: return (ea->code - eb->code);
589: }
1.1 deraadt 590:
1.4 kmos 591:
592: static ssize_t
593: http_getline(int scheme, char **buf, size_t *n)
594: {
595: ssize_t buflen;
596:
597: switch (scheme) {
598: #ifndef NOSSL
599: case S_HTTPS:
600: if ((buflen = tls_getline(buf, n, ctx)) == -1)
601: errx(1, "%s: tls_getline", __func__);
602: break;
603: #endif
604: case S_FTP:
605: case S_HTTP:
606: if ((buflen = getline(buf, n, fp)) == -1)
607: err(1, "%s: getline", __func__);
608: break;
609: default:
610: errx(1, "%s: invalid scheme", __func__);
1.1 deraadt 611: }
612:
1.4 kmos 613: return buflen;
614: }
615:
616: static size_t
617: http_read(int scheme, char *buf, size_t size)
618: {
619: size_t r;
620: #ifndef NOSSL
621: ssize_t rs;
622: #endif
623:
624: switch (scheme) {
625: #ifndef NOSSL
626: case S_HTTPS:
627: do {
628: rs = tls_read(ctx, buf, size);
629: } while (rs == TLS_WANT_POLLIN || rs == TLS_WANT_POLLOUT);
630: if (rs == -1)
631: errx(1, "%s: tls_read: %s", __func__, tls_error(ctx));
632: r = rs;
633: break;
634: #endif
635: case S_HTTP:
636: if ((r = fread(buf, 1, size, fp)) < size)
637: if (!feof(fp))
638: errx(1, "%s: fread", __func__);
639: break;
640: default:
641: errx(1, "%s: invalid scheme", __func__);
642: }
643:
644: return r;
645: }
646:
647: #ifndef NOSSL
648: void
649: https_init(char *tls_options)
650: {
651: char *str;
652: int depth;
653: const char *ca_file, *errstr;
654:
655: if (tls_init() != 0)
656: errx(1, "tls_init failed");
657:
658: if ((tls_config = tls_config_new()) == NULL)
659: errx(1, "tls_config_new failed");
1.8 ! jca 660:
! 661: if (tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL) != 0)
! 662: errx(1, "tls set protocols failed: %s",
! 663: tls_config_error(tls_config));
1.4 kmos 664:
665: if (tls_config_set_ciphers(tls_config, "legacy") != 0)
666: errx(1, "tls set ciphers failed: %s",
667: tls_config_error(tls_config));
668:
669: ca_file = tls_default_ca_cert_file();
670: while (tls_options && *tls_options) {
671: switch (getsubopt(&tls_options, tls_verify_opts, &str)) {
672: case HTTP_TLS_CAFILE:
673: if (str == NULL)
674: errx(1, "missing CA file");
675: ca_file = str;
1.1 deraadt 676: break;
1.4 kmos 677: case HTTP_TLS_CAPATH:
678: if (str == NULL)
679: errx(1, "missing ca path");
680: if (tls_config_set_ca_path(tls_config, str) != 0)
681: errx(1, "tls ca path failed");
682: break;
683: case HTTP_TLS_CIPHERS:
684: if (str == NULL)
685: errx(1, "missing cipher list");
686: if (tls_config_set_ciphers(tls_config, str) != 0)
687: errx(1, "tls set ciphers failed");
688: break;
689: case HTTP_TLS_DONTVERIFY:
690: tls_config_insecure_noverifycert(tls_config);
691: tls_config_insecure_noverifyname(tls_config);
692: break;
693: case HTTP_TLS_VERIFYDEPTH:
694: if (str == NULL)
695: errx(1, "missing depth");
696: depth = strtonum(str, 0, INT_MAX, &errstr);
697: if (errstr)
698: errx(1, "Cert validation depth is %s", errstr);
699: tls_config_set_verify_depth(tls_config, depth);
700: break;
701: case HTTP_TLS_MUSTSTAPLE:
702: tls_config_ocsp_require_stapling(tls_config);
703: break;
704: case HTTP_TLS_NOVERIFYTIME:
705: tls_config_insecure_noverifytime(tls_config);
706: break;
707: case HTTP_TLS_SESSION:
708: if (str == NULL)
709: errx(1, "missing session file");
710: tls_session_fd = open(str, O_RDWR|O_CREAT, 0600);
711: if (tls_session_fd == -1)
712: err(1, "failed to open or create session file "
713: "'%s'", str);
714: if (tls_config_set_session_fd(tls_config,
715: tls_session_fd) == -1)
716: errx(1, "failed to set session: %s",
717: tls_config_error(tls_config));
718: break;
719: case HTTP_TLS_DOVERIFY:
720: /* For compatibility, we do verify by default */
721: break;
722: default:
723: errx(1, "Unknown -S suboption `%s'",
724: suboptarg ? suboptarg : "");
725: }
1.1 deraadt 726: }
727:
1.4 kmos 728: if (tls_config_set_ca_file(tls_config, ca_file) == -1)
729: errx(1, "tls_config_set_ca_file failed");
730: }
731:
732: static ssize_t
733: tls_getline(char **buf, size_t *buflen, struct tls *tls)
734: {
735: char *newb;
736: size_t newlen, off;
737: int ret;
738: unsigned char c;
739:
740: if (buf == NULL || buflen == NULL)
741: return -1;
742:
743: /* If buf is NULL, we have to assume a size of zero */
744: if (*buf == NULL)
745: *buflen = 0;
746:
747: off = 0;
748: do {
749: do {
750: ret = tls_read(tls, &c, 1);
751: } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT);
752: if (ret == -1)
753: return -1;
754:
755: /* Ensure we can handle it */
756: if (off + 2 > SSIZE_MAX)
757: return -1;
758:
759: newlen = off + 2; /* reserve space for NUL terminator */
760: if (newlen > *buflen) {
761: newlen = newlen < MINBUF ? MINBUF : *buflen * 2;
762: newb = recallocarray(*buf, *buflen, newlen, 1);
763: if (newb == NULL)
764: return -1;
765:
766: *buf = newb;
767: *buflen = newlen;
768: }
769:
770: *(*buf + off) = c;
771: off += 1;
772: } while (c != '\n');
773:
774: *(*buf + off) = '\0';
775: return off;
776: }
777:
778: static void
779: tls_copy_file(struct url *url, FILE *dst_fp, off_t *offset)
780: {
781: char *tmp_buf;
782: ssize_t r;
783:
784: tmp_buf = xmalloc(TMPBUF_LEN);
785: for (;;) {
786: do {
787: r = tls_read(ctx, tmp_buf, TMPBUF_LEN);
788: } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
789:
790: if (r == -1)
791: errx(1, "%s: tls_read: %s", __func__, tls_error(ctx));
792: else if (r == 0)
793: break;
1.1 deraadt 794:
1.4 kmos 795: *offset += r;
796: if (fwrite(tmp_buf, 1, r, dst_fp) != (size_t)r)
797: err(1, "%s: fwrite", __func__);
798: }
799: free(tmp_buf);
800: }
801: #endif /* NOSSL */