diff options
author | Marc Horowitz <marc@mit.edu> | 1998-10-28 02:33:38 +0000 |
---|---|---|
committer | Marc Horowitz <marc@mit.edu> | 1998-10-28 02:33:38 +0000 |
commit | 49ea9cd00df2e6f4ff4e4f6117b364ca59d0e990 (patch) | |
tree | 9774b8537fea8a185305a9dfce6eceabdc35edcb /src/appl/gssftp | |
parent | 0a052ad343004519023cecf0664e016614134979 (diff) | |
download | krb5-marc-3des.zip krb5-marc-3des.tar.gz krb5-marc-3des.tar.bz2 |
merge from mainlinemarc-3des
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/marc-3des@11000 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/appl/gssftp')
-rw-r--r-- | src/appl/gssftp/configure.in | 14 | ||||
-rw-r--r-- | src/appl/gssftp/ftp/ChangeLog | 9 | ||||
-rw-r--r-- | src/appl/gssftp/ftp/ftp.M | 77 | ||||
-rw-r--r-- | src/appl/gssftp/ftp/main.c | 14 | ||||
-rw-r--r-- | src/appl/gssftp/ftpd/ChangeLog | 38 | ||||
-rw-r--r-- | src/appl/gssftp/ftpd/Makefile.in | 3 | ||||
-rw-r--r-- | src/appl/gssftp/ftpd/ftpcmd.y | 28 | ||||
-rw-r--r-- | src/appl/gssftp/ftpd/ftpd.M | 26 | ||||
-rw-r--r-- | src/appl/gssftp/ftpd/ftpd.c | 478 |
9 files changed, 494 insertions, 193 deletions
diff --git a/src/appl/gssftp/configure.in b/src/appl/gssftp/configure.in index 2ede0ba..112a297 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 49c1625..b021129 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 499b587..9c890cf 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 685c147..aa6e5a4 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 400d483..37681c4 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 41e7333..0988bb9 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 f237bb7..5b75a46 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 cdbb69b..30d8c18 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 44bf8df..db424a5 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 */ |