xref: /minix3/crypto/external/bsd/libsaslc/dist/src/mech_gssapi.c (revision ebfedea0ce5bbe81e252ddf32d732e40fb633fae)
1*ebfedea0SLionel Sambuc /* $NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $ */
2*ebfedea0SLionel Sambuc 
3*ebfedea0SLionel Sambuc /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4*ebfedea0SLionel Sambuc  * All rights reserved.
5*ebfedea0SLionel Sambuc  *
6*ebfedea0SLionel Sambuc  * This code is derived from software contributed to The NetBSD Foundation
7*ebfedea0SLionel Sambuc  * by Mateusz Kocielski.
8*ebfedea0SLionel Sambuc  *
9*ebfedea0SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
10*ebfedea0SLionel Sambuc  * modification, are permitted provided that the following conditions
11*ebfedea0SLionel Sambuc  * are met:
12*ebfedea0SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
13*ebfedea0SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
14*ebfedea0SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
15*ebfedea0SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
16*ebfedea0SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
17*ebfedea0SLionel Sambuc  * 3. All advertising materials mentioning features or use of this software
18*ebfedea0SLionel Sambuc  *    must display the following acknowledgement:
19*ebfedea0SLionel Sambuc  *        This product includes software developed by the NetBSD
20*ebfedea0SLionel Sambuc  *        Foundation, Inc. and its contributors.
21*ebfedea0SLionel Sambuc  * 4. Neither the name of The NetBSD Foundation nor the names of its
22*ebfedea0SLionel Sambuc  *    contributors may be used to endorse or promote products derived
23*ebfedea0SLionel Sambuc  *    from this software without specific prior written permission.
24*ebfedea0SLionel Sambuc  *
25*ebfedea0SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26*ebfedea0SLionel Sambuc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27*ebfedea0SLionel Sambuc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28*ebfedea0SLionel Sambuc  * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29*ebfedea0SLionel Sambuc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30*ebfedea0SLionel Sambuc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31*ebfedea0SLionel Sambuc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32*ebfedea0SLionel Sambuc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33*ebfedea0SLionel Sambuc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34*ebfedea0SLionel Sambuc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35*ebfedea0SLionel Sambuc  * POSSIBILITY OF SUCH DAMAGE.
36*ebfedea0SLionel Sambuc  */
37*ebfedea0SLionel Sambuc #include <sys/cdefs.h>
38*ebfedea0SLionel Sambuc __RCSID("$NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $");
39*ebfedea0SLionel Sambuc 
40*ebfedea0SLionel Sambuc #include <assert.h>
41*ebfedea0SLionel Sambuc #include <errno.h>
42*ebfedea0SLionel Sambuc #include <limits.h>	/* for LINE_MAX */
43*ebfedea0SLionel Sambuc #include <saslc.h>
44*ebfedea0SLionel Sambuc #include <stdio.h>
45*ebfedea0SLionel Sambuc #include <stdlib.h>
46*ebfedea0SLionel Sambuc #include <string.h>
47*ebfedea0SLionel Sambuc 
48*ebfedea0SLionel Sambuc #include <gssapi/gssapi.h>
49*ebfedea0SLionel Sambuc 
50*ebfedea0SLionel Sambuc #include "buffer.h"
51*ebfedea0SLionel Sambuc #include "list.h"
52*ebfedea0SLionel Sambuc #include "mech.h"
53*ebfedea0SLionel Sambuc #include "msg.h"
54*ebfedea0SLionel Sambuc #include "saslc_private.h"
55*ebfedea0SLionel Sambuc 
56*ebfedea0SLionel Sambuc /* See RFC 2222 section 7.2.1. */
57*ebfedea0SLionel Sambuc 
58*ebfedea0SLionel Sambuc /* properties */
59*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_AUTHCID		SASLC_PROP_AUTHCID
60*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_HOSTNAME		SASLC_PROP_HOSTNAME
61*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_SERVICE		SASLC_PROP_SERVICE
62*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_QOPMASK		SASLC_PROP_QOPMASK
63*ebfedea0SLionel Sambuc 
64*ebfedea0SLionel Sambuc #define DEFAULT_QOP_MASK	(F_QOP_NONE | F_QOP_INT | F_QOP_CONF)
65*ebfedea0SLionel Sambuc 
66*ebfedea0SLionel Sambuc /* authentication steps */
67*ebfedea0SLionel Sambuc typedef enum {	/* see RFC2222 7.2.1 section */
68*ebfedea0SLionel Sambuc 	GSSAPI_AUTH_FIRST,		/* first authentication stage */
69*ebfedea0SLionel Sambuc 	GSSAPI_AUTH_NEXT,		/* next authentication stage(s) */
70*ebfedea0SLionel Sambuc 	GSSAPI_AUTH_LAST,		/* final authentication stage */
71*ebfedea0SLionel Sambuc 	GSSAPI_AUTH_DONE		/* authenticated */
72*ebfedea0SLionel Sambuc } saslc__mech_gssapi_status_t;
73*ebfedea0SLionel Sambuc 
74*ebfedea0SLionel Sambuc /* gssapi mechanism session */
75*ebfedea0SLionel Sambuc typedef struct {
76*ebfedea0SLionel Sambuc 	saslc__mech_sess_t mech_sess;		/* mechanism session */
77*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_status_t status;	/* authentication status */
78*ebfedea0SLionel Sambuc 	gss_ctx_id_t gss_ctx;			/* GSSAPI context */
79*ebfedea0SLionel Sambuc 	gss_name_t server_name;			/* server name: service@host */
80*ebfedea0SLionel Sambuc 	gss_name_t client_name;			/* client name - XXX: unused! */
81*ebfedea0SLionel Sambuc 	uint32_t qop_mask;			/* available QOP services */
82*ebfedea0SLionel Sambuc 	uint32_t omaxbuf;			/* maximum output buffer size */
83*ebfedea0SLionel Sambuc 	uint32_t imaxbuf;			/* maximum input buffer size */
84*ebfedea0SLionel Sambuc 	saslc__buffer32_context_t *dec_ctx;	/* decode buffer context */
85*ebfedea0SLionel Sambuc 	saslc__buffer_context_t *enc_ctx;	/* encode buffer context */
86*ebfedea0SLionel Sambuc } saslc__mech_gssapi_sess_t;
87*ebfedea0SLionel Sambuc 
88*ebfedea0SLionel Sambuc /**
89*ebfedea0SLionel Sambuc  * @brief creates gssapi mechanism session.
90*ebfedea0SLionel Sambuc  * Function initializes also default options for the session.
91*ebfedea0SLionel Sambuc  * @param sess sasl session
92*ebfedea0SLionel Sambuc  * @return 0 on success, -1 on failure.
93*ebfedea0SLionel Sambuc  */
94*ebfedea0SLionel Sambuc static int
saslc__mech_gssapi_create(saslc_sess_t * sess)95*ebfedea0SLionel Sambuc saslc__mech_gssapi_create(saslc_sess_t *sess)
96*ebfedea0SLionel Sambuc {
97*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *c;
98*ebfedea0SLionel Sambuc 
99*ebfedea0SLionel Sambuc 	c = sess->mech_sess = calloc(1, sizeof(*c));
100*ebfedea0SLionel Sambuc 	if (c == NULL)
101*ebfedea0SLionel Sambuc 		return -1;
102*ebfedea0SLionel Sambuc 
103*ebfedea0SLionel Sambuc 	sess->mech_sess = c;
104*ebfedea0SLionel Sambuc 
105*ebfedea0SLionel Sambuc 	c->gss_ctx = GSS_C_NO_CONTEXT;
106*ebfedea0SLionel Sambuc 	c->server_name = GSS_C_NO_NAME;
107*ebfedea0SLionel Sambuc 	c->client_name = GSS_C_NO_NAME;
108*ebfedea0SLionel Sambuc 
109*ebfedea0SLionel Sambuc 	return 0;
110*ebfedea0SLionel Sambuc }
111*ebfedea0SLionel Sambuc 
112*ebfedea0SLionel Sambuc /**
113*ebfedea0SLionel Sambuc  * @brief destroys gssapi mechanism session.
114*ebfedea0SLionel Sambuc  * Function also is freeing assigned resources to the session.
115*ebfedea0SLionel Sambuc  * @param sess sasl session
116*ebfedea0SLionel Sambuc  * @return Functions always returns 0.
117*ebfedea0SLionel Sambuc  */
118*ebfedea0SLionel Sambuc static int
saslc__mech_gssapi_destroy(saslc_sess_t * sess)119*ebfedea0SLionel Sambuc saslc__mech_gssapi_destroy(saslc_sess_t *sess)
120*ebfedea0SLionel Sambuc {
121*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
122*ebfedea0SLionel Sambuc 	OM_uint32 min_s;
123*ebfedea0SLionel Sambuc 
124*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
125*ebfedea0SLionel Sambuc 
126*ebfedea0SLionel Sambuc 	if (ms->gss_ctx != GSS_C_NO_CONTEXT)
127*ebfedea0SLionel Sambuc 		gss_delete_sec_context(&min_s, &ms->gss_ctx, GSS_C_NO_BUFFER);
128*ebfedea0SLionel Sambuc 	if (ms->server_name != GSS_C_NO_NAME)
129*ebfedea0SLionel Sambuc 		gss_release_name(&min_s, &ms->server_name);
130*ebfedea0SLionel Sambuc 	if (ms->client_name != GSS_C_NO_NAME)
131*ebfedea0SLionel Sambuc 		gss_release_name(&min_s, &ms->client_name);
132*ebfedea0SLionel Sambuc 
133*ebfedea0SLionel Sambuc 	saslc__buffer_destroy(ms->enc_ctx);
134*ebfedea0SLionel Sambuc 	saslc__buffer32_destroy(ms->dec_ctx);
135*ebfedea0SLionel Sambuc 	free(ms);
136*ebfedea0SLionel Sambuc 	sess->mech_sess = NULL;
137*ebfedea0SLionel Sambuc 
138*ebfedea0SLionel Sambuc 	return 0;
139*ebfedea0SLionel Sambuc }
140*ebfedea0SLionel Sambuc 
141*ebfedea0SLionel Sambuc /**
142*ebfedea0SLionel Sambuc  * @brief translate the major and minor statuses an error message for
143*ebfedea0SLionel Sambuc  * the given mechanism
144*ebfedea0SLionel Sambuc  * @param maj_s major status
145*ebfedea0SLionel Sambuc  * @param min_s minor status
146*ebfedea0SLionel Sambuc  * @param mech mechanism
147*ebfedea0SLionel Sambuc  * @return pointer to a static buffer with error message
148*ebfedea0SLionel Sambuc  */
149*ebfedea0SLionel Sambuc static char *
saslc__mech_gssapi_err(OM_uint32 maj_s,OM_uint32 min_s,gss_OID mech)150*ebfedea0SLionel Sambuc saslc__mech_gssapi_err(OM_uint32 maj_s, OM_uint32 min_s, gss_OID mech)
151*ebfedea0SLionel Sambuc {
152*ebfedea0SLionel Sambuc 	static char errbuf[LINE_MAX];
153*ebfedea0SLionel Sambuc 	gss_buffer_desc maj_error_message;
154*ebfedea0SLionel Sambuc 	gss_buffer_desc min_error_message;
155*ebfedea0SLionel Sambuc 	OM_uint32 disp_min_s;
156*ebfedea0SLionel Sambuc 	OM_uint32 msg_ctx;
157*ebfedea0SLionel Sambuc 
158*ebfedea0SLionel Sambuc 	msg_ctx = 0;
159*ebfedea0SLionel Sambuc 	maj_error_message.length = 0;
160*ebfedea0SLionel Sambuc 	maj_error_message.value = NULL;
161*ebfedea0SLionel Sambuc 	min_error_message.length = 0;
162*ebfedea0SLionel Sambuc 	min_error_message.value = NULL;
163*ebfedea0SLionel Sambuc 
164*ebfedea0SLionel Sambuc 	(void)gss_display_status(&disp_min_s, maj_s, GSS_C_GSS_CODE,
165*ebfedea0SLionel Sambuc 	    mech, &msg_ctx, &maj_error_message);
166*ebfedea0SLionel Sambuc 	(void)gss_display_status(&disp_min_s, min_s, GSS_C_MECH_CODE,
167*ebfedea0SLionel Sambuc 	    mech, &msg_ctx, &min_error_message);
168*ebfedea0SLionel Sambuc 
169*ebfedea0SLionel Sambuc 	(void)snprintf(errbuf, sizeof(errbuf),
170*ebfedea0SLionel Sambuc 	    "gss-code: %lu %.*s\nmech-code: %lu %.*s",
171*ebfedea0SLionel Sambuc 	    (unsigned long)maj_s,
172*ebfedea0SLionel Sambuc 	    (int)maj_error_message.length,
173*ebfedea0SLionel Sambuc 	    (char *)maj_error_message.value,
174*ebfedea0SLionel Sambuc 	    (unsigned long)min_s,
175*ebfedea0SLionel Sambuc 	    (int)min_error_message.length,
176*ebfedea0SLionel Sambuc 	    (char *)min_error_message.value);
177*ebfedea0SLionel Sambuc 
178*ebfedea0SLionel Sambuc 	(void)gss_release_buffer(&disp_min_s, &maj_error_message);
179*ebfedea0SLionel Sambuc 	(void)gss_release_buffer(&disp_min_s, &min_error_message);
180*ebfedea0SLionel Sambuc 
181*ebfedea0SLionel Sambuc 	return errbuf;
182*ebfedea0SLionel Sambuc }
183*ebfedea0SLionel Sambuc 
184*ebfedea0SLionel Sambuc /**
185*ebfedea0SLionel Sambuc  * @brief set a session error message using saslc__mech_gssapi_err()
186*ebfedea0SLionel Sambuc  * @param sess the session
187*ebfedea0SLionel Sambuc  * @param err error number to set
188*ebfedea0SLionel Sambuc  * @param maj_s major status
189*ebfedea0SLionel Sambuc  * @param min_s minor status
190*ebfedea0SLionel Sambuc  * @return pointer to a static buffer with error message
191*ebfedea0SLionel Sambuc  */
192*ebfedea0SLionel Sambuc static void
saslc__mech_gssapi_set_err(saslc_sess_t * sess,int err,OM_uint32 maj_s,OM_uint32 min_s)193*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(saslc_sess_t *sess, int err, OM_uint32 maj_s, OM_uint32 min_s)
194*ebfedea0SLionel Sambuc {
195*ebfedea0SLionel Sambuc 
196*ebfedea0SLionel Sambuc 	saslc__error_set(ERR(sess), err,
197*ebfedea0SLionel Sambuc 	    saslc__mech_gssapi_err(maj_s, min_s, GSS_C_NO_OID));
198*ebfedea0SLionel Sambuc }
199*ebfedea0SLionel Sambuc 
200*ebfedea0SLionel Sambuc /**
201*ebfedea0SLionel Sambuc  * @brief convert an initialization output token into the out and outlen format.
202*ebfedea0SLionel Sambuc  * Also releases the output token.
203*ebfedea0SLionel Sambuc  * @param sess saslc session
204*ebfedea0SLionel Sambuc  * @param outbuf gss buffer token
205*ebfedea0SLionel Sambuc  * @param out pointer to a void pointer
206*ebfedea0SLionel Sambuc  * @param outlen pointer to size_t length storage
207*ebfedea0SLionel Sambuc  * @returns 0 on success, -1 on failure
208*ebfedea0SLionel Sambuc  */
209*ebfedea0SLionel Sambuc static int
prep_output(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)210*ebfedea0SLionel Sambuc prep_output(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
211*ebfedea0SLionel Sambuc {
212*ebfedea0SLionel Sambuc 	OM_uint32 min_s;
213*ebfedea0SLionel Sambuc 
214*ebfedea0SLionel Sambuc 	if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
215*ebfedea0SLionel Sambuc 		*outlen = 0;
216*ebfedea0SLionel Sambuc 		*out = NULL;
217*ebfedea0SLionel Sambuc 		return 0;
218*ebfedea0SLionel Sambuc 	}
219*ebfedea0SLionel Sambuc 	if (outbuf->length == 0) {
220*ebfedea0SLionel Sambuc 		*outlen = 0;
221*ebfedea0SLionel Sambuc 		*out = NULL;
222*ebfedea0SLionel Sambuc 		gss_release_buffer(&min_s, outbuf);
223*ebfedea0SLionel Sambuc 		return 0;
224*ebfedea0SLionel Sambuc 	}
225*ebfedea0SLionel Sambuc 	*out = malloc(outbuf->length);
226*ebfedea0SLionel Sambuc 	if (*out == NULL) {
227*ebfedea0SLionel Sambuc 		*outlen = 0;
228*ebfedea0SLionel Sambuc 		gss_release_buffer(&min_s, outbuf);
229*ebfedea0SLionel Sambuc 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
230*ebfedea0SLionel Sambuc 		return -1;
231*ebfedea0SLionel Sambuc 	}
232*ebfedea0SLionel Sambuc 	*outlen = outbuf->length;
233*ebfedea0SLionel Sambuc 	memcpy(*out, outbuf->value, outbuf->length);
234*ebfedea0SLionel Sambuc 	gss_release_buffer(&min_s, outbuf);
235*ebfedea0SLionel Sambuc 	return 0;
236*ebfedea0SLionel Sambuc }
237*ebfedea0SLionel Sambuc 
238*ebfedea0SLionel Sambuc /**
239*ebfedea0SLionel Sambuc  * @brief convert an output token into a valid packet where the first
240*ebfedea0SLionel Sambuc  * 4 bytes are the payload length in network byte order.
241*ebfedea0SLionel Sambuc  * Also releases the output token.
242*ebfedea0SLionel Sambuc  * @param sess saslc session
243*ebfedea0SLionel Sambuc  * @param outbuf gss buffer token
244*ebfedea0SLionel Sambuc  * @param out pointer to a void pointer
245*ebfedea0SLionel Sambuc  * @param outlen pointer to size_t length storage
246*ebfedea0SLionel Sambuc  * @returns 0 on success, -1 on failure
247*ebfedea0SLionel Sambuc  */
248*ebfedea0SLionel Sambuc static int
prep_packet(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)249*ebfedea0SLionel Sambuc prep_packet(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
250*ebfedea0SLionel Sambuc {
251*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
252*ebfedea0SLionel Sambuc 	OM_uint32 min_s;
253*ebfedea0SLionel Sambuc 	char *buf;
254*ebfedea0SLionel Sambuc 	size_t buflen;
255*ebfedea0SLionel Sambuc 
256*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
257*ebfedea0SLionel Sambuc 
258*ebfedea0SLionel Sambuc 	if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
259*ebfedea0SLionel Sambuc 		*outlen = 0;
260*ebfedea0SLionel Sambuc 		*out = NULL;
261*ebfedea0SLionel Sambuc 		return 0;
262*ebfedea0SLionel Sambuc 	}
263*ebfedea0SLionel Sambuc 	if (outbuf->length == 0) {
264*ebfedea0SLionel Sambuc 		*outlen = 0;
265*ebfedea0SLionel Sambuc 		*out = NULL;
266*ebfedea0SLionel Sambuc 		gss_release_buffer(&min_s, outbuf);
267*ebfedea0SLionel Sambuc 		return 0;
268*ebfedea0SLionel Sambuc 	}
269*ebfedea0SLionel Sambuc 	buflen = outbuf->length + 4;
270*ebfedea0SLionel Sambuc 	if (buflen > ms->omaxbuf) {
271*ebfedea0SLionel Sambuc 		saslc__error_set(ERR(sess), ERROR_MECH,
272*ebfedea0SLionel Sambuc 		    "output exceeds server maxbuf size");
273*ebfedea0SLionel Sambuc 		gss_release_buffer(&min_s, outbuf);
274*ebfedea0SLionel Sambuc 		return -1;
275*ebfedea0SLionel Sambuc 	}
276*ebfedea0SLionel Sambuc 	buf = malloc(buflen);
277*ebfedea0SLionel Sambuc 	if (buf == NULL) {
278*ebfedea0SLionel Sambuc 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
279*ebfedea0SLionel Sambuc 		return -1;
280*ebfedea0SLionel Sambuc 	}
281*ebfedea0SLionel Sambuc 	be32enc(buf, (uint32_t)outbuf->length);
282*ebfedea0SLionel Sambuc 	memcpy(buf + 4, outbuf->value, outbuf->length);
283*ebfedea0SLionel Sambuc 	gss_release_buffer(&min_s, outbuf);
284*ebfedea0SLionel Sambuc 
285*ebfedea0SLionel Sambuc 	*out = buf;
286*ebfedea0SLionel Sambuc 	*outlen = buflen;
287*ebfedea0SLionel Sambuc 	return 0;
288*ebfedea0SLionel Sambuc }
289*ebfedea0SLionel Sambuc 
290*ebfedea0SLionel Sambuc /**
291*ebfedea0SLionel Sambuc  * @brief encodes one block of data using the negotiated security layer.
292*ebfedea0SLionel Sambuc  * @param sess sasl session
293*ebfedea0SLionel Sambuc  * @param in input data
294*ebfedea0SLionel Sambuc  * @param inlen input data length
295*ebfedea0SLionel Sambuc  * @param out place to store output data
296*ebfedea0SLionel Sambuc  * @param outlen output data length
297*ebfedea0SLionel Sambuc  * @return number of bytes consumed, zero if more needed, or -1 on failure.
298*ebfedea0SLionel Sambuc  */
299*ebfedea0SLionel Sambuc static ssize_t
saslc__mech_gssapi_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)300*ebfedea0SLionel Sambuc saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen,
301*ebfedea0SLionel Sambuc     void **out, size_t *outlen)
302*ebfedea0SLionel Sambuc {
303*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
304*ebfedea0SLionel Sambuc 	gss_buffer_desc input, output;
305*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
306*ebfedea0SLionel Sambuc 	uint8_t *buf;
307*ebfedea0SLionel Sambuc 	size_t buflen;
308*ebfedea0SLionel Sambuc 	ssize_t len;
309*ebfedea0SLionel Sambuc 
310*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
311*ebfedea0SLionel Sambuc 	assert(ms->mech_sess.qop != QOP_NONE);
312*ebfedea0SLionel Sambuc 	if (ms->mech_sess.qop == QOP_NONE)
313*ebfedea0SLionel Sambuc 		return -1;
314*ebfedea0SLionel Sambuc 
315*ebfedea0SLionel Sambuc 	len = saslc__buffer_fetch(ms->enc_ctx, in, inlen, &buf, &buflen);
316*ebfedea0SLionel Sambuc 	if (len == -1)
317*ebfedea0SLionel Sambuc 		return -1;
318*ebfedea0SLionel Sambuc 
319*ebfedea0SLionel Sambuc 	if (buflen == 0) {
320*ebfedea0SLionel Sambuc 		*out = NULL;
321*ebfedea0SLionel Sambuc 		*outlen = 0;
322*ebfedea0SLionel Sambuc 		return len;
323*ebfedea0SLionel Sambuc 	}
324*ebfedea0SLionel Sambuc 
325*ebfedea0SLionel Sambuc 	input.value = buf;
326*ebfedea0SLionel Sambuc 	input.length = buflen;
327*ebfedea0SLionel Sambuc 	output.value = NULL;
328*ebfedea0SLionel Sambuc 	output.length = 0;
329*ebfedea0SLionel Sambuc 
330*ebfedea0SLionel Sambuc 	maj_s = gss_wrap(&min_s, ms->gss_ctx, ms->mech_sess.qop == QOP_CONF,
331*ebfedea0SLionel Sambuc 	    GSS_C_QOP_DEFAULT, &input, NULL, &output);
332*ebfedea0SLionel Sambuc 
333*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
334*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
335*ebfedea0SLionel Sambuc 		return -1;
336*ebfedea0SLionel Sambuc 	}
337*ebfedea0SLionel Sambuc 	if (prep_packet(sess, &output, out, outlen) == -1)
338*ebfedea0SLionel Sambuc 		return -1;
339*ebfedea0SLionel Sambuc 
340*ebfedea0SLionel Sambuc 	return len;
341*ebfedea0SLionel Sambuc }
342*ebfedea0SLionel Sambuc 
343*ebfedea0SLionel Sambuc /**
344*ebfedea0SLionel Sambuc  * @brief decodes one block of data using the negotiated security layer.
345*ebfedea0SLionel Sambuc  * @param sess sasl session
346*ebfedea0SLionel Sambuc  * @param in input data
347*ebfedea0SLionel Sambuc  * @param inlen input data length
348*ebfedea0SLionel Sambuc  * @param out place to store output data
349*ebfedea0SLionel Sambuc  * @param outlen output data length
350*ebfedea0SLionel Sambuc  * @return number of bytes consumed, zero if more needed, or -1 on failure.
351*ebfedea0SLionel Sambuc  */
352*ebfedea0SLionel Sambuc static ssize_t
saslc__mech_gssapi_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)353*ebfedea0SLionel Sambuc saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen,
354*ebfedea0SLionel Sambuc 	void **out, size_t *outlen)
355*ebfedea0SLionel Sambuc {
356*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
357*ebfedea0SLionel Sambuc 	gss_buffer_desc input, output;
358*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
359*ebfedea0SLionel Sambuc 	uint8_t *buf;
360*ebfedea0SLionel Sambuc 	size_t buflen;
361*ebfedea0SLionel Sambuc 	ssize_t len;
362*ebfedea0SLionel Sambuc 
363*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
364*ebfedea0SLionel Sambuc 	assert(ms->mech_sess.qop != QOP_NONE);
365*ebfedea0SLionel Sambuc 	if (ms->mech_sess.qop == QOP_NONE)
366*ebfedea0SLionel Sambuc 		return -1;
367*ebfedea0SLionel Sambuc 
368*ebfedea0SLionel Sambuc 	len = saslc__buffer32_fetch(ms->dec_ctx, in, inlen, &buf, &buflen);
369*ebfedea0SLionel Sambuc 	if (len == -1)
370*ebfedea0SLionel Sambuc 		return -1;
371*ebfedea0SLionel Sambuc 
372*ebfedea0SLionel Sambuc 	if (buflen == 0) {
373*ebfedea0SLionel Sambuc 		*out = NULL;
374*ebfedea0SLionel Sambuc 		*outlen = 0;
375*ebfedea0SLionel Sambuc 		return len;
376*ebfedea0SLionel Sambuc 	}
377*ebfedea0SLionel Sambuc 
378*ebfedea0SLionel Sambuc 	/* buf -> szbuf (4 bytes) followed by the payload buffer */
379*ebfedea0SLionel Sambuc 	input.value = buf + 4;
380*ebfedea0SLionel Sambuc 	input.length = buflen - 4;
381*ebfedea0SLionel Sambuc 	output.value = NULL;
382*ebfedea0SLionel Sambuc 	output.length = 0;
383*ebfedea0SLionel Sambuc 
384*ebfedea0SLionel Sambuc 	maj_s = gss_unwrap(&min_s, ms->gss_ctx, &input, &output, NULL, NULL);
385*ebfedea0SLionel Sambuc 
386*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
387*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
388*ebfedea0SLionel Sambuc 		return -1;
389*ebfedea0SLionel Sambuc 	}
390*ebfedea0SLionel Sambuc 
391*ebfedea0SLionel Sambuc 	if (prep_output(sess, &output, out, outlen) == -1)
392*ebfedea0SLionel Sambuc 		return -1;
393*ebfedea0SLionel Sambuc 
394*ebfedea0SLionel Sambuc 	return len;
395*ebfedea0SLionel Sambuc }
396*ebfedea0SLionel Sambuc 
397*ebfedea0SLionel Sambuc /**
398*ebfedea0SLionel Sambuc  * @brief get service name from properties
399*ebfedea0SLionel Sambuc  * ("<servicename>@<hostname>") and store it in service token.
400*ebfedea0SLionel Sambuc  * @param sess the session context
401*ebfedea0SLionel Sambuc  * @param service the gs_name_t token to return service name in
402*ebfedea0SLionel Sambuc  * @return 0 on success, -1 on error
403*ebfedea0SLionel Sambuc  */
404*ebfedea0SLionel Sambuc static int
get_service(saslc_sess_t * sess,gss_name_t * service)405*ebfedea0SLionel Sambuc get_service(saslc_sess_t *sess, gss_name_t *service)
406*ebfedea0SLionel Sambuc {
407*ebfedea0SLionel Sambuc 	gss_buffer_desc bufdesc;
408*ebfedea0SLionel Sambuc 	const char *hostname, *servicename;
409*ebfedea0SLionel Sambuc 	char *buf;
410*ebfedea0SLionel Sambuc 	int buflen;
411*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
412*ebfedea0SLionel Sambuc 
413*ebfedea0SLionel Sambuc 	hostname = saslc_sess_getprop(sess, SASLC_GSSAPI_HOSTNAME);
414*ebfedea0SLionel Sambuc 	if (hostname == NULL) {
415*ebfedea0SLionel Sambuc 		saslc__error_set(ERR(sess), ERROR_MECH,
416*ebfedea0SLionel Sambuc 		    "hostname is required for an authentication");
417*ebfedea0SLionel Sambuc 		return -1;
418*ebfedea0SLionel Sambuc 	}
419*ebfedea0SLionel Sambuc 	servicename = saslc_sess_getprop(sess, SASLC_GSSAPI_SERVICE);
420*ebfedea0SLionel Sambuc 	if (servicename == NULL) {
421*ebfedea0SLionel Sambuc 		saslc__error_set(ERR(sess), ERROR_MECH,
422*ebfedea0SLionel Sambuc 		    "service is required for an authentication");
423*ebfedea0SLionel Sambuc 		return -1;
424*ebfedea0SLionel Sambuc 	}
425*ebfedea0SLionel Sambuc 	buflen = asprintf(&buf, "%s@%s", servicename, hostname);
426*ebfedea0SLionel Sambuc 	if (buflen == -1) {
427*ebfedea0SLionel Sambuc 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
428*ebfedea0SLionel Sambuc 		return -1;
429*ebfedea0SLionel Sambuc 	}
430*ebfedea0SLionel Sambuc 	bufdesc.value = buf;
431*ebfedea0SLionel Sambuc 	bufdesc.length = buflen + 1;
432*ebfedea0SLionel Sambuc 
433*ebfedea0SLionel Sambuc 	saslc__msg_dbg("%s: buf='%s'", __func__, buf);
434*ebfedea0SLionel Sambuc 
435*ebfedea0SLionel Sambuc 	maj_s = gss_import_name(&min_s, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
436*ebfedea0SLionel Sambuc 	    service);
437*ebfedea0SLionel Sambuc 	free(buf);
438*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
439*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
440*ebfedea0SLionel Sambuc 		return -1;
441*ebfedea0SLionel Sambuc 	}
442*ebfedea0SLionel Sambuc 	return 0;
443*ebfedea0SLionel Sambuc }
444*ebfedea0SLionel Sambuc 
445*ebfedea0SLionel Sambuc /**
446*ebfedea0SLionel Sambuc  * @brief gss_init_sec_context() wrapper
447*ebfedea0SLionel Sambuc  * @param sess session context
448*ebfedea0SLionel Sambuc  * @param inbuf input token
449*ebfedea0SLionel Sambuc  * @param outbuf output token
450*ebfedea0SLionel Sambuc  * @return 0 if GSS_S_COMPLETE, 1 if GSS_S_CONTINUE_NEEDED, -1 on failure
451*ebfedea0SLionel Sambuc  */
452*ebfedea0SLionel Sambuc static int
init_sec_context(saslc_sess_t * sess,gss_buffer_t inbuf,gss_buffer_t outbuf)453*ebfedea0SLionel Sambuc init_sec_context(saslc_sess_t *sess, gss_buffer_t inbuf, gss_buffer_t outbuf)
454*ebfedea0SLionel Sambuc {
455*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
456*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
457*ebfedea0SLionel Sambuc 
458*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
459*ebfedea0SLionel Sambuc 
460*ebfedea0SLionel Sambuc 	outbuf->length = 0;
461*ebfedea0SLionel Sambuc 	outbuf->value = NULL;
462*ebfedea0SLionel Sambuc 	maj_s = gss_init_sec_context(
463*ebfedea0SLionel Sambuc 		&min_s,			/* minor status */
464*ebfedea0SLionel Sambuc 		GSS_C_NO_CREDENTIAL, /* use current login context credential */
465*ebfedea0SLionel Sambuc 		&ms->gss_ctx,		/* initially GSS_C_NO_CONTEXT */
466*ebfedea0SLionel Sambuc 		ms->server_name,	/* server@hostname */
467*ebfedea0SLionel Sambuc 		GSS_C_NO_OID,		/* use default mechanism */
468*ebfedea0SLionel Sambuc #if 1
469*ebfedea0SLionel Sambuc 		GSS_C_REPLAY_FLAG |	/* message replay detection */
470*ebfedea0SLionel Sambuc 		GSS_C_INTEG_FLAG |	/* request integrity */
471*ebfedea0SLionel Sambuc 		GSS_C_CONF_FLAG |	/* request confirmation */
472*ebfedea0SLionel Sambuc #endif
473*ebfedea0SLionel Sambuc 		GSS_C_MUTUAL_FLAG |	/* mutual authentication */
474*ebfedea0SLionel Sambuc 		GSS_C_SEQUENCE_FLAG,	/* message sequence checking */
475*ebfedea0SLionel Sambuc 		0,			/* default lifetime (2 hrs) */
476*ebfedea0SLionel Sambuc 		GSS_C_NO_CHANNEL_BINDINGS,
477*ebfedea0SLionel Sambuc 		inbuf,			/* input token */
478*ebfedea0SLionel Sambuc 		/* output parameters follow */
479*ebfedea0SLionel Sambuc 		NULL,			/* mechanism type for context */
480*ebfedea0SLionel Sambuc 		outbuf,			/* output token */
481*ebfedea0SLionel Sambuc 		NULL,			/* services available for context */
482*ebfedea0SLionel Sambuc 		NULL);			/* lifetime of context */
483*ebfedea0SLionel Sambuc 
484*ebfedea0SLionel Sambuc 	switch (maj_s) {
485*ebfedea0SLionel Sambuc 	case GSS_S_COMPLETE:
486*ebfedea0SLionel Sambuc 		return 0;
487*ebfedea0SLionel Sambuc 	case GSS_S_CONTINUE_NEEDED:
488*ebfedea0SLionel Sambuc 		return 1;
489*ebfedea0SLionel Sambuc 	default:
490*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
491*ebfedea0SLionel Sambuc 		return -1;
492*ebfedea0SLionel Sambuc 	}
493*ebfedea0SLionel Sambuc }
494*ebfedea0SLionel Sambuc 
495*ebfedea0SLionel Sambuc /**
496*ebfedea0SLionel Sambuc  * @brief unwrap the authentication token received from the server.
497*ebfedea0SLionel Sambuc  * This contains the qop_mask and maxbuf values which are updated in
498*ebfedea0SLionel Sambuc  * saslc__mech_gssapi_sess_t.
499*ebfedea0SLionel Sambuc  * @param sess the session context
500*ebfedea0SLionel Sambuc  * @param inbuf the received authentication token.
501*ebfedea0SLionel Sambuc  * @return 0 on success, -1 on error.
502*ebfedea0SLionel Sambuc  */
503*ebfedea0SLionel Sambuc static int
unwrap_input_token(saslc_sess_t * sess,gss_buffer_t inbuf)504*ebfedea0SLionel Sambuc unwrap_input_token(saslc_sess_t *sess, gss_buffer_t inbuf)
505*ebfedea0SLionel Sambuc {
506*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
507*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
508*ebfedea0SLionel Sambuc 	gss_buffer_t outbuf;
509*ebfedea0SLionel Sambuc 	gss_buffer_desc outdesc;
510*ebfedea0SLionel Sambuc 	unsigned char *p;
511*ebfedea0SLionel Sambuc 
512*ebfedea0SLionel Sambuc 	/********************************************************************/
513*ebfedea0SLionel Sambuc 	/* [RFC 2222 section 7.2.1]                                         */
514*ebfedea0SLionel Sambuc 	/* The client passes this token to GSS_Unwrap and interprets        */
515*ebfedea0SLionel Sambuc 	/* the first octet of resulting cleartext as a bit-mask specifying  */
516*ebfedea0SLionel Sambuc 	/* the security layers supported by the server and the second       */
517*ebfedea0SLionel Sambuc 	/* through fourth octets as the maximum size output_message to send */
518*ebfedea0SLionel Sambuc 	/* to the server.                                                   */
519*ebfedea0SLionel Sambuc 	/********************************************************************/
520*ebfedea0SLionel Sambuc 
521*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
522*ebfedea0SLionel Sambuc 
523*ebfedea0SLionel Sambuc 	outbuf = &outdesc;
524*ebfedea0SLionel Sambuc 	maj_s = gss_unwrap(&min_s, ms->gss_ctx, inbuf, outbuf, NULL, NULL);
525*ebfedea0SLionel Sambuc 
526*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
527*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
528*ebfedea0SLionel Sambuc 		return -1;
529*ebfedea0SLionel Sambuc 	}
530*ebfedea0SLionel Sambuc 	if (outbuf->length != 4) {
531*ebfedea0SLionel Sambuc 		saslc__error_set(ERR(sess), ERROR_MECH,
532*ebfedea0SLionel Sambuc 		    "invalid unwrap length");
533*ebfedea0SLionel Sambuc 		return -1;
534*ebfedea0SLionel Sambuc 	}
535*ebfedea0SLionel Sambuc 	p = outbuf->value;
536*ebfedea0SLionel Sambuc 	ms->qop_mask = p[0];
537*ebfedea0SLionel Sambuc 	ms->omaxbuf = (be32dec(p) & 0xffffff);
538*ebfedea0SLionel Sambuc 
539*ebfedea0SLionel Sambuc 	saslc__msg_dbg("%s: qop_mask=0x%02x omaxbuf=%d",
540*ebfedea0SLionel Sambuc 	    __func__, ms->qop_mask, ms->omaxbuf);
541*ebfedea0SLionel Sambuc 
542*ebfedea0SLionel Sambuc 	if (ms->qop_mask == QOP_NONE && ms->omaxbuf != 0) {
543*ebfedea0SLionel Sambuc 		saslc__error_set(ERR(sess), ERROR_MECH,
544*ebfedea0SLionel Sambuc 		    "server has no security layer support, but maxbuf != 0");
545*ebfedea0SLionel Sambuc 		return -1;
546*ebfedea0SLionel Sambuc 	}
547*ebfedea0SLionel Sambuc 	maj_s = gss_release_buffer(&min_s, outbuf);
548*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
549*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
550*ebfedea0SLionel Sambuc 		return -1;
551*ebfedea0SLionel Sambuc 	}
552*ebfedea0SLionel Sambuc 	return 0;
553*ebfedea0SLionel Sambuc }
554*ebfedea0SLionel Sambuc 
555*ebfedea0SLionel Sambuc /**
556*ebfedea0SLionel Sambuc  * @brief construct and wrap up an authentication token and put it in
557*ebfedea0SLionel Sambuc  * outbuf.  The outbuf token data is structured as follows:
558*ebfedea0SLionel Sambuc  * struct {
559*ebfedea0SLionel Sambuc  *   uint8_t qop;	// qop to use
560*ebfedea0SLionel Sambuc  *   uint8_t maxbuf[3]	// maxbuf for client (network byte order)
561*ebfedea0SLionel Sambuc  *   uint8_t authcid[]	// variable length authentication id (username)
562*ebfedea0SLionel Sambuc  * } __packed;
563*ebfedea0SLionel Sambuc  * @param sess the session
564*ebfedea0SLionel Sambuc  * @param outbuf the gss_buffer_t token to return to server.
565*ebfedea0SLionel Sambuc  * @return 0 on success, -1 on error.
566*ebfedea0SLionel Sambuc  */
567*ebfedea0SLionel Sambuc static int
wrap_output_token(saslc_sess_t * sess,gss_buffer_t outbuf)568*ebfedea0SLionel Sambuc wrap_output_token(saslc_sess_t *sess, gss_buffer_t outbuf)
569*ebfedea0SLionel Sambuc {
570*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
571*ebfedea0SLionel Sambuc 	gss_buffer_desc indesc;
572*ebfedea0SLionel Sambuc 	char *input_value;
573*ebfedea0SLionel Sambuc 	int len;
574*ebfedea0SLionel Sambuc 	const char *authcid;
575*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
576*ebfedea0SLionel Sambuc 	unsigned char *p;
577*ebfedea0SLionel Sambuc 
578*ebfedea0SLionel Sambuc 	/********************************************************************/
579*ebfedea0SLionel Sambuc 	/* [RFC 2222 section 7.2.1]                                         */
580*ebfedea0SLionel Sambuc 	/* The client then constructs data, with the first octet containing */
581*ebfedea0SLionel Sambuc 	/* the bit-mask specifying the selected security layer, the second  */
582*ebfedea0SLionel Sambuc 	/* through fourth octets containing in network byte order the       */
583*ebfedea0SLionel Sambuc 	/* maximum size output_message the client is able to receive, and   */
584*ebfedea0SLionel Sambuc 	/* the remaining octets containing the authorization identity.  The */
585*ebfedea0SLionel Sambuc 	/* authorization identity is optional in mechanisms where it is     */
586*ebfedea0SLionel Sambuc 	/* encoded in the exchange such as GSSAPI.  The client passes the   */
587*ebfedea0SLionel Sambuc 	/* data to GSS_Wrap with conf_flag set to FALSE, and responds with  */
588*ebfedea0SLionel Sambuc 	/* the generated output_message.  The client can then consider the  */
589*ebfedea0SLionel Sambuc 	/* server authenticated.                                            */
590*ebfedea0SLionel Sambuc 	/********************************************************************/
591*ebfedea0SLionel Sambuc 
592*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
593*ebfedea0SLionel Sambuc 
594*ebfedea0SLionel Sambuc 	authcid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHCID);
595*ebfedea0SLionel Sambuc 
596*ebfedea0SLionel Sambuc 	len = asprintf(&input_value, "qmax%s", authcid ? authcid : "");
597*ebfedea0SLionel Sambuc 	if (len == -1) {
598*ebfedea0SLionel Sambuc 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
599*ebfedea0SLionel Sambuc 		return -1;
600*ebfedea0SLionel Sambuc 	}
601*ebfedea0SLionel Sambuc 	be32enc(input_value, ms->imaxbuf);
602*ebfedea0SLionel Sambuc 	input_value[0] = saslc__mech_qop_flag(ms->mech_sess.qop);
603*ebfedea0SLionel Sambuc 
604*ebfedea0SLionel Sambuc 	indesc.value = input_value;
605*ebfedea0SLionel Sambuc 	indesc.length = len;	/* XXX: don't count the '\0' */
606*ebfedea0SLionel Sambuc 
607*ebfedea0SLionel Sambuc 	p = (unsigned char *)input_value;
608*ebfedea0SLionel Sambuc 	saslc__msg_dbg("%s: input_value='%02x %02x %02x %02x %s",
609*ebfedea0SLionel Sambuc 	    __func__, p[0], p[1], p[2], p[3], input_value + 4);
610*ebfedea0SLionel Sambuc 
611*ebfedea0SLionel Sambuc 	maj_s = gss_wrap(&min_s, ms->gss_ctx, 0 /* FALSE - RFC2222 */,
612*ebfedea0SLionel Sambuc 	    GSS_C_QOP_DEFAULT, &indesc, NULL, outbuf);
613*ebfedea0SLionel Sambuc 
614*ebfedea0SLionel Sambuc 	free(input_value);
615*ebfedea0SLionel Sambuc 
616*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
617*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
618*ebfedea0SLionel Sambuc 		return -1;
619*ebfedea0SLionel Sambuc 	}
620*ebfedea0SLionel Sambuc 	return 0;
621*ebfedea0SLionel Sambuc }
622*ebfedea0SLionel Sambuc 
623*ebfedea0SLionel Sambuc /************************************************************************
624*ebfedea0SLionel Sambuc  * XXX: Share this with mech_digestmd5.c?  They are almost identical.
625*ebfedea0SLionel Sambuc  */
626*ebfedea0SLionel Sambuc /**
627*ebfedea0SLionel Sambuc  * @brief choose the best qop based on what was provided by the
628*ebfedea0SLionel Sambuc  * challenge and a possible user mask.
629*ebfedea0SLionel Sambuc  * @param sess the session context
630*ebfedea0SLionel Sambuc  * @param qop_flags the qop flags parsed from the challenge string
631*ebfedea0SLionel Sambuc  * @return the selected saslc__mech_sess_qop_t or -1 if no match
632*ebfedea0SLionel Sambuc  */
633*ebfedea0SLionel Sambuc static int
choose_qop(saslc_sess_t * sess,uint32_t qop_flags)634*ebfedea0SLionel Sambuc choose_qop(saslc_sess_t *sess, uint32_t qop_flags)
635*ebfedea0SLionel Sambuc {
636*ebfedea0SLionel Sambuc 	list_t *list;
637*ebfedea0SLionel Sambuc 	const char *user_qop;
638*ebfedea0SLionel Sambuc 
639*ebfedea0SLionel Sambuc 	qop_flags &= DEFAULT_QOP_MASK;
640*ebfedea0SLionel Sambuc 	user_qop = saslc_sess_getprop(sess, SASLC_GSSAPI_QOPMASK);
641*ebfedea0SLionel Sambuc 	if (user_qop != NULL) {
642*ebfedea0SLionel Sambuc 		if (saslc__list_parse(&list, user_qop) == -1) {
643*ebfedea0SLionel Sambuc 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
644*ebfedea0SLionel Sambuc 			return -1;
645*ebfedea0SLionel Sambuc 		}
646*ebfedea0SLionel Sambuc 		qop_flags &= saslc__mech_qop_list_flags(list);
647*ebfedea0SLionel Sambuc 		saslc__list_free(list);
648*ebfedea0SLionel Sambuc 	}
649*ebfedea0SLionel Sambuc 
650*ebfedea0SLionel Sambuc 	/*
651*ebfedea0SLionel Sambuc 	 * Select the most secure supported qop.
652*ebfedea0SLionel Sambuc 	 */
653*ebfedea0SLionel Sambuc 	if ((qop_flags & F_QOP_CONF) != 0)
654*ebfedea0SLionel Sambuc 		return QOP_CONF;
655*ebfedea0SLionel Sambuc 	if ((qop_flags & F_QOP_INT) != 0)
656*ebfedea0SLionel Sambuc 		return QOP_INT;
657*ebfedea0SLionel Sambuc 	if ((qop_flags & F_QOP_NONE) != 0)
658*ebfedea0SLionel Sambuc 		return QOP_NONE;
659*ebfedea0SLionel Sambuc 
660*ebfedea0SLionel Sambuc 	saslc__error_set(ERR(sess), ERROR_MECH,
661*ebfedea0SLionel Sambuc 	    "cannot choose an acceptable qop");
662*ebfedea0SLionel Sambuc 	return -1;
663*ebfedea0SLionel Sambuc }
664*ebfedea0SLionel Sambuc /************************************************************************/
665*ebfedea0SLionel Sambuc 
666*ebfedea0SLionel Sambuc /**
667*ebfedea0SLionel Sambuc  * @brief compute the maximum buffer length we can use and not
668*ebfedea0SLionel Sambuc  * overflow the servers maxbuf.
669*ebfedea0SLionel Sambuc  * @param sess the session context
670*ebfedea0SLionel Sambuc  * @param maxbuf the server's maxbuf value
671*ebfedea0SLionel Sambuc  */
672*ebfedea0SLionel Sambuc static int
wrap_size_limit(saslc_sess_t * sess,OM_uint32 maxbuf)673*ebfedea0SLionel Sambuc wrap_size_limit(saslc_sess_t *sess, OM_uint32 maxbuf)
674*ebfedea0SLionel Sambuc {
675*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
676*ebfedea0SLionel Sambuc 	OM_uint32 min_s, maj_s;
677*ebfedea0SLionel Sambuc 	OM_uint32 max_input;
678*ebfedea0SLionel Sambuc 
679*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
680*ebfedea0SLionel Sambuc 
681*ebfedea0SLionel Sambuc 	maj_s = gss_wrap_size_limit(&min_s, ms->gss_ctx, 1, GSS_C_QOP_DEFAULT,
682*ebfedea0SLionel Sambuc 	    maxbuf, &max_input);
683*ebfedea0SLionel Sambuc 
684*ebfedea0SLionel Sambuc 	if (GSS_ERROR(maj_s)) {
685*ebfedea0SLionel Sambuc 		saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
686*ebfedea0SLionel Sambuc 		return -1;
687*ebfedea0SLionel Sambuc 	}
688*ebfedea0SLionel Sambuc 
689*ebfedea0SLionel Sambuc 	/* XXX: from cyrus-sasl: gssapi.c */
690*ebfedea0SLionel Sambuc 	if (max_input > maxbuf) {
691*ebfedea0SLionel Sambuc 		/* Heimdal appears to get this wrong */
692*ebfedea0SLionel Sambuc 		maxbuf -= (max_input - maxbuf);
693*ebfedea0SLionel Sambuc 	} else {
694*ebfedea0SLionel Sambuc 		/* This code is actually correct */
695*ebfedea0SLionel Sambuc 		maxbuf = max_input;
696*ebfedea0SLionel Sambuc 	}
697*ebfedea0SLionel Sambuc 	return maxbuf;
698*ebfedea0SLionel Sambuc }
699*ebfedea0SLionel Sambuc 
700*ebfedea0SLionel Sambuc /**
701*ebfedea0SLionel Sambuc  * @brief set our imaxbuf (from omaxbuf or from properties) and
702*ebfedea0SLionel Sambuc  * then reset omaxbuf in saslc__mech_gssapi_sess_t.
703*ebfedea0SLionel Sambuc  * @param sess the session context
704*ebfedea0SLionel Sambuc  * @return 0 on success, -1 on error
705*ebfedea0SLionel Sambuc  *
706*ebfedea0SLionel Sambuc  * Note: on entry the omaxbuf is the server's maxbuf size.  On exit
707*ebfedea0SLionel Sambuc  * the omaxbuf is the maximum buffer we can fill that will not
708*ebfedea0SLionel Sambuc  * overflow the servers maxbuf after it is encoded.  This value is
709*ebfedea0SLionel Sambuc  * given by wrap_size_limit().
710*ebfedea0SLionel Sambuc  */
711*ebfedea0SLionel Sambuc static int
set_maxbufs(saslc_sess_t * sess)712*ebfedea0SLionel Sambuc set_maxbufs(saslc_sess_t *sess)
713*ebfedea0SLionel Sambuc {
714*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
715*ebfedea0SLionel Sambuc 	const char *p;
716*ebfedea0SLionel Sambuc 	char *q;
717*ebfedea0SLionel Sambuc 	unsigned long val;
718*ebfedea0SLionel Sambuc 	int rv;
719*ebfedea0SLionel Sambuc 
720*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
721*ebfedea0SLionel Sambuc 
722*ebfedea0SLionel Sambuc 	/* by default, we use the same input maxbuf as the server. */
723*ebfedea0SLionel Sambuc 	ms->imaxbuf = ms->omaxbuf;
724*ebfedea0SLionel Sambuc 	p = saslc_sess_getprop(sess, SASLC_PROP_MAXBUF);
725*ebfedea0SLionel Sambuc 	if (p != NULL) {
726*ebfedea0SLionel Sambuc 		val = strtol(p, &q, 0);
727*ebfedea0SLionel Sambuc 		if (p[0] == '\0' || *q != '\0') {
728*ebfedea0SLionel Sambuc 
729*ebfedea0SLionel Sambuc 			return MECH_ERROR;
730*ebfedea0SLionel Sambuc 		}
731*ebfedea0SLionel Sambuc 		if (errno == ERANGE && val == ULONG_MAX) {
732*ebfedea0SLionel Sambuc 
733*ebfedea0SLionel Sambuc 			return MECH_ERROR;
734*ebfedea0SLionel Sambuc 		}
735*ebfedea0SLionel Sambuc 		if (val > 0xffffff)
736*ebfedea0SLionel Sambuc 			val = 0xffffff;
737*ebfedea0SLionel Sambuc 		ms->imaxbuf = (uint32_t)val;
738*ebfedea0SLionel Sambuc 	}
739*ebfedea0SLionel Sambuc 	rv = wrap_size_limit(sess, ms->omaxbuf);
740*ebfedea0SLionel Sambuc 	if (rv == -1)
741*ebfedea0SLionel Sambuc 		return MECH_ERROR;
742*ebfedea0SLionel Sambuc 	ms->omaxbuf = rv;	/* maxbuf size for unencoded output data */
743*ebfedea0SLionel Sambuc 
744*ebfedea0SLionel Sambuc 	return 0;
745*ebfedea0SLionel Sambuc }
746*ebfedea0SLionel Sambuc 
747*ebfedea0SLionel Sambuc /**
748*ebfedea0SLionel Sambuc  * @brief do one step of the sasl authentication
749*ebfedea0SLionel Sambuc  * @param sess sasl session
750*ebfedea0SLionel Sambuc  * @param in input data
751*ebfedea0SLionel Sambuc  * @param inlen input data length
752*ebfedea0SLionel Sambuc  * @param out place to store output data
753*ebfedea0SLionel Sambuc  * @param outlen output data length
754*ebfedea0SLionel Sambuc  * @return MECH_OK on success, MECH_STEP if more steps are needed,
755*ebfedea0SLionel Sambuc  * MECH_ERROR on failure
756*ebfedea0SLionel Sambuc  */
757*ebfedea0SLionel Sambuc static int
saslc__mech_gssapi_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)758*ebfedea0SLionel Sambuc saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen,
759*ebfedea0SLionel Sambuc     void **out, size_t *outlen)
760*ebfedea0SLionel Sambuc {
761*ebfedea0SLionel Sambuc 	saslc__mech_gssapi_sess_t *ms;
762*ebfedea0SLionel Sambuc 	gss_buffer_desc input, output;
763*ebfedea0SLionel Sambuc 	int rv;
764*ebfedea0SLionel Sambuc 
765*ebfedea0SLionel Sambuc     /**************************************************************************/
766*ebfedea0SLionel Sambuc     /* [RFC 2222 section 7.2.1]                                               */
767*ebfedea0SLionel Sambuc     /* The client calls GSS_Init_sec_context, passing in 0 for                */
768*ebfedea0SLionel Sambuc     /* input_context_handle (initially) and a targ_name equal to output_name  */
769*ebfedea0SLionel Sambuc     /* from GSS_Import_Name called with input_name_type of                    */
770*ebfedea0SLionel Sambuc     /* GSS_C_NT_HOSTBASED_SERVICE and input_name_string of                    */
771*ebfedea0SLionel Sambuc     /* "service@hostname" where "service" is the service name specified in    */
772*ebfedea0SLionel Sambuc     /* the protocol's profile, and "hostname" is the fully qualified host     */
773*ebfedea0SLionel Sambuc     /* name of the server.  The client then responds with the resulting       */
774*ebfedea0SLionel Sambuc     /* output_token.  If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED,  */
775*ebfedea0SLionel Sambuc     /* then the client should expect the server to issue a token in a         */
776*ebfedea0SLionel Sambuc     /* subsequent challenge.  The client must pass the token to another call  */
777*ebfedea0SLionel Sambuc     /* to GSS_Init_sec_context, repeating the actions in this paragraph.      */
778*ebfedea0SLionel Sambuc     /*                                                                        */
779*ebfedea0SLionel Sambuc     /* When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes     */
780*ebfedea0SLionel Sambuc     /* the following actions: If the last call to GSS_Init_sec_context        */
781*ebfedea0SLionel Sambuc     /* returned an output_token, then the client responds with the            */
782*ebfedea0SLionel Sambuc     /* output_token, otherwise the client responds with no data.  The client  */
783*ebfedea0SLionel Sambuc     /* should then expect the server to issue a token in a subsequent         */
784*ebfedea0SLionel Sambuc     /* challenge.  The client passes this token to GSS_Unwrap and interprets  */
785*ebfedea0SLionel Sambuc     /* the first octet of resulting cleartext as a bit-mask specifying the    */
786*ebfedea0SLionel Sambuc     /* security layers supported by the server and the second through fourth  */
787*ebfedea0SLionel Sambuc     /* octets as the maximum size output_message to send to the server.  The  */
788*ebfedea0SLionel Sambuc     /* client then constructs data, with the first octet containing the       */
789*ebfedea0SLionel Sambuc     /* bit-mask specifying the selected security layer, the second through    */
790*ebfedea0SLionel Sambuc     /* fourth octets containing in network byte order the maximum size        */
791*ebfedea0SLionel Sambuc     /* output_message the client is able to receive, and the remaining        */
792*ebfedea0SLionel Sambuc     /* octets containing the authorization identity.  The client passes the   */
793*ebfedea0SLionel Sambuc     /* data to GSS_Wrap with conf_flag set to FALSE, and responds with the    */
794*ebfedea0SLionel Sambuc     /* generated output_message.  The client can then consider the server     */
795*ebfedea0SLionel Sambuc     /* authenticated.                                                         */
796*ebfedea0SLionel Sambuc     /**************************************************************************/
797*ebfedea0SLionel Sambuc 
798*ebfedea0SLionel Sambuc 	ms = sess->mech_sess;
799*ebfedea0SLionel Sambuc 
800*ebfedea0SLionel Sambuc 	switch(ms->status) {
801*ebfedea0SLionel Sambuc 	case GSSAPI_AUTH_FIRST:
802*ebfedea0SLionel Sambuc 		saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_FIRST");
803*ebfedea0SLionel Sambuc 
804*ebfedea0SLionel Sambuc 		if (get_service(sess, &ms->server_name) == -1)
805*ebfedea0SLionel Sambuc 			return MECH_ERROR;
806*ebfedea0SLionel Sambuc 
807*ebfedea0SLionel Sambuc 		rv = init_sec_context(sess, GSS_C_NO_BUFFER, &output);
808*ebfedea0SLionel Sambuc 		if (rv == -1)
809*ebfedea0SLionel Sambuc 			return MECH_ERROR;
810*ebfedea0SLionel Sambuc 
811*ebfedea0SLionel Sambuc 		if (prep_output(sess, &output, out, outlen) == -1)
812*ebfedea0SLionel Sambuc 			return MECH_ERROR;
813*ebfedea0SLionel Sambuc 
814*ebfedea0SLionel Sambuc 		ms->status = rv == 0 ? GSSAPI_AUTH_LAST : GSSAPI_AUTH_NEXT;
815*ebfedea0SLionel Sambuc 		return MECH_STEP;
816*ebfedea0SLionel Sambuc 
817*ebfedea0SLionel Sambuc 	case GSSAPI_AUTH_NEXT:
818*ebfedea0SLionel Sambuc 		saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_NEXT");
819*ebfedea0SLionel Sambuc 
820*ebfedea0SLionel Sambuc 		input.value = __UNCONST(in);
821*ebfedea0SLionel Sambuc 		input.length = inlen;
822*ebfedea0SLionel Sambuc 		if ((rv = init_sec_context(sess, &input, &output)) == -1)
823*ebfedea0SLionel Sambuc 			return MECH_ERROR;
824*ebfedea0SLionel Sambuc 
825*ebfedea0SLionel Sambuc 		if (prep_output(sess, &output, out, outlen) == -1)
826*ebfedea0SLionel Sambuc 			return MECH_ERROR;
827*ebfedea0SLionel Sambuc 
828*ebfedea0SLionel Sambuc 		if (rv == 0)
829*ebfedea0SLionel Sambuc 			ms->status = GSSAPI_AUTH_LAST;
830*ebfedea0SLionel Sambuc 		return MECH_STEP;
831*ebfedea0SLionel Sambuc 
832*ebfedea0SLionel Sambuc 	case GSSAPI_AUTH_LAST:
833*ebfedea0SLionel Sambuc 		saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_LAST");
834*ebfedea0SLionel Sambuc 
835*ebfedea0SLionel Sambuc 		input.value = __UNCONST(in);
836*ebfedea0SLionel Sambuc 		input.length = inlen;
837*ebfedea0SLionel Sambuc 		if (unwrap_input_token(sess, &input) == -1)
838*ebfedea0SLionel Sambuc 			return MECH_ERROR;
839*ebfedea0SLionel Sambuc 
840*ebfedea0SLionel Sambuc 		if ((rv = choose_qop(sess, ms->qop_mask)) == -1)
841*ebfedea0SLionel Sambuc 			return MECH_ERROR;
842*ebfedea0SLionel Sambuc 
843*ebfedea0SLionel Sambuc 		ms->mech_sess.qop = rv;
844*ebfedea0SLionel Sambuc 
845*ebfedea0SLionel Sambuc 		if (ms->mech_sess.qop != QOP_NONE) {
846*ebfedea0SLionel Sambuc 			if (ms->mech_sess.qop == QOP_CONF) {
847*ebfedea0SLionel Sambuc 				/*
848*ebfedea0SLionel Sambuc 				 * XXX: where do we negotiate the cipher,
849*ebfedea0SLionel Sambuc 				 *  or do we?
850*ebfedea0SLionel Sambuc 				 */
851*ebfedea0SLionel Sambuc 			}
852*ebfedea0SLionel Sambuc 			if (set_maxbufs(sess) == -1)
853*ebfedea0SLionel Sambuc 				return MECH_ERROR;
854*ebfedea0SLionel Sambuc 			ms->dec_ctx = saslc__buffer32_create(sess, ms->imaxbuf);
855*ebfedea0SLionel Sambuc 			ms->enc_ctx = saslc__buffer_create(sess, ms->omaxbuf);
856*ebfedea0SLionel Sambuc 		}
857*ebfedea0SLionel Sambuc 		if (wrap_output_token(sess, &output) == -1)
858*ebfedea0SLionel Sambuc 			return MECH_ERROR;
859*ebfedea0SLionel Sambuc 
860*ebfedea0SLionel Sambuc 		if (prep_output(sess, &output, out, outlen) == -1)
861*ebfedea0SLionel Sambuc 			return MECH_ERROR;
862*ebfedea0SLionel Sambuc 
863*ebfedea0SLionel Sambuc 		ms->status = GSSAPI_AUTH_DONE;
864*ebfedea0SLionel Sambuc 		return MECH_OK;
865*ebfedea0SLionel Sambuc 
866*ebfedea0SLionel Sambuc 	case GSSAPI_AUTH_DONE:
867*ebfedea0SLionel Sambuc 		assert(/*CONSTCOND*/0);	/* XXX: impossible */
868*ebfedea0SLionel Sambuc 		saslc__error_set(ERR(sess), ERROR_MECH,
869*ebfedea0SLionel Sambuc 		    "already authenticated");
870*ebfedea0SLionel Sambuc 		return MECH_ERROR;
871*ebfedea0SLionel Sambuc 
872*ebfedea0SLionel Sambuc #if 0	/* no default so the compiler can tell us if we miss an enum */
873*ebfedea0SLionel Sambuc 	default:
874*ebfedea0SLionel Sambuc 		assert(/*CONSTCOND*/0); /* impossible */
875*ebfedea0SLionel Sambuc 		/*NOTREACHED*/
876*ebfedea0SLionel Sambuc #endif
877*ebfedea0SLionel Sambuc 	}
878*ebfedea0SLionel Sambuc 	/*LINTED*/
879*ebfedea0SLionel Sambuc 	assert(/*CONSTCOND*/0);		/* XXX: impossible */
880*ebfedea0SLionel Sambuc 	return MECH_ERROR;
881*ebfedea0SLionel Sambuc }
882*ebfedea0SLionel Sambuc 
883*ebfedea0SLionel Sambuc /* mechanism definition */
884*ebfedea0SLionel Sambuc const saslc__mech_t saslc__mech_gssapi = {
885*ebfedea0SLionel Sambuc 	.name	 = "GSSAPI",
886*ebfedea0SLionel Sambuc 	.flags	 = FLAG_NONE,
887*ebfedea0SLionel Sambuc 	.create	 = saslc__mech_gssapi_create,
888*ebfedea0SLionel Sambuc 	.cont	 = saslc__mech_gssapi_cont,
889*ebfedea0SLionel Sambuc 	.encode	 = saslc__mech_gssapi_encode,
890*ebfedea0SLionel Sambuc 	.decode	 = saslc__mech_gssapi_decode,
891*ebfedea0SLionel Sambuc 	.destroy = saslc__mech_gssapi_destroy
892*ebfedea0SLionel Sambuc };
893