diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2001-05-29 21:35:17 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2001-05-29 21:35:17 +0000 |
commit | 32fd1e2be4b3f247e2cfa555b8d7ededf35b51ed (patch) | |
tree | d581aebadf3336c555df3a0d6309383073d02ad9 /libexec | |
parent | a92715be63321e225791ef0b417a7e945b6eabbd (diff) |
use BSD authentication
Diffstat (limited to 'libexec')
-rw-r--r-- | libexec/ftpd/Makefile | 16 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.8 | 85 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.c | 254 |
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); |