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