--- linux-ftpd-0.10.orig/MCONFIG
+++ linux-ftpd-0.10/MCONFIG
@@ -10,13 +10,14 @@
 # -Wredundant-decls      (there are a number of such in Linux's /usr/include)
 
 CC=gcc
-CFLAGS=-O2 -pipe $(WARNINGS) -D__USE_BSD_SIGNAL # -D_GNU_SOURCE
+#CFLAGS=-O2 -pipe $(WARNINGS) -D__USE_BSD_SIGNAL # -D_GNU_SOURCE
+CFLAGS=-g -pipe $(WARNINGS) -D__USE_BSD_SIGNAL # -D_GNU_SOURCE
 LDFLAGS=
 LIBS=-lcrypt
 YACC=bison -y
 
 # Do you use shadow passwords?
-CFLAGS+=-DUSE_SHADOW
+#CFLAGS+=-DUSE_SHADOW
 
 DAEMONMODE=755
 MANMODE=644
@@ -30,8 +31,10 @@
 USE_GLIBC=1
 
 # Support Pluggable Authentication Modules
-USE_PAM=0
+USE_PAM=1
+CFLAGS+= -DUSE_PAM
+LIBS=-lpam -ldl
 
 # Support shadow passwords
-USE_SHADOW=1
+#USE_SHADOW=1
 
--- linux-ftpd-0.10.orig/support/setproctitle.c
+++ linux-ftpd-0.10/support/setproctitle.c
@@ -139,7 +139,7 @@
 	(void) strcpy(Argv[0], buf);
 	p = &Argv[0][i];
 	while (p < LastArgv)
-		*p++ = ' ';
+		*p++ = '\0';
 	Argv[1] = NULL;
 }
 
--- linux-ftpd-0.10.orig/support/Makefile
+++ linux-ftpd-0.10/support/Makefile
@@ -1,6 +1,9 @@
 include ../MCONFIG
 
-OBJS=daemon.o setproctitle.o isexpired.o vis.o
+OBJS=daemon.o setproctitle.o vis.o
+ifeq ($(USE_SHADOW),)
+OBJS+=isexpired.c
+endif
 
 all: libsupport.a
 
--- linux-ftpd-0.10.orig/ftpd/ftpcmd.y
+++ linux-ftpd-0.10/ftpd/ftpcmd.y
@@ -64,9 +64,7 @@
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
-#if defined(__linux__) && !defined(__alpha)
-typedef int64_t quad_t;
-#endif
+#include <vis.h>
 
 #include "extern.h"
 
@@ -93,6 +91,7 @@
 static	int cmd_form;
 static	int cmd_bytesz;
 char	cbuf[512];
+char	vcbuf[4 * 512];
 char	*fromname;
 
 struct tab;
@@ -994,15 +993,15 @@
 				*cp = '\0';
 			}
 #ifdef HASSETPROCTITLE
-			if (strncasecmp(cbuf, "PASS", 4) != 0) {
-				if ((cp = strpbrk(cbuf, "\n"))) {
-					c = *cp;
-					*cp = '\0';
-					setproctitle("%s: %s", proctitle, cbuf);
-					*cp = c;
-				}
-			} else
-				setproctitle("%s: %s", proctitle, cbuf);
+			if (strncasecmp(cbuf, "PASS", 4) != 0)
+				cp2 = "\n";
+			else
+				cp2 = " \n";
+			if ((cp = strpbrk(cbuf, cp2)))
+				strvisx(vcbuf, cbuf, cp - cbuf, VIS_NOSLASH);
+			else
+				strvis(vcbuf, cbuf, VIS_NOSLASH);
+			setproctitle("%s: %s", proctitle, vcbuf);
 #endif /* HASSETPROCTITLE */
 			if ((cp = strpbrk(cbuf, " \n")))
 				cpos = cp - cbuf;
--- linux-ftpd-0.10.orig/ftpd/ftpd.c
+++ linux-ftpd-0.10/ftpd/ftpd.c
@@ -101,6 +101,11 @@
 #include "isexpired.h"
 #endif
 
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#endif
+
 #include "pathnames.h"
 #include "extern.h"
 
@@ -170,6 +175,14 @@
 char	*krbtkfile_env = NULL;
 #endif
 
+#ifdef USE_PAM
+pam_handle_t *pamh = NULL;
+int pamresult = PAM_SUCCESS;
+int pamsession = 0;
+static int start_pam(const char *username);
+static void end_pam(int);
+#endif
+
 char	*ident = NULL;
 
 
@@ -617,6 +630,10 @@
 		}
 		end_login();
 	}
+#ifdef USE_PAM
+	/* pam remembers the username */
+	end_pam(1);
+#endif
 
 	guest = 0;
 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
@@ -710,6 +727,41 @@
 	return (found);
 }
 
+#ifdef USE_PAM
+/* pam termination */
+static void
+end_pam(int killpam)
+{
+	int r;
+
+	if (!pamh)
+		return;
+
+	if (pamsession >= 2) {
+		r = pam_close_session(pamh, 0);
+		if (r != PAM_SUCCESS)
+			syslog(LOG_ERR, "PAM: close session: %s",
+				pam_strerror(pamh, r));
+		--pamsession;
+	}
+	if (pamsession >= 1) {
+		r = pam_setcred(pamh, PAM_DELETE_CRED);
+		if (r != PAM_SUCCESS)
+			syslog(LOG_ERR, "PAM: delete cred: %s",
+				pam_strerror(pamh, r));
+		--pamsession;
+	}
+	if (killpam) {
+		r = pam_end(pamh, pamresult);
+		if (r != PAM_SUCCESS)
+			syslog(LOG_ERR, "PAM: finalisation error: %s",
+				pam_strerror(pamh, r));
+		pamh = NULL;
+		pamresult = PAM_SUCCESS;
+	}
+}
+#endif /* USE_PAM */
+
 /*
  * Terminate login as previous user, if any, resetting state;
  * used when USER command is given or login fails.
@@ -725,13 +777,95 @@
 		logwtmp(ttyline, "", "");
 		if (doutmp)
 			logout(utmp.ut_line);
+#if defined(KERBEROS)
+		if (!notickets && krbtkfile_env)
+			unlink(krbtkfile_env);
+#endif
 	}
+#ifdef USE_PAM
+	end_pam(0);
+#endif
 	pw = NULL;
 	logged_in = 0;
 	guest = 0;
 	dochroot = 0;
 }
 
+#ifdef USE_PAM
+static char *pampasswd = NULL;
+
+static int ftp_conv(int num_msg, const struct pam_message **msg,
+	struct pam_response **resp, void *appdata)
+{
+	int count;
+	struct pam_response *reply = NULL;
+	char *text;
+
+	reply = (struct pam_response *)
+		calloc(num_msg, sizeof(struct pam_response));
+	if (!reply) return PAM_CONV_ERR;
+
+	for (count = 0; count < num_msg; ++count) {
+		switch (msg[count]->msg_style) {
+			case PAM_PROMPT_ECHO_OFF:
+				/* cheat with the password */
+				text = strdup(pampasswd ? pampasswd : "");
+				if (!text) goto die;
+				reply[count].resp_retcode = PAM_SUCCESS;
+				reply[count].resp = text;
+				break;
+			case PAM_TEXT_INFO:
+				/* ignored */
+				break;
+			case PAM_PROMPT_ECHO_ON:
+			case PAM_ERROR_MSG:
+			default:
+				/* some error */
+				goto die;
+		}
+	}
+	*resp = reply;
+	return PAM_SUCCESS;
+
+die:
+	for (count = 0; count < num_msg; ++count)
+		if (reply[count].resp)
+			free(reply[count].resp);
+	free(reply);
+	return PAM_CONV_ERR;
+}
+
+static struct pam_conv pamconv = {
+	ftp_conv,
+	NULL      
+};
+
+int
+start_pam(const char *username)
+{
+	pamresult = pam_start("ftp", username, &pamconv, &pamh);
+	if (pamresult != PAM_SUCCESS) {
+		pamh = NULL;
+		syslog(LOG_ERR, "PAM: initialization error: %s",
+			pam_strerror(pamh, pamresult));
+		return 0;
+	}
+	pamresult = pam_set_item(pamh, PAM_RHOST, remotehost);
+	if (pamresult != PAM_SUCCESS) {
+		syslog(LOG_ERR, "PAM: set rhost error: %s",
+			pam_strerror(pamh, pamresult));
+		return 0;
+	}
+	pamresult = pam_set_item(pamh, PAM_TTY, ttyline);
+	if (pamresult != PAM_SUCCESS) {
+		syslog(LOG_ERR, "PAM: set tty: %s",
+			pam_strerror(pamh, pamresult));
+		return 0;
+	}
+	return 1;
+}
+#endif /* USE_PAM */
+
 void
 pass(passwd)
 	char *passwd;
@@ -746,6 +880,21 @@
 		reply(503, "Login with USER first.");
 		return;
 	}
+#ifdef USE_PAM
+	if (pamsession != 0) {
+		syslog(LOG_ERR, "PAM: internal error: pamsession == %d?!",
+			pamsession);
+		reply(550, "System error.");
+		return;
+	}
+	if (!pamh) {
+		const char *login_name = pw ? pw->pw_name : "ftp";
+		if (!start_pam(login_name)) {
+			reply(550, "System error.");
+			return;
+		}
+	}
+#endif /* USE_PAM */
 	askpasswd = 0;
 	if (!guest) {		/* "ftp" is only account allowed no password */
 		if (pw == NULL) {
@@ -764,12 +913,22 @@
 			goto skip;
 		}
 #endif
+#ifdef USE_PAM
+		pampasswd = passwd;
+		pamresult = pam_authenticate(pamh, 0);
+		pampasswd = NULL;
+		if (pamresult != PAM_SUCCESS) {
+			rval = 1;
+			goto skip;
+		}
+#else /* !USE_PAM */
 		/* the strcmp does not catch null passwords! */
 		if (pw == NULL || *pw->pw_passwd == '\0' ||
 		    strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) {
 			rval = 1;	 /* failure */
 			goto skip;
 		}
+#endif /* !USE_PAM */
 		rval = 0;
 
 skip:
@@ -789,6 +948,9 @@
 				syslog(LOG_NOTICE,
 				    "repeated login failures from %s",
 				    remotehost);
+#ifdef USE_PAM
+				end_pam(1);
+#endif
 				exit(0);
 			}
 			return;
@@ -800,6 +962,33 @@
 			fatal("Out of memory");
 	}
 	login_attempts = 0;		/* this time successful */
+#ifdef USE_PAM
+	pamresult = pam_acct_mgmt(pamh, 0);
+	if (pamresult != PAM_SUCCESS) {
+		syslog(LOG_NOTICE, "PAM: acct failure: %s",
+			pam_strerror(pamh, pamresult));
+		reply(530, "Account is not usable. Contact sysadmin.");
+		return;
+	}
+	pamresult = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+	if (pamresult != PAM_SUCCESS) {
+		syslog(LOG_ERR, "PAM: set cred: %s",
+			pam_strerror(pamh, pamresult));
+		reply(550, "System error.");
+		return;
+	}
+	pamsession = 1;
+	pamresult = pam_open_session(pamh, 0);
+	if (pamresult != PAM_SUCCESS) {
+		pam_setcred(pamh, PAM_DELETE_CRED); /* silent */
+		syslog(LOG_ERR, "PAM: open session: %s",
+			pam_strerror(pamh, pamresult));
+		reply(550, "System error.");
+		pamsession = 0;
+		return;
+	}
+	pamsession = 2;
+#endif /* USE_PAM */
 #ifdef USE_SHADOW
 	switch (isexpired(spw)) {
 	  case 0: /* success */
@@ -1735,7 +1924,7 @@
 {
 	char path[MAXPATHLEN + 1];
 
-	if (getwd(path) == (char *)NULL)
+	if (getcwd(path, sizeof(path)) == (char *)NULL)
 		reply(550, "%s.", path);
 	else
 		reply(257, "\"%s\" is current directory.", path);
@@ -1800,19 +1989,10 @@
 	sigset_t allsigs;
 
 	transflag = 0;
-
-	if (logged_in) {
-		sigfillset(&allsigs);
-		sigprocmask(SIG_BLOCK, &allsigs, NULL);
-		(void) seteuid((uid_t)0);
-		logwtmp(ttyline, "", "");
-		if (doutmp)
-			logout(utmp.ut_line);
-#if defined(KERBEROS)
-		if (!notickets && krbtkfile_env)
-			unlink(krbtkfile_env);
+	end_login();
+#ifdef USE_PAM
+	end_pam(1);
 #endif
-	}
 	/* beware of flushing buffers after a SIGPIPE */
 	_exit(status);
 }
--- linux-ftpd-0.10.orig/ftpd/Makefile
+++ linux-ftpd-0.10/ftpd/Makefile
@@ -3,11 +3,6 @@
 CFLAGS+=-I../support
 LIBS+=-L../support -lsupport
 
-# for glibc use libcrypt (MD5 passwords)
-ifeq ($(USE_GLIBC),1)
-LIBS+=-lcrypt
-endif
-
 
 OBJS=ftpd.o ftpcmd.o logutmp.o logwtmp.o popen.o
 
@@ -24,10 +19,8 @@
 	$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
 
 install: ftpd
-	install -s -o root -m$(DAEMONMODE) ftpd $(INSTALLROOT)$(SBINDIR)/in.ftpd
-	install -m$(MANMODE) ftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.ftpd.8
-	install -m$(MANMODE) ftpusers.5 $(INSTALLROOT)$(MANDIR)/man5/ftpusers.5
-	ln -sf in.ftpd.8 $(INSTALLROOT)$(MANDIR)/man8/ftpd.8
+	install -s -o root -m$(DAEMONMODE) ftpd $(INSTALLROOT)$(SBINDIR)/in.pam-ftpd
+	[ "$(USE_PAM)" = 1 ] && install -o root -m644 pam.ftpd $(INSTALLROOT)/etc/pam.d/ftp
 
 clean:
 	rm -f *.o ftpcmd.c y.tab.h ftpd
--- linux-ftpd-0.10.orig/ftpd/pam.ftpd
+++ linux-ftpd-0.10/ftpd/pam.ftpd
@@ -0,0 +1,6 @@
+#%PAM-1.0
+auth       required     pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
+auth       required     pam_pwdb.so shadow md5 nullok
+auth       required     pam_shells.so
+account    required     pam_pwdb.so
+session    required     pam_pwdb.so
