Subject: Re: HTTPS support patch for /usr/bin/ftp
To: YAMAMOTO Shigeru <shigeru@iij.ad.jp>
From: Gary Thorpe <gathorpe79@yahoo.com>
List: current-users
Date: 07/29/2003 14:22:51
Why does an *ftp client* need http/https support? Or does this add SSL
support to the ftp client? Are there any ftp servers which support SSL?
If you want to download from HTTP/HTTPS servers, shouldn't you use
another tool (like wget), or is there no such tool in the base system?

 --- YAMAMOTO Shigeru <shigeru@iij.ad.jp> wrote: > 
> Hi, all,
> 
> I make a patch to support HTTPS for /usr/bin/ftp.
> It is quick hack.
> 
> Please try and test it.
> 
> Thanks,
> -------
> YAMAMOTO Shigeru	<shigeru@iij.ad.jp>
> > --- Makefile.org	Tue Jul 29 14:14:50 2003
> +++ Makefile	Tue Jul 29 14:19:23 2003
> @@ -9,8 +9,8 @@
>  #
>  #CPPFLAGS+=-DGATE_SERVER=\"ftp-gw.host\" # -DGATE_PORT=21
>  
> -LDADD+=	-ledit -ltermcap -lutil
> -DPADD+=	${LIBEDIT} ${LIBTERMCAP} ${LIBUTIL}
> +LDADD+=	-ledit -ltermcap -lutil -lssl -lcrypto
> +DPADD+=	${LIBEDIT} ${LIBTERMCAP} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO}
>  
>  CPPFLAGS+= -DINET6
>  
> --- fetch.c.org	Tue Jul 29 14:14:57 2003
> +++ fetch.c	Tue Jul 29 12:21:29 2003
> @@ -71,12 +71,18 @@
>  #include <time.h>
>  #include <util.h>
>  
> +#include <openssl/bio.h>
> +#include <openssl/ssl.h>
> +
>  #include "ftp_var.h"
>  #include "version.h"
>  
> +extern	SSL_CTX*	ssl_ctx;	/* SSL CTX */
> +
>  typedef enum {
>  	UNKNOWN_URL_T=-1,
>  	HTTP_URL_T,
> +	HTTPS_URL_T,
>  	FTP_URL_T,
>  	FILE_URL_T,
>  	CLASSIC_URL_T
> @@ -99,6 +105,7 @@
>  #define	FILE_URL	"file://"	/* file URL prefix */
>  #define	FTP_URL		"ftp://"	/* ftp URL prefix */
>  #define	HTTP_URL	"http://"	/* http URL prefix */
> +#define	HTTPS_URL	"https://"	/* https URL prefix */
>  
>  
>  /*
> @@ -300,6 +307,11 @@
>  		*type = HTTP_URL_T;
>  		*portnum = HTTP_PORT;
>  		tport = httpport;
> +	} else if (strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0)
> {
> +		url += sizeof(HTTPS_URL) - 1;
> +		*type = HTTPS_URL_T;
> +		*portnum = HTTPS_PORT;
> +		tport = httpsport;
>  	} else if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
>  		url += sizeof(FTP_URL) - 1;
>  		*type = FTP_URL_T;
> @@ -412,6 +424,170 @@
>  	return (0);
>  }
>  
> +static
> +int
> +isescaped(const char* sp, char* p, int esc) {
> +	int	result = 0;
> +
> +	if (esc == '\0') {
> +		result = 1;
> +	}
> +	else {
> +		size_t	ne;
> +		char*	cp;
> +
> +		for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne ++) {
> +			continue;
> +		}
> +
> +		result = ((ne & 1) != 0);
> +	}
> +
> +	return(result);
> +}
> +
> +static
> +char*
> +BIO_parseln(BIO* bio, size_t* len, size_t* lineno, const char
> delim[3], int flags) {
> +	static const char	dstr[3] = { '\\', '\\', '#' };
> +	int		cnt = 1;
> +	char	esc, con, com;
> +	char	nl;
> +	char*	buffer = NULL;
> +	size_t	length = 0;
> +
> +	if (delim == NULL) {
> +		delim = dstr;
> +	}
> +
> +	esc = delim[0];
> +	con = delim[1];
> +	com = delim[2];
> +
> +	nl = '\n';
> +
> +	cnt = 1;
> +	while (cnt) {
> +		char	read_buffer[BUFSIZ];
> +		size_t	read_length = 0;
> +
> +		cnt = 0;
> +
> +		read_length = BIO_gets(bio, read_buffer, sizeof(read_buffer));
> +		if (read_length > 0) {
> +			if (read_length < sizeof(read_buffer) || read_buffer[read_length
> - 1] == nl) {
> +				if (lineno) {
> +					(*lineno) ++;
> +				}
> +			}
> +
> +			if (read_length && com) {
> +				char*	cp;
> +
> +				for (cp = read_buffer; cp < read_buffer + read_length; cp ++) {
> +					if (*cp == com && !isescaped(read_buffer, cp, esc)) {
> +						read_length = cp - read_buffer;
> +						cnt = (read_length == 0 && buffer == NULL);
> +						break;
> +					}
> +				}
> +			}
> +
> +			if (read_length && nl) {
> +				char*	cp;
> +
> +				cp = &(read_buffer[read_length - 1]);
> +				if (*cp == nl) {
> +					read_length --;
> +				}
> +			}
> +
> +			if (read_length && con) {
> +				char*	cp;
> +
> +				cp = &(read_buffer[read_length - 1]);
> +				if (*cp == con && !isescaped(read_buffer, cp, esc)) {
> +					read_length --;
> +					cnt = 1;
> +				}
> +			}
> +
> +			if (read_length == 0 && buffer != NULL) {
> +				continue;
> +			}
> +			else {
> +				char*	new;
> +
> +				if ((new = realloc(buffer, length + read_length + 1)) == NULL) {
> +					free(buffer);
> +				}
> +
> +				buffer = new;
> +
> +				memcpy(buffer + length, read_buffer, read_length);
> +				length += read_length;
> +				buffer[length] = '\0';
> +			}
> +		}
> +	}
> +
> +	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buffer != NULL &&
> strchr(buffer, esc) != NULL) {
> +		char*	ptr;
> +		char*	cp;
> +
> +		ptr = cp = buffer;
> +		while (cp[0] != '\0') {
> +			int	skipesc;
> +
> +			while (cp[0] != '\0' && cp[0] != esc) {
> +				*ptr = *cp;
> +				ptr ++;
> +				cp ++;
> +			}
> +
> +			if (cp[0] == '\0' || cp[1] == '\0') {
> +				break;
> +			}
> +
> +			skipesc = 0;
> +			if (cp[1] == com) {
> +				skipesc += (flags & FPARSELN_UNESCCOMM);
> +			}
> +			if (cp[1] == con) {
> +				skipesc += (flags & FPARSELN_UNESCCONT);
> +			}
> +			if (cp[1] == esc) {
> +				skipesc += (flags & FPARSELN_UNESCESC);
> +			}
> +			if (cp[1] == com && cp[1] != con && cp[1] != esc) {
> +				skipesc += (flags & FPARSELN_UNESCREST);
> +			}
> +
> +			if (skipesc) {
> +				cp ++;
> +			}
> +			else {
> +				*ptr = *cp;
> +				ptr ++;
> +				cp ++;
> +			}
> +
> +			*ptr = *cp;
> +			ptr ++;
> +			cp ++;
> +		}
> +
> +		*ptr = '\0';
> +		length = strlen(buffer);
> +	}
> +
> +	if (len) {
> +		(*len) = length;
> +	}
> +
> +	return(buffer);
> +}
> +
>  sigjmp_buf	httpabort;
>  
>  /*
> @@ -441,14 +617,16 @@
>  	char			*puser, *ppass;
>  	off_t			hashbytes, rangestart, rangeend, entitylen;
>  	int			 (*closefunc)(FILE *);
> -	FILE			*fin, *fout;
> +	BIO*			bio;
> +	FILE			*fout;
>  	time_t			mtime;
>  	url_t			urltype;
>  	in_port_t		portnum;
>  
>  	oldintr = oldintp = NULL;
>  	closefunc = NULL;
> -	fin = fout = NULL;
> +	bio = (BIO*)0;
> +	fout = NULL;
>  	s = -1;
>  	buf = savefile = NULL;
>  	auth = location = message = NULL;
> @@ -458,7 +636,7 @@
>  
>  #ifdef __GNUC__			/* shut up gcc warnings */
>  	(void)&closefunc;
> -	(void)&fin;
> +	(void)&bio;
>  	(void)&fout;
>  	(void)&buf;
>  	(void)&savefile;
> @@ -487,7 +665,7 @@
>  			rval = fetch_ftp(url);
>  			goto cleanup_fetch_url;
>  		}
> -		if (urltype != HTTP_URL_T || outfile == NULL)  {
> +		if (urltype != HTTP_URL_T || urltype != HTTPS_URL_T || outfile ==
> NULL)  {
>  			warnx("Invalid URL (no file after host) `%s'", url);
>  			goto cleanup_fetch_url;
>  		}
> @@ -527,18 +705,21 @@
>  			restart_point = sb.st_size;
>  	}
>  	if (urltype == FILE_URL_T) {		/* file:// URLs */
> +		int	fd;
> +
>  		direction = "copied";
> -		fin = fopen(decodedpath, "r");
> -		if (fin == NULL) {
> +		bio = BIO_new_file(decodedpath, "r");
> +		if (!bio) {
>  			warn("Cannot open file `%s'", decodedpath);
>  			goto cleanup_fetch_url;
>  		}
> -		if (fstat(fileno(fin), &sb) == 0) {
> +		BIO_get_fd(bio, &fd);
> +		if (fstat(fd, &sb) == 0) {
>  			mtime = sb.st_mtime;
>  			filesize = sb.st_size;
>  		}
>  		if (restart_point) {
> -			if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
> +			if (BIO_seek(bio, restart_point) < 0) {
>  				warn("Can't lseek to restart `%s'",
>  				    decodedpath);
>  				goto cleanup_fetch_url;
> @@ -556,7 +737,7 @@
>  		int hasleading;
>  
>  		if (proxyenv == NULL) {
> -			if (urltype == HTTP_URL_T)
> +			if (urltype == HTTP_URL_T || urltype == HTTPS_URL_T)
>  				proxyenv = getoptionvalue("http_proxy");
>  			else if (urltype == FTP_URL_T)
>  				proxyenv = getoptionvalue("ftp_proxy");
> @@ -613,6 +794,7 @@
>  					goto cleanup_fetch_url;
>  
>  				if ((purltype != HTTP_URL_T
> +				     && purltype != HTTPS_URL_T
>  				     && purltype != FTP_URL_T) ||
>  				    EMPTYSTRING(phost) ||
>  				    (! EMPTYSTRING(ppath)
> @@ -698,7 +880,54 @@
>  			goto cleanup_fetch_url;
>  		}
>  
> -		fin = fdopen(s, "r+");
> +		bio = BIO_new(BIO_s_socket());
> +		if (!bio) {
> +			warn("Can't create BIO for socket");
> +			goto cleanup_fetch_url;
> +		}
> +		else {
> +			BIO_set_fd(bio, s, BIO_CLOSE);
> +		}
> +
> +		if (urltype == HTTPS_URL_T) {
> +			BIO*	new;
> +			SSL*	ssl;
> +
> +			ssl = SSL_new(ssl_ctx);
> +			if (!ssl) {
> +				warn("Can't create SSL for HTTPS");
> +				goto cleanup_fetch_url;
> +			}
> +			else {
> +				SSL_set_connect_state(ssl);
> +			}
> +
> +			new = BIO_new(BIO_f_ssl());
> +			if (new) {
> +				warn("Can't create BIO for SSL");
> +				goto cleanup_fetch_url;
> +			}
> +			else {
> +				BIO_set_ssl(new, ssl, BIO_CLOSE);
> +				BIO_push(new, bio);
> +				bio = new;
> +			}
> +		}
> +
> +		{
> +			BIO*	new;
> +
> +			new = BIO_new(BIO_f_buffer());
> +			if (new) {
> +				warn("Can't create buffered BIO");
> +				goto cleanup_fetch_url;
> +			}
> +			else {
> +				BIO_push(new, bio);
> +				bio = new;
> +			}
> +		}
> +
>  		/*
>  		 * Construct and send the request.
>  		 */
> @@ -713,11 +942,11 @@
>  				leading = ", ";
>  				hasleading++;
>  			}
> -			fprintf(fin, "GET %s HTTP/1.0\r\n", path);
> +			BIO_printf(bio, "GET %s HTTP/1.0\r\n", path);
>  			if (flushcache)
> -				fprintf(fin, "Pragma: no-cache\r\n");
> +				BIO_printf(bio, "Pragma: no-cache\r\n");
>  		} else {
> -			fprintf(fin, "GET %s HTTP/1.1\r\n", path);
> +			BIO_printf(bio, "GET %s HTTP/1.1\r\n", path);
>  			if (strchr(host, ':')) {
>  				char *h, *p;
>  
> @@ -730,18 +959,18 @@
>  				    (p = strchr(h, '%')) != NULL) {
>  					*p = '\0';
>  				}
> -				fprintf(fin, "Host: [%s]", h);
> +				BIO_printf(bio, "Host: [%s]", h);
>  				free(h);
>  			} else
> -				fprintf(fin, "Host: %s", host);
> +				BIO_printf(bio, "Host: %s", host);
>  			if (portnum != HTTP_PORT)
> -				fprintf(fin, ":%u", portnum);
> -			fprintf(fin, "\r\n");
> -			fprintf(fin, "Accept: */*\r\n");
> -			fprintf(fin, "Connection: close\r\n");
> +				BIO_printf(bio, ":%u", portnum);
> +			BIO_printf(bio, "\r\n");
> +			BIO_printf(bio, "Accept: */*\r\n");
> +			BIO_printf(bio, "Connection: close\r\n");
>  			if (restart_point) {
>  				fputs(leading, ttyout);
> -				fprintf(fin, "Range: bytes=" LLF "-\r\n",
> +				BIO_printf(bio, "Range: bytes=" LLF "-\r\n",
>  				    (LLT)restart_point);
>  				fprintf(ttyout, "restarting at " LLF,
>  				    (LLT)restart_point);
> @@ -749,9 +978,9 @@
>  				hasleading++;
>  			}
>  			if (flushcache)
> -				fprintf(fin, "Cache-Control: no-cache\r\n");
> +				BIO_printf(bio, "Cache-Control: no-cache\r\n");
>  		}
> -		fprintf(fin, "User-Agent: %s/%s\r\n", FTP_PRODUCT, FTP_VERSION);
> +		BIO_printf(bio, "User-Agent: %s/%s\r\n", FTP_PRODUCT,
> FTP_VERSION);
>  		if (wwwauth) {
>  			if (verbose) {
>  				fprintf(ttyout, "%swith authorization",
> @@ -759,7 +988,7 @@
>  				leading = ", ";
>  				hasleading++;
>  			}
> -			fprintf(fin, "Authorization: %s\r\n", wwwauth);
> +			BIO_printf(bio, "Authorization: %s\r\n", wwwauth);
>  		}
>  		if (proxyauth) {
>  			if (verbose) {
> @@ -768,18 +997,18 @@
>  				leading = ", ";
>  				hasleading++;
>  			}
> -			fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
> +			BIO_printf(bio, "Proxy-Authorization: %s\r\n", proxyauth);
>  		}
>  		if (verbose && hasleading)
>  			fputs(")\n", ttyout);
> -		fprintf(fin, "\r\n");
> -		if (fflush(fin) == EOF) {
> +		BIO_printf(bio, "\r\n");
> +		if (BIO_flush(bio) == EOF) {
>  			warn("Writing HTTP request");
>  			goto cleanup_fetch_url;
>  		}
>  
>  				/* Read the response */
> -		if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) {
> +		if ((buf = BIO_parseln(bio, &len, NULL, "\0\0\0", 0)) == NULL) {
>  			warn("Receiving HTTP reply");
>  			goto cleanup_fetch_url;
>  		}
> @@ -802,7 +1031,7 @@
>  				/* Read the rest of the header. */
>  		FREEPTR(buf);
>  		while (1) {
> -			if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0))
> +			if ((buf = BIO_parseln(bio, &len, NULL, "\0\0\0", 0))
>  			    == NULL) {
>  				warn("Receiving HTTP reply");
>  				goto cleanup_fetch_url;
> @@ -1114,7 +1343,7 @@
>  		chunksize = 0;
>  					/* read chunksize */
>  		if (ischunked) {
> -			if (fgets(xferbuf, bufsize, fin) == NULL) {
> +			if (BIO_gets(bio, xferbuf, bufsize) == NULL) {
>  				warnx("Unexpected EOF reading chunksize");
>  				goto cleanup_fetch_url;
>  			}
> @@ -1149,8 +1378,7 @@
>  			if (ischunked)
>  				bufrem = MIN(chunksize, bufrem);
>  			while (bufrem > 0) {
> -				len = fread(xferbuf, sizeof(char),
> -				    MIN(bufsize, bufrem), fin);
> +				len = BIO_read(bio, xferbuf, (sizeof(char) * MIN(bufsize,
> bufrem)));
>  				if (len <= 0)
>  					goto chunkdone;
>  				bytes += len;
> @@ -1188,7 +1416,7 @@
>  					/* read CRLF after chunk*/
>   chunkdone:
>  		if (ischunked) {
> -			if (fgets(xferbuf, bufsize, fin) == NULL)
> +			if (BIO_gets(bio, xferbuf, bufsize) == NULL)
>  				break;
>  			if (strcmp(xferbuf, "\r\n") != 0) {
>  				warnx("Unexpected data following chunk");
> @@ -1201,10 +1429,12 @@
>  			(void)putc('#', ttyout);
>  		(void)putc('\n', ttyout);
>  	}
> +#if	0	/* XXX: how to re-write? */
>  	if (ferror(fin)) {
>  		warn("Reading file");
>  		goto cleanup_fetch_url;
>  	}
> +#endif
>  	progressmeter(1);
>  	bytes = 0;
>  	(void)fflush(fout);
> @@ -1237,8 +1467,8 @@
>  		(void)xsignal(SIGINT, oldintr);
>  	if (oldintp)
>  		(void)xsignal(SIGPIPE, oldintp);
> -	if (fin != NULL)
> -		fclose(fin);
> +	if (!bio)
> +		BIO_free_all(bio);
>  	else if (s != -1)
>  		close(s);
>  	if (closefunc != NULL && fout != NULL)
> @@ -1651,6 +1881,7 @@
>  	 * Check for file:// and http:// URLs.
>  	 */
>  	if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
> +	    strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
>  	    strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0)
>  		return (fetch_url(url, NULL, NULL, NULL));
>  
> --- ftp_var.h.org	Tue Jul 29 14:15:05 2003
> +++ ftp_var.h	Tue Jul 29 11:40:22 2003
> @@ -172,6 +172,14 @@
>  	FEAT_max
>  };
>  
> +struct	CA_Info {
> +	char*	path;
> +	char*	rootca;
> +	char*	privatekey;
> +	char*	certificate;
> +};
> +typedef	struct CA_Info	CA_Info_t;
> +
>  
>  /*
>   * Global defines
> @@ -184,6 +192,7 @@
>  
>  #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") */
>  #ifndef	GATE_PORT
>  #define	GATE_PORT	21	/* default if ! getservbyname("ftpgate/tcp") */
>  #endif
> @@ -279,7 +288,10 @@
>  GLOBAL	sa_family_t family;	/* address family to use for connections
> */
>  GLOBAL	char	*ftpport;	/* port number to use for FTP connections */
>  GLOBAL	char	*httpport;	/* port number to use for HTTP connections */
> +GLOBAL	char	*httpsport;	/* port number to use for HTTPS connections
> */
>  GLOBAL	char	*gateport;	/* port number to use for gateftp connections
> */
> +
> +GLOBAL	CA_Info_t	CAinfo;	/* CA path, certificate and private key
> files */
>  
>  GLOBAL	char   *outfile;	/* filename to output URLs to */
>  GLOBAL	int	restartautofetch; /* restart auto-fetch */
> --- main.c.org	Tue Jul 29 14:18:18 2003
> +++ main.c	Tue Jul 29 11:42:04 2003
> @@ -129,6 +129,8 @@
>  #include <unistd.h>
>  #include <locale.h>
>  
> +#include <openssl/ssl.h>
> +
>  #define	GLOBAL		/* force GLOBAL decls in ftp_var.h to be declared */
>  #include "ftp_var.h"
>  
> @@ -137,9 +139,20 @@
>  #define	NO_PROXY	"no_proxy"	/* env var with list of non-proxied
>  					 * hosts, comma or space separated */
>  
> +GLOBAL	SSL_CTX*	ssl_ctx = (SSL_CTX*)0;
> +
>  static void	setupoption(char *, char *, char *);
>  int		main(int, char *[]);
>  
> +static
> +void
> +free_ssl_ctx(void) {
> +	if (ssl_ctx) {
> +		SSL_CTX_free(ssl_ctx);
> +		ssl_ctx = (SSL_CTX*)0;
> +	}
> +}
> +
>  int
>  main(int argc, char *argv[])
>  {
> @@ -152,6 +165,7 @@
>  
>  	ftpport = "ftp";
>  	httpport = "http";
> +	httpsport = "https";
>  	gateport = NULL;
>  	cp = getenv("FTPSERVERPORT");
>  	if (cp != NULL)
> @@ -280,6 +294,12 @@
>  		}
>  	}
>  
> +	/* set default key and CAs for SSL */
> +	CAinfo.path = getenv("SSL_CAPATH");
> +	CAinfo.rootca = getenv("SSL_ROOTCA");
> +	CAinfo.privatekey = getenv("SSL_PRIVATEKEY");
> +	CAinfo.certificate = getenv("SSL_CERTIFICATE");
> +
>  	while ((ch = getopt(argc, argv, "46AadefginN:o:pP:r:RtT:u:vV")) !=
> -1) {
>  		switch (ch) {
>  		case '4':
> @@ -490,6 +510,24 @@
>  	(void)&argc;
>  	(void)&argv;
>  #endif
> +
> +	/* initialize SSL CTX */
> +	SSL_library_init();
> +	SSL_load_error_strings();
> +
> +	OpenSSL_add_ssl_algorithms();
> +	ssl_ctx = SSL_CTX_new(SSLv23_client_method());
> +
> +	if (CAinfo.certificate) {
> +		SSL_CTX_use_certificate_chain_file(ssl_ctx, CAinfo.certificate);
> +	}
> +	if (CAinfo.privatekey) {
> +		SSL_CTX_use_PrivateKey_file(ssl_ctx, CAinfo.privatekey,
> SSL_FILETYPE_PEM);
> +	}
> +	if (CAinfo.path || CAinfo.rootca) {
> +		SSL_CTX_load_verify_locations(ssl_ctx, CAinfo.rootca,
> CAinfo.path);
> +	}
> +	atexit(free_ssl_ctx);
>  
>  	if (argc > 0) {
>  		if (isupload) {
>  

______________________________________________________________________ 
Post your free ad now! http://personals.yahoo.ca