From kenh@cmf.nrl.navy.mil Tue Nov 26 18:47:13 1996
Received: from MIT.EDU (SOUTH-STATION-ANNEX.MIT.EDU [18.72.1.2]) by rt-11.MIT.EDU (8.7.5/8.7.3) with SMTP id SAA19278 for <bugs@RT-11.MIT.EDU>; Tue, 26 Nov 1996 18:47:11 -0500
Received: from ginger.cmf.nrl.navy.mil by MIT.EDU with SMTP
id AA18971; Tue, 26 Nov 96 18:47:10 EST
Received: from elvis.cmf.nrl.navy.mil (kenh@elvis.cmf.nrl.navy.mil [134.207.10.38]) by ginger.cmf.nrl.navy.mil (8.7.5/8.7.3) with ESMTP id SAA22018 for <krb5-bugs@mit.edu>; Tue, 26 Nov 1996 18:47:11 -0500 (EST)
Received: (kenh@localhost) by elvis.cmf.nrl.navy.mil (8.6.12/8.6.11) id SAA09013; Tue, 26 Nov 1996 18:47:06 -0500
Message-Id: <199611262347.SAA09013@elvis.cmf.nrl.navy.mil>
Date: Tue, 26 Nov 1996 18:47:06 -0500
From: Ken Hornstein <kenh@cmf.nrl.navy.mil>
Reply-To: kenh@cmf.nrl.navy.mil
To: krb5-bugs@MIT.EDU
Subject: Changes for ftp/ftpd to support ticket forwarding & AFS
X-Send-Pr-Version: 3.2
System: SunOS elvis 4.1.3_U1 13 sun4m
Architecture: sun4
I've included my patches for doing ticket forwarding and cleartext passwords
for ftp/ftpd. This also includes AFS support (ie - get a PAG and run aklog
at the right time). Comments/suggestions are welcome.
Try using the Kerberos 5 ftp/ftpd with AFS.
--- appl/gssftp/ftp/Makefile.in.orig Wed Nov 6 15:09:01 1996
+++ appl/gssftp/ftp/Makefile.in Wed Nov 6 15:09:47 1996
@@ -1,7 +1,7 @@
#
# appl/gssftp/ftp/Makefile.in
#
-CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+CFLAGS = -DGSSAPI -DKERBEROS5 -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
--- appl/gssftp/ftp/main.c.orig Wed Nov 6 15:10:26 1996
+++ appl/gssftp/ftp/main.c Wed Nov 6 16:31:31 1996
@@ -73,6 +73,11 @@
extern char realm[];
#endif /* KERBEROS */
+#ifdef KERBEROS5
+#include <krb5.h>
+#include <com_err.h>
+#endif
+
main(argc, argv)
char *argv[];
{
@@ -80,6 +85,12 @@
int top;
struct passwd *pw = NULL;
char homedir[MAXPATHLEN];
+#ifdef KERBEROS5
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_error_code code = 0;
+ krb5_principal princ;
+#endif
sp = getservbyname("ftp", "tcp");
if (sp == 0) {
@@ -92,6 +103,33 @@
memcpy(&staticsp,sp,sizeof(struct servent));
sp = &staticsp;
#endif /* KERBEROS */
+
+#ifdef KERBEROS5
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ /*
+ * Forward credentials if we get a command-line flag or if we
+ * have the right stuff set in the profile, _AND_ if our TGT
+ * is forwardable
+ */
+
+ if ((code = krb5_cc_default(context, &ccache)) != 0) {
+ com_err(argv[0], code, "while reading credential cache");
+ }
+
+ if ((code == 0) &&
+ (code = krb5_cc_get_principal(context, ccache, &princ)) != 0) {
+ com_err(argv[0], code, "while getting primary principal");
+ }
+
+ if (code == 0) {
+ krb5_appdefault_boolean(context, "ftp",
+ krb5_princ_realm(context, princ),
+ "forward", 0, &forward);
+ }
+#endif /* KERBEROS5 */
+
doglob = 1;
interactive = 1;
autologin = 1;
@@ -137,6 +175,15 @@
case 'g':
doglob = 0;
break;
+#ifdef KERBEROS5
+ case 'f':
+ forward = 1;
+ break;
+
+ case 'F':
+ forward = 0;
+ break;
+#endif /* KERBEROS5 */
default:
fprintf(stdout,
@@ -146,6 +193,51 @@
nextopt:
argc--, argv++;
}
+
+#ifdef KERBEROS5
+
+ if (code != 0)
+ forward = 0;
+
+ if (forward) {
+ krb5_creds creds, mcreds;
+
+ creds.client = princ;
+ code = krb5_build_principal(context, &creds.server,
+ krb5_princ_realm(context, princ)->length,
+ krb5_princ_realm(context, princ)->data,
+ "krbtgt",
+ krb5_princ_realm(context, princ)->data, 0);
+
+ if (code != 0) {
+ com_err(argv[0], code, "while building TGT principal");
+ forward = 0;
+ }
+
+ if (code == 0)
+ code = krb5_cc_retrieve_cred(context, ccache, 0,
+ &creds, &mcreds);
+
+ if (code == 0) {
+ krb5_free_principal(context, creds.server);
+
+ if ((mcreds.ticket_flags & TKT_FLG_FORWARDABLE) == 0)
+ forward = 0;
+
+ krb5_free_creds(context, &mcreds);
+ }
+
+ if (code != 0)
+ forward = 0;
+ }
+
+ krb5_cc_close(context, ccache);
+
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+
+#endif KERBEROS5
+
fromatty = isatty(fileno(stdin));
if (fromatty)
verbose++;
--- appl/gssftp/ftp/ftp_var.h.orig Wed Nov 6 15:10:38 1996
+++ appl/gssftp/ftp/ftp_var.h Wed Nov 6 15:11:27 1996
@@ -103,6 +103,10 @@
extern int options; /* used during socket creation */
+#ifdef KERBEROS5
+extern int forward; /* Should we forward credentials? */
+#endif
+
/*
* Format of command table.
*/
--- appl/gssftp/ftp/ftp.c.orig Wed Nov 6 16:46:57 1996
+++ appl/gssftp/ftp/ftp.c Wed Nov 6 16:47:58 1996
@@ -1963,7 +1963,8 @@
&gcontext,
target_name,
GSS_C_NULL_OID,
- GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
+ (forward ? GSS_C_DELEG_FLAG : 0),
0,
&chan, /* channel bindings */
token_ptr,
--- appl/gssftp/ftpd/ftpd.c.orig Thu Nov 7 12:06:33 1996
+++ appl/gssftp/ftpd/ftpd.c Fri Nov 15 18:34:25 1996
@@ -225,6 +225,7 @@
{
int addrlen, on = 1, tos, port = -1;
char *cp;
+ char ccname[35];
debug = 0;
#ifdef SETPROCTITLE
@@ -375,6 +376,17 @@
#define LOG_DAEMON 0
#endif
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+#ifdef GSSAPI
+ /*
+ * Just in case we're using Kerberos, setup a private
+ * credential cache
+ */
+
+ sprintf(ccname, "FILE:/tmp/krb5cc_p%d", getpid());
+ setenv("KRB5CCNAME", ccname, 0);
+#endif
+
addrlen = sizeof (his_addr);
if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
@@ -688,6 +700,9 @@
*/
end_login()
{
+#ifdef GSSAPI
+ afs_logout();
+#endif
(void) seteuid((uid_t)0);
if (logged_in)
@@ -783,6 +798,12 @@
(*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
!kpass(pw->pw_name, passwd)) ||
(!*pw->pw_passwd && !kpass(pw->pw_name, passwd))) {
+#elif defined(GSSAPI)
+ /* null pw_passwd ok if Kerberos 5 password ok */
+ if (pw == NULL ||
+ (*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
+ !k5pass(pw->pw_name, passwd)) ||
+ (!*pw->pw_passwd && !k5pass(pw->pw_name, passwd))) {
#else
/* The strcmp does not catch null passwords! */
if (pw == NULL || *pw->pw_passwd == '\0' ||
@@ -818,18 +839,41 @@
reply(550, "Can't set guest privileges.");
goto bad;
}
- } else if (chdir(pw->pw_dir) < 0) {
- if (chdir("/") < 0) {
- reply(530, "User %s: can't change directory to %s.",
- pw->pw_name, pw->pw_dir);
+ if (seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
goto bad;
- } else
- lreply(230, "No directory! Logging in with home=/");
- }
- if (seteuid((uid_t)pw->pw_uid) < 0) {
- reply(550, "Can't set uid.");
- goto bad;
+ }
+ } else {
+ /*
+ * Call afs_login to hide the extra magic we need to do
+ *
+ * Note that much of this stuff needs to happen as the user,
+ * so we're setting the effective uid now.
+ */
+#ifdef GSSAPI
+ save_credentials();
+#endif
+
+ if (seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+
+
+#ifdef GSSAPI
+ afs_login(pw->pw_uid);
+#endif
+
+ if (chdir(pw->pw_dir) < 0) {
+ if (chdir("/") < 0) {
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+ } else
+ lreply(230, "No directory! Logging in with home=/");
+ }
}
+
if (guest) {
reply(230, "Guest login ok, access restrictions apply.");
#ifdef SETPROCTITLE
@@ -1669,6 +1713,9 @@
dologout(status)
int status;
{
+#ifdef GSSAPI
+ afs_logout();
+#endif
if (logged_in) {
(void) seteuid((uid_t)0);
logwtmp(ttyline, "", "");
@@ -1985,6 +2032,8 @@
);
if (accept_maj!=GSS_S_COMPLETE && accept_maj!=GSS_S_CONTINUE_NEEDED)
continue;
+ else
+ break;
}
if (found) {
@@ -2327,4 +2376,390 @@
krb5_free_context(kc);
return retval;
}
+
+/*
+ * Save our credentials and destroy a credential cache before we call setuid
+ */
+
+static krb5_creds *saved_creds;
+
+save_credentials()
+{
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_principal me, server;
+ krb5_creds mcred;
+
+ krb5_init_context(&context);
+
+ saved_creds = NULL;
+
+ if (krb5_cc_default(context, &cc))
+ goto leave;
+
+ if (krb5_cc_get_principal(context, cc, &me)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (krb5_build_principal_ext(context, &server,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ 0)) {
+ krb5_cc_destroy(context, cc);
+ krb5_free_principal(context, me);
+ goto leave;
+ }
+
+ saved_creds = (krb5_creds *) malloc(sizeof(krb5_creds));
+ mcred.server = server;
+ mcred.client = me;
+
+ if (krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY,
+ &mcred, saved_creds)) {
+ free(saved_creds);
+ saved_creds = NULL;
+ krb5_cc_destroy(context, cc);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, server);
+ goto leave;
+ }
+
+ krb5_cc_destroy(context, cc);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, server);
+
+leave:
+ krb5_free_context(context);
+
+ return;
+}
+
+/*
+ * Handle the magic we need for AFS. Get a PAG and try to run aklog.
+ *
+ * Most of this was taken from login.
+ */
+
+#ifdef SETPAG
+
+typedef krb5_sigtype sigtype;
+
+#ifndef POSIX_SETJMP
+#undef sigjmp_buf
+#undef sigsetjmp
+#undef siglongjmp
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(j,s) setjmp(j)
+#define siglongjmp longjmp
+#endif
+
+#ifdef POSIX_SIGNALS
+typedef struct sigaction handler;
+#define handler_init(H,F) (sigemptyset(&(H).sa_mask), \
+ (H).sa_flags=0, \
+ (H).sa_handler=(F))
+#define handler_swap(S,NEW,OLD) sigaction(S, &NEW, &OLD)
+#define handler_set(S,OLD) sigaction(S, &OLD, NULL)
+#else
+typedef sigtype (*handler)();
+#define handler_init(H,F) ((H) = (F))
+#define handler_swap(S,NEW,OLD) ((OLD) = signal ((S), (NEW)))
+#define handler_set(S,OLD) (signal ((S), (OLD)))
+#endif
+
+static sigjmp_buf setpag_buf;
+
+static sigtype sigsys ()
+{
+ siglongjmp(setpag_buf, 1);
+}
+#endif
+
+static int try_setpag ()
+{
+#ifdef SETPAG
+ handler sa, osa;
+ volatile int retval = 0;
+
+ (void) &retval;
+ handler_init(sa, sigsys);
+ handler_swap(SIGSYS, sa, osa);
+ if (sigsetjmp(setpag_buf, 1) == 0) {
+ setpag();
+ retval = 1;
+ }
+ handler_set(SIGSYS, osa);
+ return retval;
+#endif
+}
+
+afs_login(uid)
+ int uid;
+{
+ krb5_context context;
+ krb5_ccache cc;
+ int try_aklog = 0;
+ char *aklog_path;
+ struct stat st;
+
+ if (saved_creds == NULL)
+ return;
+
+ krb5_init_context(&context);
+
+ krb5_appdefault_boolean(context, "ftpd",
+ krb5_princ_realm(context, saved_creds->client),
+ "krb5_run_aklog", try_aklog, &try_aklog);
+
+ if (try_aklog) {
+
+ if (krb5_cc_default(context, &cc))
+ goto leave;
+
+ if (krb5_cc_initialize(context, cc, saved_creds->client))
+ goto leave;
+
+ if (krb5_cc_store_cred(context, cc, saved_creds))
+ goto leave;
+
+ krb5_appdefault_string(context, "ftpd",
+ krb5_princ_realm(context, saved_creds->client),
+ "krb5_aklog_path", KPROGDIR "/aklog",
+ &aklog_path);
+
+ if (stat(aklog_path, &st) == 0) {
+ int pid, testpid;
+
+ try_setpag();
+
+ /*
+ * Aklog _really_ wants to run as "real user",
+ * and in some cases, aklog breaks when using it
+ * with an NFS translator. Make sure that we run
+ * aklog as the real user.
+ */
+
+ if ((pid = fork()) == 0) {
+ seteuid(0);
+ setuid((uid_t) uid);
+ system(aklog_path);
+ exit(0);
+ } else {
+ while ((testpid = wait(NULL)) != pid &&
+ testpid != -1);
+ }
+ }
+
+ free(aklog_path);
+ }
+
+leave:
+
+ if (saved_creds)
+ krb5_free_creds(context, saved_creds);
+
+ krb5_free_context(context);
+
+}
+
+afs_logout()
+{
+ krb5_context context;
+ krb5_ccache cc;
+
+ krb5_init_context(&context);
+
+ krb5_cc_default(context, &cc);
+
+ krb5_cc_destroy(context, cc);
+
+ krb5_free_context(context);
+}
+
+static char *k5services[] = { "ftp", "host", NULL };
+
+#define KRB5_DEFAULT_LIFETIME "5h 0m 0s"
+
+/*
+ * Check our Kerberos 5 password
+ */
+
+k5pass(name, passwd)
+ char *name, *passwd;
+{
+ krb5_context context;
+ krb5_principal me = NULL, server = NULL, ver_princ = NULL;
+ krb5_keyblock *kb = NULL;
+ krb5_creds my_creds;
+ krb5_ccache cc;
+ krb5_timestamp now;
+ krb5_data packet;
+ krb5_auth_context auth_context = NULL;
+ char *lifetimestring, **service;
+ krb5_deltat lifetime;
+ int valid = 0;
+
+ /*
+ * Setup Kerberos for getting the initial TGT
+ */
+
+ krb5_init_context(&context);
+
+ if (krb5_cc_default(context, &cc))
+ goto leave;
+
+ memset((char *) &my_creds, 0, sizeof(my_creds));
+
+ if (krb5_parse_name(context, name, &me)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ my_creds.client = me;
+
+ if (krb5_cc_initialize(context, cc, me)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (krb5_build_principal_ext(context, &server,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ 0)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ my_creds.server = server;
+
+ if (krb5_timeofday(context, &now)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ my_creds.times.starttime = 0;
+
+ krb5_appdefault_string(context, "ftpd", krb5_princ_realm(context, me),
+ "default_lifetime", KRB5_DEFAULT_LIFETIME,
+ &lifetimestring);
+
+ if (krb5_string_to_deltat(lifetimestring, &lifetime)) {
+ krb5_cc_destroy(context, cc);
+ free(lifetimestring);
+ goto leave;
+ }
+
+ free(lifetimestring);
+
+ my_creds.times.endtime = now + lifetime;
+ my_creds.times.renew_till = 0;
+
+ if (krb5_get_in_tkt_with_password(context, 0, 0, NULL, 0,
+ passwd, cc, &my_creds, 0)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ /*
+ * Ok, if we've reached this part successfully, then that means that
+ * we've gotten a valid ticket from the KDC. Now we try to get a
+ * service ticket from the KDC using a known key. In this case,
+ * we try "ftp" and "host". If neither of these principals are in
+ * the keytab, then let this user in anyway.
+ */
+
+ for (service = k5services; *service; service++) {
+ if (krb5_sname_to_principal(context, NULL, *service,
+ KRB5_NT_SRV_HST, &ver_princ)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (krb5_kt_read_service_key(context, NULL, ver_princ, 0,
+ ENCTYPE_DES_CBC_CRC, &kb) == 0)
+ break;
+ else {
+ krb5_free_principal(context, ver_princ);
+ ver_princ = NULL;
+ }
+ }
+
+
+ if (ver_princ == NULL) {
+ valid = 1;
+ goto leave;
+ }
+
+ /*
+ * The reason we're doing all of this is probably not very
+ * obvious. Here's the theory:
+ *
+ * Just getting a valid ticket from the KDC for that user isn't
+ * 100% secure, because if someone else is faking replies from the
+ * KDC, then they could forge a reponse that appeared to be valid,
+ * but actually wasn't. So what we do here is use a service
+ * ticket for the principal {ftp|host}/<host>} and generate an
+ * AP_REQ message (using krb5_mk_req()), and then use krb5_rd_req()
+ * to validate that message. Since the request generated by mk_req()
+ * was encrypted with the host or ftp principal's secret key, and
+ * that service ticket was generated by using the user's TGT,
+ * then if we can decode the mk_req() message, that means that
+ * the user's credentials are legitmate.
+ */
+
+ packet.data = NULL;
+
+ if (krb5_mk_req(context, &auth_context, 0, *service,
+ krb5_princ_component(context, ver_princ, 1)->data,
+ NULL, cc, &packet)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (auth_context) {
+ krb5_auth_con_free(context, auth_context);
+ auth_context = NULL;
+ }
+
+ if (krb5_rd_req(context, &auth_context, &packet, ver_princ, NULL,
+ NULL, NULL)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ /*
+ * _Whew_! Everything is cool!
+ */
+
+ valid = 1;
+
+leave:
+
+ if (packet.data)
+ krb5_xfree(packet.data);
+ if (auth_context)
+ krb5_auth_con_free(context, auth_context);
+ if (kb)
+ krb5_free_keyblock(context, kb);
+ if (my_creds.keyblock.contents)
+ krb5_free_cred_contents(context, &my_creds);
+ if (me)
+ krb5_free_principal(context, me);
+ if (server)
+ krb5_free_principal(context, server);
+ if (ver_princ)
+ krb5_free_principal(context, ver_princ);
+
+ krb5_free_context(context);
+
+ return(valid);
+}
+
#endif /* GSSAPI */
--- appl/gssftp/ftpd/configure.in.orig Thu Nov 7 16:06:23 1996
+++ appl/gssftp/ftpd/configure.in Wed Nov 13 00:25:54 1996
@@ -7,6 +7,8 @@
CHECK_UTMP
CHECK_SIGPROCMASK
CHECK_WAIT_TYPE
+CHECK_SIGNALS
+CHECK_SETJMP
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
@@ -17,6 +19,17 @@
AC_REPLACE_FUNCS(getdtablesize)
AC_HAVE_FUNCS(getcwd getusershell seteuid setreuid setresuid)
AC_CHECK_LIB(crypt,crypt) dnl
+dnl copied (mostly) from appl/bsd/configure.in
+AFSLIBS=
+AC_ARG_WITH([afs],
+[ --without-afs don't have afs libraries to build against (default)
+ --with-afs=AFSDIR use preinstalled AFS library tree],
+,with_afs=no)dnl
+if test $with_afs != no; then
+ AC_DEFINE(SETPAG)
+ AFSLIBS="$AFSLIBS -L$with_afs/lib -L$with_afs/lib/afs -lauth -lsys -lrx -llwp"
+fi
+AC_SUBST(AFSLIBS)
dnl
dnl copied from appl/bsd/configure.in
AC_MSG_CHECKING([setenv])
--- appl/gssftp/ftpd/Makefile.in.orig Thu Nov 7 16:09:43 1996
+++ appl/gssftp/ftpd/Makefile.in Wed Nov 13 15:50:45 1996
@@ -1,12 +1,13 @@
#
# appl/gssftp/ftpd/Makefile.in
#
-CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE) -DKPROGDIR=\"$(CLIENT_BINDIR)\"
SETENVSRC=@SETENVSRC@
SETENVOBJ=@SETENVOBJ@
LIBOBJS=@LIBOBJS@
COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+AFSLIBS=@AFSLIBS@
SRCS = ftpd.c ftpcmd.y logwtmp.c popen.c vers.c \
$(srcdir)../ftp/glob.c \
@@ -27,7 +28,7 @@
all:: ftpd
ftpd: $(OBJS) $(DEPKLIB)
- $(LD) $(LDFLAGS) $(LDARGS) -o $@ $(OBJS) $(KLIB) $(LIBS)
+ $(LD) $(LDFLAGS) $(LDARGS) -o $@ $(OBJS) $(KLIB) $(LIBS) $(AFSLIBS)
clean::
$(RM) ftpd ftpcmd.c
Received: from MIT.EDU (SOUTH-STATION-ANNEX.MIT.EDU [18.72.1.2]) by rt-11.MIT.EDU (8.7.5/8.7.3) with SMTP id SAA19278 for <bugs@RT-11.MIT.EDU>; Tue, 26 Nov 1996 18:47:11 -0500
Received: from ginger.cmf.nrl.navy.mil by MIT.EDU with SMTP
id AA18971; Tue, 26 Nov 96 18:47:10 EST
Received: from elvis.cmf.nrl.navy.mil (kenh@elvis.cmf.nrl.navy.mil [134.207.10.38]) by ginger.cmf.nrl.navy.mil (8.7.5/8.7.3) with ESMTP id SAA22018 for <krb5-bugs@mit.edu>; Tue, 26 Nov 1996 18:47:11 -0500 (EST)
Received: (kenh@localhost) by elvis.cmf.nrl.navy.mil (8.6.12/8.6.11) id SAA09013; Tue, 26 Nov 1996 18:47:06 -0500
Message-Id: <199611262347.SAA09013@elvis.cmf.nrl.navy.mil>
Date: Tue, 26 Nov 1996 18:47:06 -0500
From: Ken Hornstein <kenh@cmf.nrl.navy.mil>
Reply-To: kenh@cmf.nrl.navy.mil
To: krb5-bugs@MIT.EDU
Subject: Changes for ftp/ftpd to support ticket forwarding & AFS
X-Send-Pr-Version: 3.2
Show quoted text
>Number: 255
>Category: krb5-appl
>Synopsis: Changes for ftp/ftpd to support ticket forwarding & AFS
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: krb5-unassigned
>State: open
>Class: change-request
>Submitter-Id: unknown
>Arrival-Date: Tue Nov 26 18:48:00 EST 1996
>Last-Modified:
>Originator: Ken Hornstein
>Organization:
Naval Research Lab>Category: krb5-appl
>Synopsis: Changes for ftp/ftpd to support ticket forwarding & AFS
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: krb5-unassigned
>State: open
>Class: change-request
>Submitter-Id: unknown
>Arrival-Date: Tue Nov 26 18:48:00 EST 1996
>Last-Modified:
>Originator: Ken Hornstein
>Organization:
Show quoted text
>Release: beta-7
>Environment:
>Environment:
System: SunOS elvis 4.1.3_U1 13 sun4m
Architecture: sun4
Show quoted text
>Description:
I've included my patches for doing ticket forwarding and cleartext passwords
for ftp/ftpd. This also includes AFS support (ie - get a PAG and run aklog
at the right time). Comments/suggestions are welcome.
Show quoted text
>How-To-Repeat:
Try using the Kerberos 5 ftp/ftpd with AFS.
Show quoted text
>Fix:
--- appl/gssftp/ftp/Makefile.in.orig Wed Nov 6 15:09:01 1996
+++ appl/gssftp/ftp/Makefile.in Wed Nov 6 15:09:47 1996
@@ -1,7 +1,7 @@
#
# appl/gssftp/ftp/Makefile.in
#
-CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+CFLAGS = -DGSSAPI -DKERBEROS5 -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
--- appl/gssftp/ftp/main.c.orig Wed Nov 6 15:10:26 1996
+++ appl/gssftp/ftp/main.c Wed Nov 6 16:31:31 1996
@@ -73,6 +73,11 @@
extern char realm[];
#endif /* KERBEROS */
+#ifdef KERBEROS5
+#include <krb5.h>
+#include <com_err.h>
+#endif
+
main(argc, argv)
char *argv[];
{
@@ -80,6 +85,12 @@
int top;
struct passwd *pw = NULL;
char homedir[MAXPATHLEN];
+#ifdef KERBEROS5
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_error_code code = 0;
+ krb5_principal princ;
+#endif
sp = getservbyname("ftp", "tcp");
if (sp == 0) {
@@ -92,6 +103,33 @@
memcpy(&staticsp,sp,sizeof(struct servent));
sp = &staticsp;
#endif /* KERBEROS */
+
+#ifdef KERBEROS5
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ /*
+ * Forward credentials if we get a command-line flag or if we
+ * have the right stuff set in the profile, _AND_ if our TGT
+ * is forwardable
+ */
+
+ if ((code = krb5_cc_default(context, &ccache)) != 0) {
+ com_err(argv[0], code, "while reading credential cache");
+ }
+
+ if ((code == 0) &&
+ (code = krb5_cc_get_principal(context, ccache, &princ)) != 0) {
+ com_err(argv[0], code, "while getting primary principal");
+ }
+
+ if (code == 0) {
+ krb5_appdefault_boolean(context, "ftp",
+ krb5_princ_realm(context, princ),
+ "forward", 0, &forward);
+ }
+#endif /* KERBEROS5 */
+
doglob = 1;
interactive = 1;
autologin = 1;
@@ -137,6 +175,15 @@
case 'g':
doglob = 0;
break;
+#ifdef KERBEROS5
+ case 'f':
+ forward = 1;
+ break;
+
+ case 'F':
+ forward = 0;
+ break;
+#endif /* KERBEROS5 */
default:
fprintf(stdout,
@@ -146,6 +193,51 @@
nextopt:
argc--, argv++;
}
+
+#ifdef KERBEROS5
+
+ if (code != 0)
+ forward = 0;
+
+ if (forward) {
+ krb5_creds creds, mcreds;
+
+ creds.client = princ;
+ code = krb5_build_principal(context, &creds.server,
+ krb5_princ_realm(context, princ)->length,
+ krb5_princ_realm(context, princ)->data,
+ "krbtgt",
+ krb5_princ_realm(context, princ)->data, 0);
+
+ if (code != 0) {
+ com_err(argv[0], code, "while building TGT principal");
+ forward = 0;
+ }
+
+ if (code == 0)
+ code = krb5_cc_retrieve_cred(context, ccache, 0,
+ &creds, &mcreds);
+
+ if (code == 0) {
+ krb5_free_principal(context, creds.server);
+
+ if ((mcreds.ticket_flags & TKT_FLG_FORWARDABLE) == 0)
+ forward = 0;
+
+ krb5_free_creds(context, &mcreds);
+ }
+
+ if (code != 0)
+ forward = 0;
+ }
+
+ krb5_cc_close(context, ccache);
+
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+
+#endif KERBEROS5
+
fromatty = isatty(fileno(stdin));
if (fromatty)
verbose++;
--- appl/gssftp/ftp/ftp_var.h.orig Wed Nov 6 15:10:38 1996
+++ appl/gssftp/ftp/ftp_var.h Wed Nov 6 15:11:27 1996
@@ -103,6 +103,10 @@
extern int options; /* used during socket creation */
+#ifdef KERBEROS5
+extern int forward; /* Should we forward credentials? */
+#endif
+
/*
* Format of command table.
*/
--- appl/gssftp/ftp/ftp.c.orig Wed Nov 6 16:46:57 1996
+++ appl/gssftp/ftp/ftp.c Wed Nov 6 16:47:58 1996
@@ -1963,7 +1963,8 @@
&gcontext,
target_name,
GSS_C_NULL_OID,
- GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
+ (forward ? GSS_C_DELEG_FLAG : 0),
0,
&chan, /* channel bindings */
token_ptr,
--- appl/gssftp/ftpd/ftpd.c.orig Thu Nov 7 12:06:33 1996
+++ appl/gssftp/ftpd/ftpd.c Fri Nov 15 18:34:25 1996
@@ -225,6 +225,7 @@
{
int addrlen, on = 1, tos, port = -1;
char *cp;
+ char ccname[35];
debug = 0;
#ifdef SETPROCTITLE
@@ -375,6 +376,17 @@
#define LOG_DAEMON 0
#endif
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+#ifdef GSSAPI
+ /*
+ * Just in case we're using Kerberos, setup a private
+ * credential cache
+ */
+
+ sprintf(ccname, "FILE:/tmp/krb5cc_p%d", getpid());
+ setenv("KRB5CCNAME", ccname, 0);
+#endif
+
addrlen = sizeof (his_addr);
if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
@@ -688,6 +700,9 @@
*/
end_login()
{
+#ifdef GSSAPI
+ afs_logout();
+#endif
(void) seteuid((uid_t)0);
if (logged_in)
@@ -783,6 +798,12 @@
(*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
!kpass(pw->pw_name, passwd)) ||
(!*pw->pw_passwd && !kpass(pw->pw_name, passwd))) {
+#elif defined(GSSAPI)
+ /* null pw_passwd ok if Kerberos 5 password ok */
+ if (pw == NULL ||
+ (*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
+ !k5pass(pw->pw_name, passwd)) ||
+ (!*pw->pw_passwd && !k5pass(pw->pw_name, passwd))) {
#else
/* The strcmp does not catch null passwords! */
if (pw == NULL || *pw->pw_passwd == '\0' ||
@@ -818,18 +839,41 @@
reply(550, "Can't set guest privileges.");
goto bad;
}
- } else if (chdir(pw->pw_dir) < 0) {
- if (chdir("/") < 0) {
- reply(530, "User %s: can't change directory to %s.",
- pw->pw_name, pw->pw_dir);
+ if (seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
goto bad;
- } else
- lreply(230, "No directory! Logging in with home=/");
- }
- if (seteuid((uid_t)pw->pw_uid) < 0) {
- reply(550, "Can't set uid.");
- goto bad;
+ }
+ } else {
+ /*
+ * Call afs_login to hide the extra magic we need to do
+ *
+ * Note that much of this stuff needs to happen as the user,
+ * so we're setting the effective uid now.
+ */
+#ifdef GSSAPI
+ save_credentials();
+#endif
+
+ if (seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+
+
+#ifdef GSSAPI
+ afs_login(pw->pw_uid);
+#endif
+
+ if (chdir(pw->pw_dir) < 0) {
+ if (chdir("/") < 0) {
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+ } else
+ lreply(230, "No directory! Logging in with home=/");
+ }
}
+
if (guest) {
reply(230, "Guest login ok, access restrictions apply.");
#ifdef SETPROCTITLE
@@ -1669,6 +1713,9 @@
dologout(status)
int status;
{
+#ifdef GSSAPI
+ afs_logout();
+#endif
if (logged_in) {
(void) seteuid((uid_t)0);
logwtmp(ttyline, "", "");
@@ -1985,6 +2032,8 @@
);
if (accept_maj!=GSS_S_COMPLETE && accept_maj!=GSS_S_CONTINUE_NEEDED)
continue;
+ else
+ break;
}
if (found) {
@@ -2327,4 +2376,390 @@
krb5_free_context(kc);
return retval;
}
+
+/*
+ * Save our credentials and destroy a credential cache before we call setuid
+ */
+
+static krb5_creds *saved_creds;
+
+save_credentials()
+{
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_principal me, server;
+ krb5_creds mcred;
+
+ krb5_init_context(&context);
+
+ saved_creds = NULL;
+
+ if (krb5_cc_default(context, &cc))
+ goto leave;
+
+ if (krb5_cc_get_principal(context, cc, &me)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (krb5_build_principal_ext(context, &server,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ 0)) {
+ krb5_cc_destroy(context, cc);
+ krb5_free_principal(context, me);
+ goto leave;
+ }
+
+ saved_creds = (krb5_creds *) malloc(sizeof(krb5_creds));
+ mcred.server = server;
+ mcred.client = me;
+
+ if (krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY,
+ &mcred, saved_creds)) {
+ free(saved_creds);
+ saved_creds = NULL;
+ krb5_cc_destroy(context, cc);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, server);
+ goto leave;
+ }
+
+ krb5_cc_destroy(context, cc);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, server);
+
+leave:
+ krb5_free_context(context);
+
+ return;
+}
+
+/*
+ * Handle the magic we need for AFS. Get a PAG and try to run aklog.
+ *
+ * Most of this was taken from login.
+ */
+
+#ifdef SETPAG
+
+typedef krb5_sigtype sigtype;
+
+#ifndef POSIX_SETJMP
+#undef sigjmp_buf
+#undef sigsetjmp
+#undef siglongjmp
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(j,s) setjmp(j)
+#define siglongjmp longjmp
+#endif
+
+#ifdef POSIX_SIGNALS
+typedef struct sigaction handler;
+#define handler_init(H,F) (sigemptyset(&(H).sa_mask), \
+ (H).sa_flags=0, \
+ (H).sa_handler=(F))
+#define handler_swap(S,NEW,OLD) sigaction(S, &NEW, &OLD)
+#define handler_set(S,OLD) sigaction(S, &OLD, NULL)
+#else
+typedef sigtype (*handler)();
+#define handler_init(H,F) ((H) = (F))
+#define handler_swap(S,NEW,OLD) ((OLD) = signal ((S), (NEW)))
+#define handler_set(S,OLD) (signal ((S), (OLD)))
+#endif
+
+static sigjmp_buf setpag_buf;
+
+static sigtype sigsys ()
+{
+ siglongjmp(setpag_buf, 1);
+}
+#endif
+
+static int try_setpag ()
+{
+#ifdef SETPAG
+ handler sa, osa;
+ volatile int retval = 0;
+
+ (void) &retval;
+ handler_init(sa, sigsys);
+ handler_swap(SIGSYS, sa, osa);
+ if (sigsetjmp(setpag_buf, 1) == 0) {
+ setpag();
+ retval = 1;
+ }
+ handler_set(SIGSYS, osa);
+ return retval;
+#endif
+}
+
+afs_login(uid)
+ int uid;
+{
+ krb5_context context;
+ krb5_ccache cc;
+ int try_aklog = 0;
+ char *aklog_path;
+ struct stat st;
+
+ if (saved_creds == NULL)
+ return;
+
+ krb5_init_context(&context);
+
+ krb5_appdefault_boolean(context, "ftpd",
+ krb5_princ_realm(context, saved_creds->client),
+ "krb5_run_aklog", try_aklog, &try_aklog);
+
+ if (try_aklog) {
+
+ if (krb5_cc_default(context, &cc))
+ goto leave;
+
+ if (krb5_cc_initialize(context, cc, saved_creds->client))
+ goto leave;
+
+ if (krb5_cc_store_cred(context, cc, saved_creds))
+ goto leave;
+
+ krb5_appdefault_string(context, "ftpd",
+ krb5_princ_realm(context, saved_creds->client),
+ "krb5_aklog_path", KPROGDIR "/aklog",
+ &aklog_path);
+
+ if (stat(aklog_path, &st) == 0) {
+ int pid, testpid;
+
+ try_setpag();
+
+ /*
+ * Aklog _really_ wants to run as "real user",
+ * and in some cases, aklog breaks when using it
+ * with an NFS translator. Make sure that we run
+ * aklog as the real user.
+ */
+
+ if ((pid = fork()) == 0) {
+ seteuid(0);
+ setuid((uid_t) uid);
+ system(aklog_path);
+ exit(0);
+ } else {
+ while ((testpid = wait(NULL)) != pid &&
+ testpid != -1);
+ }
+ }
+
+ free(aklog_path);
+ }
+
+leave:
+
+ if (saved_creds)
+ krb5_free_creds(context, saved_creds);
+
+ krb5_free_context(context);
+
+}
+
+afs_logout()
+{
+ krb5_context context;
+ krb5_ccache cc;
+
+ krb5_init_context(&context);
+
+ krb5_cc_default(context, &cc);
+
+ krb5_cc_destroy(context, cc);
+
+ krb5_free_context(context);
+}
+
+static char *k5services[] = { "ftp", "host", NULL };
+
+#define KRB5_DEFAULT_LIFETIME "5h 0m 0s"
+
+/*
+ * Check our Kerberos 5 password
+ */
+
+k5pass(name, passwd)
+ char *name, *passwd;
+{
+ krb5_context context;
+ krb5_principal me = NULL, server = NULL, ver_princ = NULL;
+ krb5_keyblock *kb = NULL;
+ krb5_creds my_creds;
+ krb5_ccache cc;
+ krb5_timestamp now;
+ krb5_data packet;
+ krb5_auth_context auth_context = NULL;
+ char *lifetimestring, **service;
+ krb5_deltat lifetime;
+ int valid = 0;
+
+ /*
+ * Setup Kerberos for getting the initial TGT
+ */
+
+ krb5_init_context(&context);
+
+ if (krb5_cc_default(context, &cc))
+ goto leave;
+
+ memset((char *) &my_creds, 0, sizeof(my_creds));
+
+ if (krb5_parse_name(context, name, &me)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ my_creds.client = me;
+
+ if (krb5_cc_initialize(context, cc, me)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (krb5_build_principal_ext(context, &server,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ krb5_princ_realm(context, me)->length,
+ krb5_princ_realm(context, me)->data,
+ 0)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ my_creds.server = server;
+
+ if (krb5_timeofday(context, &now)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ my_creds.times.starttime = 0;
+
+ krb5_appdefault_string(context, "ftpd", krb5_princ_realm(context, me),
+ "default_lifetime", KRB5_DEFAULT_LIFETIME,
+ &lifetimestring);
+
+ if (krb5_string_to_deltat(lifetimestring, &lifetime)) {
+ krb5_cc_destroy(context, cc);
+ free(lifetimestring);
+ goto leave;
+ }
+
+ free(lifetimestring);
+
+ my_creds.times.endtime = now + lifetime;
+ my_creds.times.renew_till = 0;
+
+ if (krb5_get_in_tkt_with_password(context, 0, 0, NULL, 0,
+ passwd, cc, &my_creds, 0)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ /*
+ * Ok, if we've reached this part successfully, then that means that
+ * we've gotten a valid ticket from the KDC. Now we try to get a
+ * service ticket from the KDC using a known key. In this case,
+ * we try "ftp" and "host". If neither of these principals are in
+ * the keytab, then let this user in anyway.
+ */
+
+ for (service = k5services; *service; service++) {
+ if (krb5_sname_to_principal(context, NULL, *service,
+ KRB5_NT_SRV_HST, &ver_princ)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (krb5_kt_read_service_key(context, NULL, ver_princ, 0,
+ ENCTYPE_DES_CBC_CRC, &kb) == 0)
+ break;
+ else {
+ krb5_free_principal(context, ver_princ);
+ ver_princ = NULL;
+ }
+ }
+
+
+ if (ver_princ == NULL) {
+ valid = 1;
+ goto leave;
+ }
+
+ /*
+ * The reason we're doing all of this is probably not very
+ * obvious. Here's the theory:
+ *
+ * Just getting a valid ticket from the KDC for that user isn't
+ * 100% secure, because if someone else is faking replies from the
+ * KDC, then they could forge a reponse that appeared to be valid,
+ * but actually wasn't. So what we do here is use a service
+ * ticket for the principal {ftp|host}/<host>} and generate an
+ * AP_REQ message (using krb5_mk_req()), and then use krb5_rd_req()
+ * to validate that message. Since the request generated by mk_req()
+ * was encrypted with the host or ftp principal's secret key, and
+ * that service ticket was generated by using the user's TGT,
+ * then if we can decode the mk_req() message, that means that
+ * the user's credentials are legitmate.
+ */
+
+ packet.data = NULL;
+
+ if (krb5_mk_req(context, &auth_context, 0, *service,
+ krb5_princ_component(context, ver_princ, 1)->data,
+ NULL, cc, &packet)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ if (auth_context) {
+ krb5_auth_con_free(context, auth_context);
+ auth_context = NULL;
+ }
+
+ if (krb5_rd_req(context, &auth_context, &packet, ver_princ, NULL,
+ NULL, NULL)) {
+ krb5_cc_destroy(context, cc);
+ goto leave;
+ }
+
+ /*
+ * _Whew_! Everything is cool!
+ */
+
+ valid = 1;
+
+leave:
+
+ if (packet.data)
+ krb5_xfree(packet.data);
+ if (auth_context)
+ krb5_auth_con_free(context, auth_context);
+ if (kb)
+ krb5_free_keyblock(context, kb);
+ if (my_creds.keyblock.contents)
+ krb5_free_cred_contents(context, &my_creds);
+ if (me)
+ krb5_free_principal(context, me);
+ if (server)
+ krb5_free_principal(context, server);
+ if (ver_princ)
+ krb5_free_principal(context, ver_princ);
+
+ krb5_free_context(context);
+
+ return(valid);
+}
+
#endif /* GSSAPI */
--- appl/gssftp/ftpd/configure.in.orig Thu Nov 7 16:06:23 1996
+++ appl/gssftp/ftpd/configure.in Wed Nov 13 00:25:54 1996
@@ -7,6 +7,8 @@
CHECK_UTMP
CHECK_SIGPROCMASK
CHECK_WAIT_TYPE
+CHECK_SIGNALS
+CHECK_SETJMP
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
@@ -17,6 +19,17 @@
AC_REPLACE_FUNCS(getdtablesize)
AC_HAVE_FUNCS(getcwd getusershell seteuid setreuid setresuid)
AC_CHECK_LIB(crypt,crypt) dnl
+dnl copied (mostly) from appl/bsd/configure.in
+AFSLIBS=
+AC_ARG_WITH([afs],
+[ --without-afs don't have afs libraries to build against (default)
+ --with-afs=AFSDIR use preinstalled AFS library tree],
+,with_afs=no)dnl
+if test $with_afs != no; then
+ AC_DEFINE(SETPAG)
+ AFSLIBS="$AFSLIBS -L$with_afs/lib -L$with_afs/lib/afs -lauth -lsys -lrx -llwp"
+fi
+AC_SUBST(AFSLIBS)
dnl
dnl copied from appl/bsd/configure.in
AC_MSG_CHECKING([setenv])
--- appl/gssftp/ftpd/Makefile.in.orig Thu Nov 7 16:09:43 1996
+++ appl/gssftp/ftpd/Makefile.in Wed Nov 13 15:50:45 1996
@@ -1,12 +1,13 @@
#
# appl/gssftp/ftpd/Makefile.in
#
-CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE) -DKPROGDIR=\"$(CLIENT_BINDIR)\"
SETENVSRC=@SETENVSRC@
SETENVOBJ=@SETENVOBJ@
LIBOBJS=@LIBOBJS@
COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+AFSLIBS=@AFSLIBS@
SRCS = ftpd.c ftpcmd.y logwtmp.c popen.c vers.c \
$(srcdir)../ftp/glob.c \
@@ -27,7 +28,7 @@
all:: ftpd
ftpd: $(OBJS) $(DEPKLIB)
- $(LD) $(LDFLAGS) $(LDARGS) -o $@ $(OBJS) $(KLIB) $(LIBS)
+ $(LD) $(LDFLAGS) $(LDARGS) -o $@ $(OBJS) $(KLIB) $(LIBS) $(AFSLIBS)
clean::
$(RM) ftpd ftpcmd.c
Show quoted text
>Audit-Trail:
>Unformatted:
>Unformatted: