summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Horowitz <marc@mit.edu>1998-10-28 02:33:38 +0000
committerMarc Horowitz <marc@mit.edu>1998-10-28 02:33:38 +0000
commit49ea9cd00df2e6f4ff4e4f6117b364ca59d0e990 (patch)
tree9774b8537fea8a185305a9dfce6eceabdc35edcb
parent0a052ad343004519023cecf0664e016614134979 (diff)
downloadkrb5-marc-3des.tar.gz
merge from mainlinemarc-3des
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/marc-3des@11000 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--src/appl/gssftp/configure.in14
-rw-r--r--src/appl/gssftp/ftp/ChangeLog9
-rw-r--r--src/appl/gssftp/ftp/ftp.M77
-rw-r--r--src/appl/gssftp/ftp/main.c14
-rw-r--r--src/appl/gssftp/ftpd/ChangeLog38
-rw-r--r--src/appl/gssftp/ftpd/Makefile.in3
-rw-r--r--src/appl/gssftp/ftpd/ftpcmd.y28
-rw-r--r--src/appl/gssftp/ftpd/ftpd.M26
-rw-r--r--src/appl/gssftp/ftpd/ftpd.c478
9 files changed, 494 insertions, 193 deletions
diff --git a/src/appl/gssftp/configure.in b/src/appl/gssftp/configure.in
index 2ede0ba16..112a29748 100644
--- a/src/appl/gssftp/configure.in
+++ b/src/appl/gssftp/configure.in
@@ -52,6 +52,20 @@ AC_MSG_RESULT($krb5_cv_shadow_pwd)
if test $krb5_cv_shadow_pwd = yes; then
AC_DEFINE(HAVE_SHADOW)
fi
+AC_ARG_WITH([krb4],
+[ --without-krb4 don't include Kerberos V4 backwards compatibility
+ --with-krb4 use V4 libraries included with V5 (default)
+ --with-krb4=KRB4DIR use preinstalled V4 libraries],
+,
+withval=yes
+)dnl
+if test $withval = no; then
+ AC_MSG_RESULT(no krb4 support)
+else
+ AC_MSG_RESULT(Adding in krb4 support)
+ FTPD_LIBS="../../../krb524/libkrb524.a"
+fi
+AC_SUBST(FTPD_LIBS)
dnl
dnl
dnl
diff --git a/src/appl/gssftp/ftp/ChangeLog b/src/appl/gssftp/ftp/ChangeLog
index 49c162509..b021129b0 100644
--- a/src/appl/gssftp/ftp/ChangeLog
+++ b/src/appl/gssftp/ftp/ChangeLog
@@ -5,6 +5,15 @@
(do_auth): Try the new krb5 mech, and if that fails, try the
old one.
+1998-10-26 Geoffrey King <gjking@mit.edu>
+
+ * ftp.M: Add documentation for new ccc and cprotect commands.
+ Also, add previously omitted command line options -u and -t and
+ "passive" command to the man page.
+
+ * main.c (main): Print out a usage message instead of just
+ "unknown option."
+
Fri Oct 2 16:16:13 1998 Theodore Y. Ts'o <tytso@mit.edu>
* cmdtab.c: Update help message for passive mode so that it
diff --git a/src/appl/gssftp/ftp/ftp.M b/src/appl/gssftp/ftp/ftp.M
index 499b58777..9c890cfb1 100644
--- a/src/appl/gssftp/ftp/ftp.M
+++ b/src/appl/gssftp/ftp/ftp.M
@@ -37,7 +37,7 @@ ftp \- ARPANET file transfer program
.SH SYNOPSIS
.B ftp
[\fB\-v\fP] [\fB\-d\fP] [\fB\-i\fP] [\fB\-n\fP] [\fB\-g\fP] [\fB\-k\fP
-\fIrealm\fP] [\fB\-f\fP] [\fB\-x\fP] [\fIhost\fP]
+\fIrealm\fP] [\fB\-f\fP] [\fB\-x\fP] [\fB\-u\fP] [\fB\-t\fP] [\fIhost\fP]
.SH DESCRIPTION
.B FTP
is the user interface to the
@@ -57,8 +57,23 @@ transfer statistics.
.B \-n
Restrains
.B ftp
-from attempting ``auto-login'' upon initial connection. If
-auto-login is enabled,
+from attempting ``auto-login'' upon initial connection. If auto-login
+is enabled,
+.B ftp
+will check the
+.I .netrc
+(see below) file in the user's home directory for an entry describing an
+account on the remote machine. If no entry exists,
+.B ftp
+will prompt for the remote machine login name (default is the user
+identity on the local machine), and, if necessary, prompt for a password
+and an account with which to login.
+.TP
+.B \-u
+Restrains
+.B ftp
+from attempting ``auto-authentication'' upon initial connection. If
+auto-authentication is enabled,
.B ftp
attempts to authenticate to the
.SM FTP
@@ -68,16 +83,7 @@ command, using whichever authentication types are locally supported.
Once an authentication type is accepted, an authentication protocol
will proceed by issuing
.SM ADAT
-commands.
-.B ftp
-then will check the
-.I .netrc
-(see below) file in the user's home directory for an entry describing an
-account on the remote machine. If no entry exists,
-.B ftp
-will prompt for the remote machine login name (default is the user
-identity on the local machine), and, if necessary, prompt for a password
-and an account with which to login.
+commands. This option also disables auto-login.
.TP
.B \-i
Turns off interactive prompting during multiple file transfers.
@@ -96,8 +102,12 @@ When using Kerberos v4 authentication, gets tickets in
Causes credentials to be forwarded to the remote host.
.TP
.B \-x
-Causes the client to attempt to negotiate encryption (protection level
-`private') immediately after successfully authenticating.
+Causes the client to attempt to negotiate encryption (data and command
+protection levels ``private'') immediately after successfully
+authenticating.
+.TP
+.B \-t
+Enables packet tracing.
.SH COMMANDS
The client host with which
.B ftp
@@ -181,6 +191,15 @@ is on (default is off), remote computer file names with all letters in
upper case are written in the local directory with the letters mapped to
lower case.
.TP
+.B ccc
+Turn off integrity protection on the command channel. This command
+must be sent integrity protected, and must be proceeded by a successful
+.SM ADAT
+command. Since turning off integrity protection potentially
+allows an attacker to insert commands onto the command channel, some
+.SM FTP
+servers may refuse to honor this command.
+.TP
\fBcd\fP \fIremote-directory\fP
Change the working directory on the remote machine to
.IR remote-directory .
@@ -206,6 +225,22 @@ Terminate the
session with the remote server, and return to the command interpreter.
Any defined macros are erased.
.TP
+\fBcprotect\fP [\fIprotection-level\fP]
+Set the protection level on commands to
+.IR protection-level .
+The valid protection levels are ``clear'' for unprotected commands,
+``safe'' for commands integrity protected by
+cryptographic checksum, and ``private'' for commands
+confidentiality and integrity protected by encryption. If an
+.SM ADAT
+command succeeded, then the default command protection level is
+``safe'', otherwise the only possible level is ``clear''. If no
+level is specified, the current level is printed.
+.B cprotect clear
+is equivalent to the
+.B ccc
+command.
+.TP
.B cr
Toggle carriage return stripping during ascii type file retrieval.
Records are denoted by a carriage return/linefeed sequence during ascii
@@ -560,7 +595,7 @@ server. An optional port number may be supplied, in which case,
will attempt to contact an
.SM FTP
server at that port. If the
-.B auto-login
+.B auto-authenticate
option is on (default),
.B ftp
will attempt to authenticate to the
@@ -571,7 +606,9 @@ command, using whichever authentication types which are locally
supported. Once an authentication type is accepted, an authentication
protocol will proceed by issuing
.SM ADAT
-commands.
+commands. If the
+.B auto-login
+option is on (default),
.B ftp
will also attempt to automatically log the user in to the
.SM FTP
@@ -581,6 +618,12 @@ option is specified,
.B ftp
will forward a copy of the user's Kerberos tickets to the remote host.
.TP
+.B passive
+Toggle passive data transfer mode. In passive mode, the client initiates
+the data connection by listening on the data port. Passive mode may
+be necessary for operation from behind firewalls which do not permit
+incoming connections.
+.TP
.B private
Set the protection level on data transfers to ``private''. Data
transmissions are confidentiality and integrity protected by encryption.
diff --git a/src/appl/gssftp/ftp/main.c b/src/appl/gssftp/ftp/main.c
index 685c14758..aa6e5a4df 100644
--- a/src/appl/gssftp/ftp/main.c
+++ b/src/appl/gssftp/ftp/main.c
@@ -86,6 +86,7 @@ main(argc, argv)
int top;
struct passwd *pw = NULL;
char homedir[MAXPATHLEN];
+ char *progname = argv[0];
sp = getservbyname("ftp", "tcp");
if (sp == 0) {
@@ -147,10 +148,9 @@ main(argc, argv)
doglob = 0;
break;
-
case 'u':
- autoauth = 0;
- break;
+ autoauth = 0;
+ break;
case 'f':
forward = 1;
@@ -160,11 +160,13 @@ main(argc, argv)
autoencrypt = 1;
break;
-
default:
- fprintf(stdout,
+ fprintf(stderr,
"ftp: %c: unknown option\n", *cp);
- exit(1);
+ fprintf(stderr, "Usage: %s [-v] [-d] [-i] [-n] [-g] "
+ "[-k realm] [-f] [-x] [-u] [-t] [host]\n",
+ progname);
+ exit(1);
}
nextopt:
argc--, argv++;
diff --git a/src/appl/gssftp/ftpd/ChangeLog b/src/appl/gssftp/ftpd/ChangeLog
index 400d4834d..37681c479 100644
--- a/src/appl/gssftp/ftpd/ChangeLog
+++ b/src/appl/gssftp/ftpd/ChangeLog
@@ -1,3 +1,41 @@
+Mon Oct 26 13:46:47 1998 Dan Winship <danw@mit.edu>
+
+ * ftpd.c (main): Add -A (require authentication, but not
+ necessarily authorization) and -C (user wants local credentials).
+
+ (user): Implement -A. Reorganize code a bit. If want_creds (-C) is
+ set, require a password even if authorization succeeds.
+
+ (kpass): Add krb5 ticket-getting code too. If want_creds is set,
+ don't destroy the tickets after verifying the Kerberos password.
+
+ (pass): Check krb password before local password, so we can
+ get credentials if we need them. Ignore local password if
+ want_creds is set. In case of "too many failed login attempts",
+ exit via dologout() instead of exit() so on-disk credentials are
+ destroyed.
+
+ (auth_data): If user forwards GSSAPI creds and want_creds is set,
+ write them out to a krb5 ccache. If doing krb4 compat, convert
+ them to krb4 tickets as well. (If want_creds is not set, smile and
+ nod at the user and then destroy the creds.)
+
+ (end_login): If the user has creds on disk, destroy them.
+ (dologout): If the user has creds on disk, destroy them.
+
+ * ftpd.M: Add -A and -C.
+
+Fri Oct 23 18:18:52 1998 Theodore Y. Ts'o <tytso@mit.edu>
+
+ * ftpd.c (pass): Wait 5 seconds before returning "password
+ incorrect", and only allow three bad passwords. Then
+ return an 421 reply code before closing the connection and
+ going away.
+
+ * ftpcmd.y (cmd): Don't allow the PORT command to accept a port
+ number lower than 1024; this prevents some nasty ftp
+ "bounce attacks" to SMTP ports, etc.
+
Tue Oct 20 16:29:46 1998 Dan Winship <danw@mit.edu>
* ftpd.M: Reality check. Add -a to synopsis, document -c and -u
diff --git a/src/appl/gssftp/ftpd/Makefile.in b/src/appl/gssftp/ftpd/Makefile.in
index 41e7333bc..0988bb95e 100644
--- a/src/appl/gssftp/ftpd/Makefile.in
+++ b/src/appl/gssftp/ftpd/Makefile.in
@@ -11,6 +11,7 @@ SETENVSRC=@SETENVSRC@
SETENVOBJ=@SETENVOBJ@
LIBOBJS=@LIBOBJS@
COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+FTPD_LIBS=@FTPD_LIBS@
SRCS = $(srcdir)/ftpd.c ftpcmd.c $(srcdir)/logwtmp.c $(srcdir)/popen.c \
$(srcdir)/vers.c \
@@ -28,7 +29,7 @@ DEFINES = -DGSSAPI -DNOCONFIDENTIAL
all:: ftpd
ftpd: $(OBJS) $(GSS_DEPLIBS) $(UTIL_DEPLIB) $(KRB4COMPAT_DEPLIBS)
- $(CC_LINK) -o $@ $(OBJS) $(GSS_LIBS) $(UTIL_LIB) $(KRB4COMPAT_LIBS)
+ $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(GSS_LIBS) $(UTIL_LIB) $(KRB4COMPAT_LIBS)
clean::
$(RM) ftpd ftpcmd.c
diff --git a/src/appl/gssftp/ftpd/ftpcmd.y b/src/appl/gssftp/ftpd/ftpcmd.y
index f237bb7c0..5b75a4600 100644
--- a/src/appl/gssftp/ftpd/ftpcmd.y
+++ b/src/appl/gssftp/ftpd/ftpcmd.y
@@ -107,6 +107,8 @@ extern gss_ctx_id_t gcontext;
#endif
#endif
+static struct sockaddr_in host_port;
+
extern struct sockaddr_in data_dest;
extern int logged_in;
extern struct passwd *pw;
@@ -217,12 +219,22 @@ cmd: USER SP username CRLF
}
| PORT SP host_port CRLF
= {
- usedefault = 0;
- if (pdata >= 0) {
- (void) close(pdata);
- pdata = -1;
+ /*
+ * Don't allow a port < 1024 if we're not
+ * connecting back to the original source address
+ * This prevents nastier forms of the bounce attack.
+ */
+ if (ntohs(host_port.sin_port) < 1024)
+ reply(504, "Port number too low");
+ else {
+ data_dest = host_port;
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
}
- reply(200, "PORT command successful.");
}
| PASV check_login CRLF
= {
@@ -674,11 +686,11 @@ host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
= {
register char *a, *p;
- a = (char *)&data_dest.sin_addr;
+ a = (char *)&host_port.sin_addr;
a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
- p = (char *)&data_dest.sin_port;
+ p = (char *)&host_port.sin_port;
p[0] = $9; p[1] = $11;
- data_dest.sin_family = AF_INET;
+ host_port.sin_family = AF_INET;
}
;
diff --git a/src/appl/gssftp/ftpd/ftpd.M b/src/appl/gssftp/ftpd/ftpd.M
index cdbb69b54..30d8c18b1 100644
--- a/src/appl/gssftp/ftpd/ftpd.M
+++ b/src/appl/gssftp/ftpd/ftpd.M
@@ -39,7 +39,7 @@
Internet File Transfer Protocol server
.SH SYNOPSIS
.B ftpd
-[\fB\-a\fP] [\fB\-c\fP] [\fB\-d\fP] [\fB\-l\fP]
+[\fB\-a \fP|\fB -A\fP] [\fB\-c\fP] [\fB\-C\fP] [\fB\-d\fP] [\fB\-l\fP]
[\fB\-t\fP \fItimeout\fP] [\fB\-T\fP \fImaxtimeout\fP]
[\fB\-p\fP \fIport\fP] [\fB\-u\fP \fIumask\fP]
[\fB\-r\fP \fIrealm-file\fP] [\fB\-s\fP \fIsrvtab\fP]
@@ -55,8 +55,22 @@ specification; see
.PP
Available options:
.TP
+.B \-A
+Connections are only allowed for users who can authenticate via the
+ftp AUTH mechanism. (Anonymous ftp may also be allowed if it is
+configured.) Ftpd will ask the user for a password if one is
+required.
+.TP
.B \-a
-Only permit Kerberos-authenticated or anonymous logins.
+Connections are only allowed for users who can authenticate (via the
+ftp AUTH mechanism) and who are authorized to connect to the named
+account without a password. (Anonymous ftp may also be allowed if it is
+configured.)
+.TP
+.B \-C
+Non-anonymous users need local credentials (for example, to authenticate
+to remote fileservers), and so they should be prompted for a password
+unless they forwarded credentials as part of authentication.
.TP
.B \-c
Allow the CCC (Clear Command Channel) command to be used. This allows
@@ -95,14 +109,14 @@ Sets the umask for the ftpd process. The default value is normally 027.
\fB\-r\fP \fIrealm-file\fP
Sets the name of the
.I krb.conf
-file to use. The default value is normally
-.IR /usr/kerberos/lib/krb.conf .
+file to use. The default value is normally set by
+.IR /etc/krb5.conf .
.TP
\fB\-s\fP \fIsrvtab\fP
Sets the name of the
.I srvtab
-file to use. The default value is normally
-.IR /etc/krb5.keytab .
+file to use for Kerberos V4 authentication. The default value is normally
+.IR /etc/srvtab .
.PP
The ftp server currently supports the following ftp requests; case is
not distinguished.
diff --git a/src/appl/gssftp/ftpd/ftpd.c b/src/appl/gssftp/ftpd/ftpd.c
index 44bf8dfe5..db424a597 100644
--- a/src/appl/gssftp/ftpd/ftpd.c
+++ b/src/appl/gssftp/ftpd/ftpd.c
@@ -129,8 +129,8 @@ AUTH_DAT kdata;
KTEXT_ST ticket;
MSG_DAT msg_data;
Key_schedule schedule;
-int kerb_ok; /* Kerberos authentication and authorization succeeded */
char *keyfile;
+static char *krb4_services[] = { "ftp", "rcmd", NULL };
#endif /* KRB5_KRB4_COMPAT */
#ifdef GSSAPI
@@ -138,12 +138,17 @@ char *keyfile;
#include <gssapi/gssapi_generic.h>
gss_ctx_id_t gcontext;
gss_buffer_desc client_name;
-int gss_ok; /* GSSAPI authentication and userok authorization succeeded */
-char* gss_services[] = { "ftp", "host", 0 };
+static char *gss_services[] = { "ftp", "host", NULL };
+
+#include <krb5.h>
+krb5_context kcontext;
+krb5_ccache ccache;
#endif /* GSSAPI */
char *auth_type; /* Authentication succeeded? If so, what type? */
static char *temp_auth_type;
+int authorized; /* Auth succeeded and was accepted by krb4 or gssapi */
+int have_creds; /* User has credentials on disk */
/*
* File containing login names
@@ -177,7 +182,8 @@ int ccc_ok = 0; /* whether or not to accept cleartext commands */
int timeout = 900; /* timeout after 15 minutes of inactivity */
int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
int logging;
-int authenticate;
+int authlevel;
+int want_creds;
int guest;
int restricted;
int type;
@@ -201,6 +207,11 @@ char pathbuf[MAXPATHLEN + 1];
char hostname[MAXHOSTNAMELEN];
char remotehost[MAXHOSTNAMELEN];
+/* Defines for authlevel */
+#define AUTHLEVEL_NONE 0
+#define AUTHLEVEL_AUTHENTICATE 1
+#define AUTHLEVEL_AUTHORIZE 2
+
/*
* Timeout intervals for retrying connections
* to hosts that don't accept PORT cmds. This
@@ -266,6 +277,13 @@ main(argc, argv, envp)
LastArgv = envp[-1] + strlen(envp[-1]);
#endif /* SETPROCTITLE */
+#ifdef GSSAPI
+ krb5_init_context(&kcontext);
+#ifdef KRB5_KRB4_COMPAT
+ krb524_init_ets(kcontext);
+#endif
+#endif
+
argc--, argv++;
while (argc > 0 && *argv[0] == '-') {
for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
@@ -283,7 +301,15 @@ main(argc, argv, envp)
break;
case 'a':
- authenticate = 1;
+ authlevel = AUTHLEVEL_AUTHORIZE;
+ break;
+
+ case 'A':
+ authlevel = AUTHLEVEL_AUTHENTICATE;
+ break;
+
+ case 'C':
+ want_creds = 1;
break;
case 'c':
@@ -618,20 +644,11 @@ user(name)
{
register char *cp;
char *shell;
+ char buf[FTP_BUFSIZ];
#ifdef HAVE_GETUSERSHELL
char *getusershell();
#endif
-#ifdef PARANOID
- /*
- * Some paranoid sites may want the client to authenticate
- * before accepting the USER command.
- */
- if (!auth_type) {
- reply(530,
- "Must perform authentication before identifying USER.");
- return;
-#endif
if (logged_in) {
if (guest) {
reply(530, "Can't change user from guest login.");
@@ -640,7 +657,7 @@ user(name)
end_login();
}
- guest = 0;
+ authorized = guest = 0;
if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
if (disallowed_user("ftp") || disallowed_user("anonymous"))
reply(530, "User %s access denied.", name);
@@ -652,6 +669,17 @@ user(name)
reply(530, "User %s unknown.", name);
return;
}
+
+ /*
+ * If authentication is required, check that before anything
+ * else to avoid leaking information.
+ */
+ if (authlevel && !auth_type) {
+ reply(530,
+ "Must perform authentication before identifying USER.");
+ return;
+ }
+
if (pw = sgetpwnam(name)) {
if ((shell = pw->pw_shell) == NULL || *shell == 0)
shell = "/bin/sh";
@@ -675,71 +703,50 @@ user(name)
}
restricted = restricted_user(name);
}
+
+ if (auth_type) {
+ int result;
#ifdef GSSAPI
- if (auth_type && strcmp(auth_type, "GSSAPI") == 0) {
- char buf[FTP_BUFSIZ];
- gss_ok = ftpd_userok(&client_name, name) == 0;
- if (! gss_ok && authenticate) {
- reply(530, "User %s access denied.", name);
- if (logging)
- syslog(LOG_NOTICE,
- "FTP GSSAPI LOGIN REFUSED FROM %s, %s",
- remotehost, name);
- pw = (struct passwd *) NULL;
- return;
+ if (auth_type && strcmp(auth_type, "GSSAPI") == 0) {
+ authorized = ftpd_gss_userok(&client_name, name) == 0;
+ sprintf(buf, "GSSAPI user %s is%s authorized as %s",
+ client_name.value, authorized ? "" : " not",
+ name);
}
- sprintf(buf, "GSSAPI user %s is%s authorized as %s%s",
- client_name.value,
- gss_ok ? "" : " not",
- name, gss_ok ? "" : "; Password required.");
- /* 232 is per draft-8, but why 331 not 53z? */
- reply(gss_ok ? 232 : 331, "%s", buf);
- syslog(gss_ok ? LOG_INFO : LOG_ERR, "%s", buf);
- if (gss_ok) {
- login((char *) NULL);
- return;
- }
- } else
+#ifdef KRB5_KRB4_COMPAT
+ else
+#endif /* KRB5_KRB4_COMPAT */
#endif /* GSSAPI */
#ifdef KRB5_KRB4_COMPAT
- if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
- char buf[FTP_BUFSIZ];
- kerb_ok = kuserok(&kdata,name) == 0;
- if (! kerb_ok && authenticate) {
- reply(530, "User %s access denied.", name);
- if (logging)
- syslog(LOG_NOTICE,
- "FTP KERBEROS LOGIN REFUSED FROM %s, %s",
- remotehost, name);
- pw = (struct passwd *) NULL;
- return;
+ if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
+ authorized = kuserok(&kdata,name) == 0;
+ sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
+ kdata.pname, *kdata.pinst ? "." : "",
+ kdata.pinst, kdata.prealm,
+ authorized ? "" : " not", name);
}
- sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
- kdata.pname, *kdata.pinst ? "." : "",
- kdata.pinst, kdata.prealm,
- kerb_ok ? "" : " not",
- name, kerb_ok ? "" : "; Password required.");
- reply(kerb_ok ? 232 : 331, "%s", buf);
- syslog(kerb_ok ? LOG_INFO : LOG_ERR, "%s", buf);
- if (kerb_ok) {
- login((char *) NULL);
- return;
- }
- } else
#endif /* KRB5_KRB4_COMPAT */
- /* Other auth types go here ... */
- if (authenticate) {
- reply(530, "User %s access denied: authentication required.",
- name);
- if (logging)
- syslog(LOG_NOTICE,
- "FTP LOGIN REFUSED FROM %s, %s",
- remotehost, name);
- pw = (struct passwd *) NULL;
+
+ if (!authorized && authlevel == AUTHLEVEL_AUTHORIZE) {
+ strcat(buf, "; Access denied.");
+ result = 530;
+ pw = NULL;
+ } else if (!authorized || (want_creds && !have_creds)) {
+ strcat(buf, "; Password required.");
+ askpasswd = 1;
+ result = 331;
+ } else
+ result = 232;
+ reply(result, "%s", buf);
+ syslog(authorized ? LOG_INFO : LOG_ERR, "%s", buf);
+
+ if (result == 232)
+ login(NULL);
return;
- } else
- reply(331, "Password required for %s.", name);
+ }
+ /* User didn't authenticate and authentication wasn't required. */
+ reply(331, "Password required for %s.", name);
askpasswd = 1;
/*
@@ -814,69 +821,148 @@ end_login()
(void) krb5_seteuid((uid_t)0);
if (logged_in)
ftp_logwtmp(ttyline, "", "");
+ if (have_creds) {
+#ifdef GSSAPI
+ krb5_cc_destroy(kcontext, ccache);
+#endif
+#ifdef KRB5_KRB4_COMPAT
+ dest_tkt();
+#endif
+ have_creds = 0;
+ }
pw = NULL;
logged_in = 0;
guest = 0;
}
-#ifdef KRB5_KRB4_COMPAT
-static char *services[] = { "ftp", "rcmd", NULL };
-
kpass(name, passwd)
char *name, *passwd;
{
- char **service;
- char instance[INST_SZ];
+#ifdef GSSAPI
+ krb5_error_code code;
+ krb5_principal server, me;
+ krb5_creds my_creds;
+ krb5_timestamp now;
+#endif /* GSSAPI */
+#ifdef KRB5_KRB4_COMPAT
char realm[REALM_SZ];
- char tkt_file[20];
+#ifndef GSSAPI
+ char **service;
KTEXT_ST ticket;
AUTH_DAT authdata;
des_cblock key;
+ char instance[INST_SZ];
unsigned long faddr;
struct hostent *hp;
+#endif /* GSSAPI */
+#endif /* KRB5_KRB4_COMPAT */
+ char ccname[MAXPATHLEN];
- if (krb_get_lrealm(realm, 1) != KSUCCESS)
+#ifdef GSSAPI
+ memset((char *)&my_creds, 0, sizeof(my_creds));
+ if (krb5_parse_name(kcontext, name, &me))
+ return 0;
+ my_creds.client = me;
+
+ sprintf(ccname, "FILE:/tmp/krb5cc_ftpd%d", getpid());
+ if (krb5_cc_resolve(kcontext, ccname, &ccache))
return(0);
+ if (krb5_cc_initialize(kcontext, ccache, me))
+ return(0);
+ if (krb5_build_principal_ext(kcontext, &server,
+ krb5_princ_realm(kcontext, me)->length,
+ krb5_princ_realm(kcontext, me)->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ krb5_princ_realm(kcontext, me)->length,
+ krb5_princ_realm(kcontext, me)->data,
+ 0))
+ goto nuke_ccache;
+
+ my_creds.server = server;
+ if (krb5_timeofday(kcontext, &now))
+ goto nuke_ccache;
+ my_creds.times.starttime = 0; /* start timer when
+ request gets to KDC */
+ my_creds.times.endtime = now + 60 * 60 * 10;
+ my_creds.times.renew_till = 0;
+
+ if (krb5_get_in_tkt_with_password(kcontext, 0,
+ 0, NULL, 0 /*preauth*/,
+ passwd,
+ ccache,
+ &my_creds, 0))
+ goto nuke_ccache;
+
+ if (!want_creds) {
+ krb5_cc_destroy(kcontext, ccache);
+ return(1);
+ }
+#endif /* GSSAPI */
- strcpy(tkt_file, TKT_ROOT);
- strcat(tkt_file, "_ftpdXXXXXX");
- krb_set_tkt_string(mktemp(tkt_file));
+#ifdef KRB5_KRB4_COMPAT
+ if (krb_get_lrealm(realm, 1) != KSUCCESS)
+ goto nuke_ccache;
- (void) strncpy(instance, krb_get_phost(hostname), sizeof(instance));
+ sprintf(ccname, "%s_ftpd%d", TKT_ROOT, getpid());
+ krb_set_tkt_string(ccname);
- if ((hp = gethostbyname(instance)) == NULL)
- return(0);
+ if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, passwd))
+ goto nuke_ccache;
+
+#ifndef GSSAPI
+ /* Verify the ticket since we didn't verify the krb5 one. */
+ strncpy(instance, krb_get_phost(hostname), sizeof(instance));
+ if ((hp = gethostbyname(instance)) == NULL)
+ goto nuke_ccache;
memcpy((char *) &faddr, (char *)hp->h_addr, sizeof(faddr));
- if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, passwd)) {
- for (service = services; *service; service++)
- if (!read_service_key(*service, instance, realm, 0, keyfile, key)) {
- (void) memset(key, 0, sizeof(key));
- if (krb_mk_req(&ticket, *service, instance, realm, 33) ||
- krb_rd_req(&ticket, *service, instance, faddr, &authdata,keyfile)||
- kuserok(&authdata, name)) {
+ for (service = krb4_services; *service; service++) {
+ if (!read_service_key(*service, instance,
+ realm, 0, keyfile, key)) {
+ (void) memset(key, 0, sizeof(key));
+ if (krb_mk_req(&ticket, *service,
+ instance, realm, 33) ||
+ krb_rd_req(&ticket, *service, instance,
+ faddr, &authdata,keyfile) ||
+ kuserok(&authdata, name)) {
+ dest_tkt();
+ goto nuke_ccache;
+ } else
+ break;
+ }
+ }
+
+ if (!*service) {
dest_tkt();
- return(0);
- } else {
+ goto nuke_ccache;
+ }
+
+ if (!want_creds) {
dest_tkt();
return(1);
- }
- }
- dest_tkt();
- return(0);
}
- dest_tkt();
+#endif /* GSSAPI */
+#endif /* KRB5_KRB4_COMPAT */
+
+#if defined(GSSAPI) || defined(KRB5_KRB4_COMPAT)
+ have_creds = 1;
return(1);
+#endif /* GSSAPI || KRB5_KRB4_COMPAT */
+
+nuke_ccache:
+#ifdef GSSAPI
+ krb5_cc_destroy(kcontext, ccache);
+#endif /* GSSAPI */
+ return(0);
}
-#endif /* KRB5_KRB4_COMPAT */
pass(passwd)
char *passwd;
{
char *xpasswd, *salt;
- if (auth_ok()) {
+ if (authorized && !want_creds) {
reply(202, "PASS command superfluous.");
return;
}
@@ -886,7 +972,7 @@ pass(passwd)
return;
}
- if (!auth_ok() && !guest) {
+ if (!guest) {
/* "ftp" is only account allowed no password */
if (pw == NULL)
salt = "xx";
@@ -898,26 +984,26 @@ pass(passwd)
#else
xpasswd = crypt(passwd, salt);
#endif
-#ifdef KRB5_KRB4_COMPAT
- /* null pw_passwd ok if Kerberos password ok */
- if (pw == NULL ||
- (*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
- !kpass(pw->pw_name, passwd)) ||
- (!*pw->pw_passwd && !kpass(pw->pw_name, passwd)))
-#else
- /* The strcmp does not catch null passwords! */
- if (pw == NULL || *pw->pw_passwd == '\0' ||
- strcmp(xpasswd, pw->pw_passwd))
-#endif /* KRB5_KRB4_COMPAT */
- {
- reply(530, "Login incorrect.");
+ /* Fail if:
+ * pw is NULL
+ * kpass fails and we want_creds
+ * kpass fails and the user has no local password
+ * kpass fails and the provided password doesn't match pw
+ */
+ if (pw == NULL || (!kpass(pw->pw_name, passwd) &&
+ (want_creds || !*pw->pw_passwd ||
+ strcmp(xpasswd, pw->pw_passwd)))) {
pw = NULL;
- if (login_attempts++ >= 5) {
+ sleep(5);
+ if (++login_attempts >= 3) {
+ reply(421,
+ "Login incorrect, closing connection.");
syslog(LOG_NOTICE,
"repeated login failures from %s",
remotehost);
- exit(0);
+ dologout(0);
}
+ reply(530, "Login incorrect.");
return;
}
}
@@ -930,6 +1016,16 @@ pass(passwd)
login(passwd)
char *passwd;
{
+ if (have_creds) {
+#ifdef GSSAPI
+ char *ccname = krb5_cc_get_name(kcontext, ccache);
+ chown(ccname, pw->pw_uid, pw->pw_gid);
+#endif
+#ifdef KRB5_KRB4_COMPAT
+ chown(tkt_string(), pw->pw_uid, pw->pw_gid);
+#endif
+ }
+
(void) krb5_setegid((gid_t)pw->pw_gid);
(void) initgroups(pw->pw_name, pw->pw_gid);
@@ -1831,6 +1927,14 @@ dologout(status)
(void) krb5_seteuid((uid_t)0);
ftp_logwtmp(ttyline, "", "");
}
+ if (have_creds) {
+#ifdef GSSAPI
+ krb5_cc_destroy(kcontext, ccache);
+#endif
+#ifdef KRB5_KRB4_COMPAT
+ dest_tkt();
+#endif
+ }
/* beware of flushing buffers after a SIGPIPE */
_exit(status);
}
@@ -2207,22 +2311,29 @@ char *data;
&deleg_creds);
return 0;
}
- /* If the server accepts the security data, but does
- not require any additional data (i.e., the security
- data exchange has completed successfully), it must
- respond with reply code 235. */
- if (!replied)
- reply(235, "GSSAPI Authentication succeeded");
-
auth_type = temp_auth_type;
temp_auth_type = NULL;
(void) gss_release_cred(&stat_min, &server_creds);
if (ret_flags & GSS_C_DELEG_FLAG) {
- /* This would be a good place to do something
- useful with the forwarded credentials... */
+ if (want_creds)
+ ftpd_gss_convert_creds(client_name.value,
+ deleg_creds);
(void) gss_release_cred(&stat_min, &deleg_creds);
}
+
+ /* If the server accepts the security data, but does
+ not require any additional data (i.e., the security
+ data exchange has completed successfully), it must
+ respond with reply code 235. */
+ if (!replied)
+ {
+ if (ret_flags & GSS_C_DELEG_FLAG && !have_creds)
+ reply(235, "GSSAPI Authentication succeeded, but could not accept forwarded credentials");
+ else
+ reply(235, "GSSAPI Authentication succeeded");
+ }
+
return(1);
} else if (stat_maj == GSS_S_CONTINUE_NEEDED) {
/* If the server accepts the security data, and
@@ -2425,18 +2536,6 @@ data_err:
pdata = -1;
}
-int auth_ok(void)
-{
- return(0
-#ifdef KRB5_KRB4_COMPAT
- || kerb_ok
-#endif /* KRB5_KRB4_COMPAT */
-#ifdef GSSAPI
- || gss_ok
-#endif /* GSSAPI */
- );
-}
-
#ifdef SETPROCTITLE
/*
* clobber argv so ps will show what we're doing.
@@ -2468,6 +2567,7 @@ char *buf;
*p++ = ' ';
}
#endif /* SETPROCTITLE */
+
#ifdef GSSAPI
reply_gss_error(code, maj_stat, min_stat, s)
int code;
@@ -2519,34 +2619,102 @@ char *s;
}
-#include <krb5.h>
-/* ftpd_userok -- hide details of getting the name and verifying it */
+/* ftpd_gss_userok -- hide details of getting the name and verifying it */
/* returns 0 for OK */
-ftpd_userok(client_name, name)
+ftpd_gss_userok(client_name, name)
gss_buffer_t client_name;
char *name;
{
int retval = -1;
- krb5_boolean k5ret;
- krb5_context kc;
krb5_principal p;
- krb5_error_code kerr;
- kerr = krb5_init_context(&kc);
- if (kerr)
+ if (krb5_parse_name(kcontext, client_name->value, &p) != 0)
return -1;
-
- kerr = krb5_parse_name(kc, client_name->value, &p);
- if (kerr) { retval = -1; goto fail; }
- k5ret = krb5_kuserok(kc, p, name);
- if (k5ret == TRUE)
+ if (krb5_kuserok(kcontext, p, name))
retval = 0;
else
retval = 1;
- krb5_free_principal(kc, p);
- fail:
- krb5_free_context(kc);
+ krb5_free_principal(kcontext, p);
return retval;
}
+
+/* ftpd_gss_convert_creds -- write out forwarded creds */
+/* (code lifted from login.krb5) */
+ftpd_gss_convert_creds(name, creds)
+ char *name;
+ gss_cred_id_t creds;
+{
+ OM_uint32 major_status, minor_status;
+ krb5_principal me;
+ char ccname[MAXPATHLEN];
+#ifdef KRB5_KRB4_COMPAT
+ krb5_principal kpcserver;
+ krb5_error_code kpccode;
+ int kpcval;
+ krb5_creds increds, *v5creds;
+ CREDENTIALS v4creds;
+#endif
+
+ /* Set up ccache */
+ if (krb5_parse_name(kcontext, name, &me))
+ return;
+
+ sprintf(ccname, "FILE:/tmp/krb5cc_ftpd%d", getpid());
+ if (krb5_cc_resolve(kcontext, ccname, &ccache))
+ return;
+ if (krb5_cc_initialize(kcontext, ccache, me))
+ return;
+
+ /* Copy GSS creds into ccache */
+ major_status = gss_krb5_copy_ccache(&minor_status, creds, ccache);
+ if (major_status != GSS_S_COMPLETE)
+ goto cleanup;
+
+#ifdef KRB5_KRB4_COMPAT
+ /* Convert krb5 creds to krb4 */
+
+ if (krb5_build_principal_ext(kcontext, &kpcserver,
+ krb5_princ_realm(kcontext, me)->length,
+ krb5_princ_realm(kcontext, me)->data,
+ 6, "krbtgt",
+ krb5_princ_realm(kcontext, me)->length,
+ krb5_princ_realm(kcontext, me)->data,
+ NULL))
+ goto cleanup;
+
+ memset((char *) &increds, 0, sizeof(increds));
+ increds.client = me;
+ increds.server = kpcserver;
+ increds.times.endtime = 0;
+ increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
+ if (krb5_get_credentials(kcontext, 0, ccache, &increds, &v5creds))
+ goto cleanup;
+ if (krb524_convert_creds_kdc(kcontext, v5creds, &v4creds))
+ goto cleanup;
+
+ sprintf(ccname, "%s_ftpd%d", TKT_ROOT, getpid());
+ krb_set_tkt_string(ccname);
+
+ if (in_tkt(v4creds.pname, v4creds.pinst) != KSUCCESS)
+ goto cleanup;
+
+ if (krb_save_credentials(v4creds.service, v4creds.instance,
+ v4creds.realm, v4creds.session,
+ v4creds.lifetime, v4creds.kvno,
+ &(v4creds.ticket_st), v4creds.issue_date))
+ goto cleanup_v4;
+#endif /* KRB5_KRB4_COMPAT */
+ have_creds = 1;
+ return;
+
+#ifdef KRB5_KRB4_COMPAT
+cleanup_v4:
+ dest_tkt();
+#endif
+cleanup:
+ krb5_cc_destroy(kcontext, ccache);
+}
+
+
#endif /* GSSAPI */