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