[Date Prev] [Date Index] [Date Next] [Thread Prev] [Thread Index] [Thread Next]

[PATCH] GSS-API Authentication support

Nathan Straz nstraz@redhat.com
Thu, 7 Feb 2008 11:36:11 -0800 (PST)


Here is a patch that I'm working on.  It's not complete, but it works.

What works:
 - authentication against "host" principals.
 - built w/o openssl
What hasn't been tested
 - built w/ openssl
Still to do:
 - Switch to using "console" principals.
 - I'm using a sleep in conserver to wait for the token from console.  I
   think I should add a new I/O mode and switch to that.
 - the user logged in turns to "user@REALM@peername" which may have some
   side effects.
 
Give it a shot in a test environment and let me know if you run into any
problems.

Nate Straz
diff --git a/config.h.in b/config.h.in
index 1c3095c..a698b6b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -75,6 +75,9 @@
 /* Define to 1 if you have the `grantpt' function. */
 #undef HAVE_GRANTPT
 
+/* have gss-api support */
+#undef HAVE_GSSAPI
+
 /* Define to 1 if you have the <hpsecurity.h> header file. */
 #undef HAVE_HPSECURITY_H
 
diff --git a/configure b/configure
index 8f58bda..fb2bf67 100755
--- a/configure
+++ b/configure
@@ -868,6 +868,8 @@ Optional Packages:
                           Compile in libwrap (tcp_wrappers) support
   --with-openssl[=PATH]
                           Compile in OpenSSL support
+  --with-gssapi[=PATH]
+                          Compile in GSS-API support
   --with-dmalloc[=PATH]
                           Compile in dmalloc support
   --with-pam              Enable PAM support
@@ -6092,6 +6094,242 @@ fi
 
 fi;
 
+cons_with_gssapi="NO"
+
+# Check whether --with-gssapi or --without-gssapi was given.
+if test "${with_gssapi+set}" = set; then
+  withval="$with_gssapi"
+  if test "$withval" != "no"; then
+	if test "$withval" != "yes"; then
+	    GSSAPICPPFLAGS="-I$withval/include"
+	    if test "$use_dash_r" != "yes"; then
+		GSSAPILDFLAGS="-L$withval/lib"
+	    else
+		GSSAPILDFLAGS="-L$withval/lib -R$withval/lib"
+	    fi
+	else
+	    GSSAPICPPFLAGS=""
+	    GSSAPILDFLAGS=""
+	fi
+
+	oCPPFLAGS="$CPPFLAGS"
+	oLDFLAGS="$LDFLAGS"
+	oLIBS="$LIBS"
+	have_gssapi=no
+
+	CPPFLAGS="$CPPFLAGS $GSSAPICPPFLAGS"
+	LDFLAGS="$LDFLAGS $GSSAPILDFLAGS"
+
+	if test "${ac_cv_header_gssapi_gssapi_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for gssapi/gssapi.h" >&5
+echo $ECHO_N "checking for gssapi/gssapi.h... $ECHO_C" >&6
+if test "${ac_cv_header_gssapi_gssapi_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_gssapi_gssapi_h" >&5
+echo "${ECHO_T}$ac_cv_header_gssapi_gssapi_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking gssapi/gssapi.h usability" >&5
+echo $ECHO_N "checking gssapi/gssapi.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <gssapi/gssapi.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking gssapi/gssapi.h presence" >&5
+echo $ECHO_N "checking gssapi/gssapi.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <gssapi/gssapi.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for gssapi/gssapi.h" >&5
+echo $ECHO_N "checking for gssapi/gssapi.h... $ECHO_C" >&6
+if test "${ac_cv_header_gssapi_gssapi_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_gssapi_gssapi_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_gssapi_gssapi_h" >&5
+echo "${ECHO_T}$ac_cv_header_gssapi_gssapi_h" >&6
+
+fi
+if test $ac_cv_header_gssapi_gssapi_h = yes; then
+  LIBS="$LIBS -lgssapi"
+	    echo "$as_me:$LINENO: checking for gssapi library -lgssapi" >&5
+echo $ECHO_N "checking for gssapi library -lgssapi... $ECHO_C" >&6
+	    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <gssapi/gssapi.h>
+
+int
+main ()
+{
+gss_create_empty_oid_set(NULL, NULL)
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+		cons_with_gssapi="YES"
+		cat >>confdefs.h <<\_ACEOF
+#define HAVE_GSSAPI 1
+_ACEOF
+
+		have_gssapi=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+
+
+
+	if test $have_gssapi = no; then
+	    LIBS="$oLIBS"
+	    CPPFLAGS="$oCPPFLAGS"
+	    LDFLAGS="$oLDFLAGS"
+	fi
+    fi
+
+fi;
+
+
 cons_with_dmalloc="NO"
 
 # Check whether --with-dmalloc or --without-dmalloc was given.
@@ -8844,6 +9082,7 @@ echo ""
 echo "  Unix domain sockets (--with-uds)    : $cons_with_uds"
 echo "         TCP wrappers (--with-libwrap): $cons_with_libwrap"
 echo "              OpenSSL (--with-openssl): $cons_with_openssl"
+echo "              GSS-API (--with-gssapi) : $cons_with_gssapi"
 echo "              dmalloc (--with-dmalloc): $cons_with_dmalloc"
 echo "          PAM support (--with-pam)    : $cons_with_pam"
 echo ""
diff --git a/configure.in b/configure.in
index 8bd2620..c845c7e 100644
--- a/configure.in
+++ b/configure.in
@@ -14,6 +14,7 @@ AH_TEMPLATE([USE_LIBWRAP], [use tcp_wrappers libwrap])
 dnl AH_TEMPLATE([HAVE_POSIX_REGCOMP], [have POSIX regcomp])
 AH_TEMPLATE([HAVE_PAM], [have PAM support])
 AH_TEMPLATE([HAVE_OPENSSL], [have openssl support])
+AH_TEMPLATE([HAVE_GSSAPI], [have gss-api support])
 AH_TEMPLATE([HAVE_DMALLOC], [have dmalloc support])
 AH_TEMPLATE([HAVE_SA_LEN],[Defined if sa_len member exists in struct sockaddr])
 AH_TEMPLATE([TRUST_REVERSE_DNS],[Defined if we trust reverse DNS])
@@ -499,6 +500,51 @@ AC_ARG_WITH(openssl,
     fi]
 )
 
+cons_with_gssapi="NO"
+AC_ARG_WITH(gssapi,
+    AS_HELP_STRING([--with-gssapi@<:@=PATH@:>@],
+	[Compile in GSS-API support]),
+    [if test "$withval" != "no"; then
+	if test "$withval" != "yes"; then
+	    GSSAPICPPFLAGS="-I$withval/include"
+	    if test "$use_dash_r" != "yes"; then
+		GSSAPILDFLAGS="-L$withval/lib"
+	    else
+		GSSAPILDFLAGS="-L$withval/lib -R$withval/lib"
+	    fi
+	else
+	    GSSAPICPPFLAGS=""
+	    GSSAPILDFLAGS=""
+	fi
+
+	oCPPFLAGS="$CPPFLAGS"
+	oLDFLAGS="$LDFLAGS"
+	oLIBS="$LIBS"
+	have_gssapi=no
+
+	CPPFLAGS="$CPPFLAGS $GSSAPICPPFLAGS"
+	LDFLAGS="$LDFLAGS $GSSAPILDFLAGS"
+
+	AC_CHECK_HEADER([gssapi/gssapi.h],
+	    [LIBS="$LIBS -lgssapi"
+	    AC_MSG_CHECKING(for gssapi library -lgssapi)
+	    AC_TRY_LINK([#include <gssapi/gssapi.h>
+		],[gss_create_empty_oid_set(NULL, NULL)],
+		[AC_MSG_RESULT(yes)
+		cons_with_gssapi="YES"
+		AC_DEFINE(HAVE_GSSAPI)
+		have_gssapi=yes],
+		[AC_MSG_RESULT(no)])],)
+
+	if test $have_gssapi = no; then
+	    LIBS="$oLIBS"
+	    CPPFLAGS="$oCPPFLAGS"
+	    LDFLAGS="$oLDFLAGS"
+	fi
+    fi]
+)
+
+
 cons_with_dmalloc="NO"
 AC_ARG_WITH(dmalloc,
     AS_HELP_STRING([--with-dmalloc@<:@=PATH@:>@],
@@ -657,6 +703,7 @@ echo ""
 echo "  Unix domain sockets (--with-uds)    : $cons_with_uds"
 echo "         TCP wrappers (--with-libwrap): $cons_with_libwrap"
 echo "              OpenSSL (--with-openssl): $cons_with_openssl"
+echo "              GSS-API (--with-gssapi) : $cons_with_gssapi"
 echo "              dmalloc (--with-dmalloc): $cons_with_dmalloc"
 echo "          PAM support (--with-pam)    : $cons_with_pam"
 echo ""
diff --git a/conserver/cutil.h b/conserver/cutil.h
index a9b579a..f6e1a45 100644
--- a/conserver/cutil.h
+++ b/conserver/cutil.h
@@ -15,6 +15,9 @@
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
+#if HAVE_GSSAPI
+#include <gssapi/gssapi.h>
+#endif
 
 /* communication constants
  */
diff --git a/conserver/group.c b/conserver/group.c
index ea6bd76..c0ac61c 100644
--- a/conserver/group.c
+++ b/conserver/group.c
@@ -1869,6 +1869,65 @@ AttemptSSL(pCL)
 }
 #endif
 
+#if HAVE_GSSAPI
+int
+#if PROTOTYPES
+AttemptGSSAPI(CONSCLIENT *pCL)
+#else
+AttemptGSSAPI(pCL)
+    CONSCLIENT *pCL;
+#endif
+{
+    int nr, ret = 0;
+    char buf[1024];
+    gss_buffer_desc sendtok, recvtok, dbuf;
+    gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
+    OM_uint32 stmaj, stmin, mctx, dmin;
+    gss_name_t user = 0;
+
+    sleep(1); /* XXX: probably need an IO mode to remove this */
+    if ((nr = FileRead(pCL->fd, buf, sizeof(buf))) < 0) {
+	return 0;
+    }
+    recvtok.value = buf;
+    recvtok.length = nr;
+
+    stmaj = gss_accept_sec_context(&stmin, &gssctx, gss_mycreds,
+		    &recvtok, NULL, &user, NULL, &sendtok, NULL, NULL,  NULL);
+    switch (stmaj) {
+	    case GSS_S_COMPLETE:
+		    FileSetQuoteIAC(pCL->fd, FLAGFALSE);
+		    FileWrite(pCL->fd, FLAGFALSE, sendtok.value, sendtok.length);
+		    FileSetQuoteIAC(pCL->fd, FLAGTRUE);
+		    pCL->iState = S_NORMAL;
+		    gss_release_buffer(NULL, &sendtok);
+		    BuildString((char *)0, pCL->username);
+		    BuildString((char *)0, pCL->acid);
+		    stmaj = gss_display_name(&stmin, user, &dbuf, NULL);
+
+		    BuildStringN(dbuf.value, dbuf.length, pCL->username);
+		    BuildStringN(dbuf.value, dbuf.length, pCL->acid);
+		    BuildStringChar('@', pCL->acid);
+		    BuildString(pCL->peername->string,
+				pCL->acid);
+		    gss_release_name(&stmin, &user);
+		    gss_release_buffer(NULL, &dbuf);
+		    ret = 1;
+		    break;
+	    case GSS_S_CREDENTIALS_EXPIRED:
+		    /* reacquire creds and try again */
+		    Error("Credentials expired");
+		    break;
+	    default:
+		    do {
+	    		gss_display_status(&dmin, stmaj, GSS_C_GSS_CODE, GSS_C_NULL_OID, &mctx, &dbuf);
+	    		Error("GSSAPI didn't work, %*s", dbuf.length, dbuf.value);
+		    } while (mctx && dbuf.length);
+    }
+    return ret;
+}
+#endif
+
 CONSENT *
 #if PROTOTYPES
 HuntForConsole(GRPENT *pGE, char *name)
@@ -2945,6 +3004,7 @@ DoClientRead(pGE, pCLServing)
 		static char *pcArgs;
 		static char *pcCmd;
 
+		CONDDEBUG((1, "state = %d", pCLServing->iState));
 		if ('\n' != acIn[i]) {
 		    BuildStringChar(acIn[i], pCLServing->accmd);
 		    continue;
@@ -2993,6 +3053,9 @@ DoClientRead(pGE, pCLServing)
 #if HAVE_OPENSSL
 			"ssl    start ssl session\r\n",
 #endif
+#if HAVE_GSSAPI
+			"gssapi log in with gssapi\r\n",
+#endif
 			(char *)0
 		    };
 		    static char *apcHelp2[] = {
@@ -3033,6 +3096,16 @@ DoClientRead(pGE, pCLServing)
 			return;
 		    }
 #endif
+#if HAVE_GSSAPI
+		} else if (pCLServing->iState == S_IDENT &&
+			   strcmp(pcCmd, "gssapi") == 0) {
+		    FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1);
+		    if (!AttemptGSSAPI(pCLServing)) {
+			DisconnectClient(pGE, pCLServing, (char *)0,
+					 FLAGFALSE);
+			return;
+		    }
+#endif
 		} else if (pCLServing->iState == S_IDENT &&
 			   strcmp(pcCmd, "login") == 0) {
 #if HAVE_OPENSSL
@@ -3267,6 +3340,7 @@ DoClientRead(pGE, pCLServing)
 		} else {
 		    FileWrite(pCLServing->fd, FLAGFALSE,
 			      "unknown command\r\n", -1);
+		    CONDDEBUG((1, "command %s state %d", pcCmd, pCLServing->iState));
 		}
 		BuildString((char *)0, pCLServing->accmd);
 	    } else
diff --git a/conserver/main.c b/conserver/main.c
index 38b66dd..24fbcbe 100644
--- a/conserver/main.c
+++ b/conserver/main.c
@@ -44,6 +44,9 @@
 #if HAVE_OPENSSL
 # include <openssl/opensslv.h>
 #endif
+#if HAVE_GSSAPI
+# include <gssapi/gssapi.h>
+#endif
 
 
 int fAll = 0, fNoinit = 0, fVersion = 0, fStrip = 0, fReopen =
@@ -378,6 +381,40 @@ SetupSSL()
 }
 #endif
 
+#if HAVE_GSSAPI
+gss_name_t gss_myname = GSS_C_NO_NAME;
+gss_cred_id_t gss_mycreds = GSS_C_NO_CREDENTIAL;
+
+void
+#if PROTOTYPES
+SetupGSSAPI(void)
+#else
+SetupGSSAPI()
+#endif
+{
+    OM_uint32 stmaj, stmin;
+    char namestr[128];
+    gss_buffer_desc namebuf;
+
+    snprintf(namestr, 128, "host@%s", myHostname);
+    namebuf.value = namestr;
+    namebuf.length = strlen(namestr) + 1;
+    stmaj = gss_import_name(&stmin, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, 
+		    &gss_myname);
+    /* XXX: handle error */
+    if (stmaj != GSS_S_COMPLETE) {
+	Error("gss_import_name failed");
+    }
+    /* Get some initial credentials */
+    stmaj = gss_acquire_cred(&stmin, gss_myname, 0, GSS_C_NULL_OID_SET,
+		    GSS_C_ACCEPT, &gss_mycreds, NULL, NULL);
+    if (stmaj != GSS_S_COMPLETE) {
+	Error("Could not acquire GSS-API credentials");
+    }
+    
+}
+#endif
+
 void
 #if PROTOTYPES
 ReopenLogfile(void)
@@ -1563,6 +1600,9 @@ main(argc, argv)
 	/* Prep the SSL layer */
 	SetupSSL();
 #endif
+#if HAVE_GSSAPI
+	SetupGSSAPI();
+#endif
 
 	if (config->daemonmode == FLAGTRUE)
 	    Daemonize();
diff --git a/conserver/main.h b/conserver/main.h
index 1b59a5a..aae8a10 100644
--- a/conserver/main.h
+++ b/conserver/main.h
@@ -54,6 +54,10 @@ extern char *interface;
 #if HAVE_OPENSSL
 extern SSL_CTX *ctx;
 #endif
+#if HAVE_GSSAPI
+extern gss_name_t gss_myname;
+extern gss_cred_id_t gss_mycreds;
+#endif
 extern void ReopenLogfile PARAMS((void));
 extern void ReopenUnifiedlog PARAMS((void));
 extern void DumpDataStructures PARAMS((void));
diff --git a/conserver/master.c b/conserver/master.c
index 36622cc..ed838ef 100644
--- a/conserver/master.c
+++ b/conserver/master.c
@@ -494,6 +494,9 @@ DoNormalRead(pCLServing)
 #if HAVE_OPENSSL
 		    "ssl    start ssl session\r\n",
 #endif
+#if HAVE_GSSAPI
+		    "gssapi log in with gssapi\r\n",
+#endif
 		    (char *)0
 		};
 		static char *apcHelp2[] = {
@@ -532,6 +535,15 @@ DoNormalRead(pCLServing)
 		    return;
 		}
 #endif
+#if HAVE_GSSAPI
+	    } else if (pCLServing->iState == S_IDENT &&
+		       strcmp(pcCmd, "gssapi") == 0) {
+		FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1);
+		if (!AttemptGSSAPI(pCLServing)) {
+		    DropMasterClient(pCLServing, FLAGFALSE);
+		    return;
+		}
+#endif
 	    } else if (pCLServing->iState == S_IDENT &&
 		       strcmp(pcCmd, "login") == 0) {
 #if HAVE_OPENSSL
diff --git a/console/console.c b/console/console.c
index 4ec949b..d9be01f 100644
--- a/console/console.c
+++ b/console/console.c
@@ -40,6 +40,9 @@
 #include <openssl/err.h>
 #include <openssl/opensslv.h>
 #endif
+#if HAVE_GSSAPI
+#include <gssapi/gssapi.h>
+#endif
 
 
 int fReplay = 0, fVersion = 0;
@@ -152,6 +155,83 @@ AttemptSSL(pcf)
 }
 #endif
 
+#if HAVE_GSSAPI
+gss_name_t gss_server_name = GSS_C_NO_NAME;
+gss_ctx_id_t secctx = GSS_C_NO_CONTEXT;
+gss_buffer_desc mytok = GSS_C_EMPTY_BUFFER;
+
+int
+#if PROTOTYPES
+CanGetGSSContext(const char *servername)
+#else
+CanGetGSSContext(servername)
+    const char *servername;
+#endif
+{
+    char namestr[128];
+    gss_buffer_desc namebuf, dbuf;
+    OM_uint32 stmaj, stmin, mctx, dmin;
+    
+    snprintf(namestr, 128, "host@%s", servername);
+    namebuf.value = namestr;
+    namebuf.length = strlen(namestr) + 1;
+    stmaj = gss_import_name(&stmin, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, 
+		    &gss_server_name);
+    /* XXX: handle error */
+    if (stmaj != GSS_S_COMPLETE) {
+	Error("gss_import_name failed");
+	return 0;
+    }
+    secctx = GSS_C_NO_CONTEXT;
+    mytok.length = 0; mytok.value = NULL;
+
+    stmaj = gss_init_sec_context(&stmin, GSS_C_NO_CREDENTIAL, &secctx,
+		    gss_server_name, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG, 0,
+		    GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
+		    &mytok, NULL, NULL);
+
+    if (stmaj != GSS_S_COMPLETE && stmaj != GSS_S_CONTINUE_NEEDED) {
+	do {
+	    gss_display_status(&dmin, stmaj, GSS_C_GSS_CODE, GSS_C_NULL_OID, &mctx, &dbuf);
+	    Error("init sec context failed: %*s", dbuf.length, dbuf.value);
+	} while (mctx && dbuf.length);
+	return 0;
+    } 
+}
+
+int
+#if PROTOTYPES
+AttemptGSSAPI(CONSFILE *pcf)
+#else
+AttemptGSSAPI(pcf)
+	CONSFILE *pcf;
+#endif
+{
+    OM_uint32 stmaj, stmin;
+    gss_buffer_desc servertok;
+    char buf[1024];
+    int nr;
+    int ret;
+
+    FileSetQuoteIAC(pcf, FLAGFALSE);
+    FileWrite(pcf, FLAGFALSE, mytok.value, mytok.length);
+    FileSetQuoteIAC(pcf, FLAGTRUE);
+    nr = FileRead(pcf, buf, sizeof(buf));
+    servertok.length = nr;
+    servertok.value = buf;
+
+    stmaj = gss_init_sec_context(&stmin, GSS_C_NO_CREDENTIAL, &secctx,
+		    gss_server_name, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG, 0,
+		    GSS_C_NO_CHANNEL_BINDINGS, &servertok,
+		    NULL, &mytok, NULL, NULL);
+    gss_release_buffer(NULL, &mytok);
+
+    ret = (stmaj == GSS_S_COMPLETE);
+    gss_release_name(&stmin, &gss_server_name);
+    return ret;
+}
+#endif
+
 /* output a control (or plain) character as a UNIX user would expect it	(ksb)
  */
 static void
@@ -271,6 +351,9 @@ Version()
 #if HAVE_OPENSSL
 	"openssl",
 #endif
+#if HAVE_GSSAPI
+	"gssapi",
+#endif
 #if HAVE_PAM
 	"pam",
 #endif
@@ -1522,6 +1605,9 @@ DoCmds(master, pports, cmdi)
     char *ports;
     char *pcopy;
     char *serverName;
+#if HAVE_GSSAPI
+    int toksize;
+#endif
 
     if ((pcopy = ports = StrDup(pports)) == (char *)0)
 	OutOfMem();
@@ -1599,6 +1685,17 @@ DoCmds(master, pports, cmdi)
 	    }
 	}
 #endif
+#if HAVE_GSSAPI
+	if ((toksize = CanGetGSSContext(server)) > 0) {
+	    FilePrint(pcf, FLAGFALSE, "gssapi %d\r\n", toksize);
+	    t = ReadReply(pcf, FLAGFALSE);
+	    if (strcmp(t, "ok\r\n") == 0) {
+		if (AttemptGSSAPI(pcf)) {
+		    goto gssapi_logged_me_in;
+		}
+	    } 
+	}
+#endif
 
 	FilePrint(pcf, FLAGFALSE, "login %s\r\n", config->username);
 
@@ -1651,6 +1748,9 @@ DoCmds(master, pports, cmdi)
 	    FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t);
 	    continue;
 	}
+#if HAVE_GSSAPI
+gssapi_logged_me_in:
+#endif
 
 	/* now that we're logged in, we can do something */
 	/* if we're on the last cmd or the command is 'call' and we