Index: configure.ac
===================================================================
RCS file: /cvsroot/lsh/lsh/configure.ac,v
retrieving revision 1.45
diff -u -p -r1.45 configure.ac
--- configure.ac	19 Sep 2003 14:58:32 -0000	1.45
+++ configure.ac	19 Sep 2003 21:40:25 -0000
@@ -102,6 +102,10 @@ AC_ARG_ENABLE(srp,
   AC_HELP_STRING([--disable-srp], [Disable the (experimental) support for SRP]),,
   [enable_srp=yes])
 
+AC_ARG_ENABLE(gss,
+  AC_HELP_STRING([--disable-gss], [Disable the (experimental) support for GSS]),,
+  [enable_gss=yes])
+
 AC_ARG_ENABLE(kerberos,
   AC_HELP_STRING([--disable-kerberos], [Don't support kerberos]),,
   [enable_kerberos=yes])
@@ -343,7 +347,35 @@ if test x$have_utmpx_h = xyes; then
 ])
 fi
 
-
+AH_TEMPLATE([WITH_GSS_K5], [Whether to use gss K5 authorization (Heimdal/MIT)])
+if test x$enable_gss != xno; then
+  if test x$enable_gss = xk5; then
+    AC_CHECK_PROG(KRB5CONFIG, krb5-config, krb5-config, no)
+    if test x$KRB5CONFIG != xno; then
+      CPPFLAGS="$CPPFLAGS `$KRB5CONFIG --cflags gssapi`"
+      LIBS="`$KRB5CONFIG --libs gssapi`"
+      AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h])
+      AC_DEFINE(WITH_GSS_K5)
+      AC_CHECK_DECL(GSS_C_NT_HOSTBASED_SERVICE,, [
+	AC_DEFINE(GSS_C_NT_HOSTBASED_SERVICE,
+		gss_nt_service_name,
+		[Work around buggy MIT library])], [
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+])
+    fi
+  else
+    AC_CHECK_HEADERS(gss.h,, [enable_gss=no])
+    AC_CHECK_LIB(gss, gss_check_version,, [enable_gss=no])
+  fi
+fi
 
 if test x$enable_kerberos = xyes; then
   AC_CHECK_HEADERS(krb5.h,, [enable_kerberos=no])
@@ -457,6 +489,12 @@ AH_TEMPLATE([WITH_ZLIB], [Define if zlib
 # Should we use zlib?
 if test x$with_zlib = xyes ; then
   AC_DEFINE(WITH_ZLIB)
+fi
+
+AH_TEMPLATE([WITH_GSS], [Define if gss should be used])
+# Should we use gss?
+if test x$enable_gss != xno ; then
+  AC_DEFINE(WITH_GSS)
 fi
 
 # The kerberos libraries are needed only to support the
Index: src/.dist_classes
===================================================================
RCS file: /cvsroot/lsh/lsh/src/.dist_classes,v
retrieving revision 1.10
diff -u -p -r1.10 .dist_classes
--- src/.dist_classes	5 Jun 2003 14:48:28 -0000	1.10
+++ src/.dist_classes	19 Sep 2003 21:40:25 -0000
@@ -72,6 +72,7 @@ dist_classes = \
     rsa.c.x \
     server.c.x \
     server_authorization.c.x \
+    server_gssapi.c.x \
     server_keyexchange.c.x \
     server_password.c.x \
     server_pty.h.x \
Index: src/Makefile.am
===================================================================
RCS file: /cvsroot/lsh/lsh/src/Makefile.am,v
retrieving revision 1.14
diff -u -p -r1.14 Makefile.am
--- src/Makefile.am	6 Jun 2003 18:25:02 -0000	1.14
+++ src/Makefile.am	19 Sep 2003 21:40:25 -0000
@@ -82,7 +82,7 @@ liblsh_a_SOURCES = abstract_io.c abstrac
 	reaper.c resource.c \
 	rsa.c rsa_keygen.c \
 	server.c server_authorization.c server_keyexchange.c \
-	server_password.c server_publickey.c \
+	server_password.c server_publickey.c server_gssapi.c \
 	server_pty.c server_session.c server_userauth.c \
 	server_x11.c sexp.c \
 	spki.c srp_exchange.c ssh1_fallback.c \
Index: src/atoms.in
===================================================================
RCS file: /cvsroot/lsh/lsh/src/atoms.in,v
retrieving revision 1.30
diff -u -p -r1.30 atoms.in
--- src/atoms.in	22 Apr 2003 11:57:11 -0000	1.30
+++ src/atoms.in	19 Sep 2003 21:40:25 -0000
@@ -76,6 +76,7 @@ ssh-connection
 publickey
 password
 hostbased
+gssapi
 
 # Channel related
 
Index: src/client_userauth.c
===================================================================
RCS file: /cvsroot/lsh/lsh/src/client_userauth.c,v
retrieving revision 1.54
diff -u -p -r1.54 client_userauth.c
--- src/client_userauth.c	19 Sep 2003 14:20:22 -0000	1.54
+++ src/client_userauth.c	19 Sep 2003 21:40:26 -0000
@@ -27,6 +27,23 @@
 
 #include <assert.h>
 
+#if WITH_GSS
+#if WITH_GSS_K5
+#include <krb5.h> /* for guserok */
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+#else /* !WITH_GSS_K5 */
+#include <gss.h>
+#endif /* WITH_GSS_K5 */
+#endif /* WITH_GSS */
+
 #include "client_userauth.h"
 
 #include "charset.h"
@@ -964,3 +981,463 @@ make_client_publickey_auth(struct object
   
   return &self->super;
 }
+
+/* GSSAPI authentication. */
+
+#if WITH_GSS
+
+static struct lsh_string *
+get_status_1 (OM_uint32 code, int type)
+{
+  OM_uint32 maj_stat, min_stat;
+  struct lsh_string *str = NULL;
+  gss_buffer_desc msg;
+  OM_uint32 msg_ctx = 0;
+
+  do
+    {
+      maj_stat = gss_display_status (&min_stat, code, type, GSS_C_NULL_OID,
+				     &msg_ctx, &msg);
+      if (!GSS_ERROR(maj_stat))
+	{
+	  if (str)
+	    str = ssh_format("%flS\n%ls", str, msg.length, (char *) msg.value);
+	  else
+	    str = ssh_format("%ls", msg.length, (char *) msg.value);
+	}
+      gss_release_buffer (&min_stat, &msg);
+    }
+  while (msg_ctx);
+
+  return str;
+}
+
+static struct lsh_string *
+get_status (OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+  return ssh_format ("%flS\n%flS", get_status_1 (maj_stat, GSS_C_GSS_CODE),
+		     get_status_1 (min_stat, GSS_C_MECH_CODE));
+}
+
+/* GABA:
+   (class
+     (name client_gssapi_method)
+     (condition "WITH_GSS")
+     (super client_userauth_method)
+     (vars
+       (hostname . "const char *")))
+*/
+
+/* GABA:
+   (class
+     (name client_gssapi_state)
+     (condition "WITH_GSS")
+     (super client_userauth_failure)
+     (vars
+       (userauth object client_userauth)
+       (connection object ssh_connection)
+       (e object exception_handler)
+       (name . gss_name_t)
+       (ctx . gss_ctx_id_t)))
+*/
+
+static void
+do_gssapi_failure(struct client_userauth_failure *s, int again UNUSED)
+{
+  CAST(client_gssapi_state, self, s);
+  static const struct exception gssapi_not_useful =
+    STATIC_EXCEPTION(EXC_USERAUTH, "gssapi authentication not useful.");
+
+  EXCEPTION_RAISE(self->e, &gssapi_not_useful);
+}
+
+static void
+do_gc_gssapi (struct client_gssapi_state *gssapi)
+{
+  OM_uint32 maj_stat, min_stat;
+
+  if (gssapi->ctx)
+    {
+      maj_stat = gss_delete_sec_context (&min_stat, &gssapi->ctx,
+					 GSS_C_NO_BUFFER);
+      if (GSS_ERROR(maj_stat))
+	verbose("GSSAPI error deleting security context: %fS\n",
+		get_status(maj_stat, min_stat));
+      gssapi->ctx = NULL;
+    }
+
+  if (gssapi->name)
+    {
+      maj_stat = gss_release_name (&min_stat, &gssapi->name);
+      if (GSS_ERROR(maj_stat))
+	verbose("GSSAPI error deleting security context: %fS\n",
+		get_status(maj_stat, min_stat));
+      gssapi->name = NULL;
+    }
+}
+
+static struct client_gssapi_state *
+make_client_gssapi_state(struct client_userauth *userauth,
+			 struct ssh_connection *connection,
+			 struct exception_handler *e)
+{
+  NEW(client_gssapi_state, self);
+  self->super.failure = do_gssapi_failure;
+  self->userauth = userauth;
+  self->connection = connection;
+  self->e = e;
+  self->name = NULL;
+  self->ctx = GSS_C_NO_CONTEXT;
+
+  return self;
+}
+
+/* GABA:
+   (class
+     (name userauth_gssapi_token_handler)
+     (condition "WITH_GSS")
+     (super packet_handler)
+     (vars
+       (state object client_gssapi_state))) */
+
+static struct lsh_string *
+format_userauth_gssapi_token(uint32_t len, char *data)
+{
+  return ssh_format("%c%s", SSH_MSG_USERAUTH_GSSAPI_TOKEN, len, data);
+}
+
+static void
+do_gssapi_token(struct packet_handler *s,
+		   struct ssh_connection *connection,
+		   struct lsh_string *packet UNUSED)
+{
+  CAST(userauth_gssapi_token_handler, self, s);
+  OM_uint32 maj_stat, min_stat;
+  gss_buffer_desc inbuf, bufdesc2;
+
+  /* 1 for the SSH message code and 4 for the uint32_t length */
+#define GSS_TOKEN_OFFSET (1 + 4)
+  inbuf.value = packet->data + GSS_TOKEN_OFFSET;
+  inbuf.length = packet->length - GSS_TOKEN_OFFSET;
+  bufdesc2.value = NULL;
+  bufdesc2.length = 0;
+
+  maj_stat = gss_init_sec_context (&min_stat,
+				   GSS_C_NO_CREDENTIAL,
+				   &self->state->ctx,
+				   self->state->name,
+				   GSS_C_NO_OID,
+				   GSS_C_MUTUAL_FLAG |
+				   GSS_C_REPLAY_FLAG |
+				   GSS_C_SEQUENCE_FLAG,
+				   0,
+				   GSS_C_NO_CHANNEL_BINDINGS,
+				   &inbuf,
+				   NULL,
+				   &bufdesc2,
+				   NULL,
+				   NULL);
+  if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+    {
+      static const struct exception gssapi_init_sec_context =
+	STATIC_EXCEPTION(EXC_USERAUTH, "GSS authentication failed.");
+      verbose("GSS authentication failed: %fS\n",
+	      get_status(maj_stat, min_stat));
+      EXCEPTION_RAISE(self->state->e, &gssapi_init_sec_context);
+      connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
+	&connection_unimplemented_handler;
+      return;
+    }
+
+  verbose("Sending GSS token.\n");
+
+  if (bufdesc2.length > 0)
+    C_WRITE(connection,
+	    format_userauth_gssapi_token(bufdesc2.length, bufdesc2.value));
+
+  if (maj_stat == GSS_S_COMPLETE)
+    {
+      verbose("GSS authentication done.\n");
+      connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
+	&connection_unimplemented_handler;
+      C_WRITE(connection,
+	      ssh_format("%c", SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE));
+    }
+}
+
+static struct packet_handler *
+make_gssapi_token_handler(struct client_gssapi_state *state)
+{
+  NEW(userauth_gssapi_token_handler, self);
+
+  self->super.handler = do_gssapi_token;
+  self->state = state;
+
+  return &self->super;
+}
+
+/* GABA:
+   (class
+     (name userauth_gssapi_response_handler)
+     (condition "WITH_GSS")
+     (super packet_handler)
+     (vars
+       (state object client_gssapi_state))) */
+
+static void
+do_gssapi_response(struct packet_handler *s,
+		   struct ssh_connection *connection,
+		   struct lsh_string *packet UNUSED)
+{
+  CAST(userauth_gssapi_response_handler, self, s);
+  OM_uint32 maj_stat, min_stat;
+  gss_buffer_desc bufdesc;
+
+  connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_RESPONSE] =
+    &connection_unimplemented_handler;
+
+  bufdesc.value = NULL;
+  bufdesc.length = 0;
+
+  maj_stat = gss_init_sec_context (&min_stat,
+				   GSS_C_NO_CREDENTIAL,
+				   &self->state->ctx,
+				   self->state->name,
+				   GSS_C_NO_OID,
+				   GSS_C_MUTUAL_FLAG |
+				   GSS_C_REPLAY_FLAG |
+				   GSS_C_SEQUENCE_FLAG,
+				   0,
+				   GSS_C_NO_CHANNEL_BINDINGS,
+				   NULL,
+				   NULL,
+				   &bufdesc,
+				   NULL,
+				   NULL);
+  if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+    {
+      static const struct exception gssapi_init_sec_context =
+	STATIC_EXCEPTION(EXC_USERAUTH, "GSS authentication failed.");
+      verbose("GSS authentication failed: %fS\n",
+	      get_status(maj_stat, min_stat));
+      EXCEPTION_RAISE(self->state->e, &gssapi_init_sec_context);
+      return;
+    }
+
+  verbose("Sending initial GSS token.\n");
+
+  if (bufdesc.length > 0)
+    C_WRITE(connection,
+	    format_userauth_gssapi_token(bufdesc.length, bufdesc.value));
+
+  if (maj_stat == GSS_S_COMPLETE)
+    {
+      verbose("GSS authentication done.\n");
+      C_WRITE(connection,
+	      ssh_format("%c", SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE));
+    }
+  else
+    connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
+      make_gssapi_token_handler(self->state);
+}
+
+static struct packet_handler *
+make_gssapi_response_handler(struct client_gssapi_state *state)
+{
+  NEW(userauth_gssapi_response_handler, self);
+
+  self->super.handler = do_gssapi_response;
+  self->state = state;
+
+  return &self->super;
+}
+
+static struct lsh_string *
+format_userauth_gssapi_request(struct lsh_string *name,
+			       uint32_t service,
+			       uint32_t n,
+			       struct lsh_string *oids)
+{
+  return ssh_format("%c%S%a%a%i%S", SSH_MSG_USERAUTH_REQUEST,
+		    name, service, ATOM_GSSAPI, n, oids);
+}
+
+#if !WITH_GSS_K5
+static struct lsh_string *
+format_supported_oids (gss_OID_set mechs)
+{
+  size_t i;
+  gss_OID mech;
+  struct lsh_string *out = NULL;
+
+  for (i = 0, mech = mechs->elements; i < mechs->count; i++, mech++)
+    {
+      if (mech->length > 255)
+	{
+	  verbose("Skipping too long GSS OID %S\n",
+		  ssh_format("%lxs", mech->length, mech->elements));
+	  continue;
+	}
+
+      if (out)
+	out = ssh_format("%fS\x06%c%ls", out, mech->length, mech->length,
+			 mech->elements);
+      else
+	out = ssh_format("\x06%c%ls", mech->length, mech->length,
+			 mech->elements);
+    }
+
+  return out;
+}
+#endif
+
+static void
+maybe_cleanup (struct ssh_connection *connection)
+{
+
+  if (connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_RESPONSE] !=
+      &connection_fail_handler)
+    {
+      verbose("Cleaning up attempted GSS authentication.\n");
+      CAST(userauth_gssapi_response_handler, response_handler,
+	   connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_RESPONSE]);
+      do_gc_gssapi(response_handler->state);
+      KILL(response_handler);
+    }
+
+  if (connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] !=
+      &connection_unimplemented_handler)
+    {
+      verbose("Cleaning up GSS authentication attempt.\n");
+      CAST(userauth_gssapi_token_handler, token_handler,
+	   connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN]);
+      do_gc_gssapi(token_handler->state);
+      KILL(token_handler);
+    }
+}
+
+static void
+send_gssapi(struct client_userauth_method *s,
+	    struct client_userauth *userauth,
+	    struct ssh_connection *connection,
+	    struct exception_handler *e UNUSED,
+	    struct client_gssapi_state *state)
+{
+  CAST(client_gssapi_method, self, s);
+  OM_uint32 maj_stat, min_stat;
+  gss_buffer_desc bufdesc;
+  struct lsh_string *oids = NULL;
+  size_t noids;
+#if !WITH_GSS_K5
+  gss_OID_set mechs;
+#endif
+
+  maybe_cleanup (connection);
+
+  bufdesc.value =
+    (char*) lsh_get_cstring (ssh_format ("host@%lz", self->hostname));
+  bufdesc.length = strlen(bufdesc.value);
+
+  maj_stat = gss_import_name (&min_stat, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
+			      &state->name);
+  if (GSS_ERROR(maj_stat))
+    {
+      static const struct exception gssapi_import_name =
+	STATIC_EXCEPTION(EXC_USERAUTH,
+			 "GSS error importing hostname.");
+      verbose("GSSAPI error importing name: %fS\n",
+	      get_status(maj_stat, min_stat));
+      EXCEPTION_RAISE(state->e, &gssapi_import_name);
+      return;
+    }
+
+#if WITH_GSS_K5
+  /* MIT Kerberos 5 and Heimdal do not implement GSSAPI version 2,
+     so we hard code the Kerberos 5 OID. */
+  noids = 1;
+  oids = ssh_format("\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02");
+#else
+  /* XXX we probably shouldn't do this, but I refuse to implement
+     english with four negations; draft-ietf-secsh-gsskeyex-06.txt:
+     The client SHOULD NOT send more then one gssapi mechanism OID
+     unless there are no non-GSSAPI authentication methods between the
+     GSSAPI mechanisms in the order of preference, otherwise,
+     authentication methods may be executed out of order.
+  */
+  maj_stat = gss_inquire_mechs_for_name (&min_stat, state->name, &mechs);
+  if (GSS_ERROR(maj_stat))
+    {
+      static const struct exception gssapi_inquire_mechs =
+	STATIC_EXCEPTION(EXC_USERAUTH,
+			 "GSS error inquiring mechanisms.");
+      verbose("GSSAPI error inquiring mechanisms: %fS\n",
+	      get_status(maj_stat, min_stat));
+      EXCEPTION_RAISE(state->e, &gssapi_inquire_mechs);
+      return;
+    }
+
+  if (mechs->count > 0)
+    {
+      noids = mechs->count;
+      oids = format_supported_oids(mechs);
+    }
+
+  maj_stat = gss_release_oid_set (&min_stat, &mechs);
+  if (GSS_ERROR(maj_stat))
+    {
+      static const struct exception gssapi_release_oids =
+	STATIC_EXCEPTION(EXC_USERAUTH,
+			 "GSS error releasing OID set.");
+      verbose("GSSAPI error releasing OID set: %fS\n",
+	      get_status(maj_stat, min_stat));
+      EXCEPTION_RAISE(state->e, &gssapi_release_oids);
+      return;
+    }
+
+  if (!oids)
+    {
+      static const struct exception no_gssapi =
+	STATIC_EXCEPTION(EXC_USERAUTH,
+			 "GSSAPI do not support hostbased authentication.");
+      verbose("GSSAPI do not support hostbased authentication\n");
+      EXCEPTION_RAISE(state->e, &no_gssapi);
+      return;
+    }
+#endif
+
+  verbose("Sending list of GSS mechanisms we support.\n");
+
+  C_WRITE(connection,
+	  format_userauth_gssapi_request(userauth->username,
+					 userauth->service_name,
+					 noids, oids));
+  connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_RESPONSE] =
+    make_gssapi_response_handler(state);
+}
+
+static struct client_userauth_failure *
+do_gssapi_login(struct client_userauth_method *s,
+		struct client_userauth *userauth,
+		struct ssh_connection *connection,
+		struct exception_handler *e)
+{
+  struct client_gssapi_state *state =
+    make_client_gssapi_state(userauth, connection, e);
+
+  send_gssapi(s, userauth, connection, e, state);
+
+  return &state->super;
+}
+
+struct client_userauth_method *
+make_client_gssapi_auth(const char *hostname)
+{
+  NEW(client_gssapi_method, self);
+
+  self->super.type = ATOM_GSSAPI;
+  self->super.login = do_gssapi_login;
+  self->hostname = hostname;
+
+  return &self->super;
+}
+#endif /* WITH_GSS */
Index: src/client_userauth.h
===================================================================
RCS file: /cvsroot/lsh/lsh/src/client_userauth.h,v
retrieving revision 1.7
diff -u -p -r1.7 client_userauth.h
--- src/client_userauth.h	25 Aug 2002 08:14:44 -0000	1.7
+++ src/client_userauth.h	19 Sep 2003 21:40:26 -0000
@@ -51,6 +51,9 @@ struct client_userauth_method *
 make_client_publickey_auth(struct object_list *);
 
 struct client_userauth_method *
+make_client_gssapi_auth(const char *);
+
+struct client_userauth_method *
 make_client_none_auth(void);
 
 #endif /* LSH_CLIENT_USERAUTH_H_INCLUDED */
Index: src/lsh.c
===================================================================
RCS file: /cvsroot/lsh/lsh/src/lsh.c,v
retrieving revision 1.190
diff -u -p -r1.190 lsh.c
--- src/lsh.c	15 Sep 2003 15:17:55 -0000	1.190
+++ src/lsh.c	19 Sep 2003 21:40:26 -0000
@@ -104,6 +104,7 @@ STATIC_REQUEST_SERVICE(ATOM_SSH_USERAUTH
        
        (identity . "char *")
        (with_publickey . int)
+       (with_gssapi . int)
 
        (with_srp_keyexchange . int)
        (with_dh_keyexchange . int)
@@ -147,6 +148,9 @@ make_options(struct exception_handler *h
   self->start_gateway = 0;
 
   self->with_publickey = 1;
+#if WITH_GSS
+  self->with_gssapi = 1;
+#endif
 
   self->with_srp_keyexchange = 0;
 
@@ -576,11 +580,27 @@ make_lsh_login(struct lsh_options *optio
   struct client_userauth_method *password
     = make_client_password_auth(options->super.tty);
 
+#if WITH_GSS
+  struct client_userauth_method *gssapi =
+    make_client_gssapi_auth(options->super.target);
+#endif
+
   /* FIXME: Perhaps we should try "none" only when using SRP. */
   struct client_userauth_method *none
     = make_client_none_auth();
 
   struct object_list *methods;
+#if WITH_GSS
+  if (LIST_LENGTH(keys))
+    methods = make_object_list(4,
+			       none,
+			       make_client_publickey_auth(keys),
+			       gssapi,
+			       password,
+			       -1);
+  else
+    methods = make_object_list(3, none, gssapi, password, -1);
+#else
   if (LIST_LENGTH(keys))
     methods = make_object_list(3,
 			       none,
@@ -589,7 +609,8 @@ make_lsh_login(struct lsh_options *optio
 			       -1);
   else
     methods = make_object_list(2, none, password, -1);
-  
+#endif
+
   return make_client_userauth
     (ssh_format("%lz", options->super.user),
      ATOM_SSH_CONNECTION, methods);
@@ -645,6 +666,8 @@ const char *argp_program_bug_address = B
 
 #define OPT_FORK_STDIO 0x213
 
+#define OPT_GSSAPI 0x214
+
 static const struct argp_option
 main_options[] =
 {
@@ -654,6 +677,12 @@ main_options[] =
     "Try publickey user authentication (default).", 0 },
   { "no-publickey", OPT_PUBLICKEY | ARG_NOT, NULL, 0,
     "Don't try publickey user authentication.", 0 },
+#if WITH_GSS
+  { "gssapi", OPT_GSSAPI, NULL, 0,
+    "Try GSSAPI user authentication (default).", 0 },
+  { "no-gssapi", OPT_GSSAPI | ARG_NOT, NULL, 0,
+    "Don't try GSSAPI user authentication.", 0 },
+#endif
   { "host-db", OPT_HOST_DB, "Filename", 0, "By default, ~/.lsh/host-acls", 0},
   { "sloppy-host-authentication", OPT_SLOPPY, NULL, 0,
     "Allow untrusted hostkeys.", 0 },
@@ -864,6 +893,10 @@ main_argp_parser(int key, char *arg, str
       break;
 
     CASE_FLAG(OPT_PUBLICKEY, with_publickey);
+
+#if WITH_GSS
+    CASE_FLAG(OPT_GSSAPI, with_gssapi);
+#endif
 
     case OPT_HOST_DB:
       self->known_hosts = arg;
Index: src/lshd.c
===================================================================
RCS file: /cvsroot/lsh/lsh/src/lshd.c,v
retrieving revision 1.159
diff -u -p -r1.159 lshd.c
--- src/lshd.c	15 Sep 2003 15:18:05 -0000	1.159
+++ src/lshd.c	19 Sep 2003 21:40:26 -0000
@@ -123,6 +123,8 @@ const char *argp_program_bug_address = B
 #define OPT_NO_PUBLICKEY (OPT_PUBLICKEY | OPT_NO)
 #define OPT_PASSWORD 0x221
 #define OPT_NO_PASSWORD (OPT_PASSWORD | OPT_NO)
+#define OPT_GSSAPI 0x229
+#define OPT_NO_GSSAPI (OPT_GSSAPI | OPT_NO)
 
 #define OPT_ROOT_LOGIN 0x222
 #define OPT_NO_ROOT_LOGIN (OPT_ROOT_LOGIN | OPT_NO)
@@ -170,6 +172,7 @@ const char *argp_program_bug_address = B
        (with_loginauthmode . int)
        (with_publickey . int)
        (with_password . int)
+       (with_gssapi . int)
        (allow_root . int)
        (pw_helper . "const char *")
        (login_shell . "const char *")
@@ -225,6 +228,7 @@ make_lshd_options(void)
   self->with_loginauthmode = 0;
   self->with_publickey = 1;
   self->with_password = 1;
+  self->with_gssapi = 1;
   self->with_tcpip_forward = 1;
   /* Experimental, so disabled by default. */
   self->with_x11_forward = 0;
@@ -379,6 +383,13 @@ main_options[] =
   { "no-publickey", OPT_NO_PUBLICKEY, NULL, 0,
     "Disable publickey user authentication.", 0},
 
+#if WITH_GSS
+  { "gssapi", OPT_GSSAPI, NULL, 0,
+    "Enable gssapi user authentication (default).", 0},
+  { "no-gssapi", OPT_NO_GSSAPI, NULL, 0,
+    "Disable gssapi user authentication.", 0},
+#endif
+
   { "root-login", OPT_ROOT_LOGIN, NULL, 0,
     "Allow root to login.", 0 },
   { "no-root-login", OPT_NO_ROOT_LOGIN, NULL, 0,
@@ -554,7 +565,7 @@ main_argp_parser(int key, char *arg, str
 	if (!self->random)
 	  argp_failure( state, EXIT_FAILURE, 0,  "No randomness generator available.");
 	
-       	if (self->with_password || self->with_publickey || self->with_srp_keyexchange || self->with_loginauthmode)
+	if (self->with_password || self->with_publickey || self->with_srp_keyexchange || self->with_gssapi || self->with_loginauthmode)
 	  user_db = make_unix_user_db(self->reaper,
 				      self->pw_helper, self->login_shell,
 				      self->allow_root);
@@ -611,8 +622,17 @@ main_argp_parser(int key, char *arg, str
 	  {
 	    int i = 0;
 	    
+#if WITH_GSS
+	    self->userauth_methods
+	      = alloc_int_list(self->with_password +
+			       self->with_gssapi +
+			       self->with_publickey);
+#else
 	    self->userauth_methods
-	      = alloc_int_list(self->with_password + self->with_publickey);
+	      = alloc_int_list(self->with_password +
+			       self->with_publickey);
+#endif
+	    self->userauth_algorithms = make_alist(0, -1);
 	    
 	    if (self->with_password)
 	      {
@@ -639,6 +659,15 @@ main_argp_parser(int key, char *arg, str
 				      -1))
 			  ->super);
 	      }
+#if WITH_GSS
+	    if (self->with_gssapi)
+	      {
+		LIST(self->userauth_methods)[i++] = ATOM_GSSAPI;
+		ALIST_SET(self->userauth_algorithms,
+			  ATOM_GSSAPI,
+			  &make_userauth_gssapi(user_db)->super);
+	      }
+#endif
 	  }
 
 
@@ -743,6 +772,14 @@ main_argp_parser(int key, char *arg, str
       
     case OPT_NO_PUBLICKEY:
       self->with_publickey = 0;
+      break;
+
+    case OPT_GSSAPI:
+      self->with_gssapi = 1;
+      break;
+      
+    case OPT_NO_GSSAPI:
+      self->with_gssapi = 0;
       break;
 
     case OPT_LOGIN_AUTH_MODE:
Index: src/server_gssapi.c
===================================================================
RCS file: src/server_gssapi.c
diff -N src/server_gssapi.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/server_gssapi.c	19 Sep 2003 21:40:26 -0000
@@ -0,0 +1,493 @@
+/* server_gssapi.c
+ *
+ * GSSAPI authentication method
+ */
+
+/* lsh, an implementation of the ssh protocol
+ *
+ * Copyright (C) 2003 Simon Josefsson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "charset.h"
+#include "format.h"
+#include "parse.h"
+#include "ssh.h"
+#include "server_userauth.h"
+#include "werror.h"
+#include "xalloc.h"
+
+#if WITH_GSS
+
+#if WITH_GSS_K5
+#include <krb5.h> /* for guserok */
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+#else /* !WITH_GSS_K5 */
+#include <gss.h>
+#endif /* WITH_GSS_K5 */
+
+#include "server_gssapi.c.x"
+
+static struct lsh_string *
+get_status_1 (OM_uint32 code, int type)
+{
+  OM_uint32 maj_stat, min_stat;
+  struct lsh_string *str = NULL;
+  gss_buffer_desc msg;
+  OM_uint32 msg_ctx = 0;
+
+  do
+    {
+      maj_stat = gss_display_status (&min_stat, code, type, GSS_C_NULL_OID,
+				     &msg_ctx, &msg);
+      if (!GSS_ERROR(maj_stat))
+	{
+	  if (str)
+	    str = ssh_format("%flS\n%ls", str, msg.length, (char *) msg.value);
+	  else
+	    str = ssh_format("%ls", msg.length, (char *) msg.value);
+	}
+      gss_release_buffer (&min_stat, &msg);
+    }
+  while (msg_ctx);
+
+  return str;
+}
+
+static struct lsh_string *
+get_status (OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+  return ssh_format ("%flS\n%flS", get_status_1 (maj_stat, GSS_C_GSS_CODE),
+		     get_status_1 (min_stat, GSS_C_MECH_CODE));
+}
+
+static struct packet_handler *
+make_gssapi_token_handler(struct gssapi_server_instance *gssapi);
+
+/* GABA:
+   (class
+     (name userauth_gssapi)
+     (super userauth)
+     (vars
+       (db object user_db)))
+*/
+
+/* GABA:
+   (class
+     (name gssapi_server_instance)
+     (super userauth_gssapi)
+     (vars
+       (user object lsh_string)
+       (cont object command_continuation)
+       (db object user_db)
+       (e object exception_handler)
+       (cred . gss_cred_id_t)
+       (ctx . gss_ctx_id_t)
+       (mech . gss_OID_desc)
+       (client . gss_name_t)))
+*/
+
+
+static void
+do_gc_gssapi (struct gssapi_server_instance *gssapi)
+{
+  OM_uint32 maj_stat, min_stat;
+
+  if (gssapi->ctx)
+    {
+      maj_stat = gss_delete_sec_context (&min_stat, &gssapi->ctx,
+					 GSS_C_NO_BUFFER);
+      if (GSS_ERROR(maj_stat))
+	verbose("GSSAPI error deleting security context: %fS\n",
+		get_status(maj_stat, min_stat));
+    }
+  if (gssapi->cred)
+    {
+      maj_stat = gss_release_cred (&min_stat, &gssapi->cred);
+      if (GSS_ERROR(maj_stat))
+	verbose("GSSAPI error deleting security context: %fS\n",
+		get_status(maj_stat, min_stat));
+    }
+
+  if (gssapi->client)
+    {
+      maj_stat = gss_release_name(&min_stat, &gssapi->client);
+      if (GSS_ERROR(maj_stat))
+	verbose("GSSAPI error releasing client name: %fS\n",
+		get_status(maj_stat, min_stat));
+    }
+}
+
+static void
+do_authenticate(struct userauth *s,
+		struct ssh_connection *connection,
+		struct lsh_string *username,
+		uint32_t service UNUSED,
+		struct simple_buffer *args,
+		struct command_continuation *c,
+		struct exception_handler *e)
+{
+  CAST(userauth_gssapi, self, s);
+  NEW(gssapi_server_instance, gssapi);
+  int number_of_mechanisms;
+  OM_uint32 maj_stat, min_stat;
+  static const struct exception gssapi_acquire_cred
+    = STATIC_EXCEPTION(EXC_USERAUTH,
+		       "Cannot acquire credential for any mechanism.");
+
+  gssapi->user = username;
+  gssapi->db = self->db;
+  gssapi->cont = c;
+  gssapi->e = e;
+  gssapi->ctx = GSS_C_NO_CONTEXT;
+  gssapi->cred = GSS_C_NO_CREDENTIAL;
+
+  if (parse_uint32(args, &number_of_mechanisms))
+    {
+      int i;
+
+      verbose("Client requests %i GSS mechanism(s).\n", number_of_mechanisms);
+
+      for (i = 0; i < number_of_mechanisms; i++)
+	{
+	  gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
+	  gss_OID_desc tmp;
+	  OM_uint32 junk;
+
+	  if (!parse_string(args, &gssapi->mech.length,
+			    (const uint8_t**)&gssapi->mech.elements))
+	    goto fail;
+
+	  if (gssapi->mech.length < 2 ||
+	      ((uint8_t*)gssapi->mech.elements)[0] != 0x06)
+	    goto fail;
+
+	  {
+	    uint8_t oidlen = (((uint8_t*)gssapi->mech.elements)[1] & 128) ?
+	      ((uint8_t*)gssapi->mech.elements)[1] : 1;
+	    if (gssapi->mech.length <= 1 + oidlen)
+	      goto fail;
+	    /* XXX for oids longer than 255 char, we don't check if
+	       asn.1 der length is correct. */
+	    if (oidlen == 1 &&
+		((uint8_t*)gssapi->mech.elements)[1] !=
+		gssapi->mech.length - 2)
+	      goto fail;
+	    tmp.length = gssapi->mech.length - 1 - oidlen;
+	    tmp.elements = (char*)gssapi->mech.elements + 1 + oidlen;
+	  }
+
+	  verbose("Acquiring GSS credentials for GSS mechanism %S (%i).\n",
+		  ssh_format("%lxs", tmp.length, (uint8_t*)tmp.elements), i);
+
+	  maj_stat =  gss_create_empty_oid_set (&min_stat, &desired_mechs);
+	  if (GSS_ERROR(maj_stat))
+	    {
+	      verbose("GSSAPI error creating OID set: %fS\n",
+		      get_status(maj_stat, min_stat));
+	      continue;
+	    }
+
+	  maj_stat = gss_add_oid_set_member (&min_stat, &tmp, &desired_mechs);
+	  if (GSS_ERROR(maj_stat))
+	    {
+	      verbose("GSSAPI error adding OID to set: %fS\n",
+		      get_status(maj_stat, min_stat));
+	      gss_release_oid_set (&junk, &desired_mechs);
+	      continue;
+	    }
+
+	  maj_stat = gss_acquire_cred (&min_stat, GSS_C_NO_NAME, 0,
+				       desired_mechs, GSS_C_ACCEPT,
+				       &gssapi->cred, NULL, NULL);
+	  gss_release_oid_set (&junk, &desired_mechs);
+	  if (GSS_ERROR(maj_stat))
+	    {
+	      verbose("GSSAPI error acquiring credential: %fS\n",
+		      get_status(maj_stat, min_stat));
+	      continue;
+	    }
+
+	  verbose("Ready to continue with mechanism %S (%i).\n",
+		  ssh_format("%lxs", gssapi->mech.length,
+			     (uint8_t*)gssapi->mech.elements), i);
+
+	  if (connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] !=
+	      &connection_unimplemented_handler)
+	    {
+	      verbose("Cleaning up attempted GSS authentication.\n");
+	      CAST(gssapi_token_handler, token_handler,
+		   connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN]);
+	      do_gc_gssapi(token_handler->gssapi);
+	      KILL(token_handler);
+	    }
+
+	  if (connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE]
+	      != &connection_unimplemented_handler)
+	    {
+	      verbose("Cleaning up finished GSS authentication.\n");
+	      CAST(gssapi_finish_handler, finish_handler,
+		   connection->dispatch
+		   [SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE]);
+	      do_gc_gssapi(finish_handler->gssapi);
+	      KILL(finish_handler);
+	    }
+	  connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
+	    make_gssapi_token_handler (gssapi);
+	  EXCEPTION_RAISE(e, make_userauth_special_exception
+			  (ssh_format("%c%s", SSH_MSG_USERAUTH_GSSAPI_RESPONSE,
+				      gssapi->mech.length,
+				      (uint8_t*)gssapi->mech.elements),
+			   NULL));
+	  return;
+	}
+    }
+
+  EXCEPTION_RAISE(e, &gssapi_acquire_cred);
+  return;
+
+ fail:
+  PROTOCOL_ERROR(e, "Invalid gssapi USERAUTH message.");
+}
+
+struct userauth *
+make_userauth_gssapi(struct user_db *db)
+{
+  NEW(userauth_gssapi, self);
+  self->super.authenticate = do_authenticate;
+  self->db = db;
+
+  return &self->super;
+}
+
+/* GABA:
+   (class
+     (name gssapi_finish_handler)
+     (super packet_handler)
+     (vars
+       (gssapi object gssapi_server_instance)))
+*/
+
+static int
+gss_userok (gss_buffer_t client_name, const char *name)
+{
+#if WITH_GSS_K5
+  int rc = -1;
+  krb5_principal p;
+  krb5_context kcontext;
+
+  krb5_init_context (&kcontext);
+
+  if (krb5_parse_name (kcontext, client_name->value, &p) != 0)
+    return -1;
+  if (krb5_kuserok (kcontext, p, name))
+    rc = 0;
+  else
+    rc = 1;
+  krb5_free_principal (kcontext, p);
+  return rc;
+#else
+  return (strlen(name) == client_name->length &&
+	  memcmp(name, client_name->value, client_name->length) == 0) ? 0 : 1;
+#endif
+}
+
+static void
+do_handle_gssapi_finish(struct packet_handler *s,
+			struct ssh_connection *connection,
+			struct lsh_string *packet UNUSED)
+{
+  CAST(gssapi_finish_handler, self, s);
+  OM_uint32 maj_stat, min_stat;
+  gss_buffer_desc client_name;
+
+  verbose("Finishing GSS.\n");
+
+  connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE] =
+    &connection_unimplemented_handler;
+
+  /* Serialize handling of userauth requests */
+  connection_lock(connection);
+
+  /* Find authenticated user. */
+  maj_stat = gss_display_name (&min_stat, self->gssapi->client,
+			       &client_name, NULL);
+  if (GSS_ERROR(maj_stat))
+    {
+      static const struct exception gssapi_display_name
+	= STATIC_EXCEPTION(EXC_USERAUTH,
+			   "Cannot extract username from GSS.");
+
+      verbose("GSSAPI error getting client name: %fS\n",
+	      get_status(maj_stat, min_stat));
+
+      EXCEPTION_RAISE(self->gssapi->e, &gssapi_display_name);
+      goto done;
+    }
+
+  /* Check authorization: is GSS user authorized to log on as user? */
+  if (gss_userok (&client_name, lsh_get_cstring(self->gssapi->user)))
+    {
+      static const struct exception gssapi_not_auth
+	= STATIC_EXCEPTION(EXC_USERAUTH,
+			   "GSS user not authorized to log on.");
+
+      verbose("GSS user %s not authorized to log on as %s.\n",
+	      client_name.length, (char*)client_name.value,
+	      self->gssapi->user);
+
+      EXCEPTION_RAISE(self->gssapi->e, &gssapi_not_auth);
+      goto done;
+    }
+
+  connection->user = USER_LOOKUP(self->gssapi->db, self->gssapi->user, 1);
+  if (connection->user)
+    verbose("GSS user %s authorized to log on as %S.\n", client_name.length,
+	    (char*)client_name.value, self->gssapi->user);
+  else
+    verbose("GSS user %s requested unknown user %S.\n", client_name.length,
+	    (char*)client_name.value, self->gssapi->user);
+
+  maj_stat = gss_release_buffer(&min_stat, &client_name);
+  if (GSS_ERROR(maj_stat))
+    verbose("GSSAPI error releasing client name: %fS\n",
+	    get_status(maj_stat, min_stat));
+
+  if (!connection->user)
+    {
+      static const struct exception no_such_user
+	= STATIC_EXCEPTION(EXC_USERAUTH, "No such user");
+      EXCEPTION_RAISE(self->gssapi->e, &no_such_user);
+      goto done;
+    }
+
+  COMMAND_RETURN(self->gssapi->cont, connection->user);
+
+ done:
+  do_gc_gssapi (self->gssapi);
+}
+
+static struct packet_handler *
+make_gssapi_finish_handler(struct gssapi_server_instance *gssapi)
+{
+  NEW(gssapi_finish_handler, self);
+  self->super.handler = do_handle_gssapi_finish;
+  self->gssapi = gssapi;
+
+  return &self->super;
+}
+
+/* GABA:
+   (class
+     (name gssapi_token_handler)
+     (super packet_handler)
+     (vars
+       (gssapi object gssapi_server_instance)))
+*/
+
+static void
+do_handle_gssapi_token(struct packet_handler *s,
+		       struct ssh_connection *connection,
+		       struct lsh_string *packet)
+{
+  CAST(gssapi_token_handler, self, s);
+  OM_uint32 maj_stat, min_stat;
+  OM_uint32 retflags;
+  gss_buffer_desc inbuf, outbuf;
+
+  verbose("Received GSS token.\n");
+
+  /* Serialize handling of userauth requests */
+  connection_lock(connection);
+
+  /* 1 for the SSH message code and 4 for the uint32_t length */
+#define GSS_TOKEN_OFFSET (1 + 4)
+  inbuf.value = packet->data + GSS_TOKEN_OFFSET;
+  inbuf.length = packet->length - GSS_TOKEN_OFFSET;
+  maj_stat = gss_accept_sec_context (&min_stat,
+				     &self->gssapi->ctx,
+				     self->gssapi->cred,
+				     &inbuf,
+				     GSS_C_NO_CHANNEL_BINDINGS,
+				     &self->gssapi->client,
+				     NULL,
+				     &outbuf,
+				     &retflags,
+				     NULL,
+				     NULL);
+  if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+    {
+      static const struct exception gssapi_accept_sec_context
+	= STATIC_EXCEPTION(EXC_USERAUTH, "Authentication failed.");
+
+      verbose("GSSAPI error accept_sec_context: %fS\n",
+	      get_status(maj_stat, min_stat));
+
+      connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
+	&connection_unimplemented_handler;
+
+      EXCEPTION_RAISE(self->gssapi->e, make_userauth_special_exception
+		      (ssh_format("%c%li%li%S%S",
+				  SSH_MSG_USERAUTH_GSSAPI_ERROR,
+				  maj_stat, min_stat,
+				  get_status(maj_stat, min_stat),
+				  /* XXX GSS is multilingual, but how
+				     do we get the RFC 1766 tag it uses? */
+				  ssh_format("en")),
+		       NULL));
+      /* XXX ERRTOK */
+      /* Serialize handling of userauth requests */
+      connection_lock(connection);
+      EXCEPTION_RAISE(self->gssapi->e, &gssapi_accept_sec_context);
+      do_gc_gssapi (self->gssapi);
+      return;
+    }
+
+  if (maj_stat == GSS_S_COMPLETE)
+    {
+      verbose("Preparing to finish GSS authentication.\n");
+      connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
+	&connection_unimplemented_handler;
+      connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE] =
+	make_gssapi_finish_handler(self->gssapi);
+    }
+
+  verbose("Sending GSS token.\n");
+  EXCEPTION_RAISE(self->gssapi->e, make_userauth_special_exception
+		  (ssh_format("%c%s", SSH_MSG_USERAUTH_GSSAPI_TOKEN,
+			      outbuf.length, outbuf.value),
+		   NULL));
+}
+
+static struct packet_handler *
+make_gssapi_token_handler(struct gssapi_server_instance *gssapi)
+{
+  NEW(gssapi_token_handler, self);
+  self->super.handler = do_handle_gssapi_token;
+  self->gssapi = gssapi;
+
+  return &self->super;
+}
+#endif /* WITH_GSS */
Index: src/server_userauth.h
===================================================================
RCS file: /cvsroot/lsh/lsh/src/server_userauth.h,v
retrieving revision 1.22
diff -u -p -r1.22 server_userauth.h
--- src/server_userauth.h	15 Sep 2003 15:18:16 -0000	1.22
+++ src/server_userauth.h	19 Sep 2003 21:40:26 -0000
@@ -104,6 +104,9 @@ make_userauth_publickey(struct user_db *
 			struct alist *verifiers);
 
 struct userauth *
+make_userauth_gssapi(struct user_db *db);
+
+struct userauth *
 make_userauth_none(int ignore_user, 
 		   struct lsh_user *user);
 
Index: src/ssh.h
===================================================================
RCS file: /cvsroot/lsh/lsh/src/ssh.h,v
retrieving revision 1.9
diff -u -p -r1.9 ssh.h
--- src/ssh.h	13 Mar 2002 16:01:59 -0000	1.9
+++ src/ssh.h	19 Sep 2003 21:40:26 -0000
@@ -55,6 +55,14 @@
 #define SSH_MSG_USERAUTH_PK_OK 60
 #define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60
 
+/* The following message numbers have been defined for use with the
+ * 'gssapi' user authentication method: */
+
+#define SSH_MSG_USERAUTH_GSSAPI_RESPONSE 60
+#define SSH_MSG_USERAUTH_GSSAPI_TOKEN 61
+#define SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63
+#define SSH_MSG_USERAUTH_GSSAPI_ERROR 64
+
 /* 80-89 Connection protocol generic */
 
 #define SSH_FIRST_CONNECTION_GENERIC 80
