summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>2006-05-16 16:20:43 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>2006-05-16 16:20:43 +0000
commitf029b18f4a0920d3d7f0f9dfbdd86bb219fd6718 (patch)
tree50969d92e10135e9e3e39990b9af0f62f72c5947
parent6eec75e97e7070f836c42439533a794c4094dad1 (diff)
https URL support; rototilled by a few people including me; originally
from Rainer_Giedat@genua.de
-rw-r--r--usr.bin/ftp/Makefile6
-rw-r--r--usr.bin/ftp/fetch.c265
-rw-r--r--usr.bin/ftp/ftp.116
-rw-r--r--usr.bin/ftp/ftp.c8
-rw-r--r--usr.bin/ftp/ftp_var.h6
-rw-r--r--usr.bin/ftp/main.c14
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);
}