From aa872d88d3959924bea1b718371a8817d3837774 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 26 Oct 1998 19:00:46 +0000 Subject: [PATCH] Add -A (require authentication but not necessarily authorization) and -C (user wants local credentials) flags to ftpd. If -C is set, write out forwarded credentials to disk and use krb524 to get krb4 tickets as well. If user doesn't forward credentials, require a password even if auth was successful and use that to get credentials. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@10993 dc483132-0cff-0310-8789-dd5450dbe970 --- src/appl/gssftp/configure.in | 14 + src/appl/gssftp/ftpd/ChangeLog | 27 ++ src/appl/gssftp/ftpd/Makefile.in | 3 +- src/appl/gssftp/ftpd/ftpd.M | 26 +- src/appl/gssftp/ftpd/ftpd.c | 471 +++++++++++++++++++++---------- 5 files changed, 381 insertions(+), 160 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/ftpd/ChangeLog b/src/appl/gssftp/ftpd/ChangeLog index 92e0e9b5a..37681c479 100644 --- a/src/appl/gssftp/ftpd/ChangeLog +++ b/src/appl/gssftp/ftpd/ChangeLog @@ -1,3 +1,30 @@ +Mon Oct 26 13:46:47 1998 Dan Winship + + * 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 * ftpd.c (pass): Wait 5 seconds before returning "password 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/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 5b861c6f1..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 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_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 */ + +#ifdef KRB5_KRB4_COMPAT + if (krb_get_lrealm(realm, 1) != KSUCCESS) + goto nuke_ccache; - strcpy(tkt_file, TKT_ROOT); - strcat(tkt_file, "_ftpdXXXXXX"); - krb_set_tkt_string(mktemp(tkt_file)); + sprintf(ccname, "%s_ftpd%d", TKT_ROOT, getpid()); + krb_set_tkt_string(ccname); - (void) strncpy(instance, krb_get_phost(hostname), sizeof(instance)); + if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, passwd)) + goto nuke_ccache; - if ((hp = gethostbyname(instance)) == NULL) - return(0); +#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,18 +984,15 @@ 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 */ - { + /* 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; sleep(5); if (++login_attempts >= 3) { @@ -918,7 +1001,7 @@ pass(passwd) syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); - exit(0); + dologout(0); } reply(530, "Login incorrect."); return; @@ -933,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); @@ -1834,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); } @@ -2210,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 @@ -2428,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. @@ -2471,6 +2567,7 @@ char *buf; *p++ = ' '; } #endif /* SETPROCTITLE */ + #ifdef GSSAPI reply_gss_error(code, maj_stat, min_stat, s) int code; @@ -2522,34 +2619,102 @@ char *s; } -#include -/* 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 */ -- 2.26.2