[BACK]Return to http.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ftp

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 */