- markus@cvs.openbsd.org 2003/08/22 10:56:09
     [auth2.c auth2-gss.c auth.h compat.c compat.h gss-genr.c gss-serv-krb5.c
     gss-serv.c monitor.c monitor.h monitor_wrap.c monitor_wrap.h readconf.c
     readconf.h servconf.c servconf.h session.c session.h ssh-gss.h
     ssh_config.5 sshconnect2.c sshd_config sshd_config.5]
     support GSS API user authentication; patches from Simon Wilkinson,
     stripped down and tested by Jakob and myself.
diff --git a/sshconnect2.c b/sshconnect2.c
index 6a0bd40..c71ad50 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.120 2003/06/24 08:23:46 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.121 2003/08/22 10:56:09 markus Exp $");
 
 #ifdef KRB5
 #include <krb5.h>
@@ -57,6 +57,10 @@
 #include "msg.h"
 #include "pathnames.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 /* import */
 extern char *client_version_string;
 extern char *server_version_string;
@@ -178,6 +182,8 @@
 	Sensitive *sensitive;
 	/* kbd-interactive */
 	int info_req_seen;
+	/* generic */
+	void *methoddata;
 };
 struct Authmethod {
 	char	*name;		/* string to compare against server's list */
@@ -201,6 +207,15 @@
 int	userauth_hostbased(Authctxt *);
 int	userauth_kerberos(Authctxt *);
 
+#ifdef GSSAPI
+int	userauth_gssapi(Authctxt *authctxt);
+void	input_gssapi_response(int type, u_int32_t, void *);
+void	input_gssapi_token(int type, u_int32_t, void *);
+void	input_gssapi_hash(int type, u_int32_t, void *);
+void	input_gssapi_error(int, u_int32_t, void *);
+void	input_gssapi_errtok(int, u_int32_t, void *);
+#endif
+
 void	userauth(Authctxt *, char *);
 
 static int sign_and_send_pubkey(Authctxt *, Identity *);
@@ -213,6 +228,12 @@
 static char *authmethods_get(void);
 
 Authmethod authmethods[] = {
+#ifdef GSSAPI
+	{"gssapi",
+		userauth_gssapi,
+		&options.gss_authentication,
+		NULL},
+#endif
 	{"hostbased",
 		userauth_hostbased,
 		&options.hostbased_authentication,
@@ -283,6 +304,7 @@
 	authctxt.success = 0;
 	authctxt.method = authmethod_lookup("none");
 	authctxt.authlist = NULL;
+	authctxt.methoddata = NULL;
 	authctxt.sensitive = sensitive;
 	authctxt.info_req_seen = 0;
 	if (authctxt.method == NULL)
@@ -306,6 +328,10 @@
 void
 userauth(Authctxt *authctxt, char *authlist)
 {
+	if (authctxt->methoddata) {
+		xfree(authctxt->methoddata);
+		authctxt->methoddata = NULL;
+	}
 	if (authlist == NULL) {
 		authlist = authctxt->authlist;
 	} else {
@@ -361,6 +387,8 @@
 		fatal("input_userauth_success: no authentication context");
 	if (authctxt->authlist)
 		xfree(authctxt->authlist);
+	if (authctxt->methoddata)
+		xfree(authctxt->methoddata);
 	authctxt->success = 1;			/* break out */
 }
 
@@ -449,6 +477,228 @@
 		userauth(authctxt, NULL);
 }
 
+#ifdef GSSAPI
+int 
+userauth_gssapi(Authctxt *authctxt)
+{
+	Gssctxt *gssctxt = NULL;
+	static gss_OID_set supported = NULL;
+	static int mech = 0;
+	OM_uint32 min;
+	int ok = 0;
+
+	/* Try one GSSAPI method at a time, rather than sending them all at
+	 * once. */
+
+	if (supported == NULL)
+		gss_indicate_mechs(&min, &supported);
+
+	/* Check to see if the mechanism is usable before we offer it */
+	while (mech<supported->count && !ok) {
+		if (gssctxt)
+			ssh_gssapi_delete_ctx(&gssctxt);
+		ssh_gssapi_build_ctx(&gssctxt);
+		ssh_gssapi_set_oid(gssctxt, &supported->elements[mech]);
+
+		/* My DER encoding requires length<128 */
+		if (supported->elements[mech].length < 128 &&
+		    !GSS_ERROR(ssh_gssapi_import_name(gssctxt,
+		    authctxt->host))) {
+			ok = 1; /* Mechanism works */
+		} else {
+			mech++;
+		}
+	}
+
+	if (!ok) return 0;
+
+	authctxt->methoddata=(void *)gssctxt;
+
+	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+	packet_put_cstring(authctxt->server_user);
+	packet_put_cstring(authctxt->service);
+	packet_put_cstring(authctxt->method->name);
+
+	packet_put_int(1);
+
+	/* Some servers encode the OID incorrectly (as we used to) */
+	if (datafellows & SSH_BUG_GSSAPI_BER) {
+		packet_put_string(supported->elements[mech].elements,
+		    supported->elements[mech].length);
+	} else {
+		packet_put_int((supported->elements[mech].length)+2);
+		packet_put_char(SSH_GSS_OIDTYPE);
+		packet_put_char(supported->elements[mech].length);
+		packet_put_raw(supported->elements[mech].elements,
+		    supported->elements[mech].length);
+	}
+
+	packet_send();
+
+	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
+	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
+	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
+	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
+
+	mech++; /* Move along to next candidate */
+
+	return 1;
+}
+
+void
+input_gssapi_response(int type, u_int32_t plen, void *ctxt)
+{
+	Authctxt *authctxt = ctxt;
+	Gssctxt *gssctxt;
+	OM_uint32 status, ms;
+	int oidlen;
+	char *oidv;
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+
+	if (authctxt == NULL)
+		fatal("input_gssapi_response: no authentication context");
+	gssctxt = authctxt->methoddata;
+
+	/* Setup our OID */
+	oidv = packet_get_string(&oidlen);
+
+	if (datafellows & SSH_BUG_GSSAPI_BER) {
+		if (!ssh_gssapi_check_oid(gssctxt, oidv, oidlen))
+			fatal("Server returned different OID than expected");
+	} else {
+		if(oidv[0] != SSH_GSS_OIDTYPE || oidv[1] != oidlen-2) {
+			debug("Badly encoded mechanism OID received");
+			userauth(authctxt, NULL);
+			xfree(oidv);
+			return;
+		}
+		if (!ssh_gssapi_check_oid(gssctxt, oidv+2, oidlen-2))
+			fatal("Server returned different OID than expected");
+	}
+
+	packet_check_eom();
+
+	xfree(oidv);
+
+	status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+	    GSS_C_NO_BUFFER, &send_tok, NULL);
+	if (GSS_ERROR(status)) {
+		if (send_tok.length > 0) {
+			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+			packet_put_string(send_tok.value, send_tok.length);
+			packet_send();
+			gss_release_buffer(&ms, &send_tok);
+		}
+		/* Start again with next method on list */
+		debug("Trying to start again");
+		userauth(authctxt, NULL);
+		return;
+	}
+
+	/* We must have data to send */
+	packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+	packet_put_string(send_tok.value, send_tok.length);
+	packet_send();
+	gss_release_buffer(&ms, &send_tok);
+}
+
+void
+input_gssapi_token(int type, u_int32_t plen, void *ctxt)
+{
+	Authctxt *authctxt = ctxt;
+	Gssctxt *gssctxt;
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+	gss_buffer_desc recv_tok;
+	OM_uint32 status, ms;
+	u_int slen;
+
+	if (authctxt == NULL)
+		fatal("input_gssapi_response: no authentication context");
+	gssctxt = authctxt->methoddata;
+
+	recv_tok.value = packet_get_string(&slen);
+	recv_tok.length = slen;	/* safe typecast */
+
+	packet_check_eom();
+
+	status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+	    &recv_tok, &send_tok, NULL);
+
+	xfree(recv_tok.value);
+
+	if (GSS_ERROR(status)) {
+		if (send_tok.length > 0) {
+			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+			packet_put_string(send_tok.value, send_tok.length);
+			packet_send();
+			gss_release_buffer(&ms, &send_tok);
+		}
+		/* Start again with the next method in the list */
+		userauth(authctxt, NULL);
+		return;
+	}
+
+	if (send_tok.length > 0) {
+		packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+		packet_put_string(send_tok.value, send_tok.length);
+		packet_send();
+		gss_release_buffer(&ms, &send_tok);
+	}
+
+	if (status == GSS_S_COMPLETE) {
+		/* If that succeeded, send a exchange complete message */
+		packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
+		packet_send();
+	}
+}
+
+void
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
+{
+	Authctxt *authctxt = ctxt;
+	Gssctxt *gssctxt;
+	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+	gss_buffer_desc recv_tok;
+	OM_uint32 status, ms;
+
+	if (authctxt == NULL)
+		fatal("input_gssapi_response: no authentication context");
+	gssctxt = authctxt->methoddata;
+
+	recv_tok.value = packet_get_string(&recv_tok.length);
+
+	packet_check_eom();
+
+	/* Stick it into GSSAPI and see what it says */
+	status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+				     &recv_tok, &send_tok, NULL);
+
+	xfree(recv_tok.value);
+	gss_release_buffer(&ms, &send_tok);
+
+	/* Server will be returning a failed packet after this one */
+}
+
+void
+input_gssapi_error(int type, u_int32_t plen, void *ctxt)
+{
+	OM_uint32 maj, min;
+	char *msg;
+	char *lang;
+
+	maj=packet_get_int();
+	min=packet_get_int();
+	msg=packet_get_string(NULL);
+	lang=packet_get_string(NULL);
+
+	packet_check_eom();
+
+	debug("Server GSSAPI Error:\n%s\n", msg);
+	xfree(msg);
+	xfree(lang);
+}
+#endif /* GSSAPI */
+
 int
 userauth_none(Authctxt *authctxt)
 {