diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2006-05-16 16:20:43 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2006-05-16 16:20:43 +0000 |
commit | f029b18f4a0920d3d7f0f9dfbdd86bb219fd6718 (patch) | |
tree | 50969d92e10135e9e3e39990b9af0f62f72c5947 | |
parent | 6eec75e97e7070f836c42439533a794c4094dad1 (diff) |
https URL support; rototilled by a few people including me; originally
from Rainer_Giedat@genua.de
-rw-r--r-- | usr.bin/ftp/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/ftp/fetch.c | 265 | ||||
-rw-r--r-- | usr.bin/ftp/ftp.1 | 16 | ||||
-rw-r--r-- | usr.bin/ftp/ftp.c | 8 | ||||
-rw-r--r-- | usr.bin/ftp/ftp_var.h | 6 | ||||
-rw-r--r-- | usr.bin/ftp/main.c | 14 |
6 files changed, 283 insertions, 32 deletions
diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile index 0aeaea32569..3cb50c7b584 100644 --- a/usr.bin/ftp/Makefile +++ b/usr.bin/ftp/Makefile @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile,v 1.20 2003/11/20 23:32:26 tedu Exp $ +# $OpenBSD: Makefile,v 1.21 2006/05/16 16:20:42 deraadt Exp $ -# Define SMALL to disable command line editing +# Define SMALL to disable command line editing and https support #CFLAGS+=-DSMALL # Uncomment the following to provide defaults for gate-ftp operation @@ -17,7 +17,7 @@ SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c ruserpass.c \ CPPFLAGS+= -DINET6 -LDADD+= -ledit -lcurses -lutil +LDADD+= -ledit -lcurses -lutil -lssl -lcrypto DPADD+= ${LIBEDIT} ${LIBCURSES} ${LIBUTIL} LDSTATIC= ${STATIC} diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c index 730e69faf1e..a628822aa93 100644 --- a/usr.bin/ftp/fetch.c +++ b/usr.bin/ftp/fetch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fetch.c,v 1.60 2006/04/25 05:45:20 tedu Exp $ */ +/* $OpenBSD: fetch.c,v 1.61 2006/05/16 16:20:42 deraadt Exp $ */ /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */ /*- @@ -38,7 +38,7 @@ */ #if !defined(lint) && !defined(SMALL) -static const char rcsid[] = "$OpenBSD: fetch.c,v 1.60 2006/04/25 05:45:20 tedu Exp $"; +static const char rcsid[] = "$OpenBSD: fetch.c,v 1.61 2006/05/16 16:20:42 deraadt Exp $"; #endif /* not lint and not SMALL */ /* @@ -63,12 +63,20 @@ static const char rcsid[] = "$OpenBSD: fetch.c,v 1.60 2006/04/25 05:45:20 tedu E #include <fcntl.h> #include <signal.h> #include <stdio.h> +#include <stdarg.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <util.h> +#ifndef SMALL +#include <openssl/ssl.h> +#include <openssl/err.h> +#else +#define SSL void +#endif + #include "ftp_var.h" static int url_get(const char *, const char *, const char *); @@ -76,9 +84,18 @@ void aborthttp(int); void abortfile(int); char hextochar(const char *); char *urldecode(const char *); +int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4))); +char *ftp_readline(FILE *, SSL *, size_t *); +int ftp_read(FILE *, SSL *, char *, size_t); +#ifndef SMALL +int proxy_connect(int, char *); +int SSL_vprintf(SSL *, const char *, va_list); +char *SSL_readline(SSL *, size_t *); +#endif #define FTP_URL "ftp://" /* ftp URL prefix */ #define HTTP_URL "http://" /* http URL prefix */ +#define HTTPS_URL "https://" /* https URL prefix */ #define FILE_URL "file:" /* file URL prefix */ #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ #define HTTP_PROXY "http_proxy" /* env var with http proxy location */ @@ -111,8 +128,14 @@ url_get(const char *origline, const char *proxyenv, const char *outfile) volatile sig_t oldintr; FILE *fin = NULL; off_t hashbytes; - size_t len; const char *errstr; + size_t len, wlen; +#ifndef SMALL + char *sslpath = NULL, *sslhost = NULL; + int ishttpsurl = 0; + SSL_CTX *ssl_ctx = NULL; +#endif + SSL *ssl = NULL; line = strdup(origline); if (line == NULL) @@ -125,6 +148,11 @@ url_get(const char *origline, const char *proxyenv, const char *outfile) } else if (strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { host = line + sizeof(FILE_URL) - 1; isfileurl = 1; +#ifndef SMALL + } else if (strncasecmp(line, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) { + host = line + sizeof(HTTPS_URL) - 1; + ishttpsurl = 1; +#endif } else errx(1, "url_get: Invalid URL '%s'", line); @@ -160,6 +188,14 @@ url_get(const char *origline, const char *proxyenv, const char *outfile) } if (!isfileurl && proxyenv != NULL) { /* use proxy */ +#ifndef SMALL + if (ishttpsurl) { + sslpath = strdup(path); + sslhost = strdup(host); + if (! sslpath || ! sslhost) + errx(1, "Can't allocate memory for https path/host."); + } +#endif proxy = strdup(proxyenv); if (proxy == NULL) errx(1, "Can't allocate memory for proxy URL."); @@ -280,15 +316,24 @@ url_get(const char *origline, const char *proxyenv, const char *outfile) memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; +#ifndef SMALL + port = portnum ? portnum : (ishttpsurl ? httpsport : httpport); +#else port = portnum ? portnum : httpport; +#endif error = getaddrinfo(host, port, &hints, &res0); + /* + * If the services file is corrupt/missing, fall back + * on our hard-coded defines. + */ if (error == EAI_SERVICE && port == httpport) { - /* - * If the services file is corrupt/missing, fall back - * on our hard-coded defines. - */ snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); error = getaddrinfo(host, pbuf, &hints, &res0); +#ifndef SMALL + } else if (error == EAI_SERVICE && port == httpsport) { + snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); + error = getaddrinfo(host, pbuf, &hints, &res0); +#endif } if (error) { warnx("%s: %s", gai_strerror(error), host); @@ -330,6 +375,10 @@ again: else port = NULL; +#ifndef SMALL + if (proxyenv && sslhost) + proxy_connect(s, sslhost); +#endif break; } freeaddrinfo(res0); @@ -338,7 +387,36 @@ again: goto cleanup_url_get; } +#ifndef SMALL + if (ishttpsurl) { + if (proxyenv && sslpath) { + ishttpsurl = 0; + proxy = NULL; + path = sslpath; + } + SSL_library_init(); + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + ssl = SSL_new(ssl_ctx); + if (ssl == NULL || ssl_ctx == NULL) { + ERR_print_errors_fp(ttyout); + goto cleanup_url_get; + } + if (SSL_set_fd(ssl, s) == 0) { + ERR_print_errors_fp(ttyout); + goto cleanup_url_get; + } + if (SSL_connect(ssl) <= 0) { + ERR_print_errors_fp(ttyout); + goto cleanup_url_get;; + } + } else { + fin = fdopen(s, "r+"); + } +#else fin = fdopen(s, "r+"); +#endif if (verbose) fprintf(ttyout, "Requesting %s", origline); @@ -352,10 +430,10 @@ again: * Host: directive must use the destination host address for * the original URI (path). We do not attach it at this moment. */ - fprintf(fin, "GET %s HTTP/1.0\r\n%s\r\n\r\n", path, + ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s\r\n\r\n", path, HTTP_USER_AGENT); } else { - fprintf(fin, "GET /%s HTTP/1.0\r\nHost: ", path); + ftp_printf(fin, ssl, "GET /%s HTTP/1.0\r\nHost: ", path); if (strchr(host, ':')) { char *h, *p; @@ -368,28 +446,32 @@ again: errx(1, "Can't allocate memory."); if ((p = strchr(h, '%')) != NULL) *p = '\0'; - fprintf(fin, "[%s]", h); + ftp_printf(fin, ssl, "[%s]", h); free(h); } else - fprintf(fin, "%s", host); + ftp_printf(fin, ssl, "%s", host); /* * Send port number only if it's specified and does not equal * 80. Some broken HTTP servers get confused if you explicitly * send them the port number. */ +#ifndef SMALL + if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0) + ftp_printf(fin, ssl, ":%s", port); +#else if (port && strcmp(port, "80") != 0) - fprintf(fin, ":%s", port); - fprintf(fin, "\r\n%s\r\n\r\n", HTTP_USER_AGENT); + ftp_printf(fin, ssl, ":%s", port); +#endif + ftp_printf(fin, ssl, "\r\n%s\r\n\r\n", HTTP_USER_AGENT); if (verbose) fprintf(ttyout, "\n"); } - if (fflush(fin) == EOF) { + if (fin != NULL && fflush(fin) == EOF) { warn("Writing HTTP request"); goto cleanup_url_get; } - - if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { + if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { warn("Receiving HTTP reply"); goto cleanup_url_get; } @@ -422,10 +504,11 @@ again: filesize = -1; while (1) { - if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { + if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { warn("Receiving HTTP reply"); goto cleanup_url_get; } + while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) buf[--len] = '\0'; if (len == 0) @@ -490,10 +573,12 @@ again: if ((buf = malloc(4096)) == NULL) errx(1, "Can't allocate memory for transfer buffer"); i = 0; - while ((len = fread(buf, sizeof(char), 4096, fin)) > 0) { + len = 1; + while (len > 0) { + len = ftp_read(fin, ssl, buf, 4096); bytes += len; - for (cp = buf; len > 0; len -= i, cp += i) { - if ((i = write(out, cp, len)) == -1) { + for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { + if ((i = write(out, cp, wlen)) == -1) { warn("Writing %s", savefile); goto cleanup_url_get; } @@ -541,6 +626,12 @@ improper: warnx("Improper response from %s", host); cleanup_url_get: +#ifndef SMALL + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + } +#endif if (fin != NULL) fclose(fin); else if (s != -1) @@ -641,6 +732,10 @@ auto_fetch(int argc, char *argv[], char *outfile) * Try HTTP URL-style arguments first. */ if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || +#ifndef SMALL + /* even if we compiled without SSL, url_get will check */ + strncasecmp(line, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 || +#endif strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { redirect_loop = 0; if (url_get(line, httpproxy, outfile) == -1) @@ -881,8 +976,10 @@ urldecode(const char *str) *ret = ' '; continue; } - /* Can't use strtol here because next char after %xx may be - * a digit. */ + + /* Cannot use strtol here because next char + * after %xx may be a digit. + */ if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { *ret = hextochar(&str[i+1]); i+=2; @@ -923,8 +1020,132 @@ isurl(const char *p) if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || +#ifndef SMALL + strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 || +#endif strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || strstr(p, ":/")) return (1); return (0); } + +char * +ftp_readline(FILE *fp, SSL *ssl, size_t *lenp) +{ + if (fp != NULL) + return fparseln(fp, lenp, NULL, "\0\0\0", 0); +#ifndef SMALL + else if (ssl != NULL) + return SSL_readline(ssl, lenp); +#endif + else + return NULL; +} + +int +ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len) +{ + int ret; + if (fp != NULL) + ret = fread(buf, sizeof(char), len, fp); +#ifndef SMALL + else if (ssl != NULL) + ret = SSL_read(ssl, buf, (int)len); +#endif + else + ret = 0; + return (ret); +} + +int +ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + + if (fp != NULL) + ret = vfprintf(fp, fmt, ap); +#ifndef SMALL + else if (ssl != NULL) + ret = SSL_vprintf((SSL*)ssl, fmt, ap); +#endif + else + ret = NULL; + + va_end(ap); + return (ret); +} + +#ifndef SMALL +int +SSL_vprintf(SSL *ssl, const char *fmt, va_list ap) +{ + int ret; + char *string; + + if ((ret = vasprintf(&string, fmt, ap)) == -1) + return ret; + ret = SSL_write(ssl, (void *)string, (int)strlen(string)); + free(string); + return ret; +} + +char * +SSL_readline(SSL *ssl, size_t *lenp) +{ + int i; + size_t len; + char *buf, *q, c; + + len = 128; + if ((buf = malloc(len)) == NULL) + errx(1, "Can't allocate memory for transfer buffer"); + for (i = 0; ; i++) { + if (i >= len - 1) { + if ((q = realloc(buf, 2 * len)) == NULL) + errx(1, "Can't expand transfer buffer"); + buf = q; + len *= 2; + } + if (SSL_read(ssl, &c, 1) <= 0) + break; + buf[i] = c; + if (c == '\n') + break; + } + *lenp = i; + return (buf); +} + +int +proxy_connect(int socket, char *host) +{ + char buf[1024]; + char *connstr, *hosttail, *port; + + if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && + (hosttail[1] == '\0' || hosttail[1] == ':')) { + host++; + *hosttail++ = '\0'; + } else + hosttail = host; + + port = strrchr(hosttail, ':'); /* find portnum */ + if (port != NULL) + *port++ = '\0'; + if (!port) + port = "443"; + + if (debug) + printf("CONNECT %s:%s HTTP/1.1\n\n", host, port); + asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\n\n", host, port); + if (!connstr) + errx(1, "Could not allocate memory to assemble connect string!"); + if (write(socket, connstr, strlen(connstr)) != strlen(connstr)) + errx(1, "Could not send connect string: %s", strerror(errno)); + read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */ + return(200); +} +#endif diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 index 5783671acc7..39ed87dbfdd 100644 --- a/usr.bin/ftp/ftp.1 +++ b/usr.bin/ftp/ftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ftp.1,v 1.53 2005/09/21 22:31:47 fgsch Exp $ +.\" $OpenBSD: ftp.1,v 1.54 2006/05/16 16:20:42 deraadt Exp $ .\" $NetBSD: ftp.1,v 1.22 1997/08/18 10:20:22 lukem Exp $ .\" .\" Copyright (c) 1985, 1989, 1990, 1993 @@ -59,6 +59,12 @@ .Nm ftp .Op Fl o Ar output .Sm off +.No https:// Ar host Oo : Ar port +.Oc No / Ar file +.Sm on +.Nm ftp +.Op Fl o Ar output +.Sm off .Ar host : No / Ar file Oo / .Oc .Sm on @@ -1136,6 +1142,12 @@ An HTTP URL, retrieved using the HTTP protocol. If .Ev http_proxy is defined, it is used as a URL to an HTTP proxy server. +.It https://host[:port]/file +An HTTPS URL, retrieved using the HTTPS protocol. +If +.Ev http_proxy +is defined, this HTTPS proxy server will be used to fetch the +file using the CONNECT method. .El .Pp If a classic format or an FTP URL format has a trailing @@ -1492,7 +1504,7 @@ Directory to put temporary files. URL of FTP proxy to use when making FTP URL requests (if not defined, use the standard FTP protocol). .It Ev http_proxy -URL of HTTP proxy to use when making HTTP URL requests. +URL of HTTP proxy to use when making HTTP or HTTPS URL requests. .El .Sh PORT ALLOCATION For active mode data connections, diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c index e3b2d022454..55b96df447b 100644 --- a/usr.bin/ftp/ftp.c +++ b/usr.bin/ftp/ftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ftp.c,v 1.62 2006/04/25 05:45:20 tedu Exp $ */ +/* $OpenBSD: ftp.c,v 1.63 2006/05/16 16:20:42 deraadt Exp $ */ /* $NetBSD: ftp.c,v 1.27 1997/08/18 10:20:23 lukem Exp $ */ /* @@ -60,7 +60,7 @@ */ #if !defined(lint) && !defined(SMALL) -static const char rcsid[] = "$OpenBSD: ftp.c,v 1.62 2006/04/25 05:45:20 tedu Exp $"; +static const char rcsid[] = "$OpenBSD: ftp.c,v 1.63 2006/05/16 16:20:42 deraadt Exp $"; #endif /* not lint and not SMALL */ #include <sys/types.h> @@ -146,6 +146,10 @@ hookup(char *host, char *port) snprintf(pbuf, sizeof(pbuf), "%d", GATE_PORT); else if (strcmp(port, "http") == 0) snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); +#ifndef SMALL + else if (strcmp(port, "https") == 0) + snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); +#endif if (pbuf[0]) error = getaddrinfo(host, pbuf, &hints, &res0); } diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h index f5bd8a9b14d..606a786abb3 100644 --- a/usr.bin/ftp/ftp_var.h +++ b/usr.bin/ftp/ftp_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ftp_var.h,v 1.22 2003/06/03 02:56:08 millert Exp $ */ +/* $OpenBSD: ftp_var.h,v 1.23 2006/05/16 16:20:42 deraadt Exp $ */ /* $NetBSD: ftp_var.h,v 1.18 1997/08/18 10:20:25 lukem Exp $ */ /* @@ -87,6 +87,7 @@ int fclose(FILE *); #define FTP_PORT 21 /* default if ! getservbyname("ftp/tcp") */ #define HTTP_PORT 80 /* default if ! getservbyname("http/tcp") */ +#define HTTPS_PORT 443 /* default if ! getservbyname("https/tcp") */ #define HTTP_USER_AGENT "User-Agent: OpenBSD ftp" /* User-Agent string sent to web server */ #ifndef GATE_PORT #define GATE_PORT 21 /* default if ! getservbyname("ftpgate/tcp") */ @@ -171,6 +172,9 @@ int unix_proxy; /* proxy is unix, can use binary for ascii */ char *ftpport; /* port number to use for ftp connections */ char *httpport; /* port number to use for http connections */ +#ifndef SMALL +char *httpsport; /* port number to use for https connections */ +#endif char *gateport; /* port number to use for gateftp connections */ jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c index 7448ef9266b..51590c9ed7c 100644 --- a/usr.bin/ftp/main.c +++ b/usr.bin/ftp/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.60 2006/04/25 05:45:20 tedu Exp $ */ +/* $OpenBSD: main.c,v 1.61 2006/05/16 16:20:42 deraadt Exp $ */ /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */ /* @@ -66,7 +66,7 @@ static const char copyright[] = #endif /* not lint */ #if !defined(lint) && !defined(SMALL) -static const char rcsid[] = "$OpenBSD: main.c,v 1.60 2006/04/25 05:45:20 tedu Exp $"; +static const char rcsid[] = "$OpenBSD: main.c,v 1.61 2006/05/16 16:20:42 deraadt Exp $"; #endif /* not lint and not SMALL */ /* @@ -100,6 +100,9 @@ main(volatile int argc, char *argv[]) ftpport = "ftp"; httpport = "http"; +#ifndef SMALL + httpsport = "https"; +#endif gateport = getenv("FTPSERVERPORT"); if (gateport == NULL || *gateport == '\0') gateport = "ftpgate"; @@ -730,7 +733,14 @@ usage(void) "usage: %s [-46AadEegimnptVv] [-P port] [-r seconds] [host [port]]\n" " %s [-o output] ftp://[user:password@]host[:port]/file[/]\n" " %s [-o output] http://host[:port]/file\n" +#ifndef SMALL + " %s [-o output] https://host[:port]/file\n" +#endif " %s [-o output] host:[/path/]file[/]\n", +#ifndef SMALL + __progname, __progname, __progname, __progname, __progname); +#else __progname, __progname, __progname, __progname); +#endif exit(1); } |