summaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2001-05-29 21:35:17 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2001-05-29 21:35:17 +0000
commit32fd1e2be4b3f247e2cfa555b8d7ededf35b51ed (patch)
treed581aebadf3336c555df3a0d6309383073d02ad9 /libexec
parenta92715be63321e225791ef0b417a7e945b6eabbd (diff)
use BSD authentication
Diffstat (limited to 'libexec')
-rw-r--r--libexec/ftpd/Makefile16
-rw-r--r--libexec/ftpd/ftpd.885
-rw-r--r--libexec/ftpd/ftpd.c254
3 files changed, 193 insertions, 162 deletions
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile
index 6c39d019047..91a45ad5ed9 100644
--- a/libexec/ftpd/Makefile
+++ b/libexec/ftpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.18 2000/09/03 18:41:14 espie Exp $
+# $OpenBSD: Makefile,v 1.19 2001/05/29 21:35:16 millert Exp $
# $NetBSD: Makefile,v 1.13 1996/02/16 02:07:41 cgd Exp $
# @(#)Makefile 8.2 (Berkeley) 4/4/94
@@ -20,20 +20,6 @@ CFLAGS += -I${LSDIR}
# not really used
CPPFLAGS+=-DINET6
-.if (${SKEY:L} == "yes")
-CFLAGS+=-DSKEY
-LDADD+= -lskey
-DPADD+= ${LIBSKEY}
-.endif
-
-.if (${KERBEROS:L} == "yes")
-SRCS+= klogin.c
-.PATH: ${.CURDIR}/../../usr.bin/login
-CFLAGS+= -DKERBEROS
-LDADD+= -lkafs -lkrb -ldes
-DPADD+= ${LIBKRB} ${LIBKRB} ${LIBKAFS}
-.endif
-
.if (${TCP_WRAPPERS:L} == "yes")
CFLAGS+=-DTCPWRAPPERS
LDADD+= -lwrap
diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8
index d976dfad9e8..b4d0234f13b 100644
--- a/libexec/ftpd/ftpd.8
+++ b/libexec/ftpd/ftpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ftpd.8,v 1.39 2001/04/24 12:54:42 aaron Exp $
+.\" $OpenBSD: ftpd.8,v 1.40 2001/05/29 21:35:16 millert Exp $
.\" $NetBSD: ftpd.8,v 1.8 1996/01/14 20:55:23 thorpej Exp $
.\"
.\" Copyright (c) 1985, 1988, 1991, 1993
@@ -186,7 +186,7 @@ prints it when that directory is entered.
The ftp server currently supports the following ftp requests.
The case of the requests is ignored.
.Bl -column "Request" -offset indent
-.It Request Ta "Description"
+.It Sy Request Ta Sy Description
.It ABOR Ta "abort previous command"
.It ACCT Ta "specify account (ignored)"
.It ALLO Ta "allocate storage (vacuously)"
@@ -194,8 +194,12 @@ The case of the requests is ignored.
.It CDUP Ta "change to parent of current working directory"
.It CWD Ta "change working directory"
.It DELE Ta "delete a file"
+.It EPSV Ta "prepare for server-to-server transfer"
+.It EPRT Ta "specify data connection port"
.It HELP Ta "give help information"
.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
+.It LPSV Ta "prepare for server-to-server transfer"
+.It LPRT Ta "specify data connection port"
.It MKD Ta "make a directory"
.It MDTM Ta "show last modification time of file"
.It MODE Ta "specify data transfer" Em mode
@@ -266,25 +270,35 @@ This allows users to utilize the metacharacters
.Dq Li \&*?[]{}~ .
.Pp
.Nm
+authenticates users by using the service and type of
+.Ar ftp ,
+as defined in the
+.Pa /etc/login.conf
+file (see
+.Xr login.conf 5 ) .
+An authentication style
+may be specified by appending with a colon (
+.Do
+:
+.Dc
+)
+following the authentication style, i.e.
+.Dq joe:skey .
+The allowed authentication styles for
+.Nm
+may be explicitly specified by the
+.Dq auth-ftpd
+entry in
+.Pa /etc/login.conf .
+.Pp
+.Nm
authenticates users according to five rules.
.Pp
.Bl -enum -offset indent
.It
-The login name must be in the password data base,
-.Pa /etc/pwd.db ,
-and not have a null password.
+The login name must be in the password database and not have a null password.
In this case a password must be provided by the client before any
file operations may be performed.
-If the user has an S/Key key, the response from a successful USER
-command will include an S/Key challenge.
-The client may choose to respond with a PASS command giving either a
-standard password or an S/Key one-time password.
-The server will automatically determine which type of
-password it has been given and attempt to authenticate accordingly.
-See
-.Xr skey 1
-for more information on S/Key authentication.
-S/Key is a Trademark of Bellcore.
.It
The login name must not appear in the file
.Pa /etc/ftpusers .
@@ -319,7 +333,35 @@ to log in by specifying any password (by convention an email address for
the user should be used as the password).
.El
.Pp
-In the last case,
+Once a user is authenticated the user must be approved by any approval
+script defined (see
+.Xr login.conf ) .
+If a valid approval script (by either :approve=...: or :approve-ftp=...:
+for the user's class) is defined then it is run and must exit with a 0
+(success) status. When
+.Nm
+is running under the
+.Fl D
+flag (and debugging is not turned on) then the approval script will be
+called with at least the following variables specified via the
+.Fl v
+option (see
+.Xr login.conf 5 )
+to the approve script:
+.Bl -column "Variable" -offset indent
+.It Sy Variable Ta Sy Description
+.It FTPD_HOST Ta "The server's (virtual) hostname"
+.El
+.Pp
+For example (the line is broken to fit the page):
+.sp
+.Bd -ragged -offset indent
+.Pa /usr/libexec/auth/approve_ftpd Fl v
+FTPD_HOST=ftp.mycompany.com \\
+.Dl Ar username Ar class Ar service
+.Ed
+.Pp
+When the user logs in to the anonymous ftp account,
.Nm
takes special measures to restrict the client's access privileges.
The server performs a
@@ -366,7 +408,7 @@ These files should be mode 444.
.It Pa ~ftp/pub
Make this directory mode 555 and owned by
.Dq root .
-This is traditionally where publically accessible files are
+This is traditionally where publicly accessible files are
stored for download.
.El
.Pp
@@ -433,6 +475,10 @@ The
.Nm
daemon uses the following ftp specific parameters:
.Bl -tag -width ftp-chroot
+.It Pa auth-ftp
+The list of authentication types available to this class.
+See
+.Xr login.conf 5 .
.It Pa ftp-chroot
A boolean value.
If set, users in this class will be automatically chrooted to login
@@ -451,7 +497,7 @@ will be expanded based on the contents of the password database.
.It Pa /etc/ftpusers
list of unwelcome/restricted users
.It Pa /etc/ftpchroot
-list of normal users who should be chroot'd
+list of normal users who should be chrooted
.It Pa /etc/ftpwelcome
welcome notice
.It Pa /etc/motd
@@ -469,7 +515,10 @@ log file for anonymous downloads
.Xr ftp 1 ,
.Xr skey 1 ,
.Xr who 1 ,
+.Xr chroot 2 ,
+.Xr login.conf 5 ,
.Xr shells 5 ,
+.Xr inetd 8 ,
.Xr syslogd 8
.Sh HISTORY
The
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
index c282d1c5aa3..db3cec1d4d5 100644
--- a/libexec/ftpd/ftpd.c
+++ b/libexec/ftpd/ftpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ftpd.c,v 1.96 2001/05/11 15:34:02 art Exp $ */
+/* $OpenBSD: ftpd.c,v 1.97 2001/05/29 21:35:16 millert Exp $ */
/* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */
/*
@@ -73,7 +73,7 @@ static char copyright[] =
#if 0
static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";
#else
-static char rcsid[] = "$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $";
+static char rcsid[] = "$OpenBSD: ftpd.c,v 1.97 2001/05/29 21:35:16 millert Exp $";
#endif
#endif /* not lint */
@@ -118,15 +118,12 @@ static char rcsid[] = "$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $"
#include <unistd.h>
#include <util.h>
#include <utmp.h>
+#include <bsd_auth.h>
#if defined(TCPWRAPPERS)
#include <tcpd.h>
#endif /* TCPWRAPPERS */
-#if defined(SKEY)
-#include <skey.h>
-#endif
-
#include "pathnames.h"
#include "extern.h"
@@ -192,18 +189,14 @@ char *guestpw;
static char ttyline[20];
char *tty = ttyline; /* for klogin */
static struct utmp utmp; /* for utmp */
-static login_cap_t *lc;
+static login_cap_t *lc;
+static auth_session_t *as;
#if defined(TCPWRAPPERS)
int allow_severity = LOG_INFO;
int deny_severity = LOG_NOTICE;
#endif /* TCPWRAPPERS */
-#if defined(KERBEROS)
-int notickets = 1;
-char *krbtkfile_env = NULL;
-#endif
-
char *ident = NULL;
@@ -260,7 +253,6 @@ static void replydirname __P((const char *, const char *));
static void send_data __P((FILE *, FILE *, off_t, off_t, int));
static struct passwd *
sgetpwnam __P((char *));
-static char *sgetsave __P((char *));
static void reapchild __P((int));
static int check_host __P((struct sockaddr *));
static void usage __P((void));
@@ -639,31 +631,13 @@ static void
sigquit(signo)
int signo;
{
-
+
sigprocmask(SIG_BLOCK, &allsigs, NULL);
syslog(LOG_ERR, "got signal %s", sys_signame[signo]);
dologout(1);
}
/*
- * Helper function for sgetpwnam().
- */
-static char *
-sgetsave(s)
- char *s;
-{
- char *new = malloc((unsigned) strlen(s) + 1);
-
- if (new == NULL) {
- perror_reply(421, "Local resource failure: malloc");
- dologout(1);
- /* NOTREACHED */
- }
- (void) strcpy(new, s);
- return (new);
-}
-
-/*
* Save the result of a getpwnam. Used for USER command, since
* the data returned must not be clobbered by any other command
* (e.g., globbing).
@@ -672,28 +646,22 @@ static struct passwd *
sgetpwnam(name)
char *name;
{
- static struct passwd save;
- struct passwd *p;
-
- if ((p = getpwnam(name)) == NULL)
- return (p);
- if (save.pw_name) {
- free(save.pw_name);
- memset(save.pw_passwd, 0, strlen(save.pw_passwd));
- free(save.pw_passwd);
- free(save.pw_class);
- free(save.pw_gecos);
- free(save.pw_dir);
- free(save.pw_shell);
- }
- save = *p;
- save.pw_name = sgetsave(p->pw_name);
- save.pw_passwd = sgetsave(p->pw_passwd);
- save.pw_class = sgetsave(p->pw_class);
- save.pw_gecos = sgetsave(p->pw_gecos);
- save.pw_dir = sgetsave(p->pw_dir);
- save.pw_shell = sgetsave(p->pw_shell);
- return (&save);
+ static struct passwd *save;
+ struct passwd *pw;
+
+ if ((pw = getpwnam(name)) == NULL)
+ return (pw);
+ if (save) {
+ memset(save->pw_passwd, 0, strlen(save->pw_passwd));
+ free(save);
+ }
+ save = pw_dup(pw);
+ if (save == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ return (save);
}
static int login_attempts; /* number of failed login attempts */
@@ -715,12 +683,8 @@ void
user(name)
char *name;
{
- char *cp, *shell;
-
- if (lc) {
- login_close(lc);
- lc = NULL;
- }
+ char *cp, *shell, *style;
+ char *class = NULL;
if (logged_in) {
if (guest) {
@@ -730,9 +694,18 @@ user(name)
reply(530, "Can't change user from chroot user.");
return;
}
+ login_close(lc);
+ lc = NULL;
+ if (as) {
+ auth_close(as);
+ as = NULL;
+ }
end_login();
}
+ if ((style = strchr(name, ':')) != NULL)
+ *style++ = 0;
+
guest = 0;
if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
if (checkuser(_PATH_FTPUSERS, "ftp") ||
@@ -743,7 +716,7 @@ user(name)
askpasswd = 1;
lc = login_getclass(pw->pw_class);
reply(331,
- "Guest login ok, type your name as password.");
+ "Guest login ok, send your email address as password.");
} else
reply(530, "User %s unknown.", name);
if (!askpasswd && logging)
@@ -751,41 +724,65 @@ user(name)
"ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
return;
}
- if (anon_only && !checkuser(_PATH_FTPCHROOT, name)) {
- reply(530, "Sorry, only anonymous ftp allowed.");
- return;
- }
+ shell = _PATH_BSHELL;
if ((pw = sgetpwnam(name))) {
- if ((shell = pw->pw_shell) == NULL || *shell == 0)
- shell = _PATH_BSHELL;
+ class = pw->pw_class;
+ if (pw->pw_shell != NULL && *pw->pw_shell != '\0')
+ shell = pw->pw_shell;
while ((cp = getusershell()) != NULL)
if (strcmp(cp, shell) == 0)
break;
+ shell = cp;
endusershell();
+ }
+
+ /* Get login class; if invalid style treat like unknown user. */
+ lc = login_getclass(class);
+ if (lc && (style = login_getstyle(lc, style, "auth-ftp")) == NULL) {
+ login_close(lc);
+ lc = NULL;
+ pw = NULL;
+ }
- if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) {
+ /* Do pre-authentication setup. */
+ if (lc && ((as = auth_open()) == NULL ||
+ auth_setitem(as, AUTHV_STYLE, style) < 0 ||
+ auth_setitem(as, AUTHV_NAME, name) < 0 ||
+ auth_setitem(as, AUTHV_CLASS, class)) < 0) {
+ if (as) {
+ auth_close(as);
+ as = NULL;
+ }
+ login_close(lc);
+ lc = NULL;
+ reply(421, "Local resource failure");
+ return;
+ }
+ if (logging)
+ strlcpy(curname, name, sizeof(curname));
+
+ dochroot = (lc && login_getcapbool(lc, "ftp-chroot", 0)) ||
+ checkuser(_PATH_FTPCHROOT, name);
+ if (anon_only && !dochroot) {
+ reply(530, "Sorry, only anonymous ftp allowed.");
+ return;
+ }
+ if (pw) {
+ if ((!shell && !dochroot) || checkuser(_PATH_FTPUSERS, name)) {
reply(530, "User %s access denied.", name);
if (logging)
syslog(LOG_NOTICE,
"FTP LOGIN REFUSED FROM %s, %s",
remotehost, name);
- pw = (struct passwd *) NULL;
+ pw = NULL;
return;
}
- lc = login_getclass(pw->pw_class);
}
- if (logging)
- strlcpy(curname, name, sizeof(curname));
-#ifdef SKEY
- if (!skey_haskey(name)) {
- char *myskey, *skey_keyinfo __P((char *name));
- myskey = skey_keyinfo(name);
- reply(331, "Password [ %s ] for %s required.",
- myskey ? myskey : "error getting challenge", name);
- } else
-#endif
+ if (as != NULL && (cp = auth_challenge(as)) != NULL)
+ reply(331, cp);
+ else
reply(331, "Password required for %s.", name);
askpasswd = 1;
@@ -853,7 +850,7 @@ void
pass(passwd)
char *passwd;
{
- int rval, flags;
+ int authok, flags;
FILE *fp;
static char homedir[MAXPATHLEN];
char *dir, rootdir[MAXPATHLEN];
@@ -864,42 +861,19 @@ pass(passwd)
}
askpasswd = 0;
if (!guest) { /* "ftp" is only account allowed no password */
+ authok = 0;
if (pw == NULL) {
useconds_t us;
/* Sleep between 1 and 3 seconds to emulate a crypt. */
us = arc4random() % 3000000;
usleep(us);
- rval = 1; /* failure below */
- goto skip;
- }
-#if defined(KERBEROS)
- rval = klogin(pw, "", hostname, passwd);
- if (rval == 0)
- goto skip;
-#endif
-#ifdef SKEY
- if (skey_haskey(pw->pw_name) == 0 &&
- (skey_passcheck(pw->pw_name, passwd) != -1)) {
- rval = 0;
- goto skip;
- }
-#endif
- /* the strcmp does not catch null passwords! */
- if (strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) ||
- *pw->pw_passwd == '\0') {
- rval = 1; /* failure */
- goto skip;
+ } else {
+ authok = auth_userresponse(as, passwd, 0);
+ auth_close(as);
+ as = NULL;
}
- rval = 0;
-
-skip:
- /*
- * If rval == 1, the user failed the authentication check
- * above. If rval == 0, either Kerberos or local authentication
- * succeeded.
- */
- if (rval) {
+ if (authok == 0) {
reply(530, "Login incorrect.");
if (logging)
syslog(LOG_NOTICE,
@@ -914,11 +888,32 @@ skip:
}
return;
}
- } else {
+ } else if (lc != NULL) {
/* Save anonymous' password. */
guestpw = strdup(passwd);
if (guestpw == (char *)NULL)
fatal("Out of memory");
+
+ if ((as = auth_open()) == NULL)
+ fatal("Out of memory");
+ auth_setoption(as, "FTPD_HOST",
+ multihome ? dhostname : hostname);
+ authok = auth_approval(as, lc, pw->pw_name, "ftp");
+ auth_close(as);
+ as = NULL;
+ if (authok == 0) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN FAILED (HOST) as %s: approval failure.",
+ pw->pw_name);
+ reply(530, "Approval failure.\n");
+ exit(0);
+ }
+ } else {
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN CLASS %s MISSING for %s: approval failure.",
+ pw->pw_class, pw->pw_name);
+ reply(530, "Permission denied.\n");
+ exit(0);
}
login_attempts = 0; /* this time successful */
if (setegid((gid_t)pw->pw_gid) < 0) {
@@ -953,8 +948,6 @@ skip:
logged_in = 1;
- dochroot = login_getcapbool(lc, "ftp-chroot", 0) ||
- checkuser(_PATH_FTPCHROOT, pw->pw_name);
if ((dir = login_getcapstr(lc, "ftp-dir", NULL, NULL))) {
char *newdir;
@@ -1283,12 +1276,12 @@ getdatasock(mode)
* in heavy-load situations.
*/
on = 1;
- if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
+ if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof(on)) < 0)
syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
#endif
#ifdef SO_SNDBUF
on = 65536;
- if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof(on)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
#endif
@@ -1845,20 +1838,27 @@ reply(n, fmt, va_alist)
va_dcl
#endif
{
+ char *buf, *p, *next;
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
- (void)printf("%d ", n);
- (void)vprintf(fmt, ap);
- (void)printf("\r\n");
- (void)fflush(stdout);
- if (debug) {
- syslog(LOG_DEBUG, "<--- %d ", n);
- vsyslog(LOG_DEBUG, fmt, ap);
+ if (vasprintf(&buf, fmt, ap) == -1 || buf == NULL) {
+ printf("412 Local resource failure: malloc\r\n");
+ fflush(stdout);
+ dologout(1);
+ }
+ next = buf;
+ while ((p = strsep(&next, "\n\r"))) {
+ printf("%d%s %s\r\n", n, (next != '\0') ? "-" : "", p);
+ if (debug)
+ syslog(LOG_DEBUG, "<--- %d%s %s", n,
+ (next != '\0') ? "-" : "", p);
}
+ (void)fflush(stdout);
+ free(buf);
}
void
@@ -2019,7 +2019,7 @@ pwd()
{
char path[MAXPATHLEN];
- if (getcwd(path, sizeof path) == (char *)NULL)
+ if (getcwd(path, sizeof(path)) == NULL)
reply(550, "Can't get current directory: %s.", strerror(errno));
else
replydirname(path, "is current directory.");
@@ -2086,10 +2086,6 @@ dologout(status)
ftpdlogwtmp(ttyline, "", "");
if (doutmp)
logout(utmp.ut_line);
-#if defined(KERBEROS)
- if (!notickets && krbtkfile_env)
- unlink(krbtkfile_env);
-#endif
}
/* beware of flushing buffers after a SIGPIPE */
_exit(status);
@@ -2715,9 +2711,9 @@ logxfer(name, size, start)
if (vpw == NULL)
return;
- snprintf(path, sizeof path, "%s/%s", dir, name);
+ snprintf(path, sizeof(path), "%s/%s", dir, name);
if (realpath(path, rpath) == NULL)
- strlcpy(rpath, path, sizeof rpath);
+ strlcpy(rpath, path, sizeof(rpath));
strvis(vpath, rpath, VIS_SAFE|VIS_NOSLASH);
strvis(vremotehost, remotehost, VIS_SAFE|VIS_NOSLASH);