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