diff -Nrup linux-ftpd-0.17.orig/ftpd/Makefile linux-ftpd-0.17/ftpd/Makefile --- linux-ftpd-0.17.orig/ftpd/Makefile 1999-12-12 13:04:58.000000000 -0500 +++ linux-ftpd-0.17/ftpd/Makefile 2005-11-06 08:19:35.000000000 -0500 @@ -1,7 +1,7 @@ include ../MCONFIG -CFLAGS+=-I../support -LIBS+=-L../support -lsupport +CFLAGS+=-I../support -DUSE_SSL -g +LIBS+=-L../support -lsupport -lssl -lcrypto OBJS=ftpd.o ftpcmd.o logutmp.o logwtmp.o popen.o diff -Nrup linux-ftpd-0.17.orig/ftpd/ftpcmd.y linux-ftpd-0.17/ftpd/ftpcmd.y --- linux-ftpd-0.17.orig/ftpd/ftpcmd.y 1999-10-08 22:32:12.000000000 -0400 +++ linux-ftpd-0.17/ftpd/ftpcmd.y 2005-11-06 08:19:35.000000000 -0500 @@ -1,3 +1,15 @@ +/* + * The modifications to support SSLeay we done by Tim Hudson + * tjh@mincom.oz.au + * + * You can do whatever you like with these patches except pretend that + * you wrote them. + * + * Email ssl-users-request@mincom.oz.au to get instructions on how to + * join the mailing list that discusses SSLeay and also these patches. + * + */ + /* * Copyright (c) 1985, 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. @@ -92,6 +104,19 @@ extern char tmpline[]; extern int portcheck; extern struct sockaddr_in his_addr; +#ifdef USE_SSL +/*#include "ssl_port.h"*/ +typedef struct ssl_st SSL; +int SSL_write(SSL *ssl,const char *buf,int num); +extern int do_ssl_start(void); +extern int ssl_secure_flag; +extern int ssl_active_flag; +extern SSL *ssl_con; +#define FFLUSH(X) (ssl_active_flag && (((X)==cin)||((X)==cout)) ? 1 : fflush((X)) ) +#define GETC(X) (ssl_active_flag && (((X)==cin)||((X)==cout)) ? ssl_getc(ssl_con) : getc((X)) ) +extern FILE *cin, *cout; +#endif /* USE_SSL */ + off_t restart_point; static int cmd_type; @@ -129,7 +154,7 @@ extern struct tab sitetab[]; STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM - UMASK IDLE CHMOD + UMASK IDLE CHMOD AUTH LEXERR @@ -138,7 +163,7 @@ extern struct tab sitetab[]; %type check_login octal_number byte_size %type struct_code mode_code type_code form_code -%type pathstring pathname password username +%type pathstring pathname password username auth_type %type host_port %start cmd_list @@ -156,13 +181,43 @@ cmd_list ; cmd - : USER SP username CRLF + : AUTH SP auth_type CRLF + { + if (!strncmp((char *) $3,"SSL",3)) { +#ifdef USE_SSL + reply(334, "AUTH SSL OK."); + + /* now do all the hard work :-) */ + do_ssl_start(); + +#else /* !USE_SSL */ + reply(504,"AUTH type not supported."); +#endif /* USE_SSL */ + } else { + reply(504,"AUTH type not supported."); + } + if ($3 != NULL) + free((char *)$3); + } + | USER SP username CRLF { +#ifdef USE_SSL + if (ssl_secure_flag && !ssl_active_flag) { + reply(504,"SSL is mandatory."); + break; + } +#endif /* USE_SSL */ user($3); free($3); } | PASS SP password CRLF { +#ifdef USE_SSL + if (ssl_secure_flag && !ssl_active_flag) { + reply(504,"SSL is mandatory."); + break; + } +#endif /* USE_SSL */ pass($3); memset($3, 0, strlen($3)); free($3); @@ -607,6 +662,10 @@ username : STRING ; +auth_type + : STRING + ; + password : /* empty */ { @@ -841,6 +900,7 @@ struct tab { }; struct tab cmdtab[] = { /* In order defined in RFC 765 */ + { "AUTH", AUTH, STR1, 1, " auth_type" }, { "USER", USER, STR1, 1, " username" }, { "PASS", PASS, ZSTR1, 1, " password" }, { "ACCT", ACCT, STR1, 0, "(specify account)" }, @@ -923,6 +983,7 @@ char * ftpd_getline(char *s, int n, FILE { int c; register char *cs; + char buf[16]; cs = s; /* tmpline may contain saved command from urgent mode interruption */ @@ -938,23 +999,34 @@ char * ftpd_getline(char *s, int n, FILE if (c == 0) tmpline[0] = '\0'; } - while ((c = getc(iop)) != EOF) { + while ((c = GETC(iop)) != EOF) { c &= 0377; if (c == IAC) { - if ((c = getc(iop)) != EOF) { + if ((c = GETC(iop)) != EOF) { c &= 0377; switch (c) { case WILL: case WONT: - c = getc(iop); - printf("%c%c%c", IAC, DONT, 0377&c); - (void) fflush(stdout); + c = GETC(iop); + sprintf(buf,"%c%c%c", IAC, DONT, 0377&c); +#ifdef USE_SSL + if (ssl_active_flag) + SSL_write(ssl_con,buf,strlen(buf)); + else +#endif /* USE_SSL */ + fwrite(buf,strlen(buf),1,stdout); + (void) FFLUSH(stdout); continue; case DO: case DONT: - c = getc(iop); - printf("%c%c%c", IAC, WONT, 0377&c); - (void) fflush(stdout); + c = GETC(iop); + sprintf(buf,"%c%c%c", IAC, WONT, 0377&c); +#ifdef USE_SSL + if (ssl_active_flag) + SSL_write(ssl_con,buf,strlen(buf)); + else +#endif /* USE_SSL */ + (void) FFLUSH(stdout); continue; case IAC: break; diff -Nrup linux-ftpd-0.17.orig/ftpd/ftpd.c linux-ftpd-0.17/ftpd/ftpd.c --- linux-ftpd-0.17.orig/ftpd/ftpd.c 2000-07-22 23:34:56.000000000 -0400 +++ linux-ftpd-0.17/ftpd/ftpd.c 2005-11-06 09:36:51.000000000 -0500 @@ -1,3 +1,15 @@ +/* + * The modifications to support SSLeay were done by Tim Hudson + * tjh@cryptsoft.com + * + * You can do whatever you like with these patches except pretend that + * you wrote them. + * + * Email ssl-users-request@lists.cryptsoft.com to get instructions on how to + * join the mailing list that discusses SSLeay and also these patches. + * + */ + /* * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. @@ -126,6 +138,41 @@ typedef unsigned int useconds_t; #include "pathnames.h" #include "extern.h" +#ifdef USE_SSL + +#include "sslapp.c" + +BIO *bio_err; +SSL *ssl_data_con; +int ssl_auto_login=0; + +static char *auth_ssl_name=NULL; +FILE *cin, *cout; + +int ssl_data_active_flag=0; + +/* for the moment this is a compile time option only --tjh */ +int ssl_encrypt_data=1; + +char ssl_file_path[1024]; /* don't look at that nasty value to the left */ + +X509 *ssl_public_cert; +RSA *ssl_private_key; + +static char *my_ssl_key_file=NULL; +static char *my_ssl_cert_file=NULL; + +#include "ssl_port.h" + +int +ssl_getc(SSL *ssl_con); +static int +ssl_putc(SSL *ssl_con,int oneint); +static int +ssl_putc_flush(SSL *ssl_con); + +#endif /* USE_SSL */ + #ifdef __STDC__ #include #else @@ -294,7 +341,7 @@ main(int argc, char *argv[], char **envp socklen_t addrlen; char *cp, line[LINE_MAX]; FILE *fd; - const char *argstr = "AdDhlMSt:T:u:UvP"; + const char *argstr = "AdDhlMSt:T:u:UvPz:"; struct hostent *hp; #ifdef __linux__ @@ -390,12 +437,78 @@ main(int argc, char *argv[], char **envp debug = 1; break; +#ifdef USE_SSL + case 'z': + if (strcmp(optarg, "debug") == 0 ) { + ssl_debug_flag=1; + } + if (strcmp(optarg, "verbose") == 0 ) { + ssl_verbose_flag=1; + } + if (strcmp(optarg, "ssl") == 0 ) { + ssl_only_flag=1; + } + if (strcmp(optarg, "secure") == 0 ) { + ssl_secure_flag=1; + } + if (strcmp(optarg, "certsok") == 0) { + ssl_certsok_flag=1; + } + if (strncmp(optarg, "verify=", strlen("verify=")) == 0 ) { + ssl_verify_flag=atoi(optarg+strlen("verify=")); + + } + if (strncmp(optarg, "cert=", strlen("cert=")) == 0 ) { + my_ssl_cert_file=optarg+strlen("cert="); + } + if (strncmp(optarg, "key=", strlen("key=")) == 0 ) { + my_ssl_key_file=optarg+strlen("key="); + } + /* we have swallowed an extra arg */ + /*argc--; + argv++;*/ + break; +#endif /* USE_SSL */ + default: warnx("unknown flag -%c ignored", optopt); break; } } +#ifdef USE_SSL + /* make sure we have access to the required certificate + * and key files now ... before we perhaps chroot and + * do the other "muck" for anon-ftp style setup ... though + * why we want to run SSL for anon I don't know + */ + + { + /* keep the macros that are common between the client + * and the server happy + */ + cin=stdin; + cout=stderr; + + /* do things the "default" way */ + if (my_ssl_cert_file==NULL) { + snprintf(ssl_file_path, sizeof(ssl_file_path), "%s/%s", X509_get_default_cert_dir(), + "ftpd.pem"); + ssl_cert_file=ssl_file_path; + } else { + ssl_cert_file=my_ssl_cert_file; + } + + if (!do_ssleay_init(1)) { + fprintf(stderr,"ftpd: SSLeay initialisation failed\n"); + fflush(stderr); + sleep(1); + exit(1); + } + + } +#endif /* USE_SSL */ + (void) freopen(_PATH_DEVNULL, "w", stderr); /* @@ -552,16 +665,16 @@ main(int argc, char *argv[], char **envp /* Make sure hostname is fully qualified. */ hp = gethostbyname(hostname); if (hp != NULL) - strcpy(hostname, hp->h_name); + strncpy(hostname, hp->h_name, sizeof(hostname)); if (multihome) { hp = gethostbyaddr((char *) &ctrl_addr.sin_addr, sizeof (struct in_addr), AF_INET); if (hp != NULL) { - strcpy(dhostname, hp->h_name); + strncpy(dhostname, hp->h_name, sizeof(dhostname)); } else { /* Default. */ - strcpy(dhostname, inet_ntoa(ctrl_addr.sin_addr)); + strncpy(dhostname, inet_ntoa(ctrl_addr.sin_addr), sizeof(dhostname)); } } @@ -648,6 +761,9 @@ static int login_attempts; /* number of static int askpasswd; /* had user command, ask for passwd */ static char curname[16]; /* current USER name */ +int +good_ssl_user(char *name); + /* * USER command. * Sets global passwd pointer pw if named account exists and is acceptable; @@ -718,6 +834,12 @@ void user(char *name) strncpy(curname, name, sizeof(curname)-1); curname[sizeof(curname)-1] = '\0'; } +#ifdef USE_SSL + if (pw && good_ssl_user(name)) { + reply(331, "Send dummy password to login."); + ssl_auto_login = 1; + } else +#endif #ifdef SKEY if (!skey_haskey(name)) { char *myskey, *skey_keyinfo __P((char *name)); @@ -797,7 +919,11 @@ void pass(char *passwd) return; } askpasswd = 0; - if (!guest) { /* "ftp" is only account allowed no password */ + if (!guest +#ifdef USE_SSL + && !ssl_auto_login +#endif + ) { /* "ftp" is only account allowed no password */ if (pw == NULL) { useconds_t us; @@ -1072,6 +1198,15 @@ void retrieve(const char *cmd, const cha (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode))); if ((cmd == 0) && stats) logxfer(name, st.st_size, start); + +#ifdef USE_SSL + if (ssl_data_active_flag && (ssl_data_con!=NULL)) { + SSL_free(ssl_data_con); + ssl_data_active_flag=0; + ssl_data_con=NULL; + } +#endif /* USE_SSL */ + (void) fclose(dout); data = -1; pdata = -1; @@ -1276,8 +1411,72 @@ static FILE * dataconn(const char *name, (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)); #endif +#ifdef USE_SSL + /* time to negotiate SSL on the data connection ... + * do this via SSL_accept (as we are still the server + * even though things are started around the other way) + * + * note: we really *must* make sure the session stuff + * is copied correctly as we cannot afford a full + * SSL negotiation for each data socket! + */ + /* TODO XXXX fill in the blanks :-) + */ + ssl_data_active_flag=0; + if (ssl_active_flag && ssl_encrypt_data) { + /* do SSL */ + + reply(150, "Opening %s mode SSL data connection for %s%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + + if (ssl_data_con!=NULL) { + SSL_free(ssl_data_con); + ssl_data_con=NULL; + } + ssl_data_con=(SSL *)SSL_new(ssl_ctx); + + /* copy session details ... */ + SSL_copy_session_id(ssl_data_con,ssl_con); + + /* for 0.5.2 - want to change the timeout value etc ... */ + + SSL_set_fd(ssl_data_con,pdata); + SSL_set_verify(ssl_data_con,ssl_verify_flag,NULL); + + /* if is "safe" to read ahead */ + /* SSL_set_read_ahead(ssl_data_con,1); */ + + if (ssl_debug_flag) + BIO_printf(bio_err,"===>START SSL_accept on DATA\n"); + + if (SSL_accept(ssl_data_con)<=0) { + static char errbuf[1024]; + + snprintf(errbuf, sizeof(errbuf), "ftpd: SSL_accept DATA error %s\n", + ERR_error_string(ERR_get_error(),NULL)); + perror_reply(425, errbuf); + /* abort time methinks ... */ + fclose(file); + return NULL; + } else { + if (ssl_debug_flag) { + BIO_printf(bio_err,"[SSL DATA Cipher %s]\n", + SSL_get_cipher(ssl_con)); + } + ssl_data_active_flag=1; + } + + if (ssl_debug_flag) + BIO_printf(bio_err,"===>DONE SSL_accept on DATA\n"); + + } else { + reply(150, "Opening %s mode data connection for %s%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + } +#else /* !USE_SSL */ reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); +#endif /* USE_SSL */ return (fdopen(pdata, mode)); } if (data >= 0) { @@ -1327,8 +1526,71 @@ static FILE * dataconn(const char *name, data = -1; return (NULL); } +#ifdef USE_SSL + /* time to negotiate SSL on the data connection ... + * do this via SSL_accept (as we are still the server + * even though things are started around the other way) + * + * note: we really *must* make sure the session stuff + * is copied correctly as we cannot afford a full + * SSL negotiation for each data socket! + */ + /* TODO XXXX fill in the blanks :-) + */ + ssl_data_active_flag=0; + if (ssl_active_flag && ssl_encrypt_data) { + /* do SSL */ + + reply(150, "Opening %s mode SSL data connection for %s%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + + if (ssl_data_con!=NULL) { + SSL_free(ssl_data_con); + ssl_data_con=NULL; + } + ssl_data_con=(SSL *)SSL_new(ssl_ctx); + + /* copy session details ... */ + SSL_copy_session_id(ssl_data_con,ssl_con); + + /* for 0.5.2 - want to change the timeout value etc ... */ + + SSL_set_fd(ssl_data_con,data); + SSL_set_verify(ssl_data_con,ssl_verify_flag,NULL); + + /* if is "safe" to read ahead */ + /* SSL_set_read_ahead(ssl_data_con,1); */ + + if (ssl_debug_flag) + BIO_printf(bio_err,"===>START SSL_accept on DATA\n"); + + if (SSL_accept(ssl_data_con)<=0) { + static char errbuf[1024]; + + snprintf(errbuf, sizeof(errbuf), "ftpd: SSL_accept DATA error %s\n", + ERR_error_string(ERR_get_error(),NULL)); + perror_reply(425, errbuf); + /* abort time methinks ... */ + fclose(file); + return NULL; + } else { + if (ssl_debug_flag) + BIO_printf(bio_err,"[SSL DATA Cipher %s]\n", + SSL_get_cipher(ssl_con)); + ssl_data_active_flag=1; + } + + if (ssl_debug_flag) + BIO_printf(bio_err,"===>DONE SSL_accept on DATA\n"); + + } else { + reply(150, "Opening %s mode data connection for %s%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + } +#else /* !USE_SSL */ reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); +#endif /* USE_SSL */ return (file); } @@ -1357,11 +1619,11 @@ static void send_data(FILE *instr, FILE if (c == '\n') { if (ferror(outstr)) goto data_err; - (void) putc('\r', outstr); + (void) DATAPUTC('\r', outstr); } - (void) putc(c, outstr); + (void) DATAPUTC(c, outstr); } - fflush(outstr); + DATAFLUSH(outstr); transflag = 0; if (ferror(instr)) goto file_err; @@ -1379,7 +1641,11 @@ static void send_data(FILE *instr, FILE netfd = fileno(outstr); filefd = fileno(instr); - if (isreg && filesize < (off_t)16 * 1024 * 1024) { + if ( +#ifdef USE_SSL + !ssl_data_active_flag && +#endif /* USE_SSL */ + isreg && filesize < (off_t)16 * 1024 * 1024) { buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, (off_t)0); if (buf==MAP_FAILED || buf==NULL) { @@ -1420,6 +1686,13 @@ oldway: /* failure is harmless */ } #endif +#ifdef USE_SSL + if (ssl_data_active_flag) { + while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && + SSL_write(ssl_data_con, buf, cnt) == cnt) + byte_count += cnt; + } else +#endif /* USE_SSL */ while ((cnt = read(filefd, buf, size)) > 0 && write(netfd, buf, cnt) == cnt) byte_count += cnt; @@ -1478,6 +1751,16 @@ static int receive_data(FILE *instr, FIL case TYPE_L: signal (SIGALRM, lostconn); +#ifdef USE_SSL + if (ssl_data_active_flag) { + while ((cnt = SSL_read(ssl_data_con,buf,sizeof buf)) > 0) { + if (write(fileno(outstr), buf, cnt) != cnt) + goto file_err; + byte_count += cnt; + } + } else +#endif /* !USE_SSL */ + { do { (void) alarm ((unsigned) timeout); cnt = read(fileno(instr), buf, sizeof(buf)); @@ -1489,6 +1772,7 @@ static int receive_data(FILE *instr, FIL byte_count += cnt; } } while (cnt > 0); + } if (cnt < 0) goto data_err; transflag = 0; @@ -1500,14 +1784,14 @@ static int receive_data(FILE *instr, FIL return (-1); case TYPE_A: - while ((c = getc(instr)) != EOF) { + while ((c = DATAGETC(instr)) != EOF) { byte_count++; if (c == '\n') bare_lfs++; while (c == '\r') { if (ferror(outstr)) goto data_err; - if ((c = getc(instr)) != '\n') { + if ((c = DATAGETC(instr)) != '\n') { (void) putc ('\r', outstr); if (c == '\0' || c == EOF) goto contin2; @@ -1644,12 +1928,33 @@ reply(int n, const char *fmt, ...) reply(int n, char *fmt, va_dcl va_alist) #endif { +#ifdef USE_SSL + char outputbuf[2048]; /* allow for a 2k command string */ +#endif /* USE_SSL */ va_list ap; #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif +#ifdef USE_SSL + /* assemble the output into a buffer, checking for length*/ + sprintf(outputbuf,"%d ",n); + vsnprintf(outputbuf+strlen(outputbuf),sizeof(outputbuf)-(strlen(outputbuf) + 3), fmt, ap); + strcat(outputbuf,"\r\n"); + + if (ssl_debug_flag) + BIO_printf(bio_err,"\n<--- %s",outputbuf); + + if (ssl_active_flag) { + SSL_write(ssl_con,outputbuf,strlen(outputbuf)); + } else { + fprintf(stdout,"%s",outputbuf); + fflush(stdout); + } + if (debug) + syslog(LOG_DEBUG, "<--- %s ", outputbuf); +#else /* !USE_SSL */ (void)printf("%d ", n); (void)vprintf(fmt, ap); (void)printf("\r\n"); @@ -1658,6 +1963,8 @@ reply(int n, char *fmt, va_dcl va_alist) syslog(LOG_DEBUG, "<--- %d ", n); vsyslog(LOG_DEBUG, fmt, ap); } +#endif /* USE_SSL */ + va_end(ap); } void @@ -1670,12 +1977,34 @@ lreply(n, fmt, va_alist) va_dcl #endif { +#ifdef USE_SSL + char outputbuf[2048]; /* allow for a 2k command string */ +#endif /* USE_SSL */ va_list ap; #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif + +#ifdef USE_SSL + /* assemble the output into a buffer */ + sprintf(outputbuf,"%d- ",n); + vsnprintf(outputbuf+strlen(outputbuf), sizeof(outputbuf)-(strlen(outputbuf) + 3), fmt, ap); + strcat(outputbuf,"\r\n"); + + if (ssl_debug_flag) + BIO_printf(bio_err,"\n<--- %s",outputbuf); + + if (ssl_active_flag) { + SSL_write(ssl_con,outputbuf,strlen(outputbuf)); + } else { + fprintf(stdout,"%s",outputbuf); + fflush(stdout); + } + if (debug) + syslog(LOG_DEBUG, "<--- %s ", outputbuf); +#else /* !USE_SSL */ (void)printf("%d- ", n); (void)vprintf(fmt, ap); (void)printf("\r\n"); @@ -1684,6 +2013,8 @@ lreply(n, fmt, va_alist) syslog(LOG_DEBUG, "<--- %d- ", n); vsyslog(LOG_DEBUG, fmt, ap); } +#endif /* USE_SSL */ + va_end(ap); } static void ack(const char *s) @@ -2046,6 +2377,7 @@ void send_file_list(const char *whichf) volatile int simple = 0; volatile int freeglob = 0; glob_t gl; + char buf[1024]; /* XXX: should the { go away if __linux__? */ if (strpbrk(whichf, "~{[*?") != NULL) { @@ -2106,8 +2438,14 @@ void send_file_list(const char *whichf) goto out; transflag++; } - fprintf(dout, "%s%s\n", dirname, + snprintf(buf, sizeof(buf), "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); +#ifdef USE_SSL + if (ssl_active_flag) + SSL_write(ssl_data_con,buf,strlen(buf)); + else +#endif /* USE_SSL */ + fwrite(buf,strlen(buf),1,dout); byte_count += strlen(dirname) + 1; continue; } else if (!S_ISDIR(st.st_mode)) @@ -2149,11 +2487,17 @@ void send_file_list(const char *whichf) transflag++; } if (nbuf[0] == '.' && nbuf[1] == '/') - fprintf(dout, "%s%s\n", &nbuf[2], + sprintf(buf, "%s%s\n", &nbuf[2], type == TYPE_A ? "\r" : ""); else - fprintf(dout, "%s%s\n", nbuf, + sprintf(buf, "%s%s\n", nbuf, type == TYPE_A ? "\r" : ""); +#ifdef USE_SSL + if (ssl_active_flag) + SSL_write(ssl_data_con,buf,strlen(buf)); + else +#endif /* USE_SSL */ + fwrite(buf,strlen(buf),1,dout); byte_count += strlen(nbuf) + 1; } } @@ -2169,6 +2513,13 @@ void send_file_list(const char *whichf) transflag = 0; if (dout != NULL) +#ifdef USE_SSL + if (ssl_data_active_flag && (ssl_data_con!=NULL)) { + SSL_free(ssl_data_con); + ssl_data_active_flag=0; + ssl_data_con=NULL; + } +#endif /* USE_SSL */ (void) fclose(dout); data = -1; pdata = -1; @@ -2252,3 +2603,223 @@ static int check_host(struct sockaddr_in } #endif /* TCPWRAPPERS */ +#ifdef USE_SSL + +static +int +verify_callback(int ok, + X509_STORE_CTX *ctx); + +int +do_ssl_start(void) +{ + static char errstr[1024]; + + if (ssl_debug_flag) + BIO_printf(bio_err,"do_ssl_start triggered\n"); + + /* do the SSL stuff now ... before we play with pty's */ + ssl_con=(SSL *)SSL_new(ssl_ctx); + + /* we are working with stdin (inetd based) by default */ + SSL_set_fd(ssl_con,0); + +#if 0 + if (SSL_use_RSAPrivateKey(ssl_con,ssl_private_key)==0) { + snprintf(errstr, sizeof(errstr), "ftpd: SSL_use_RSAPrivateKey %s",ERR_error_string(ERR_get_error(),NULL)); + perror_reply(421, errstr); + dologout(1); + } + + if (SSL_use_certificate(ssl_con,ssl_public_cert)==0) { + snprintf(errstr, sizeof(errstr), "ftpd: SSL_use_certificate %s",ERR_error_string(ERR_get_error(),NULL)); + perror_reply(421, errstr); + dologout(1); + } +#endif + + SSL_set_verify(ssl_con,ssl_verify_flag, + ssl_certsok_flag ? verify_callback : NULL); + + if (SSL_accept(ssl_con)<=0) { + snprintf(errstr, sizeof(errstr), "ftpd: SSL_accept %s",ERR_error_string(ERR_get_error(),NULL)); + + perror_reply(421, errstr); + dologout(1); + + SSL_free(ssl_con); + ssl_con=NULL; + + /* we will probably want to know this sort of stuff ... + * at least for the moment I'd like to keep track of + * who is using SSL - later I will probably make this + * just a debug option and only log after the user has + * actually connected --tjh + */ + if (logging) + syslog(LOG_NOTICE, "SSL FAILED WITH %s", remotehost); + + } else { + ssl_active_flag=1; + + if (logging) { + if (auth_ssl_name) + syslog(LOG_NOTICE, "SSL SUCCEEDED WITH %s as %s", remotehost, + auth_ssl_name); + else + syslog(LOG_NOTICE, "SSL SUCCEEDED WITH %s", remotehost); + } + } + + /* ssl_fprintf calls require that this be null to test + * for being an ssl stream + */ + if (!ssl_active_flag) { + if (ssl_con!=NULL) + SSL_free(ssl_con); + ssl_con=NULL; + } + + return 0; + +} + +/* we really shouldn't have code like this! --tjh */ +int +ssl_getc(SSL *ssl_con) +{ + char onebyte; + + if (SSL_read(ssl_con,&onebyte,1)!=1) + return -1; + else { + if (ssl_debug_flag) + BIO_printf(bio_err,"ssl_getc: SSL_read %d (%c) ",onebyte & 0xff,isprint(onebyte)?onebyte:'.'); + return onebyte & 0xff; + } +} + + +/* got back to this an implemented some rather "simple" buffering */ +static char putc_buf[BUFSIZ]; +static int putc_buf_pos=0; + +static int +ssl_putc_flush(SSL *ssl_con) +{ + if (putc_buf_pos>0) { + if (SSL_write(ssl_con,putc_buf,putc_buf_pos)!=putc_buf_pos) { + if (ssl_debug_flag) + BIO_printf(bio_err,"ssl_putc_flush: WRITE FAILED\n"); + putc_buf_pos=0; + return -1; + } + } + putc_buf_pos=0; + return 0; +} + +static int +ssl_putc(SSL *ssl_con,int oneint) +{ + char onebyte; + + onebyte = oneint & 0xff; + + /* make sure there is space */ + if (putc_buf_pos>=BUFSIZ) + if (ssl_putc_flush(ssl_con)!=0) + return EOF; + putc_buf[putc_buf_pos++]=onebyte; + + return onebyte; +} + +static +int +verify_callback(int ok, + X509_STORE_CTX *ctx) +{ + int depth,error; + X509 *xs; + + depth=ctx->error_depth; + error=ctx->error; + xs=X509_STORE_CTX_get_current_cert(ctx); + + /* + * If the verification fails, then don't remember the name. However, + * if we don't require a certificate, then return success which will + * still allow us to set up an encrypted session. + * + */ + if (!ok) { + /* If we can't verify the issuer, then don't accept the name. */ + if (depth != 0 && auth_ssl_name) { + free(auth_ssl_name); + auth_ssl_name = 0; + } + ok=ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT ? 0 : 1; + goto done; + } + if (depth == 0) + auth_ssl_name = + (char *)ONELINE_NAME(X509_get_subject_name(xs)); +done: ; + + if (ssl_debug_flag) + BIO_printf(bio_err,"verify_callback: returning %d\n",ok); + + return ok; +} + +/* return true if this auth_ssl_name is authorized to use name. */ +int +good_ssl_user(char *name) +{ + FILE *user_fp; + char buf[2048]; + + if (!auth_ssl_name) + return 0; + if (!ssl_certsok_flag) + return 0; /* can't happen */ + user_fp = fopen("/etc/ssl.users", "r"); + if (!user_fp) + return 0; + while (fgets(buf, sizeof buf, user_fp)) { + char *cp; + char *n; + + /* allow for comments in the file ... always nice + * to be able to add a little novel in files and + * also disable easily --tjh + */ + if (buf[0]=='#') + continue; + + if ((cp = strchr(buf, '\n'))) + *cp = '\0'; + cp = strchr(buf, ':'); + if (!cp) + continue; + *cp++ = '\0'; + if (strcasecmp(cp, auth_ssl_name) == 0) { + n = buf; + while (n) { + cp = strchr(n, ','); + if (cp) + *cp++ = '\0'; + if (!strcmp(name, n)) { + fclose(user_fp); + return 1; + } + n = cp; + } + } + } + fclose(user_fp); + return 0; +} + +#endif /* USE_SSL */ diff -Nrup linux-ftpd-0.17.orig/ftpd/ssl_port.h linux-ftpd-0.17/ftpd/ssl_port.h --- linux-ftpd-0.17.orig/ftpd/ssl_port.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-ftpd-0.17/ftpd/ssl_port.h 2005-11-06 08:19:35.000000000 -0500 @@ -0,0 +1,85 @@ +/* ssl_port.h - standard porting things + * + * The modifications to support SSLeay were done by Tim Hudson + * tjh@mincom.oz.au + * + * You can do whatever you like with these patches except pretend that + * you wrote them. + * + * Email ssl-users-request@mincom.oz.au to get instructions on how to + * join the mailing list that discusses SSLeay and also these patches. + * + */ + +#ifndef HEADER_SSL_PORT_H +#define HEADER_SSL_PORT_H + +#ifdef USE_SSL + +#include + +#define OLDPROTO NOPROTO +#define NOPROTO +#include +#undef NOPROTO +#define NOPROTO OLDPROTO + +#include +#include +#include + +extern SSL *ssl_con; +extern SSL_CTX *ssl_ctx; +extern int ssl_debug_flag; +extern int ssl_only_flag; +extern int ssl_active_flag; +extern int ssl_verify_flag; +extern int ssl_secure_flag; +extern int ssl_enabled; + +extern int ssl_encrypt_data; +extern SSL *ssl_data_con; +extern int ssl_data_active_flag; + +extern char *my_ssl_cert_file; +extern char *my_ssl_key_file; +extern int ssl_certsok_flag; + +extern int set_ssl_trace(SSL *s); + +extern FILE *cin, *cout; + +#define is_ssl_fd(X,Y) ( (SSL_get_fd((X))==0) || \ + (SSL_get_fd((X))==1) || \ + (SSL_get_fd((X))==pdata) || \ + (SSL_get_fd((X))==(Y)) \ + ) + +#define is_ssl_fp(X,Y) ( ( (SSL_get_fd((X))==0) && (fileno((Y))==0) ) || \ + ( (SSL_get_fd((X))==1) && (fileno((Y))==1) ) || \ + ( (SSL_get_fd((X))==pdata) && \ + (fileno((Y))==pdata) ) || \ + (SSL_get_fd((X))==fileno(Y)) \ + ) + +/* these macros make things much easier to handle ... */ + +#define FFLUSH(X) (ssl_active_flag && (((X)==cin)||((X)==cout)) ? 1 : fflush((X)) ) + +#define GETC(X) (ssl_active_flag && (((X)==cin)||((X)==cout)) ? ssl_getc(ssl_con) : getc((X)) ) + +#define DATAGETC(X) (ssl_data_active_flag && ((fileno(X)==data)||(fileno(X)==pdata)) ? ssl_getc(ssl_data_con) : getc((X)) ) +#define DATAPUTC(X,Y) (ssl_data_active_flag && ((fileno(Y)==data)||(fileno(Y)==pdata)) ? ssl_putc(ssl_data_con,(X)) : putc((X),(Y)) ) +#define DATAFLUSH(X) (ssl_data_active_flag && ((fileno(X)==data)||(fileno(X)==pdata)) ? ssl_putc_flush(ssl_data_con) : fflush((X)) ) + +#else + +#define GETC(X) getc((X)) +#define DATAGETC(X) getc((X)) +#define DATAPUTC(X,Y) putc((X),(Y)) +#define DATAFLUSH(X) fflush((X)) +#define FFLUSH(X) fflush((X)) + +#endif /* USE_SSL */ + +#endif /* HEADER_SSL_PORT_H */ diff -Nrup linux-ftpd-0.17.orig/ftpd/sslapp.c linux-ftpd-0.17/ftpd/sslapp.c --- linux-ftpd-0.17.orig/ftpd/sslapp.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-ftpd-0.17/ftpd/sslapp.c 2005-11-06 08:23:36.000000000 -0500 @@ -0,0 +1,182 @@ +/* sslapp.c - ssl application code */ + +/* + * The modifications to support SSLeay were done by Tim Hudson + * tjh@cryptsoft.com + * + * You can do whatever you like with these patches except pretend that + * you wrote them. + * + * Email ssl-users-request@lists.cryptsoft.com to get instructions on how to + * join the mailing list that discusses SSLeay and also these patches. + * + */ + +#ifdef USE_SSL + +#include "sslapp.h" + +SSL_CTX *ssl_ctx; +SSL *ssl_con; +int ssl_debug_flag=0; +int ssl_only_flag=0; +int ssl_active_flag=0; +int ssl_verify_flag=SSL_VERIFY_NONE; +int ssl_secure_flag=0; +int ssl_certsok_flag=0; +int ssl_cert_required=0; +int ssl_verbose_flag=0; +int ssl_disabled_flag=0; +char *ssl_cert_file=NULL; +char *ssl_key_file=NULL; +char *ssl_cipher_list=NULL; +char *ssl_log_file=NULL; + +/* fwd decl */ +static void +client_info_callback(SSL *s, int where, int ret); + +int +do_ssleay_init(int server) +{ + char *p; + + /* make sure we have somewhere we can log errors to */ + if (bio_err==NULL) { + if ((bio_err=BIO_new(BIO_s_file()))!=NULL) { + if (ssl_log_file==NULL) + BIO_set_fp(bio_err,stderr,BIO_NOCLOSE); + else { + if (BIO_write_filename(bio_err,ssl_log_file)<=0) { + /* not a lot we can do */ + } + } + } + } + + /* rather simple things these days ... the old SSL_LOG and SSL_ERR + * vars are long gone now SSLeay8 has rolled around and we have + * a clean interface for doing things + */ + if (ssl_debug_flag) + BIO_printf(bio_err,"SSL_DEBUG_FLAG on\r\n"); + + + /* init things so we will get meaningful error messages + * rather than numbers + */ + SSL_load_error_strings(); + + SSLeay_add_ssl_algorithms(); + ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_method()); + + /* we may require a temp 512 bit RSA key because of the + * wonderful way export things work ... if so we generate + * one now! + */ + if (server) { + if (SSL_CTX_need_tmp_RSA(ssl_ctx)) { + RSA *rsa; + + if (ssl_debug_flag) + BIO_printf(bio_err,"Generating temp (512 bit) RSA key ...\r\n"); + rsa=RSA_generate_key(512,RSA_F4,NULL,NULL); + if (ssl_debug_flag) + BIO_printf(bio_err,"Generation of temp (512 bit) RSA key done\r\n"); + + if (!SSL_CTX_set_tmp_rsa(ssl_ctx,rsa)) { + BIO_printf(bio_err,"Failed to assign generated temp RSA key!\r\n"); + } + RSA_free(rsa); + if (ssl_debug_flag) + BIO_printf(bio_err,"Assigned temp (512 bit) RSA key\r\n"); + } + } + + /* also switch on all the interoperability and bug + * workarounds so that we will communicate with people + * that cannot read poorly written specs :-) + */ + SSL_CTX_set_options(ssl_ctx,SSL_OP_ALL); + + /* the user can set whatever ciphers they want to use */ + if (ssl_cipher_list==NULL) { + p=getenv("SSL_CIPHER"); + if (p!=NULL) + SSL_CTX_set_cipher_list(ssl_ctx,p); + } else + SSL_CTX_set_cipher_list(ssl_ctx,ssl_cipher_list); + + /* for verbose we use the 0.6.x info callback that I got + * eric to finally add into the code :-) --tjh + */ + if (ssl_verbose_flag) { + SSL_CTX_set_info_callback(ssl_ctx,client_info_callback); + } + + /* Add in any certificates if you want to here ... */ + if (ssl_cert_file) { + if (!SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_file, + X509_FILETYPE_PEM)) { + BIO_printf(bio_err,"Error loading %s: ",ssl_cert_file); + ERR_print_errors(bio_err); + BIO_printf(bio_err,"\r\n"); + return(0); + } else { + if (!ssl_key_file) + ssl_key_file = ssl_cert_file; + if (!SSL_CTX_use_RSAPrivateKey_file(ssl_ctx, ssl_key_file, + X509_FILETYPE_PEM)) { + BIO_printf(bio_err,"Error loading %s: ",ssl_key_file); + ERR_print_errors(bio_err); + BIO_printf(bio_err,"\r\n"); + return(0); + } + } + } + + /* make sure we will find certificates in the standard + * location ... otherwise we don't look anywhere for + * these things which is going to make client certificate + * exchange rather useless :-) + */ + SSL_CTX_set_default_verify_paths(ssl_ctx); + + /* now create a connection */ + ssl_con=(SSL *)SSL_new(ssl_ctx); + SSL_set_verify(ssl_con,ssl_verify_flag,NULL); + + return(1); +} + + +static void +client_info_callback(SSL *s, int where, int ret) +{ + if (where==SSL_CB_CONNECT_LOOP) { + BIO_printf(bio_err,"SSL_connect:%s %s\r\n", + SSL_state_string(s),SSL_state_string_long(s)); + } else if (where==SSL_CB_CONNECT_EXIT) { + if (ret == 0) { + BIO_printf(bio_err,"SSL_connect:failed in %s %s\r\n", + SSL_state_string(s),SSL_state_string_long(s)); + } else if (ret < 0) { + BIO_printf(bio_err,"SSL_connect:error in %s %s\r\n", + SSL_state_string(s),SSL_state_string_long(s)); + } + } +} + + +#else /* !USE_SSL */ + +/* something here to stop warnings if we build without SSL support */ +static int dummy_func() +{ + int i; + + i++; +} + +#endif /* USE_SSL */ + diff -Nrup linux-ftpd-0.17.orig/ftpd/sslapp.h linux-ftpd-0.17/ftpd/sslapp.h --- linux-ftpd-0.17.orig/ftpd/sslapp.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-ftpd-0.17/ftpd/sslapp.h 2005-11-06 08:19:35.000000000 -0500 @@ -0,0 +1,63 @@ +/* sslapp.h - ssl application code */ + +/* + * The modifications to support SSLeay were done by Tim Hudson + * tjh@cryptsoft.com + * + * You can do whatever you like with these patches except pretend that + * you wrote them. + * + * Email ssl-users-request@mincom.oz.au to get instructions on how to + * join the mailing list that discusses SSLeay and also these patches. + * + */ + +#ifdef USE_SSL + +#include + +#include + +#define SSL_set_pref_cipher(c,n) SSL_set_cipher_list(c,n) +#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) + +#define OLDPROTO NOPROTO +#define NOPROTO +#include +#undef NOPROTO +#define NOPROTO OLDPROTO +#undef OLDPROTO +#include + +#include +#include +#include + +extern BIO *bio_err; +extern SSL *ssl_con; +extern SSL_CTX *ssl_ctx; +extern int ssl_debug_flag; +extern int ssl_only_flag; +extern int ssl_active_flag; +extern int ssl_verify_flag; +extern int ssl_secure_flag; +extern int ssl_verbose_flag; +extern int ssl_disabled_flag; +extern int ssl_cert_required; +extern int ssl_certsok_flag; + +extern char *ssl_log_file; +extern char *ssl_cert_file; +extern char *ssl_key_file; +extern char *ssl_cipher_list; + +/* we hide all the initialisation code in a separate file now */ +extern int do_ssleay_init(int server); + +/*extern int display_connect_details(); +extern int server_verify_callback(); +extern int client_verify_callback();*/ + +#endif /* USE_SSL */ + +