Follows an SSL enabling diff to wu-ftpd-2.4.2-beta-15 from Academ
Consulting Services.  The original package can be retreived from
ftp://ftp.academ.com/pub/wu-ftpd/private/ The changes are mostly just a
copy of Tim Hudson's changes for BSD ftpd.  Note that here is only ftpd
server, you still need Tim's ftp client
(see ftp://ftp.psy.uq.oz.au/pub/Crypto/SSLapps/).  I made this patches
because wu-ftpd has many nice features missing in BSD ftpd, namely
per-directory permissions, guest users with personal `homes' and
virtual servers.
 
Patches for more recent versions of wuftpd-academ, if any, are on
ftp://ftp.average.org/pub/wuftp/
 
Note that you will need to change config and makefile sutable for your
platform by hand, adding appropriate include directory and SSL libs.
 
SSL will not work on systems without vsprintf().  If you have such
system and really need wu-ftpd+SSL, tell me, I'll fix this.
 
Eugene Crosser <crosser@average.org> 14 Nov 97.
 
=================
--- ssl_port.h.orig	Fri Nov 14 20:12:27 1997
+++ ssl_port.h	Fri Nov 14 21:02:02 1997
@@ -0,0 +1,66 @@
+/* ssl_port.h    - standard porting things 
+ *
+ * Slightly modified by Eugene Crosser to work with ssleay 0.8
+ *
+ * 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 "crypto.h"
+#include "ssl.h"
+
+#if SSLEAY_VERSION_NUMBER >= 0x0800
+#define SSLEAY8
+#endif
+
+#include <stdio.h>
+#include "x509.h"
+extern SSL *ssl_con;
+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 char *my_ssl_cert_file;
+extern char *my_ssl_key_file;
+extern int ssl_certsok_flag;
+
+#define is_ssl_fd(X,Y)    ( (SSL_get_fd((X))==0) || \
+                            (SSL_get_fd((X))==1) || \
+			    (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))==fileno(Y)) \
+			  )
+
+#define GETC(X)           (ssl_active_flag && (((X)==stdin)||((X)==stdin)) ? ssl_getc(ssl_con) : getc((X)) )
+
+#define DATAGETC(X)       (ssl_data_active_flag && (fileno(X)==data) ? ssl_getc(ssl_data_con) : getc((X)) )
+#define DATAPUTC(X,Y)     (ssl_data_active_flag && (fileno(Y)==data) ? ssl_putc(ssl_data_con,(X)) : putc((X),(Y)) )
+#define DATAFLUSH(X)      (ssl_data_active_flag && (fileno(X)==data) ? 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))
+
+#endif /* USE_SSL */
+
+#endif /*  HEADER_SSL_PORT_H */
--- src/ftpd.c.orig	Fri Sep 12 19:42:46 1997
+++ src/ftpd.c	Fri Nov 14 21:13:37 1997
@@ -1,3 +1,16 @@
+/*
+ * The modifications to support SSLeay done by Eugene Crosser
+ * <crosser@average.org> after BSD ftpd patches by Tim Hudson
+ * tjh@mincom.oz.au.  The latter were mostly just pasted in.
+ *
+ * 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, 1990 Regents of the University of California.
  * All rights reserved.
  *
@@ -84,6 +97,66 @@
 #include <errno.h>
 #include <string.h>
 
+#ifdef USE_SSL
+
+#include "rsa.h"        /* extra ... */
+/*
+#include "asn1.h"
+*/
+#include "x509.h"
+#include "pem.h"
+#include "ssl.h"
+/*
+#include "ssl_err.h"
+*/
+
+SSL *ssl_con;
+SSL_CTX *ssl_ctx;
+SSL *ssl_data_con;
+int ssl_debug_flag=0;
+int ssl_only_flag=0;
+int ssl_active_flag=0;
+int ssl_secure_flag=0;
+int ssl_verify_flag=SSL_VERIFY_NONE;
+int ssl_certsok_flag=0;
+int ssl_auto_login=0;
+
+static char *auth_ssl_name=NULL;
+
+
+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"
+
+#ifdef SSLEAY8
+BIO *bio_err=NULL;
+BIO *bio_log=NULL;
+#endif
+
+void
+#ifdef __STDC__
+pass(char *passwd);
+#else
+pass();
+#endif
+
+#else /* !USE_SSL */
+#define DATAGETC getc
+#define DATAPUTC putc
+#define DATAFLUSH fflush
+#endif /* USE_SSL */
+
 /*
  *  Arrange to use either varargs or stdargs
  *
@@ -450,7 +523,11 @@
         envp++;
     LastArgv = envp[-1] + strlen(envp[-1]);
 
+#ifdef USE_SSL
+    while ((c = getopt(argc, argv, ":aAvdlLiot:T:u:z:")) != -1) {
+#else
     while ((c = getopt(argc, argv, ":aAvdlLiot:T:u:")) != -1) {
+#endif
         switch (c) {
 
         case 'a':
@@ -514,11 +591,187 @@
             syslog(LOG_ERR, "option -%c requires an argument", optopt);
             break;
 
+#ifdef USE_SSL
+        case 'z':
+            if (strcmp(optarg, "debug") == 0 ) {
+                ssl_debug_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=");
+            }
+            break;
+#endif /* USE_SSL */
+
         default:
             syslog(LOG_ERR, "unknown option -%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
+        */
+
+        {
+            int i;
+           FILE *fp;
+            char *filename;
+
+            /* if we are not running in debug then any error
+             * stuff from SSL debug *must* not go down
+             * the socket (which 0,1,2 are all pointing to by
+             * default)
+             */
+#ifdef SSLEAY8
+            if (ssl_debug_flag) {
+               (void)freopen("ftpd.err","w",stderr);
+               bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
+               bio_log=BIO_new_file("ftpd.log","a");
+            } else {
+               bio_err=BIO_new(BIO_s_null());
+               bio_log=BIO_new(BIO_s_null());
+            }
+            BIO_printf(bio_err,"SSL_ERR started\n");
+            BIO_printf(bio_log,"SSL_LOG started\n");
+#else
+            if (ssl_debug_flag) {
+               (void)freopen("ftpd.err","w",stderr);
+               SSL_ERR=stderr;
+               SSL_LOG=NULL;
+                SSL_debug("ftpd.log");
+               if (SSL_LOG==NULL) {
+                   SSL_LOG=fopen("ftpd.log","w");
+                   if (SSL_LOG==NULL)
+                       SSL_LOG=stderr;
+               }
+               if (SSL_LOG!=NULL) {
+                   fprintf(SSL_LOG,"SSL_LOG started\n");
+                   fflush(SSL_LOG);
+               }
+               if (SSL_ERR!=NULL) {
+                   fprintf(SSL_ERR,"SSL_ERR started\n");
+                   fflush(SSL_ERR);
+               }
+            } else {
+                /* disable all the debug and trace */
+               SSL_LOG=SSL_ERR=NULL; /**/
+            }
+#endif
+
+           SSL_load_error_strings();
+#ifdef SSLEAY8
+           SSLeay_add_ssl_algorithms();
+#endif
+
+#ifdef SSLEAY8
+           ssl_ctx=SSL_CTX_new(SSLv23_server_method());
+#else
+           ssl_ctx=SSL_CTX_new();
+#endif
+
+           /* I really should syslog any of the following
+            * errors but I haven't bothered at this stage
+            * as that can wait
+            */
+#ifdef SSLEAY8
+            if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {
+               fprintf(stderr,"ftpd: cannot set default path via SSL_CTX_set_default_verify_paths\n");
+#else
+            if (!X509_set_default_verify_paths(ssl_ctx->cert)) {
+               fprintf(stderr,"ftpd: cannot set default path via X509_set_default_verify_paths\n");
+#endif
+               fflush(stderr);
+               sleep(1);
+               exit(1);
+            }
+
+#if 0
+            sprintf(ssl_file_path,"%s/%s",X509_get_default_cert_dir(),
+                        "ftpd.cert");
+#endif
+
+            sprintf(ssl_file_path,"%s/%s",X509_get_default_cert_dir(),
+                        "ftpd.pem");
+
+           filename=my_ssl_cert_file==NULL?ssl_file_path:my_ssl_cert_file;
+           fp=fopen(filename,"r");
+           if (fp==NULL) {
+               fprintf(stderr,"ftpd: cannot open public cert file \"%s\"\n",filename);
+               fflush(stderr);
+               exit(1);
+           }
+
+           ssl_public_cert=X509_new();
+           if (PEM_read_X509(fp,&ssl_public_cert,NULL)==NULL) {
+               fprintf(stderr,"ftpd: error reading public cert - %s\n",
+                                ERR_error_string(ERR_get_error(),NULL));
+               fflush(stderr);
+               exit(1);
+           }
+           fclose(fp);
+
+#ifdef SSLEAY8
+           BIO_printf(bio_log,"ftpd: got public cert\n");
+#else
+           if (ssl_debug_flag) {
+               fprintf(SSL_LOG,"ftpd: got public cert\n");
+               fflush(SSL_LOG);
+           }
+#endif
+
+#if 0
+            sprintf(ssl_file_path,"%s/private/%s",X509_get_default_cert_area(),
+                        "ftpd.key");
+#endif
+
+            sprintf(ssl_file_path,"%s/%s",X509_get_default_cert_dir(),
+                        "ftpd.pem");
+
+           filename=my_ssl_key_file==NULL?ssl_file_path:my_ssl_key_file;
+           fp=fopen(filename,"r");
+           if (fp==NULL) {
+               fprintf(stderr,"ftpd: cannot open private key file \"%s\"\n",filename);
+               fflush(stderr);
+               exit(1);
+           }
+
+           ssl_private_key=RSA_new();
+           if (PEM_read_RSAPrivateKey(fp,&ssl_private_key,NULL)==0) {
+               fprintf(stderr,"ftpd: error reading private key - %s\n",
+                       ERR_error_string(ERR_get_error(),NULL));
+               fflush(stderr);
+               exit(1);
+           }
+           fclose(fp);
+
+#ifdef SSLEAY8
+           BIO_printf(bio_log,"ftpd: got private key\n");
+#else
+           if (ssl_debug_flag) {
+               fprintf(SSL_LOG,"ftpd: got private key\n");
+               fflush(SSL_LOG);
+           }
+#endif
+
+        }
+#endif /* USE_SSL */
     (void) freopen(_PATH_DEVNULL, "w", stderr);
 
     /* Checking for random signals ... */
@@ -1263,19 +1516,28 @@
 	if (use_accessfile)		/* see above.  _H*/
             acl_setfunctions();
 
+#ifdef USE_SSL
+    if (pw && good_ssl_user(name)) {
+	askpasswd = 1;
+	ssl_auto_login = 1;
+	pass("xxx");
+    } else
+#endif /* USE_SSL */
+	   {
 #ifdef SKEY
 #ifdef SKEY_NAME
-    /* this is the old way, but freebsd uses it */
-    pwok = skeyaccess(name, NULL, remotehost, remoteaddr);
+	/* this is the old way, but freebsd uses it */
+	pwok = skeyaccess(name, NULL, remotehost, remoteaddr);
 #else
-    /* this is the new way */
-    pwok = skeyaccess(pw, NULL, remotehost, remoteaddr);
+	/* this is the new way */
+	pwok = skeyaccess(pw, NULL, remotehost, remoteaddr);
 #endif
-    reply(331, "%s", skey_challenge(name, pw, pwok));
+	reply(331, "%s", skey_challenge(name, pw, pwok));
 #else
-    reply(331, "Password required for %s.", name);
+	reply(331, "Password required for %s.", name);
 #endif
-    askpasswd = 1;
+	askpasswd = 1;
+    }
     /* Delay before reading passwd after first failed attempt to slow down
      * passwd-guessing programs. */
     if (login_attempts)
@@ -1411,7 +1673,11 @@
     else
         dolreplies = 1;
 /* ******** REGULAR/GUEST USER PASSWORD PROCESSING ********** */
-    if (!anonymous) {    /* "ftp" is only account allowed no password */
+    if (!anonymous
+#ifdef USE_SSL
+             && !ssl_auto_login
+#endif
+                  ) {    /* "ftp" is only account allowed no password */
         if (*passwd == '-')
             passwd++;
         *guestpw = '\0';
@@ -1900,6 +2166,13 @@
 #else
     send_data(fin, dout, BUFSIZ);
 #endif
+#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);
 
   dolog:
@@ -2386,8 +2659,89 @@
         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); /**/
+
+#ifdef SSLEAY8
+        BIO_printf(bio_log,"===>START SSL_accept on DATA\n");
+#else
+        if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+            fprintf(SSL_LOG,"===>START SSL_accept on DATA\n");
+            fflush(SSL_LOG);
+        }
+#endif
+
+        if (SSL_accept(ssl_data_con)<=0) {
+            static char errbuf[1024];
+
+            sprintf(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 {
+#ifdef SSLEAY8
+          BIO_printf(bio_log,"[SSL DATA Cipher %s]\n",SSL_get_cipher(ssl_con));
+#else
+       if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+           fprintf(SSL_LOG,"[SSL DATA Cipher %s]\n",
+                           SSL_get_cipher(ssl_con));
+           fflush(SSL_LOG);
+       }
+#endif
+       ssl_data_active_flag=1;
+        }
+
+#ifdef SSLEAY8
+        BIO_printf(bio_log,"===>DONE SSL_accept on DATA\n");
+#else
+        if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+       fprintf(SSL_LOG,"===>DONE SSL_accept on DATA\n");
+       fflush(SSL_LOG);
+        }
+#endif
+
+    } 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);
 }
 
@@ -2425,11 +2779,11 @@
             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;
@@ -2447,6 +2801,13 @@
         }
         netfd = fileno(outstr);
         filefd = fileno(instr);
+#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 */
 /* Debian fix: this seems gratuitous somehow, testing ... XXX: */
 #ifdef bogus__linux__
 	while ((cnt = read(filefd, buf, (u_int)blksize)) > 0)
@@ -2514,6 +2875,15 @@
 
     case TYPE_I:
     case TYPE_L:
+#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 */
         while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
             if (write(fileno(outstr), buf, cnt) != cnt)
                 goto file_err;
@@ -2530,14 +2900,14 @@
         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 == EOF) /* null byte fix, noid@cyborg.larc.nasa.gov */
                         goto contin2;
@@ -2697,6 +3067,10 @@
      va_dcl
 #endif
 {
+#ifdef USE_SSL
+    char outputbuf[2048];        /* allow for a 2k command string */
+    int outptr;
+#endif /* USE_SSL */
     VA_LOCAL_DECL
     
     VA_START(fmt); 
@@ -2704,6 +3078,31 @@
     if (autospout != NULL) {
         char *ptr = autospout;
 
+#ifdef USE_SSL
+        sprintf(outputbuf,"%d-", n);
+        outptr=strlen(outputbuf);
+        while (*ptr && (outptr < sizeof(outputbuf)-5)) {
+            if (*ptr == '\n') {
+                strcat(outputbuf+outptr,"\r\n");
+                outptr+=2;
+                if (*(++ptr))
+                    sprintf(outputbuf+outptr,"%03d-", n);
+                    outptr+=4;
+            } else {
+                outputbuf[outptr++] = *ptr++;
+            }
+        }
+        if (*(--ptr) != '\n') {
+            strcat(outputbuf+outptr,"\r\n");
+            outptr+=2;
+        }
+        if (ssl_active_flag) {
+            SSL_write(ssl_con,outputbuf,outptr);
+        } else {
+            fprintf(stdout,"%s",outputbuf);
+            fflush(stdout);
+        }
+#else /* !USE_SSL */
         printf("%d-", n);
         while (*ptr) {
             if (*ptr == '\n') {
@@ -2716,16 +3115,32 @@
         }
         if (*(--ptr) != '\n')
             printf("\r\n");
+#endif /* USE_SSL */
         if (autospout_free) {
             (void) free(autospout);
             autospout_free = 0;
         }
         autospout = 0;
     }
+#ifdef USE_SSL
+    sprintf(outputbuf, "%d ", n);
+    outptr=strlen(outputbuf);
+    vsprintf(outputbuf+outptr, fmt, ap);
+    outptr=strlen(outputbuf);
+    strcat(outputbuf+outptr,"\r\n");
+    outptr+=2;
+    if (ssl_active_flag) {
+        SSL_write(ssl_con,outputbuf,outptr);
+    } else {
+        fprintf(stdout,"%s",outputbuf);
+        fflush(stdout);
+    }
+#else /* !USE_SSL */
     printf("%d ", n);
     vprintf(fmt, ap);
     printf("\r\n");
     (void) fflush(stdout);
+#endif /* USE_SSL */
 
     if (debug) {
         char buf[BUFSIZ];
@@ -2749,16 +3164,35 @@
      va_dcl
 #endif
 {
+#ifdef USE_SSL
+    char outputbuf[2048];        /* allow for a 2k command string */
+    int outptr;
+#endif /* USE_SSL */
     VA_LOCAL_DECL
 
     VA_START(fmt);
 
     if (!dolreplies)
         return;
+#ifdef USE_SSL
+    sprintf(outputbuf, "%d-", n);
+    outptr=strlen(outputbuf);
+    vsprintf(outputbuf+outptr, fmt, ap);
+    outptr=strlen(outputbuf);
+    strcat(outputbuf+outptr,"\r\n");
+    outptr+=2;
+    if (ssl_active_flag) {
+        SSL_write(ssl_con,outputbuf,outptr);
+    } else {
+        fprintf(stdout,"%s",outputbuf);
+        fflush(stdout);
+    }
+#else /* !USE_SSL */
     printf("%d-", n);
     vprintf(fmt, ap);
     printf("\r\n");
     (void) fflush(stdout);
+#endif /* USE_SSL */
 
     if (debug) {
         char buf[BUFSIZ];
@@ -3488,6 +3922,15 @@
                     goto globfree;
                 transflag++;
             }
+#ifdef USE_SSL
+	    if (ssl_data_active_flag) {
+                SSL_write(ssl_data_con, dirname, strlen(dirname));
+                if (type == TYPE_A)
+                    SSL_write(ssl_data_con, "\r\n", 2);
+                else
+                    SSL_write(ssl_data_con, "\n", 1);
+            } else
+#endif
             fprintf(dout, "%s%s\n", dirname,
                     type == TYPE_A ? "\r" : "");
             byte_count += strlen(dirname) + 1;
@@ -3531,12 +3974,31 @@
                     }
                     transflag++;
                 }
-                if (nbuf[0] == '.' && nbuf[1] == '/')
+                if (nbuf[0] == '.' && nbuf[1] == '/') {
+#ifdef USE_SSL
+                    if (ssl_data_active_flag) {
+                        SSL_write(ssl_data_con, &nbuf[2], strlen(&nbuf[2]));
+                        if (type == TYPE_A)
+                            SSL_write(ssl_data_con, "\r\n", 2);
+                        else
+                            SSL_write(ssl_data_con, "\n", 1);
+                    } else
+#endif
                     fprintf(dout, "%s%s\n", &nbuf[2],
                             type == TYPE_A ? "\r" : "");
-                else
+		} else {
+#ifdef USE_SSL
+                    if (ssl_data_active_flag) {
+                        SSL_write(ssl_data_con, nbuf, strlen(nbuf));
+                        if (type == TYPE_A)
+                            SSL_write(ssl_data_con, "\r\n", 2);
+                        else
+                            SSL_write(ssl_data_con, "\n", 1);
+                    } else
+#endif
                     fprintf(dout, "%s%s\n", nbuf,
                             type == TYPE_A ? "\r" : "");
+                }
                 byte_count += strlen(nbuf) + 1;
             }
         }
@@ -3552,8 +4014,16 @@
         reply(226, "Transfer complete.");
 
     transflag = 0;
-    if (dout != NULL)
+    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;
 globfree:
@@ -3823,3 +4293,253 @@
     return -1;
 }
 #endif /* ULTRIX_AUTH */
+
+#ifdef USE_SSL
+
+static int verify_callback();
+
+do_ssl_start()
+{
+    static char errstr[1024];
+
+#ifdef SSLEAY8
+    BIO_printf(bio_log,"do_ssl_start triggered\n");
+#else
+    if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+       fprintf(SSL_LOG,"do_ssl_start triggered\n");
+       fflush(SSL_LOG);
+    }
+#endif
+
+    /* 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 (SSL_use_RSAPrivateKey(ssl_con,ssl_private_key)==0) {
+        sprintf(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) {
+        sprintf(errstr,"ftpd: SSL_use_certificate %s",ERR_error_string(ERR_get_error(),NULL));
+       perror_reply(421, errstr);
+       dologout(1);
+    }
+
+    SSL_set_verify(ssl_con,ssl_verify_flag,
+           ssl_certsok_flag ? verify_callback : NULL);
+
+    if (SSL_accept(ssl_con)<=0) {
+       sprintf(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 {
+#ifdef SSLEAY8
+       BIO_printf(bio_log,"ssl_getc: SSL_read %d (%c) ",
+			onebyte & 0xff,isprint(onebyte)?onebyte:'.');
+#else
+       if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+           fprintf(SSL_LOG,"ssl_getc: SSL_read %d (%c) ",onebyte & 0xff,isprint(onebyte)?onebyte:'.');
+           fflush(SSL_LOG);
+       }
+#endif
+       return onebyte & 0xff;
+    }
+}
+
+
+/* got back to this an implemented some rather "simple" buffering */
+static char putc_buf[BUFSIZ];
+static int putc_buf_pos=0;
+
+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) {
+#ifdef SSLEAY8
+           BIO_printf(bio_log,"ssl_putc_flush: WRITE FAILED\n");
+#else
+           if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+               fprintf(SSL_LOG,"ssl_putc_flush: WRITE FAILED\n");
+               fflush(SSL_LOG);
+           }
+#endif
+           putc_buf_pos=0;
+           return -1;
+       }
+    }
+    putc_buf_pos=0;
+    return 0;
+}
+
+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;
+}
+
+/* we really shouldn't have code like this! --tjh */
+int 
+old_ssl_putc(SSL *ssl_con,int oneint)
+{
+    char onebyte;
+
+    onebyte = oneint & 0xff;
+
+    if (SSL_write(ssl_con,&onebyte,1)!=1)
+      return -1;
+    else {
+#ifdef SSLEAY8
+       BIO_printf(bio_log,"ssl_putc: SSL_write %d (%c) ",
+                      onebyte & 0xff,isprint(onebyte)?onebyte:'.');
+#else
+       if (ssl_debug_flag && (SSL_LOG!=NULL)) {
+           fprintf(SSL_LOG,"ssl_putc: SSL_write %d (%c) ",onebyte & 0xff,isprint(onebyte)?onebyte:'.');
+           fflush(SSL_LOG);
+       }
+#endif
+       return onebyte & 0xff;
+    }
+}
+
+static int
+verify_callback(ok, xs, xi, depth, error, dummy)
+int ok;
+char *xs, *xi;
+int depth, error;
+{
+    /*
+     * 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;
+       }
+       return ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT ? 0 : 1;
+     }
+     if (depth == 0)
+       auth_ssl_name =
+#ifdef SSLEAY8
+           (char *)X509_NAME_oneline(X509_get_subject_name(xs),NULL,0);
+#else
+           (char *)X509_NAME_oneline(X509_get_subject_name(xs));
+#endif
+     return ok;
+}
+
+/* return true if this auth_ssl_name is authorized to use name. */
+int
+good_ssl_user(name)
+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 */
+
--- src/ftpcmd.y.orig	Fri Sep 12 19:42:44 1997
+++ src/ftpcmd.y	Fri Nov 14 20:12:28 1997
@@ -1,4 +1,17 @@
 /*
+ * The modifications to support SSLeay done by Eugene Crosser
+ * <crosser@average.org> after BSD ftpd patches by Tim Hudson
+ * tjh@mincom.oz.au.  The latter were mostly just pasted in.
+ *
+ * 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 Regents of the University of California.
  * All rights reserved.
  *
@@ -102,6 +115,15 @@
 extern  char    *modenames[];
 extern  char    *formnames[];
 
+#ifdef USE_SSL
+#include "ssl_port.h"
+extern int do_ssl_start();
+extern int ssl_secure_flag;
+extern int ssl_active_flag;
+#else /* !USE_SSL */
+#define GETC getc
+#endif /* USE_SSL */
+
 static	unsigned short cliport = 0;
 static  int cmd_type;
 static  int cmd_form;
@@ -127,8 +149,8 @@
 %}
 
 %token
-    A   B   C   E   F   I
-    L   N   P   R   S   T
+    cA   cB   cC   cE   cF   cI
+    cL   cN   cP   cR   cS   cT
 
     SP  CRLF    COMMA   STRING  NUMBER
 
@@ -139,6 +161,7 @@
     ABOR    DELE    CWD     LIST    NLST    SITE
     STAT    HELP    NOOP    MKD     RMD     PWD
     CDUP    STOU    SMNT    SYST    SIZE    MDTM
+    AUTH
 
     UMASK   IDLE    CHMOD   GROUP   GPASS   NEWER
     MINFO   INDEX   EXEC    ALIAS   CDPATH  GROUPS
@@ -169,12 +192,24 @@
 
 cmd:        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);
             if (log_commands) syslog(LOG_INFO, "USER %s", $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 */
             if (log_commands)
                 if (anonymous)
                     syslog(LOG_INFO, "PASS %s", $3);
@@ -280,9 +315,9 @@
             if (log_commands) syslog(LOG_INFO, "ALLO %d", $3);
             reply(202, "ALLO command ignored.");
         }
-    |   ALLO SP NUMBER SP R SP NUMBER CRLF
+    |   ALLO SP NUMBER SP cR SP NUMBER CRLF
         = {
-            if (log_commands) syslog(LOG_INFO, "ALLO %d R %d", $3, $7);
+            if (log_commands) syslog(LOG_INFO, "ALLO %d cR %d", $3, $7);
             reply(202, "ALLO command ignored.");
         }
     |   RETR check_login SP pathname CRLF
@@ -686,6 +721,21 @@
             if ($4 != NULL)
                 free($4);
         }
+    |   AUTH SP STRING CRLF
+        = {
+#ifdef USE_SSL
+            if (!strncmp((char *) $3,"SSL",3)) {
+                reply(334, "AUTH SSL OK.");
+                do_ssl_start();
+            } else {
+                reply(504,"AUTH type not supported.");
+            }
+#else /* !USE_SSL */
+            reply(500,"AUTH not supported.");
+#endif /* USE_SSL */
+            if ($3 != NULL)
+                free((char *)$3);
+        }
     |   QUIT CRLF
         = {
             if (log_commands) syslog(LOG_INFO, "QUIT");
@@ -780,85 +830,85 @@
         }
     ;
 
-form_code:  N
+form_code:  cN
     = {
         $$ = FORM_N;
     }
-    |   T
+    |   cT
     = {
         $$ = FORM_T;
     }
-    |   C
+    |   cC
     = {
         $$ = FORM_C;
     }
     ;
 
-type_code:  A
+type_code:  cA
     = {
         cmd_type = TYPE_A;
         cmd_form = FORM_N;
     }
-    |   A SP form_code
+    |   cA SP form_code
     = {
         cmd_type = TYPE_A;
         cmd_form = $3;
     }
-    |   E
+    |   cE
     = {
         cmd_type = TYPE_E;
         cmd_form = FORM_N;
     }
-    |   E SP form_code
+    |   cE SP form_code
     = {
         cmd_type = TYPE_E;
         cmd_form = $3;
     }
-    |   I
+    |   cI
     = {
         cmd_type = TYPE_I;
     }
-    |   L
+    |   cL
     = {
         cmd_type = TYPE_L;
         cmd_bytesz = NBBY;
     }
-    |   L SP byte_size
+    |   cL SP byte_size
     = {
         cmd_type = TYPE_L;
         cmd_bytesz = $3;
     }
     /* this is for a bug in the BBN ftp */
-    |   L byte_size
+    |   cL byte_size
     = {
         cmd_type = TYPE_L;
         cmd_bytesz = $2;
     }
     ;
 
-struct_code:    F
+struct_code:    cF
     = {
         $$ = STRU_F;
     }
-    |   R
+    |   cR
     = {
         $$ = STRU_R;
     }
-    |   P
+    |   cP
     = {
         $$ = STRU_P;
     }
     ;
 
-mode_code:  S
+mode_code:  cS
     = {
         $$ = MODE_S;
     }
-    |   B
+    |   cB
     = {
         $$ = MODE_B;
     }
-    |   C
+    |   cC
     = {
         $$ = MODE_C;
     }
@@ -1003,6 +1053,7 @@
     { "STOU", STOU, STR1, 1,    "<sp> file-name" },
     { "SIZE", SIZE, OSTR, 1,    "<sp> path-name" },
     { "MDTM", MDTM, OSTR, 1,    "<sp> path-name" },
+    { "AUTH", AUTH, STR1, 1,    "<sp> auth-type" },
     { NULL,   0,    0,    0,    0 }
 };
 
@@ -1069,21 +1120,21 @@
             tmpline[0] = '\0';
     }
 retry:
-    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);
+                c = GETC(iop);
                 printf("%c%c%c", IAC, DONT, 0377&c);
                 (void) fflush(stdout);
                 continue;
             case DO:
             case DONT:
-                c = getc(iop);
+                c = GETC(iop);
                 printf("%c%c%c", IAC, WONT, 0377&c);
                 (void) fflush(stdout);
                 continue;
@@ -1334,51 +1385,51 @@
 
             case 'A':
             case 'a':
-                return (A);
+                return (cA);
 
             case 'B':
             case 'b':
-                return (B);
+                return (cB);
 
             case 'C':
             case 'c':
-                return (C);
+                return (cC);
 
             case 'E':
             case 'e':
-                return (E);
+                return (cE);
 
             case 'F':
             case 'f':
-                return (F);
+                return (cF);
 
             case 'I':
             case 'i':
-                return (I);
+                return (cI);
 
             case 'L':
             case 'l':
-                return (L);
+                return (cL);
 
             case 'N':
             case 'n':
-                return (N);
+                return (cN);
 
             case 'P':
             case 'p':
-                return (P);
+                return (cP);
 
             case 'R':
             case 'r':
-                return (R);
+                return (cR);
 
             case 'S':
             case 's':
-                return (S);
+                return (cS);
 
             case 'T':
             case 't':
-                return (T);
+                return (cT);
 
             }
             break;
