xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/mech_gssapi.c (revision 3966285084f12284ac71d48a152142370a217742)
1*39662850Selric /* $NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $ */
2231558cbSagc 
3231558cbSagc /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4231558cbSagc  * All rights reserved.
5231558cbSagc  *
6231558cbSagc  * This code is derived from software contributed to The NetBSD Foundation
7231558cbSagc  * by Mateusz Kocielski.
8231558cbSagc  *
9231558cbSagc  * Redistribution and use in source and binary forms, with or without
10231558cbSagc  * modification, are permitted provided that the following conditions
11231558cbSagc  * are met:
12231558cbSagc  * 1. Redistributions of source code must retain the above copyright
13231558cbSagc  *    notice, this list of conditions and the following disclaimer.
14231558cbSagc  * 2. Redistributions in binary form must reproduce the above copyright
15231558cbSagc  *    notice, this list of conditions and the following disclaimer in the
16231558cbSagc  *    documentation and/or other materials provided with the distribution.
17231558cbSagc  * 3. All advertising materials mentioning features or use of this software
18231558cbSagc  *    must display the following acknowledgement:
19231558cbSagc  *        This product includes software developed by the NetBSD
20231558cbSagc  *        Foundation, Inc. and its contributors.
21231558cbSagc  * 4. Neither the name of The NetBSD Foundation nor the names of its
22231558cbSagc  *    contributors may be used to endorse or promote products derived
23231558cbSagc  *    from this software without specific prior written permission.
24231558cbSagc  *
25231558cbSagc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26231558cbSagc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27231558cbSagc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28231558cbSagc  * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29231558cbSagc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30231558cbSagc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31231558cbSagc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32231558cbSagc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33231558cbSagc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34231558cbSagc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35231558cbSagc  * POSSIBILITY OF SUCH DAMAGE.
36231558cbSagc  */
3719c14409Schristos #include <sys/cdefs.h>
38*39662850Selric __RCSID("$NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $");
39231558cbSagc 
4019c14409Schristos #include <assert.h>
4119c14409Schristos #include <errno.h>
4219c14409Schristos #include <limits.h>	/* for LINE_MAX */
4319c14409Schristos #include <saslc.h>
44231558cbSagc #include <stdio.h>
4519c14409Schristos #include <stdlib.h>
46231558cbSagc #include <string.h>
47beea8b97Schristos 
48231558cbSagc #include <gssapi/gssapi.h>
4919c14409Schristos 
5019c14409Schristos #include "buffer.h"
5119c14409Schristos #include "list.h"
52231558cbSagc #include "mech.h"
5319c14409Schristos #include "msg.h"
5419c14409Schristos #include "saslc_private.h"
55231558cbSagc 
5619c14409Schristos /* See RFC 2222 section 7.2.1. */
57231558cbSagc 
58231558cbSagc /* properties */
5919c14409Schristos #define SASLC_GSSAPI_AUTHCID		SASLC_PROP_AUTHCID
6019c14409Schristos #define SASLC_GSSAPI_HOSTNAME		SASLC_PROP_HOSTNAME
6119c14409Schristos #define SASLC_GSSAPI_SERVICE		SASLC_PROP_SERVICE
6219c14409Schristos #define SASLC_GSSAPI_QOPMASK		SASLC_PROP_QOPMASK
63231558cbSagc 
6419c14409Schristos #define DEFAULT_QOP_MASK	(F_QOP_NONE | F_QOP_INT | F_QOP_CONF)
65231558cbSagc 
6619c14409Schristos /* authentication steps */
6719c14409Schristos typedef enum {	/* see RFC2222 7.2.1 section */
6819c14409Schristos 	GSSAPI_AUTH_FIRST,		/* first authentication stage */
6919c14409Schristos 	GSSAPI_AUTH_NEXT,		/* next authentication stage(s) */
7019c14409Schristos 	GSSAPI_AUTH_LAST,		/* final authentication stage */
7119c14409Schristos 	GSSAPI_AUTH_DONE		/* authenticated */
7219c14409Schristos } saslc__mech_gssapi_status_t;
73231558cbSagc 
7419c14409Schristos /* gssapi mechanism session */
75231558cbSagc typedef struct {
7619c14409Schristos 	saslc__mech_sess_t mech_sess;		/* mechanism session */
7719c14409Schristos 	saslc__mech_gssapi_status_t status;	/* authentication status */
7819c14409Schristos 	gss_ctx_id_t gss_ctx;			/* GSSAPI context */
7919c14409Schristos 	gss_name_t server_name;			/* server name: service@host */
8019c14409Schristos 	gss_name_t client_name;			/* client name - XXX: unused! */
8119c14409Schristos 	uint32_t qop_mask;			/* available QOP services */
8219c14409Schristos 	uint32_t omaxbuf;			/* maximum output buffer size */
8319c14409Schristos 	uint32_t imaxbuf;			/* maximum input buffer size */
8419c14409Schristos 	saslc__buffer32_context_t *dec_ctx;	/* decode buffer context */
8519c14409Schristos 	saslc__buffer_context_t *enc_ctx;	/* encode buffer context */
86231558cbSagc } saslc__mech_gssapi_sess_t;
87231558cbSagc 
88231558cbSagc /**
89231558cbSagc  * @brief creates gssapi mechanism session.
90231558cbSagc  * Function initializes also default options for the session.
91231558cbSagc  * @param sess sasl session
92231558cbSagc  * @return 0 on success, -1 on failure.
93231558cbSagc  */
94231558cbSagc static int
saslc__mech_gssapi_create(saslc_sess_t * sess)95231558cbSagc saslc__mech_gssapi_create(saslc_sess_t *sess)
96231558cbSagc {
97231558cbSagc 	saslc__mech_gssapi_sess_t *c;
98231558cbSagc 
99231558cbSagc 	c = sess->mech_sess = calloc(1, sizeof(*c));
100231558cbSagc 	if (c == NULL)
101231558cbSagc 		return -1;
102231558cbSagc 
103231558cbSagc 	sess->mech_sess = c;
104231558cbSagc 
10519c14409Schristos 	c->gss_ctx = GSS_C_NO_CONTEXT;
10619c14409Schristos 	c->server_name = GSS_C_NO_NAME;
10719c14409Schristos 	c->client_name = GSS_C_NO_NAME;
108231558cbSagc 
109231558cbSagc 	return 0;
110231558cbSagc }
111231558cbSagc 
112231558cbSagc /**
113231558cbSagc  * @brief destroys gssapi mechanism session.
114231558cbSagc  * Function also is freeing assigned resources to the session.
115231558cbSagc  * @param sess sasl session
116231558cbSagc  * @return Functions always returns 0.
117231558cbSagc  */
118231558cbSagc static int
saslc__mech_gssapi_destroy(saslc_sess_t * sess)119231558cbSagc saslc__mech_gssapi_destroy(saslc_sess_t *sess)
120231558cbSagc {
12119c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
122231558cbSagc 	OM_uint32 min_s;
123231558cbSagc 
12419c14409Schristos 	ms = sess->mech_sess;
125231558cbSagc 
12619c14409Schristos 	if (ms->gss_ctx != GSS_C_NO_CONTEXT)
12719c14409Schristos 		gss_delete_sec_context(&min_s, &ms->gss_ctx, GSS_C_NO_BUFFER);
12819c14409Schristos 	if (ms->server_name != GSS_C_NO_NAME)
12919c14409Schristos 		gss_release_name(&min_s, &ms->server_name);
13019c14409Schristos 	if (ms->client_name != GSS_C_NO_NAME)
13119c14409Schristos 		gss_release_name(&min_s, &ms->client_name);
132beea8b97Schristos 
13319c14409Schristos 	saslc__buffer_destroy(ms->enc_ctx);
13419c14409Schristos 	saslc__buffer32_destroy(ms->dec_ctx);
13519c14409Schristos 	free(ms);
136231558cbSagc 	sess->mech_sess = NULL;
137231558cbSagc 
138231558cbSagc 	return 0;
139231558cbSagc }
140231558cbSagc 
14119c14409Schristos /**
14219c14409Schristos  * @brief translate the major and minor statuses an error message for
14319c14409Schristos  * the given mechanism
14419c14409Schristos  * @param maj_s major status
14519c14409Schristos  * @param min_s minor status
14619c14409Schristos  * @param mech mechanism
14719c14409Schristos  * @return pointer to a static buffer with error message
14819c14409Schristos  */
14919c14409Schristos static char *
saslc__mech_gssapi_err(OM_uint32 maj_s,OM_uint32 min_s,gss_OID mech)15019c14409Schristos saslc__mech_gssapi_err(OM_uint32 maj_s, OM_uint32 min_s, gss_OID mech)
15119c14409Schristos {
15219c14409Schristos 	static char errbuf[LINE_MAX];
15319c14409Schristos 	gss_buffer_desc maj_error_message;
15419c14409Schristos 	gss_buffer_desc min_error_message;
15519c14409Schristos 	OM_uint32 disp_min_s;
15619c14409Schristos 	OM_uint32 msg_ctx;
15719c14409Schristos 
15819c14409Schristos 	msg_ctx = 0;
15919c14409Schristos 	maj_error_message.length = 0;
16019c14409Schristos 	maj_error_message.value = NULL;
16119c14409Schristos 	min_error_message.length = 0;
16219c14409Schristos 	min_error_message.value = NULL;
16319c14409Schristos 
16419c14409Schristos 	(void)gss_display_status(&disp_min_s, maj_s, GSS_C_GSS_CODE,
16519c14409Schristos 	    mech, &msg_ctx, &maj_error_message);
16619c14409Schristos 	(void)gss_display_status(&disp_min_s, min_s, GSS_C_MECH_CODE,
16719c14409Schristos 	    mech, &msg_ctx, &min_error_message);
16819c14409Schristos 
16919c14409Schristos 	(void)snprintf(errbuf, sizeof(errbuf),
17019c14409Schristos 	    "gss-code: %lu %.*s\nmech-code: %lu %.*s",
17119c14409Schristos 	    (unsigned long)maj_s,
17219c14409Schristos 	    (int)maj_error_message.length,
17319c14409Schristos 	    (char *)maj_error_message.value,
17419c14409Schristos 	    (unsigned long)min_s,
17519c14409Schristos 	    (int)min_error_message.length,
17619c14409Schristos 	    (char *)min_error_message.value);
17719c14409Schristos 
17819c14409Schristos 	(void)gss_release_buffer(&disp_min_s, &maj_error_message);
17919c14409Schristos 	(void)gss_release_buffer(&disp_min_s, &min_error_message);
18019c14409Schristos 
18119c14409Schristos 	return errbuf;
18219c14409Schristos }
183231558cbSagc 
184231558cbSagc /**
18519c14409Schristos  * @brief set a session error message using saslc__mech_gssapi_err()
18619c14409Schristos  * @param sess the session
18719c14409Schristos  * @param err error number to set
18819c14409Schristos  * @param maj_s major status
18919c14409Schristos  * @param min_s minor status
19019c14409Schristos  * @return pointer to a static buffer with error message
19119c14409Schristos  */
19219c14409Schristos static void
saslc__mech_gssapi_set_err(saslc_sess_t * sess,int err,OM_uint32 maj_s,OM_uint32 min_s)19319c14409Schristos saslc__mech_gssapi_set_err(saslc_sess_t *sess, int err, OM_uint32 maj_s, OM_uint32 min_s)
19419c14409Schristos {
19519c14409Schristos 
19619c14409Schristos 	saslc__error_set(ERR(sess), err,
19719c14409Schristos 	    saslc__mech_gssapi_err(maj_s, min_s, GSS_C_NO_OID));
19819c14409Schristos }
19919c14409Schristos 
20019c14409Schristos /**
20119c14409Schristos  * @brief convert an initialization output token into the out and outlen format.
20219c14409Schristos  * Also releases the output token.
20319c14409Schristos  * @param sess saslc session
20419c14409Schristos  * @param outbuf gss buffer token
20519c14409Schristos  * @param out pointer to a void pointer
20619c14409Schristos  * @param outlen pointer to size_t length storage
20719c14409Schristos  * @returns 0 on success, -1 on failure
20819c14409Schristos  */
20919c14409Schristos static int
prep_output(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)21019c14409Schristos prep_output(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
21119c14409Schristos {
21219c14409Schristos 	OM_uint32 min_s;
21319c14409Schristos 
21419c14409Schristos 	if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
21519c14409Schristos 		*outlen = 0;
21619c14409Schristos 		*out = NULL;
21719c14409Schristos 		return 0;
21819c14409Schristos 	}
21919c14409Schristos 	if (outbuf->length == 0) {
22019c14409Schristos 		*outlen = 0;
22119c14409Schristos 		*out = NULL;
22219c14409Schristos 		gss_release_buffer(&min_s, outbuf);
22319c14409Schristos 		return 0;
22419c14409Schristos 	}
22519c14409Schristos 	*out = malloc(outbuf->length);
22619c14409Schristos 	if (*out == NULL) {
22719c14409Schristos 		*outlen = 0;
22819c14409Schristos 		gss_release_buffer(&min_s, outbuf);
22919c14409Schristos 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
23019c14409Schristos 		return -1;
23119c14409Schristos 	}
23219c14409Schristos 	*outlen = outbuf->length;
23319c14409Schristos 	memcpy(*out, outbuf->value, outbuf->length);
23419c14409Schristos 	gss_release_buffer(&min_s, outbuf);
23519c14409Schristos 	return 0;
23619c14409Schristos }
23719c14409Schristos 
23819c14409Schristos /**
23919c14409Schristos  * @brief convert an output token into a valid packet where the first
24019c14409Schristos  * 4 bytes are the payload length in network byte order.
24119c14409Schristos  * Also releases the output token.
24219c14409Schristos  * @param sess saslc session
24319c14409Schristos  * @param outbuf gss buffer token
24419c14409Schristos  * @param out pointer to a void pointer
24519c14409Schristos  * @param outlen pointer to size_t length storage
24619c14409Schristos  * @returns 0 on success, -1 on failure
24719c14409Schristos  */
24819c14409Schristos static int
prep_packet(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)24919c14409Schristos prep_packet(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
25019c14409Schristos {
25119c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
25219c14409Schristos 	OM_uint32 min_s;
25319c14409Schristos 	char *buf;
25419c14409Schristos 	size_t buflen;
25519c14409Schristos 
25619c14409Schristos 	ms = sess->mech_sess;
25719c14409Schristos 
25819c14409Schristos 	if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
25919c14409Schristos 		*outlen = 0;
26019c14409Schristos 		*out = NULL;
26119c14409Schristos 		return 0;
26219c14409Schristos 	}
26319c14409Schristos 	if (outbuf->length == 0) {
26419c14409Schristos 		*outlen = 0;
26519c14409Schristos 		*out = NULL;
26619c14409Schristos 		gss_release_buffer(&min_s, outbuf);
26719c14409Schristos 		return 0;
26819c14409Schristos 	}
26919c14409Schristos 	buflen = outbuf->length + 4;
27019c14409Schristos 	if (buflen > ms->omaxbuf) {
27116e81cb9Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
27219c14409Schristos 		    "output exceeds server maxbuf size");
27319c14409Schristos 		gss_release_buffer(&min_s, outbuf);
27419c14409Schristos 		return -1;
27519c14409Schristos 	}
27619c14409Schristos 	buf = malloc(buflen);
27719c14409Schristos 	if (buf == NULL) {
27819c14409Schristos 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
27919c14409Schristos 		return -1;
28019c14409Schristos 	}
28119c14409Schristos 	be32enc(buf, (uint32_t)outbuf->length);
28219c14409Schristos 	memcpy(buf + 4, outbuf->value, outbuf->length);
28319c14409Schristos 	gss_release_buffer(&min_s, outbuf);
28419c14409Schristos 
28519c14409Schristos 	*out = buf;
28619c14409Schristos 	*outlen = buflen;
28719c14409Schristos 	return 0;
28819c14409Schristos }
28919c14409Schristos 
29019c14409Schristos /**
29119c14409Schristos  * @brief encodes one block of data using the negotiated security layer.
292231558cbSagc  * @param sess sasl session
293231558cbSagc  * @param in input data
294231558cbSagc  * @param inlen input data length
295231558cbSagc  * @param out place to store output data
296231558cbSagc  * @param outlen output data length
29719c14409Schristos  * @return number of bytes consumed, zero if more needed, or -1 on failure.
298231558cbSagc  */
29919c14409Schristos static ssize_t
saslc__mech_gssapi_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)300231558cbSagc saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen,
301231558cbSagc     void **out, size_t *outlen)
302231558cbSagc {
30319c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
304231558cbSagc 	gss_buffer_desc input, output;
305231558cbSagc 	OM_uint32 min_s, maj_s;
30619c14409Schristos 	uint8_t *buf;
30719c14409Schristos 	size_t buflen;
30819c14409Schristos 	ssize_t len;
309231558cbSagc 
31019c14409Schristos 	ms = sess->mech_sess;
31119c14409Schristos 	assert(ms->mech_sess.qop != QOP_NONE);
31219c14409Schristos 	if (ms->mech_sess.qop == QOP_NONE)
31319c14409Schristos 		return -1;
314231558cbSagc 
31519c14409Schristos 	len = saslc__buffer_fetch(ms->enc_ctx, in, inlen, &buf, &buflen);
31619c14409Schristos 	if (len == -1)
31719c14409Schristos 		return -1;
318231558cbSagc 
31919c14409Schristos 	if (buflen == 0) {
320231558cbSagc 		*out = NULL;
32119c14409Schristos 		*outlen = 0;
32219c14409Schristos 		return len;
32319c14409Schristos 	}
324231558cbSagc 
32519c14409Schristos 	input.value = buf;
32619c14409Schristos 	input.length = buflen;
32719c14409Schristos 	output.value = NULL;
32819c14409Schristos 	output.length = 0;
32919c14409Schristos 
33019c14409Schristos 	maj_s = gss_wrap(&min_s, ms->gss_ctx, ms->mech_sess.qop == QOP_CONF,
33119c14409Schristos 	    GSS_C_QOP_DEFAULT, &input, NULL, &output);
33219c14409Schristos 
33319c14409Schristos 	if (GSS_ERROR(maj_s)) {
33419c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
33519c14409Schristos 		return -1;
33619c14409Schristos 	}
33719c14409Schristos 	if (prep_packet(sess, &output, out, outlen) == -1)
33819c14409Schristos 		return -1;
33919c14409Schristos 
34019c14409Schristos 	return len;
341231558cbSagc }
342231558cbSagc 
343231558cbSagc /**
34419c14409Schristos  * @brief decodes one block of data using the negotiated security layer.
345231558cbSagc  * @param sess sasl session
346231558cbSagc  * @param in input data
347231558cbSagc  * @param inlen input data length
348231558cbSagc  * @param out place to store output data
349231558cbSagc  * @param outlen output data length
35019c14409Schristos  * @return number of bytes consumed, zero if more needed, or -1 on failure.
351231558cbSagc  */
35219c14409Schristos static ssize_t
saslc__mech_gssapi_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)353231558cbSagc saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen,
354231558cbSagc 	void **out, size_t *outlen)
355231558cbSagc {
35619c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
357231558cbSagc 	gss_buffer_desc input, output;
358231558cbSagc 	OM_uint32 min_s, maj_s;
35919c14409Schristos 	uint8_t *buf;
36019c14409Schristos 	size_t buflen;
36119c14409Schristos 	ssize_t len;
362231558cbSagc 
36319c14409Schristos 	ms = sess->mech_sess;
36419c14409Schristos 	assert(ms->mech_sess.qop != QOP_NONE);
36519c14409Schristos 	if (ms->mech_sess.qop == QOP_NONE)
36619c14409Schristos 		return -1;
367231558cbSagc 
36819c14409Schristos 	len = saslc__buffer32_fetch(ms->dec_ctx, in, inlen, &buf, &buflen);
36919c14409Schristos 	if (len == -1)
37019c14409Schristos 		return -1;
371231558cbSagc 
37219c14409Schristos 	if (buflen == 0) {
373231558cbSagc 		*out = NULL;
37419c14409Schristos 		*outlen = 0;
37519c14409Schristos 		return len;
37619c14409Schristos 	}
377231558cbSagc 
37819c14409Schristos 	/* buf -> szbuf (4 bytes) followed by the payload buffer */
37919c14409Schristos 	input.value = buf + 4;
38019c14409Schristos 	input.length = buflen - 4;
38119c14409Schristos 	output.value = NULL;
38219c14409Schristos 	output.length = 0;
38319c14409Schristos 
38419c14409Schristos 	maj_s = gss_unwrap(&min_s, ms->gss_ctx, &input, &output, NULL, NULL);
38519c14409Schristos 
38619c14409Schristos 	if (GSS_ERROR(maj_s)) {
38719c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
38819c14409Schristos 		return -1;
38919c14409Schristos 	}
39019c14409Schristos 
39119c14409Schristos 	if (prep_output(sess, &output, out, outlen) == -1)
39219c14409Schristos 		return -1;
39319c14409Schristos 
39419c14409Schristos 	return len;
395231558cbSagc }
396231558cbSagc 
397231558cbSagc /**
39819c14409Schristos  * @brief get service name from properties
39919c14409Schristos  * ("<servicename>@<hostname>") and store it in service token.
40019c14409Schristos  * @param sess the session context
40119c14409Schristos  * @param service the gs_name_t token to return service name in
40219c14409Schristos  * @return 0 on success, -1 on error
40319c14409Schristos  */
40419c14409Schristos static int
get_service(saslc_sess_t * sess,gss_name_t * service)40519c14409Schristos get_service(saslc_sess_t *sess, gss_name_t *service)
40619c14409Schristos {
40719c14409Schristos 	gss_buffer_desc bufdesc;
40819c14409Schristos 	const char *hostname, *servicename;
40919c14409Schristos 	char *buf;
41019c14409Schristos 	int buflen;
41119c14409Schristos 	OM_uint32 min_s, maj_s;
41219c14409Schristos 
41319c14409Schristos 	hostname = saslc_sess_getprop(sess, SASLC_GSSAPI_HOSTNAME);
41419c14409Schristos 	if (hostname == NULL) {
41519c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
41619c14409Schristos 		    "hostname is required for an authentication");
41719c14409Schristos 		return -1;
41819c14409Schristos 	}
41919c14409Schristos 	servicename = saslc_sess_getprop(sess, SASLC_GSSAPI_SERVICE);
42019c14409Schristos 	if (servicename == NULL) {
42119c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
42219c14409Schristos 		    "service is required for an authentication");
42319c14409Schristos 		return -1;
42419c14409Schristos 	}
42519c14409Schristos 	buflen = asprintf(&buf, "%s@%s", servicename, hostname);
42619c14409Schristos 	if (buflen == -1) {
42719c14409Schristos 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
42819c14409Schristos 		return -1;
42919c14409Schristos 	}
43019c14409Schristos 	bufdesc.value = buf;
43119c14409Schristos 	bufdesc.length = buflen + 1;
43219c14409Schristos 
43319c14409Schristos 	saslc__msg_dbg("%s: buf='%s'", __func__, buf);
43419c14409Schristos 
43519c14409Schristos 	maj_s = gss_import_name(&min_s, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
43619c14409Schristos 	    service);
43719c14409Schristos 	free(buf);
43819c14409Schristos 	if (GSS_ERROR(maj_s)) {
43919c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
44019c14409Schristos 		return -1;
44119c14409Schristos 	}
44219c14409Schristos 	return 0;
44319c14409Schristos }
44419c14409Schristos 
44519c14409Schristos /**
44619c14409Schristos  * @brief gss_init_sec_context() wrapper
44719c14409Schristos  * @param sess session context
44819c14409Schristos  * @param inbuf input token
44919c14409Schristos  * @param outbuf output token
45019c14409Schristos  * @return 0 if GSS_S_COMPLETE, 1 if GSS_S_CONTINUE_NEEDED, -1 on failure
45119c14409Schristos  */
45219c14409Schristos static int
init_sec_context(saslc_sess_t * sess,gss_buffer_t inbuf,gss_buffer_t outbuf)45319c14409Schristos init_sec_context(saslc_sess_t *sess, gss_buffer_t inbuf, gss_buffer_t outbuf)
45419c14409Schristos {
45519c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
45619c14409Schristos 	OM_uint32 min_s, maj_s;
45719c14409Schristos 
45819c14409Schristos 	ms = sess->mech_sess;
45919c14409Schristos 
46019c14409Schristos 	outbuf->length = 0;
46119c14409Schristos 	outbuf->value = NULL;
46219c14409Schristos 	maj_s = gss_init_sec_context(
46319c14409Schristos 		&min_s,			/* minor status */
46419c14409Schristos 		GSS_C_NO_CREDENTIAL, /* use current login context credential */
46519c14409Schristos 		&ms->gss_ctx,		/* initially GSS_C_NO_CONTEXT */
46619c14409Schristos 		ms->server_name,	/* server@hostname */
46719c14409Schristos 		GSS_C_NO_OID,		/* use default mechanism */
46819c14409Schristos #if 1
46919c14409Schristos 		GSS_C_REPLAY_FLAG |	/* message replay detection */
47019c14409Schristos 		GSS_C_INTEG_FLAG |	/* request integrity */
47119c14409Schristos 		GSS_C_CONF_FLAG |	/* request confirmation */
47219c14409Schristos #endif
47319c14409Schristos 		GSS_C_MUTUAL_FLAG |	/* mutual authentication */
47419c14409Schristos 		GSS_C_SEQUENCE_FLAG,	/* message sequence checking */
47519c14409Schristos 		0,			/* default lifetime (2 hrs) */
47619c14409Schristos 		GSS_C_NO_CHANNEL_BINDINGS,
47719c14409Schristos 		inbuf,			/* input token */
47819c14409Schristos 		/* output parameters follow */
47919c14409Schristos 		NULL,			/* mechanism type for context */
48019c14409Schristos 		outbuf,			/* output token */
48119c14409Schristos 		NULL,			/* services available for context */
48219c14409Schristos 		NULL);			/* lifetime of context */
48319c14409Schristos 
48419c14409Schristos 	switch (maj_s) {
48519c14409Schristos 	case GSS_S_COMPLETE:
48619c14409Schristos 		return 0;
48719c14409Schristos 	case GSS_S_CONTINUE_NEEDED:
48819c14409Schristos 		return 1;
48919c14409Schristos 	default:
49019c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
49119c14409Schristos 		return -1;
49219c14409Schristos 	}
49319c14409Schristos }
49419c14409Schristos 
49519c14409Schristos /**
49619c14409Schristos  * @brief unwrap the authentication token received from the server.
49719c14409Schristos  * This contains the qop_mask and maxbuf values which are updated in
49819c14409Schristos  * saslc__mech_gssapi_sess_t.
49919c14409Schristos  * @param sess the session context
50019c14409Schristos  * @param inbuf the received authentication token.
50119c14409Schristos  * @return 0 on success, -1 on error.
50219c14409Schristos  */
50319c14409Schristos static int
unwrap_input_token(saslc_sess_t * sess,gss_buffer_t inbuf)50419c14409Schristos unwrap_input_token(saslc_sess_t *sess, gss_buffer_t inbuf)
50519c14409Schristos {
50619c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
50719c14409Schristos 	OM_uint32 min_s, maj_s;
50819c14409Schristos 	gss_buffer_t outbuf;
50919c14409Schristos 	gss_buffer_desc outdesc;
51019c14409Schristos 	unsigned char *p;
51119c14409Schristos 
51219c14409Schristos 	/********************************************************************/
51319c14409Schristos 	/* [RFC 2222 section 7.2.1]                                         */
51419c14409Schristos 	/* The client passes this token to GSS_Unwrap and interprets        */
51519c14409Schristos 	/* the first octet of resulting cleartext as a bit-mask specifying  */
51619c14409Schristos 	/* the security layers supported by the server and the second       */
51719c14409Schristos 	/* through fourth octets as the maximum size output_message to send */
51819c14409Schristos 	/* to the server.                                                   */
51919c14409Schristos 	/********************************************************************/
52019c14409Schristos 
52119c14409Schristos 	ms = sess->mech_sess;
52219c14409Schristos 
52319c14409Schristos 	outbuf = &outdesc;
52419c14409Schristos 	maj_s = gss_unwrap(&min_s, ms->gss_ctx, inbuf, outbuf, NULL, NULL);
52519c14409Schristos 
52619c14409Schristos 	if (GSS_ERROR(maj_s)) {
52719c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
52819c14409Schristos 		return -1;
52919c14409Schristos 	}
53019c14409Schristos 	if (outbuf->length != 4) {
53119c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
53219c14409Schristos 		    "invalid unwrap length");
53319c14409Schristos 		return -1;
53419c14409Schristos 	}
53519c14409Schristos 	p = outbuf->value;
53619c14409Schristos 	ms->qop_mask = p[0];
53719c14409Schristos 	ms->omaxbuf = (be32dec(p) & 0xffffff);
53819c14409Schristos 
53919c14409Schristos 	saslc__msg_dbg("%s: qop_mask=0x%02x omaxbuf=%d",
54019c14409Schristos 	    __func__, ms->qop_mask, ms->omaxbuf);
54119c14409Schristos 
54219c14409Schristos 	if (ms->qop_mask == QOP_NONE && ms->omaxbuf != 0) {
54319c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
54419c14409Schristos 		    "server has no security layer support, but maxbuf != 0");
54519c14409Schristos 		return -1;
54619c14409Schristos 	}
54719c14409Schristos 	maj_s = gss_release_buffer(&min_s, outbuf);
54819c14409Schristos 	if (GSS_ERROR(maj_s)) {
54919c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
55019c14409Schristos 		return -1;
55119c14409Schristos 	}
55219c14409Schristos 	return 0;
55319c14409Schristos }
55419c14409Schristos 
55519c14409Schristos /**
55619c14409Schristos  * @brief construct and wrap up an authentication token and put it in
55719c14409Schristos  * outbuf.  The outbuf token data is structured as follows:
55819c14409Schristos  * struct {
55919c14409Schristos  *   uint8_t qop;	// qop to use
56019c14409Schristos  *   uint8_t maxbuf[3]	// maxbuf for client (network byte order)
56119c14409Schristos  *   uint8_t authcid[]	// variable length authentication id (username)
56219c14409Schristos  * } __packed;
56319c14409Schristos  * @param sess the session
56419c14409Schristos  * @param outbuf the gss_buffer_t token to return to server.
56519c14409Schristos  * @return 0 on success, -1 on error.
56619c14409Schristos  */
56719c14409Schristos static int
wrap_output_token(saslc_sess_t * sess,gss_buffer_t outbuf)56819c14409Schristos wrap_output_token(saslc_sess_t *sess, gss_buffer_t outbuf)
56919c14409Schristos {
57019c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
57119c14409Schristos 	gss_buffer_desc indesc;
57219c14409Schristos 	char *input_value;
57319c14409Schristos 	int len;
57419c14409Schristos 	const char *authcid;
57519c14409Schristos 	OM_uint32 min_s, maj_s;
57619c14409Schristos 	unsigned char *p;
57719c14409Schristos 
57819c14409Schristos 	/********************************************************************/
57919c14409Schristos 	/* [RFC 2222 section 7.2.1]                                         */
58019c14409Schristos 	/* The client then constructs data, with the first octet containing */
58119c14409Schristos 	/* the bit-mask specifying the selected security layer, the second  */
58219c14409Schristos 	/* through fourth octets containing in network byte order the       */
58319c14409Schristos 	/* maximum size output_message the client is able to receive, and   */
58419c14409Schristos 	/* the remaining octets containing the authorization identity.  The */
585*39662850Selric 	/* authorization identity is optional in mechanisms where it is     */
586*39662850Selric 	/* encoded in the exchange such as GSSAPI.  The client passes the   */
587*39662850Selric 	/* data to GSS_Wrap with conf_flag set to FALSE, and responds with  */
588*39662850Selric 	/* the generated output_message.  The client can then consider the  */
589*39662850Selric 	/* server authenticated.                                            */
59019c14409Schristos 	/********************************************************************/
59119c14409Schristos 
59219c14409Schristos 	ms = sess->mech_sess;
59319c14409Schristos 
594*39662850Selric 	authcid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHCID);
59519c14409Schristos 
596*39662850Selric 	len = asprintf(&input_value, "qmax%s", authcid ? authcid : "");
59719c14409Schristos 	if (len == -1) {
59819c14409Schristos 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
59919c14409Schristos 		return -1;
60019c14409Schristos 	}
60119c14409Schristos 	be32enc(input_value, ms->imaxbuf);
60219c14409Schristos 	input_value[0] = saslc__mech_qop_flag(ms->mech_sess.qop);
60319c14409Schristos 
60419c14409Schristos 	indesc.value = input_value;
60519c14409Schristos 	indesc.length = len;	/* XXX: don't count the '\0' */
60619c14409Schristos 
60719c14409Schristos 	p = (unsigned char *)input_value;
60819c14409Schristos 	saslc__msg_dbg("%s: input_value='%02x %02x %02x %02x %s",
60919c14409Schristos 	    __func__, p[0], p[1], p[2], p[3], input_value + 4);
61019c14409Schristos 
61119c14409Schristos 	maj_s = gss_wrap(&min_s, ms->gss_ctx, 0 /* FALSE - RFC2222 */,
61219c14409Schristos 	    GSS_C_QOP_DEFAULT, &indesc, NULL, outbuf);
61319c14409Schristos 
61419c14409Schristos 	free(input_value);
61519c14409Schristos 
61619c14409Schristos 	if (GSS_ERROR(maj_s)) {
61719c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
61819c14409Schristos 		return -1;
61919c14409Schristos 	}
62019c14409Schristos 	return 0;
62119c14409Schristos }
62219c14409Schristos 
62319c14409Schristos /************************************************************************
62419c14409Schristos  * XXX: Share this with mech_digestmd5.c?  They are almost identical.
62519c14409Schristos  */
62619c14409Schristos /**
62719c14409Schristos  * @brief choose the best qop based on what was provided by the
62819c14409Schristos  * challenge and a possible user mask.
62919c14409Schristos  * @param sess the session context
63019c14409Schristos  * @param qop_flags the qop flags parsed from the challenge string
63119c14409Schristos  * @return the selected saslc__mech_sess_qop_t or -1 if no match
63219c14409Schristos  */
63319c14409Schristos static int
choose_qop(saslc_sess_t * sess,uint32_t qop_flags)63419c14409Schristos choose_qop(saslc_sess_t *sess, uint32_t qop_flags)
63519c14409Schristos {
63619c14409Schristos 	list_t *list;
63719c14409Schristos 	const char *user_qop;
63819c14409Schristos 
63919c14409Schristos 	qop_flags &= DEFAULT_QOP_MASK;
64019c14409Schristos 	user_qop = saslc_sess_getprop(sess, SASLC_GSSAPI_QOPMASK);
64119c14409Schristos 	if (user_qop != NULL) {
64209484ebbSchristos 		if (saslc__list_parse(&list, user_qop) == -1) {
64309484ebbSchristos 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
64409484ebbSchristos 			return -1;
64509484ebbSchristos 		}
64619c14409Schristos 		qop_flags &= saslc__mech_qop_list_flags(list);
64719c14409Schristos 		saslc__list_free(list);
64819c14409Schristos 	}
64919c14409Schristos 
65019c14409Schristos 	/*
65119c14409Schristos 	 * Select the most secure supported qop.
65219c14409Schristos 	 */
65319c14409Schristos 	if ((qop_flags & F_QOP_CONF) != 0)
65419c14409Schristos 		return QOP_CONF;
65519c14409Schristos 	if ((qop_flags & F_QOP_INT) != 0)
65619c14409Schristos 		return QOP_INT;
65719c14409Schristos 	if ((qop_flags & F_QOP_NONE) != 0)
65819c14409Schristos 		return QOP_NONE;
65919c14409Schristos 
66019c14409Schristos 	saslc__error_set(ERR(sess), ERROR_MECH,
66119c14409Schristos 	    "cannot choose an acceptable qop");
66219c14409Schristos 	return -1;
66319c14409Schristos }
66419c14409Schristos /************************************************************************/
66519c14409Schristos 
66619c14409Schristos /**
66719c14409Schristos  * @brief compute the maximum buffer length we can use and not
66819c14409Schristos  * overflow the servers maxbuf.
66919c14409Schristos  * @param sess the session context
67019c14409Schristos  * @param maxbuf the server's maxbuf value
67119c14409Schristos  */
67219c14409Schristos static int
wrap_size_limit(saslc_sess_t * sess,OM_uint32 maxbuf)67319c14409Schristos wrap_size_limit(saslc_sess_t *sess, OM_uint32 maxbuf)
67419c14409Schristos {
67519c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
67619c14409Schristos 	OM_uint32 min_s, maj_s;
67719c14409Schristos 	OM_uint32 max_input;
67819c14409Schristos 
67919c14409Schristos 	ms = sess->mech_sess;
68019c14409Schristos 
68119c14409Schristos 	maj_s = gss_wrap_size_limit(&min_s, ms->gss_ctx, 1, GSS_C_QOP_DEFAULT,
68219c14409Schristos 	    maxbuf, &max_input);
68319c14409Schristos 
68419c14409Schristos 	if (GSS_ERROR(maj_s)) {
68519c14409Schristos 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
68619c14409Schristos 		return -1;
68719c14409Schristos 	}
68819c14409Schristos 
68919c14409Schristos 	/* XXX: from cyrus-sasl: gssapi.c */
69019c14409Schristos 	if (max_input > maxbuf) {
69119c14409Schristos 		/* Heimdal appears to get this wrong */
69219c14409Schristos 		maxbuf -= (max_input - maxbuf);
69319c14409Schristos 	} else {
69419c14409Schristos 		/* This code is actually correct */
69519c14409Schristos 		maxbuf = max_input;
69619c14409Schristos 	}
69719c14409Schristos 	return maxbuf;
69819c14409Schristos }
69919c14409Schristos 
70019c14409Schristos /**
70119c14409Schristos  * @brief set our imaxbuf (from omaxbuf or from properties) and
70219c14409Schristos  * then reset omaxbuf in saslc__mech_gssapi_sess_t.
70319c14409Schristos  * @param sess the session context
70419c14409Schristos  * @return 0 on success, -1 on error
705231558cbSagc  *
70619c14409Schristos  * Note: on entry the omaxbuf is the server's maxbuf size.  On exit
70719c14409Schristos  * the omaxbuf is the maximum buffer we can fill that will not
70819c14409Schristos  * overflow the servers maxbuf after it is encoded.  This value is
70919c14409Schristos  * given by wrap_size_limit().
71019c14409Schristos  */
71119c14409Schristos static int
set_maxbufs(saslc_sess_t * sess)71219c14409Schristos set_maxbufs(saslc_sess_t *sess)
71319c14409Schristos {
71419c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
71519c14409Schristos 	const char *p;
71619c14409Schristos 	char *q;
71719c14409Schristos 	unsigned long val;
71819c14409Schristos 	int rv;
71919c14409Schristos 
72019c14409Schristos 	ms = sess->mech_sess;
72119c14409Schristos 
72219c14409Schristos 	/* by default, we use the same input maxbuf as the server. */
72319c14409Schristos 	ms->imaxbuf = ms->omaxbuf;
72419c14409Schristos 	p = saslc_sess_getprop(sess, SASLC_PROP_MAXBUF);
72519c14409Schristos 	if (p != NULL) {
72619c14409Schristos 		val = strtol(p, &q, 0);
72719c14409Schristos 		if (p[0] == '\0' || *q != '\0') {
72819c14409Schristos 
72919c14409Schristos 			return MECH_ERROR;
73019c14409Schristos 		}
73119c14409Schristos 		if (errno == ERANGE && val == ULONG_MAX) {
73219c14409Schristos 
73319c14409Schristos 			return MECH_ERROR;
73419c14409Schristos 		}
73519c14409Schristos 		if (val > 0xffffff)
73619c14409Schristos 			val = 0xffffff;
73719c14409Schristos 		ms->imaxbuf = (uint32_t)val;
73819c14409Schristos 	}
73919c14409Schristos 	rv = wrap_size_limit(sess, ms->omaxbuf);
74019c14409Schristos 	if (rv == -1)
74119c14409Schristos 		return MECH_ERROR;
74219c14409Schristos 	ms->omaxbuf = rv;	/* maxbuf size for unencoded output data */
74319c14409Schristos 
74419c14409Schristos 	return 0;
74519c14409Schristos }
74619c14409Schristos 
74719c14409Schristos /**
74819c14409Schristos  * @brief do one step of the sasl authentication
749231558cbSagc  * @param sess sasl session
750231558cbSagc  * @param in input data
751231558cbSagc  * @param inlen input data length
752231558cbSagc  * @param out place to store output data
753231558cbSagc  * @param outlen output data length
75419c14409Schristos  * @return MECH_OK on success, MECH_STEP if more steps are needed,
75519c14409Schristos  * MECH_ERROR on failure
756231558cbSagc  */
757231558cbSagc static int
saslc__mech_gssapi_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)758231558cbSagc saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen,
759231558cbSagc     void **out, size_t *outlen)
760231558cbSagc {
76119c14409Schristos 	saslc__mech_gssapi_sess_t *ms;
76219c14409Schristos 	gss_buffer_desc input, output;
76319c14409Schristos 	int rv;
764231558cbSagc 
76519c14409Schristos     /**************************************************************************/
76619c14409Schristos     /* [RFC 2222 section 7.2.1]                                               */
76719c14409Schristos     /* The client calls GSS_Init_sec_context, passing in 0 for                */
76819c14409Schristos     /* input_context_handle (initially) and a targ_name equal to output_name  */
76919c14409Schristos     /* from GSS_Import_Name called with input_name_type of                    */
77019c14409Schristos     /* GSS_C_NT_HOSTBASED_SERVICE and input_name_string of                    */
77119c14409Schristos     /* "service@hostname" where "service" is the service name specified in    */
77219c14409Schristos     /* the protocol's profile, and "hostname" is the fully qualified host     */
77319c14409Schristos     /* name of the server.  The client then responds with the resulting       */
77419c14409Schristos     /* output_token.  If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED,  */
77519c14409Schristos     /* then the client should expect the server to issue a token in a         */
77619c14409Schristos     /* subsequent challenge.  The client must pass the token to another call  */
77719c14409Schristos     /* to GSS_Init_sec_context, repeating the actions in this paragraph.      */
77819c14409Schristos     /*                                                                        */
77919c14409Schristos     /* When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes     */
78019c14409Schristos     /* the following actions: If the last call to GSS_Init_sec_context        */
78119c14409Schristos     /* returned an output_token, then the client responds with the            */
78219c14409Schristos     /* output_token, otherwise the client responds with no data.  The client  */
78319c14409Schristos     /* should then expect the server to issue a token in a subsequent         */
78419c14409Schristos     /* challenge.  The client passes this token to GSS_Unwrap and interprets  */
78519c14409Schristos     /* the first octet of resulting cleartext as a bit-mask specifying the    */
78619c14409Schristos     /* security layers supported by the server and the second through fourth  */
78719c14409Schristos     /* octets as the maximum size output_message to send to the server.  The  */
78819c14409Schristos     /* client then constructs data, with the first octet containing the       */
78919c14409Schristos     /* bit-mask specifying the selected security layer, the second through    */
79019c14409Schristos     /* fourth octets containing in network byte order the maximum size        */
79119c14409Schristos     /* output_message the client is able to receive, and the remaining        */
79219c14409Schristos     /* octets containing the authorization identity.  The client passes the   */
79319c14409Schristos     /* data to GSS_Wrap with conf_flag set to FALSE, and responds with the    */
79419c14409Schristos     /* generated output_message.  The client can then consider the server     */
79519c14409Schristos     /* authenticated.                                                         */
79619c14409Schristos     /**************************************************************************/
797231558cbSagc 
79819c14409Schristos 	ms = sess->mech_sess;
799231558cbSagc 
80019c14409Schristos 	switch(ms->status) {
80119c14409Schristos 	case GSSAPI_AUTH_FIRST:
80219c14409Schristos 		saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_FIRST");
803231558cbSagc 
80419c14409Schristos 		if (get_service(sess, &ms->server_name) == -1)
805231558cbSagc 			return MECH_ERROR;
806231558cbSagc 
80719c14409Schristos 		rv = init_sec_context(sess, GSS_C_NO_BUFFER, &output);
80819c14409Schristos 		if (rv == -1)
809231558cbSagc 			return MECH_ERROR;
81019c14409Schristos 
81119c14409Schristos 		if (prep_output(sess, &output, out, outlen) == -1)
81219c14409Schristos 			return MECH_ERROR;
81319c14409Schristos 
81419c14409Schristos 		ms->status = rv == 0 ? GSSAPI_AUTH_LAST : GSSAPI_AUTH_NEXT;
815231558cbSagc 		return MECH_STEP;
816231558cbSagc 
81719c14409Schristos 	case GSSAPI_AUTH_NEXT:
81819c14409Schristos 		saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_NEXT");
81919c14409Schristos 
82019c14409Schristos 		input.value = __UNCONST(in);
821231558cbSagc 		input.length = inlen;
82219c14409Schristos 		if ((rv = init_sec_context(sess, &input, &output)) == -1)
823231558cbSagc 			return MECH_ERROR;
82419c14409Schristos 
82519c14409Schristos 		if (prep_output(sess, &output, out, outlen) == -1)
82619c14409Schristos 			return MECH_ERROR;
82719c14409Schristos 
82819c14409Schristos 		if (rv == 0)
82919c14409Schristos 			ms->status = GSSAPI_AUTH_LAST;
83019c14409Schristos 		return MECH_STEP;
83119c14409Schristos 
83219c14409Schristos 	case GSSAPI_AUTH_LAST:
83319c14409Schristos 		saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_LAST");
83419c14409Schristos 
83519c14409Schristos 		input.value = __UNCONST(in);
83619c14409Schristos 		input.length = inlen;
83719c14409Schristos 		if (unwrap_input_token(sess, &input) == -1)
83819c14409Schristos 			return MECH_ERROR;
83919c14409Schristos 
84019c14409Schristos 		if ((rv = choose_qop(sess, ms->qop_mask)) == -1)
84119c14409Schristos 			return MECH_ERROR;
84219c14409Schristos 
84319c14409Schristos 		ms->mech_sess.qop = rv;
84419c14409Schristos 
84519c14409Schristos 		if (ms->mech_sess.qop != QOP_NONE) {
84619c14409Schristos 			if (ms->mech_sess.qop == QOP_CONF) {
84719c14409Schristos 				/*
84819c14409Schristos 				 * XXX: where do we negotiate the cipher,
84919c14409Schristos 				 *  or do we?
85019c14409Schristos 				 */
851231558cbSagc 			}
85219c14409Schristos 			if (set_maxbufs(sess) == -1)
853231558cbSagc 				return MECH_ERROR;
85419c14409Schristos 			ms->dec_ctx = saslc__buffer32_create(sess, ms->imaxbuf);
85519c14409Schristos 			ms->enc_ctx = saslc__buffer_create(sess, ms->omaxbuf);
856231558cbSagc 		}
85719c14409Schristos 		if (wrap_output_token(sess, &output) == -1)
858231558cbSagc 			return MECH_ERROR;
859231558cbSagc 
86019c14409Schristos 		if (prep_output(sess, &output, out, outlen) == -1)
861231558cbSagc 			return MECH_ERROR;
862231558cbSagc 
86319c14409Schristos 		ms->status = GSSAPI_AUTH_DONE;
864231558cbSagc 		return MECH_OK;
86519c14409Schristos 
86619c14409Schristos 	case GSSAPI_AUTH_DONE:
86719c14409Schristos 		assert(/*CONSTCOND*/0);	/* XXX: impossible */
86819c14409Schristos 		saslc__error_set(ERR(sess), ERROR_MECH,
86919c14409Schristos 		    "already authenticated");
87019c14409Schristos 		return MECH_ERROR;
87119c14409Schristos 
872beea8b97Schristos #if 0	/* no default so the compiler can tell us if we miss an enum */
873231558cbSagc 	default:
874231558cbSagc 		assert(/*CONSTCOND*/0); /* impossible */
875231558cbSagc 		/*NOTREACHED*/
876beea8b97Schristos #endif
877231558cbSagc 	}
87819c14409Schristos 	/*LINTED*/
87919c14409Schristos 	assert(/*CONSTCOND*/0);		/* XXX: impossible */
88019c14409Schristos 	return MECH_ERROR;
881231558cbSagc }
882231558cbSagc 
883231558cbSagc /* mechanism definition */
884231558cbSagc const saslc__mech_t saslc__mech_gssapi = {
88519c14409Schristos 	.name	 = "GSSAPI",
88619c14409Schristos 	.flags	 = FLAG_NONE,
88719c14409Schristos 	.create	 = saslc__mech_gssapi_create,
88819c14409Schristos 	.cont	 = saslc__mech_gssapi_cont,
88919c14409Schristos 	.encode	 = saslc__mech_gssapi_encode,
89019c14409Schristos 	.decode	 = saslc__mech_gssapi_decode,
89119c14409Schristos 	.destroy = saslc__mech_gssapi_destroy
892231558cbSagc };
893