auth2-gss.c revision 149749
190075Sobrien/*	$OpenBSD: auth2-gss.c,v 1.10 2005/07/17 07:17:54 djm Exp $	*/
290075Sobrien
390075Sobrien/*
490075Sobrien * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
590075Sobrien *
690075Sobrien * Redistribution and use in source and binary forms, with or without
790075Sobrien * modification, are permitted provided that the following conditions
890075Sobrien * are met:
990075Sobrien * 1. Redistributions of source code must retain the above copyright
1090075Sobrien *    notice, this list of conditions and the following disclaimer.
1190075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1290075Sobrien *    notice, this list of conditions and the following disclaimer in the
1390075Sobrien *    documentation and/or other materials provided with the distribution.
1490075Sobrien *
1590075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
1690075Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1790075Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1890075Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1990075Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2090075Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2190075Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2290075Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2390075Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2490075Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2590075Sobrien */
2690075Sobrien
2790075Sobrien#include "includes.h"
2890075Sobrien
2990075Sobrien#ifdef GSSAPI
3090075Sobrien
3190075Sobrien#include "auth.h"
3290075Sobrien#include "ssh2.h"
3390075Sobrien#include "xmalloc.h"
3490075Sobrien#include "log.h"
3590075Sobrien#include "dispatch.h"
3690075Sobrien#include "servconf.h"
3790075Sobrien#include "compat.h"
3890075Sobrien#include "packet.h"
3990075Sobrien#include "monitor_wrap.h"
4090075Sobrien
4190075Sobrien#include "ssh-gss.h"
4290075Sobrien
4390075Sobrienextern ServerOptions options;
4490075Sobrien
4590075Sobrienstatic void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
4690075Sobrienstatic void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
4790075Sobrienstatic void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
4890075Sobrienstatic void input_gssapi_errtok(int, u_int32_t, void *);
4990075Sobrien
5090075Sobrien/*
5190075Sobrien * We only support those mechanisms that we know about (ie ones that we know
5290075Sobrien * how to check local user kuserok and the like
5390075Sobrien */
5490075Sobrienstatic int
5590075Sobrienuserauth_gssapi(Authctxt *authctxt)
5690075Sobrien{
5790075Sobrien	gss_OID_desc goid = {0, NULL};
5890075Sobrien	Gssctxt *ctxt = NULL;
5990075Sobrien	int mechs;
6090075Sobrien	gss_OID_set supported;
6190075Sobrien	int present;
6290075Sobrien	OM_uint32 ms;
6390075Sobrien	u_int len;
6490075Sobrien	u_char *doid = NULL;
6590075Sobrien
6690075Sobrien	if (!authctxt->valid || authctxt->user == NULL)
6790075Sobrien		return (0);
6890075Sobrien
6990075Sobrien	mechs = packet_get_int();
7090075Sobrien	if (mechs == 0) {
7190075Sobrien		debug("Mechanism negotiation is not supported");
7290075Sobrien		return (0);
7390075Sobrien	}
7490075Sobrien
7590075Sobrien	ssh_gssapi_supported_oids(&supported);
7690075Sobrien	do {
7790075Sobrien		mechs--;
7890075Sobrien
7990075Sobrien		if (doid)
8090075Sobrien			xfree(doid);
8190075Sobrien
8290075Sobrien		present = 0;
8390075Sobrien		doid = packet_get_string(&len);
8490075Sobrien
8590075Sobrien		if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
8690075Sobrien		    doid[1] == len - 2) {
8790075Sobrien			goid.elements = doid + 2;
8890075Sobrien			goid.length   = len - 2;
8990075Sobrien			gss_test_oid_set_member(&ms, &goid, supported,
9090075Sobrien			    &present);
9190075Sobrien		} else {
9290075Sobrien			logit("Badly formed OID received");
9390075Sobrien		}
9490075Sobrien	} while (mechs > 0 && !present);
9590075Sobrien
9690075Sobrien	gss_release_oid_set(&ms, &supported);
9790075Sobrien
9890075Sobrien	if (!present) {
9990075Sobrien		xfree(doid);
10090075Sobrien		return (0);
10190075Sobrien	}
10290075Sobrien
10390075Sobrien	if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
10490075Sobrien		xfree(doid);
10590075Sobrien		return (0);
10690075Sobrien	}
10790075Sobrien
10890075Sobrien	authctxt->methoddata=(void *)ctxt;
10990075Sobrien
11090075Sobrien	packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
11190075Sobrien
11290075Sobrien	/* Return the OID that we received */
11390075Sobrien	packet_put_string(doid, len);
11490075Sobrien
11590075Sobrien	packet_send();
11690075Sobrien	xfree(doid);
11790075Sobrien
11890075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
11990075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
12090075Sobrien	authctxt->postponed = 1;
12190075Sobrien
12290075Sobrien	return (0);
12390075Sobrien}
12490075Sobrien
12590075Sobrienstatic void
12690075Sobrieninput_gssapi_token(int type, u_int32_t plen, void *ctxt)
12790075Sobrien{
12890075Sobrien	Authctxt *authctxt = ctxt;
12990075Sobrien	Gssctxt *gssctxt;
13090075Sobrien	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
13190075Sobrien	gss_buffer_desc recv_tok;
13290075Sobrien	OM_uint32 maj_status, min_status, flags;
13390075Sobrien	u_int len;
13490075Sobrien
13590075Sobrien	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
13690075Sobrien		fatal("No authentication or GSSAPI context");
13790075Sobrien
13890075Sobrien	gssctxt = authctxt->methoddata;
13990075Sobrien	recv_tok.value = packet_get_string(&len);
14090075Sobrien	recv_tok.length = len; /* u_int vs. size_t */
14190075Sobrien
14290075Sobrien	packet_check_eom();
14390075Sobrien
14490075Sobrien	maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
14590075Sobrien	    &send_tok, &flags));
14690075Sobrien
14790075Sobrien	xfree(recv_tok.value);
14890075Sobrien
14990075Sobrien	if (GSS_ERROR(maj_status)) {
15090075Sobrien		if (send_tok.length != 0) {
15190075Sobrien			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
15290075Sobrien			packet_put_string(send_tok.value, send_tok.length);
15390075Sobrien			packet_send();
15490075Sobrien		}
15590075Sobrien		authctxt->postponed = 0;
15690075Sobrien		dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
15790075Sobrien		userauth_finish(authctxt, 0, "gssapi-with-mic");
15890075Sobrien	} else {
15990075Sobrien		if (send_tok.length != 0) {
16090075Sobrien			packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
16190075Sobrien			packet_put_string(send_tok.value, send_tok.length);
16290075Sobrien			packet_send();
16390075Sobrien		}
16490075Sobrien		if (maj_status == GSS_S_COMPLETE) {
16590075Sobrien			dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
16690075Sobrien			if (flags & GSS_C_INTEG_FLAG)
16790075Sobrien				dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
16890075Sobrien				    &input_gssapi_mic);
16990075Sobrien			else
17090075Sobrien				dispatch_set(
17190075Sobrien				    SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
17290075Sobrien				    &input_gssapi_exchange_complete);
17390075Sobrien		}
17490075Sobrien	}
17590075Sobrien
17690075Sobrien	gss_release_buffer(&min_status, &send_tok);
17790075Sobrien}
17890075Sobrien
17990075Sobrienstatic void
18090075Sobrieninput_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
18190075Sobrien{
18290075Sobrien	Authctxt *authctxt = ctxt;
18390075Sobrien	Gssctxt *gssctxt;
18490075Sobrien	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
18590075Sobrien	gss_buffer_desc recv_tok;
18690075Sobrien	OM_uint32 maj_status;
18790075Sobrien	u_int len;
18890075Sobrien
18990075Sobrien	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
19090075Sobrien		fatal("No authentication or GSSAPI context");
19190075Sobrien
19290075Sobrien	gssctxt = authctxt->methoddata;
19390075Sobrien	recv_tok.value = packet_get_string(&len);
19490075Sobrien	recv_tok.length = len;
19590075Sobrien
19690075Sobrien	packet_check_eom();
19790075Sobrien
19890075Sobrien	/* Push the error token into GSSAPI to see what it says */
19990075Sobrien	maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
20090075Sobrien	    &send_tok, NULL));
20190075Sobrien
20290075Sobrien	xfree(recv_tok.value);
20390075Sobrien
20490075Sobrien	/* We can't return anything to the client, even if we wanted to */
20590075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
20690075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
20790075Sobrien
20890075Sobrien	/* The client will have already moved on to the next auth */
20990075Sobrien
21090075Sobrien	gss_release_buffer(&maj_status, &send_tok);
21190075Sobrien}
21290075Sobrien
21390075Sobrien/*
21490075Sobrien * This is called when the client thinks we've completed authentication.
21590075Sobrien * It should only be enabled in the dispatch handler by the function above,
21690075Sobrien * which only enables it once the GSSAPI exchange is complete.
21790075Sobrien */
21890075Sobrien
21990075Sobrienstatic void
22090075Sobrieninput_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
22190075Sobrien{
22290075Sobrien	Authctxt *authctxt = ctxt;
22390075Sobrien	Gssctxt *gssctxt;
22490075Sobrien	int authenticated;
22590075Sobrien
22690075Sobrien	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
22790075Sobrien		fatal("No authentication or GSSAPI context");
22890075Sobrien
22990075Sobrien	gssctxt = authctxt->methoddata;
23090075Sobrien
23190075Sobrien	/*
23290075Sobrien	 * We don't need to check the status, because we're only enabled in
23390075Sobrien	 * the dispatcher once the exchange is complete
23490075Sobrien	 */
23590075Sobrien
23690075Sobrien	packet_check_eom();
23790075Sobrien
23890075Sobrien	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
23990075Sobrien
24090075Sobrien	authctxt->postponed = 0;
24190075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
24290075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
24390075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
24490075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
24590075Sobrien	userauth_finish(authctxt, authenticated, "gssapi-with-mic");
24690075Sobrien}
24790075Sobrien
24890075Sobrienstatic void
24990075Sobrieninput_gssapi_mic(int type, u_int32_t plen, void *ctxt)
25090075Sobrien{
25190075Sobrien	Authctxt *authctxt = ctxt;
25290075Sobrien	Gssctxt *gssctxt;
25390075Sobrien	int authenticated = 0;
25490075Sobrien	Buffer b;
25590075Sobrien	gss_buffer_desc mic, gssbuf;
25690075Sobrien	u_int len;
25790075Sobrien
25890075Sobrien	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
25990075Sobrien		fatal("No authentication or GSSAPI context");
26090075Sobrien
26190075Sobrien	gssctxt = authctxt->methoddata;
26290075Sobrien
26390075Sobrien	mic.value = packet_get_string(&len);
26490075Sobrien	mic.length = len;
26590075Sobrien
26690075Sobrien	ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
26790075Sobrien	    "gssapi-with-mic");
26890075Sobrien
26990075Sobrien	gssbuf.value = buffer_ptr(&b);
27090075Sobrien	gssbuf.length = buffer_len(&b);
27190075Sobrien
27290075Sobrien	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
27390075Sobrien		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
27490075Sobrien	else
27590075Sobrien		logit("GSSAPI MIC check failed");
27690075Sobrien
27790075Sobrien	buffer_free(&b);
27890075Sobrien	xfree(mic.value);
27990075Sobrien
28090075Sobrien	authctxt->postponed = 0;
28190075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
28290075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
28390075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
28490075Sobrien	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
28590075Sobrien	userauth_finish(authctxt, authenticated, "gssapi-with-mic");
28690075Sobrien}
28790075Sobrien
28890075SobrienAuthmethod method_gssapi = {
28990075Sobrien	"gssapi-with-mic",
29090075Sobrien	userauth_gssapi,
29190075Sobrien	&options.gss_authentication
29290075Sobrien};
29390075Sobrien
29490075Sobrien#endif /* GSSAPI */
29590075Sobrien