1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /* GSSAPI SASL plugin
7  * Leif Johansson
8  * Rob Siemborski (SASL v2 Conversion)
9  * $Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $
10  */
11 /*
12  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  *
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  *
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in
23  *    the documentation and/or other materials provided with the
24  *    distribution.
25  *
26  * 3. The name "Carnegie Mellon University" must not be used to
27  *    endorse or promote products derived from this software without
28  *    prior written permission. For permission or any other legal
29  *    details, please contact
30  *      Office of Technology Transfer
31  *      Carnegie Mellon University
32  *      5000 Forbes Avenue
33  *      Pittsburgh, PA  15213-3890
34  *      (412) 268-4387, fax: (412) 268-7395
35  *      tech-transfer@andrew.cmu.edu
36  *
37  * 4. Redistributions of any form whatsoever must retain the following
38  *    acknowledgment:
39  *    "This product includes software developed by Computing Services
40  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
41  *
42  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
43  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
44  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
45  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
46  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
47  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
48  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49  */
50 
51 #include <config.h>
52 
53 #ifdef HAVE_GSSAPI_H
54 #include <gssapi.h>
55 #else
56 #include <gssapi/gssapi.h>
57 #endif
58 
59 #ifdef WIN32
60 #  include <winsock.h>
61 
62 #  ifndef R_OK
63 #    define R_OK 04
64 #  endif
65 /* we also need io.h for access() prototype */
66 #  include <io.h>
67 #else
68 #  include <sys/param.h>
69 #  include <sys/socket.h>
70 #  include <netinet/in.h>
71 #  include <arpa/inet.h>
72 #  include <netdb.h>
73 #endif /* WIN32 */
74 #include <fcntl.h>
75 #include <stdio.h>
76 #include <sasl.h>
77 #include <saslutil.h>
78 #include <saslplug.h>
79 
80 #include "plugin_common.h"
81 
82 #ifdef HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
85 
86 #include <errno.h>
87 
88 #ifdef WIN32
89 /* This must be after sasl.h */
90 # include "saslgssapi.h"
91 #endif /* WIN32 */
92 
93 /*****************************  Common Section  *****************************/
94 
95 #ifndef _SUN_SDK_
96 static const char plugin_id[] = "$Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $";
97 #endif /* !_SUN_SDK_ */
98 
99 static const char * GSSAPI_BLANK_STRING = "";
100 
101 #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
102 extern gss_OID gss_nt_service_name;
103 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
104 #endif
105 
106 #ifdef _SUN_SDK_
107 static int
108 get_oid(const sasl_utils_t *utils, gss_OID *oid);
109 #ifdef GSSAPI_PROTECT
110 DEFINE_STATIC_MUTEX(global_mutex);
111 #endif /* GSSAPI_PROTECT */
112 #endif /* _SUN_SDK_ */
113 
114 /* GSSAPI SASL Mechanism by Leif Johansson <leifj@matematik.su.se>
115  * inspired by the kerberos mechanism and the gssapi_server and
116  * gssapi_client from the heimdal distribution by Assar Westerlund
117  * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>.
118  * See the configure.in file for details on dependencies.
119  * Heimdal can be obtained from http://www.pdc.kth.se/heimdal
120  *
121  * Important contributions from Sam Hartman <hartmans@fundsxpress.com>.
122  */
123 
124 typedef struct context {
125     int state;
126 
127     gss_ctx_id_t gss_ctx;
128     gss_name_t   client_name;
129     gss_name_t   server_name;
130     gss_cred_id_t server_creds;
131     sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the
132 					server */
133 #ifdef _SUN_SDK_
134     gss_cred_id_t client_creds;
135     gss_OID	mech_oid;
136     int		use_authid;
137 #endif /* _SUN_SDK_ */
138     const sasl_utils_t *utils;
139 
140     /* layers buffering */
141     char *buffer;
142 #ifdef _SUN_SDK_
143     unsigned bufsize;
144 #else
145     int bufsize;
146 #endif /* _SUN_SDK_ */
147     char sizebuf[4];
148 #ifdef _SUN_SDK_
149     unsigned cursize;
150     unsigned size;
151 #else
152     int cursize;
153     int size;
154 #endif /* _SUN_SDK_ */
155     unsigned needsize;
156 
157     char *encode_buf;                /* For encoding/decoding mem management */
158     char *decode_buf;
159     char *decode_once_buf;
160     unsigned encode_buf_len;
161     unsigned decode_buf_len;
162     unsigned decode_once_buf_len;
163     buffer_info_t *enc_in_buf;
164 
165     char *out_buf;                   /* per-step mem management */
166     unsigned out_buf_len;
167 
168     char *authid; /* hold the authid between steps - server */
169     const char *user;   /* hold the userid between steps - client */
170 #ifdef _SUN_SDK_
171     const char *client_authid;
172 #endif /* _SUN_SDK_ */
173 #ifdef _INTEGRATED_SOLARIS_
174     void *h;
175 #endif /* _INTEGRATED_SOLARIS_ */
176 } context_t;
177 
178 enum {
179     SASL_GSSAPI_STATE_AUTHNEG = 1,
180     SASL_GSSAPI_STATE_SSFCAP = 2,
181     SASL_GSSAPI_STATE_SSFREQ = 3,
182     SASL_GSSAPI_STATE_AUTHENTICATED = 4
183 };
184 
185 #ifdef _SUN_SDK_
186 /* sasl_gss_log only logs gss_display_status() error string */
187 #define sasl_gss_log(x,y,z) sasl_gss_seterror_(text,y,z,1)
188 #define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(text,y,z,0)
189 static void
sasl_gss_seterror_(const context_t * text,OM_uint32 maj,OM_uint32 min,int logonly)190 sasl_gss_seterror_(const context_t *text, OM_uint32 maj, OM_uint32 min,
191 	int logonly)
192 #else
193 static void
194 sasl_gss_seterror(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min)
195 #endif /* _SUN_SDK_ */
196 {
197     OM_uint32 maj_stat, min_stat;
198     gss_buffer_desc msg;
199     OM_uint32 msg_ctx;
200     int ret;
201     char *out = NULL;
202 #ifdef _SUN_SDK_
203     unsigned len, curlen = 0;
204     const sasl_utils_t *utils = text->utils;
205     char *prefix = dgettext(TEXT_DOMAIN, "GSSAPI Error: ");
206 #else
207     size_t len, curlen = 0;
208     const char prefix[] = "GSSAPI Error: ";
209 #endif /* _SUN_SDK_ */
210 
211     if(!utils) return;
212 
213     len = sizeof(prefix);
214     ret = _plug_buf_alloc(utils, &out, &curlen, 256);
215     if(ret != SASL_OK) return;
216 
217     strcpy(out, prefix);
218 
219     msg_ctx = 0;
220     while (1) {
221 	maj_stat = gss_display_status(&min_stat, maj,
222 #ifdef _SUN_SDK_
223 				      GSS_C_GSS_CODE, text->mech_oid,
224 #else
225 				      GSS_C_GSS_CODE, GSS_C_NULL_OID,
226 #endif /* _SUN_SDK_ */
227 				      &msg_ctx, &msg);
228 	if(GSS_ERROR(maj_stat)) {
229 #ifdef _SUN_SDK_
230 	    if (logonly) {
231 		utils->log(text->utils->conn, SASL_LOG_FAIL,
232 		    "GSSAPI Failure: (could not get major error message)");
233 	    } else {
234 #endif /* _SUN_SDK_ */
235 #ifdef _INTEGRATED_SOLARIS_
236 		utils->seterror(utils->conn, 0,
237 				gettext("GSSAPI Failure "
238 				"(could not get major error message)"));
239 #ifdef _SUN_SDK_
240 	    }
241 #endif /* _SUN_SDK_ */
242 #else
243 	    utils->seterror(utils->conn, 0,
244 			    "GSSAPI Failure "
245 			    "(could not get major error message)");
246 #ifdef _SUN_SDK_
247 	    }
248 #endif /* _SUN_SDK_ */
249 #endif /* _INTEGRATED_SOLARIS_ */
250 	    utils->free(out);
251 	    return;
252 	}
253 
254 	len += len + msg.length;
255 	ret = _plug_buf_alloc(utils, &out, &curlen, len);
256 
257 	if(ret != SASL_OK) {
258 	    utils->free(out);
259 	    return;
260 	}
261 
262 	strcat(out, msg.value);
263 
264 	gss_release_buffer(&min_stat, &msg);
265 
266 	if (!msg_ctx)
267 	    break;
268     }
269 
270     /* Now get the minor status */
271 
272     len += 2;
273     ret = _plug_buf_alloc(utils, &out, &curlen, len);
274     if(ret != SASL_OK) {
275 	utils->free(out);
276 	return;
277     }
278 
279     strcat(out, " (");
280 
281     msg_ctx = 0;
282     while (1) {
283 	maj_stat = gss_display_status(&min_stat, min,
284 #ifdef _SUN_SDK_
285 				      GSS_C_MECH_CODE, text->mech_oid,
286 #else
287 				      GSS_C_MECH_CODE, GSS_C_NULL_OID,
288 #endif /* _SUN_SDK_ */
289 				      &msg_ctx, &msg);
290 	if(GSS_ERROR(maj_stat)) {
291 #ifdef _SUN_SDK_
292 	    if (logonly) {
293 		utils->log(text->utils->conn, SASL_LOG_FAIL,
294 		    "GSSAPI Failure: (could not get minor error message)");
295 	    } else {
296 #endif /* _SUN_SDK_ */
297 #ifdef _INTEGRATED_SOLARIS_
298 		utils->seterror(utils->conn, 0,
299 				gettext("GSSAPI Failure "
300 				"(could not get minor error message)"));
301 #ifdef _SUN_SDK_
302 	    }
303 #endif /* _SUN_SDK_ */
304 #else
305 	    utils->seterror(utils->conn, 0,
306 			    "GSSAPI Failure "
307 			    "(could not get minor error message)");
308 #ifdef _SUN_SDK_
309 	    }
310 #endif /* _SUN_SDK_ */
311 #endif /* _INTEGRATED_SOLARIS_ */
312 	    utils->free(out);
313 	    return;
314 	}
315 
316 	len += len + msg.length;
317 	ret = _plug_buf_alloc(utils, &out, &curlen, len);
318 
319 	if(ret != SASL_OK) {
320 	    utils->free(out);
321 	    return;
322 	}
323 
324 	strcat(out, msg.value);
325 
326 	gss_release_buffer(&min_stat, &msg);
327 
328 	if (!msg_ctx)
329 	    break;
330     }
331 
332     len += 1;
333     ret = _plug_buf_alloc(utils, &out, &curlen, len);
334     if(ret != SASL_OK) {
335 	utils->free(out);
336 	return;
337     }
338 
339     strcat(out, ")");
340 
341 #ifdef _SUN_SDK_
342     if (logonly) {
343 	utils->log(text->utils->conn, SASL_LOG_FAIL, out);
344     } else {
345 	utils->seterror(utils->conn, 0, out);
346     }
347 #else
348     utils->seterror(utils->conn, 0, out);
349 #endif /* _SUN_SDK_ */
350     utils->free(out);
351 }
352 
353 static int
sasl_gss_encode(void * context,const struct iovec * invec,unsigned numiov,const char ** output,unsigned * outputlen,int privacy)354 sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov,
355 		const char **output, unsigned *outputlen, int privacy)
356 {
357     context_t *text = (context_t *)context;
358     OM_uint32 maj_stat, min_stat;
359     gss_buffer_t input_token, output_token;
360     gss_buffer_desc real_input_token, real_output_token;
361     int ret;
362     struct buffer_info *inblob, bufinfo;
363 
364     if(!output) return SASL_BADPARAM;
365 
366     if(numiov > 1) {
367 	ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
368 	if(ret != SASL_OK) return ret;
369 	inblob = text->enc_in_buf;
370     } else {
371 	bufinfo.data = invec[0].iov_base;
372 	bufinfo.curlen = invec[0].iov_len;
373 	inblob = &bufinfo;
374     }
375 
376     if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE;
377 
378     input_token = &real_input_token;
379 
380     real_input_token.value  = inblob->data;
381     real_input_token.length = inblob->curlen;
382 
383     output_token = &real_output_token;
384     output_token->value = NULL;
385     output_token->length = 0;
386 
387 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
388     if (LOCK_MUTEX(&global_mutex) < 0)
389 	return (SASL_FAIL);
390 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
391     maj_stat = gss_wrap (&min_stat,
392 			 text->gss_ctx,
393 			 privacy,
394 			 GSS_C_QOP_DEFAULT,
395 			 input_token,
396 			 NULL,
397 			 output_token);
398 
399     if (GSS_ERROR(maj_stat))
400 	{
401 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
402 	    if (output_token->value)
403 		gss_release_buffer(&min_stat, output_token);
404 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
405 	    UNLOCK_MUTEX(&global_mutex);
406 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
407 	    return SASL_FAIL;
408 	}
409 
410     if (output_token->value && output) {
411 	int len;
412 
413 	ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
414 			      &(text->encode_buf_len), output_token->length + 4);
415 
416 	if (ret != SASL_OK) {
417 	    gss_release_buffer(&min_stat, output_token);
418 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
419 	    UNLOCK_MUTEX(&global_mutex);
420 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
421 	    return ret;
422 	}
423 
424 	len = htonl(output_token->length);
425 	memcpy(text->encode_buf, &len, 4);
426 	memcpy(text->encode_buf + 4, output_token->value, output_token->length);
427     }
428 
429     if (outputlen) {
430 	*outputlen = output_token->length + 4;
431     }
432 
433     *output = text->encode_buf;
434 
435     if (output_token->value)
436 	gss_release_buffer(&min_stat, output_token);
437 
438 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
439     UNLOCK_MUTEX(&global_mutex);
440 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
441 
442     return SASL_OK;
443 }
444 
gssapi_privacy_encode(void * context,const struct iovec * invec,unsigned numiov,const char ** output,unsigned * outputlen)445 static int gssapi_privacy_encode(void *context, const struct iovec *invec,
446 				 unsigned numiov, const char **output,
447 				 unsigned *outputlen)
448 {
449     return sasl_gss_encode(context,invec,numiov,output,outputlen,1);
450 }
451 
gssapi_integrity_encode(void * context,const struct iovec * invec,unsigned numiov,const char ** output,unsigned * outputlen)452 static int gssapi_integrity_encode(void *context, const struct iovec *invec,
453 				   unsigned numiov, const char **output,
454 				   unsigned *outputlen)
455 {
456     return sasl_gss_encode(context,invec,numiov,output,outputlen,0);
457 }
458 
459 #define myMIN(a,b) (((a) < (b)) ? (a) : (b))
460 
gssapi_decode_once(void * context,const char ** input,unsigned * inputlen,char ** output,unsigned * outputlen)461 static int gssapi_decode_once(void *context,
462 			      const char **input, unsigned *inputlen,
463 			      char **output, unsigned *outputlen)
464 {
465     context_t *text = (context_t *) context;
466     OM_uint32 maj_stat, min_stat;
467     gss_buffer_t input_token, output_token;
468     gss_buffer_desc real_input_token, real_output_token;
469     int result;
470     unsigned diff;
471 
472     if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) {
473 #ifdef _INTEGRATED_SOLARIS_
474 	SETERROR(text->utils, gettext("GSSAPI Failure"));
475 #else
476 	SETERROR(text->utils, "GSSAPI Failure");
477 #endif /* _INTEGRATED_SOLARIS_ */
478 	return SASL_NOTDONE;
479     }
480 
481     /* first we need to extract a packet */
482     if (text->needsize > 0) {
483 	/* how long is it? */
484 	int tocopy = myMIN(text->needsize, *inputlen);
485 
486 	memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy);
487 	text->needsize -= tocopy;
488 	*input += tocopy;
489 	*inputlen -= tocopy;
490 
491 	if (text->needsize == 0) {
492 	    /* got the entire size */
493 	    memcpy(&text->size, text->sizebuf, 4);
494 	    text->size = ntohl(text->size);
495 	    text->cursize = 0;
496 
497 #ifdef _SUN_SDK_
498 	    if (text->size > 0xFFFFFF) {
499 		text->utils->log(text->utils->conn, SASL_LOG_ERR,
500 				 "Illegal size in sasl_gss_decode_once");
501 #else
502 	    if (text->size > 0xFFFFFF || text->size <= 0) {
503 		SETERROR(text->utils, "Illegal size in sasl_gss_decode_once");
504 #endif /* _SUN_SDK_ */
505 		return SASL_FAIL;
506 	    }
507 
508 	    if (text->bufsize < text->size + 5) {
509 		result = _plug_buf_alloc(text->utils, &text->buffer,
510 					 &(text->bufsize), text->size+5);
511 		if(result != SASL_OK) return result;
512 	    }
513 	}
514 	if (*inputlen == 0) {
515 	    /* need more data ! */
516 	    *outputlen = 0;
517 	    *output = NULL;
518 
519 	    return SASL_OK;
520 	}
521     }
522 
523     diff = text->size - text->cursize;
524 
525     if (*inputlen < diff) {
526 	/* ok, let's queue it up; not enough data */
527 	memcpy(text->buffer + text->cursize, *input, *inputlen);
528 	text->cursize += *inputlen;
529 	*inputlen = 0;
530 	*outputlen = 0;
531 	*output = NULL;
532 	return SASL_OK;
533     } else {
534 	memcpy(text->buffer + text->cursize, *input, diff);
535 	*input += diff;
536 	*inputlen -= diff;
537     }
538 
539     input_token = &real_input_token;
540     real_input_token.value = text->buffer;
541     real_input_token.length = text->size;
542 
543     output_token = &real_output_token;
544     output_token->value = NULL;
545     output_token->length = 0;
546 
547 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
548     if (LOCK_MUTEX(&global_mutex) < 0)
549 	return (SASL_FAIL);
550 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
551 
552     maj_stat = gss_unwrap (&min_stat,
553 			   text->gss_ctx,
554 			   input_token,
555 			   output_token,
556 			   NULL,
557 			   NULL);
558 
559     if (GSS_ERROR(maj_stat))
560 	{
561 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
562 	    if (output_token->value)
563 		gss_release_buffer(&min_stat, output_token);
564 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
565 	    UNLOCK_MUTEX(&global_mutex);
566 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
567 	    return SASL_FAIL;
568 	}
569 
570     if (outputlen)
571 	*outputlen = output_token->length;
572 
573     if (output_token->value) {
574 	if (output) {
575 	    result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
576 				     &text->decode_once_buf_len,
577 				     *outputlen);
578 	    if(result != SASL_OK) {
579 		gss_release_buffer(&min_stat, output_token);
580 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
581 	    UNLOCK_MUTEX(&global_mutex);
582 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
583 		return result;
584 	    }
585 	    *output = text->decode_once_buf;
586 	    memcpy(*output, output_token->value, *outputlen);
587 	}
588 	gss_release_buffer(&min_stat, output_token);
589     }
590 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
591 	    UNLOCK_MUTEX(&global_mutex);
592 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
593 
594     /* reset for the next packet */
595 #ifndef _SUN_SDK_
596     text->size = -1;
597 #endif /* !_SUN_SDK_ */
598     text->needsize = 4;
599 
600     return SASL_OK;
601 }
602 
603 static int gssapi_decode(void *context,
604 			 const char *input, unsigned inputlen,
605 			 const char **output, unsigned *outputlen)
606 {
607     context_t *text = (context_t *) context;
608     int ret;
609 
610     ret = _plug_decode(text->utils, context, input, inputlen,
611 		       &text->decode_buf, &text->decode_buf_len, outputlen,
612 		       gssapi_decode_once);
613 
614     *output = text->decode_buf;
615 
616     return ret;
617 }
618 
619 static context_t *gss_new_context(const sasl_utils_t *utils)
620 {
621     context_t *ret;
622 
623     ret = utils->malloc(sizeof(context_t));
624     if(!ret) return NULL;
625 
626     memset(ret,0,sizeof(context_t));
627     ret->utils = utils;
628 #ifdef _SUN_SDK_
629     ret->gss_ctx = GSS_C_NO_CONTEXT;
630     ret->client_name = GSS_C_NO_NAME;
631     ret->server_name = GSS_C_NO_NAME;
632     ret->server_creds = GSS_C_NO_CREDENTIAL;
633     ret->client_creds = GSS_C_NO_CREDENTIAL;
634     if (get_oid(utils, &ret->mech_oid) != SASL_OK) {
635 	utils->free(ret);
636 	return (NULL);
637     }
638 #endif /* _SUN_SDK_ */
639 
640     ret->needsize = 4;
641 
642     return ret;
643 }
644 
645 static void sasl_gss_free_context_contents(context_t *text)
646 {
647     OM_uint32 min_stat;
648 
649     if (!text) return;
650 
651     if (text->gss_ctx != GSS_C_NO_CONTEXT) {
652 	(void) gss_delete_sec_context(&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
653 	text->gss_ctx = GSS_C_NO_CONTEXT;
654     }
655 
656     if (text->client_name != GSS_C_NO_NAME) {
657 	(void) gss_release_name(&min_stat,&text->client_name);
658 	text->client_name = GSS_C_NO_NAME;
659     }
660 
661     if (text->server_name != GSS_C_NO_NAME) {
662 	(void) gss_release_name(&min_stat,&text->server_name);
663 	text->server_name = GSS_C_NO_NAME;
664     }
665 
666     if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
667 	(void) gss_release_cred(&min_stat, &text->server_creds);
668 	text->server_creds = GSS_C_NO_CREDENTIAL;
669     }
670 
671 #ifdef _SUN_SDK_
672     if ( text->client_creds != GSS_C_NO_CREDENTIAL) {
673 	(void) gss_release_cred(&min_stat, &text->client_creds);
674 	text->client_creds = GSS_C_NO_CREDENTIAL;
675     }
676 
677     /*
678      * Note that the oid returned by rpc_gss_mech_to_oid should not
679      * be released
680      */
681 #endif /* _SUN_SDK_ */
682 
683     if (text->out_buf) {
684 	text->utils->free(text->out_buf);
685 	text->out_buf = NULL;
686     }
687 
688     if (text->encode_buf) {
689 	text->utils->free(text->encode_buf);
690 	text->encode_buf = NULL;
691     }
692 
693     if (text->decode_buf) {
694 	text->utils->free(text->decode_buf);
695 	text->decode_buf = NULL;
696     }
697 
698     if (text->decode_once_buf) {
699 	text->utils->free(text->decode_once_buf);
700 	text->decode_once_buf = NULL;
701     }
702 
703     if (text->enc_in_buf) {
704 	if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data);
705 	text->utils->free(text->enc_in_buf);
706 	text->enc_in_buf = NULL;
707     }
708 
709     if (text->buffer) {
710 	text->utils->free(text->buffer);
711 	text->buffer = NULL;
712     }
713 
714     if (text->authid) { /* works for both client and server */
715 	text->utils->free(text->authid);
716 	text->authid = NULL;
717     }
718 }
719 
720 #ifdef _SUN_SDK_
721 
722 #ifdef HAVE_RPC_GSS_MECH_TO_OID
723 #include <rpc/rpcsec_gss.h>
724 #endif /* HAVE_RPC_GSS_MECH_TO_OID */
725 
726 static int
727 get_oid(const sasl_utils_t *utils, gss_OID *oid)
728 {
729 #ifdef HAVE_RPC_GSS_MECH_TO_OID
730     static gss_OID_desc kerb_v5 =
731 	{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
732 	/* 1.2.840.113554.1.2.2 */
733     *oid = &kerb_v5;
734 #endif /* HAVE_RPC_GSS_MECH_TO_OID */
735     return (SASL_OK);
736 }
737 
738 static int
739 add_mech_to_set(context_t *text, gss_OID_set *desired_mechs)
740 {
741     OM_uint32 maj_stat, min_stat;
742 
743     maj_stat = gss_create_empty_oid_set(&min_stat, desired_mechs);
744 
745     if (GSS_ERROR(maj_stat)) {
746 	sasl_gss_seterror(text->utils, maj_stat, min_stat);
747 	sasl_gss_free_context_contents(text);
748 	return SASL_FAIL;
749     }
750 
751     maj_stat = gss_add_oid_set_member(&min_stat, text->mech_oid, desired_mechs);
752     if (GSS_ERROR(maj_stat)) {
753 	sasl_gss_seterror(text->utils, maj_stat, min_stat);
754 	sasl_gss_free_context_contents(text);
755 	(void) gss_release_oid_set(&min_stat, desired_mechs);
756 	return SASL_FAIL;
757     }
758     return SASL_OK;
759 }
760 #endif /* _SUN_SDK_ */
761 
762 static void gssapi_common_mech_dispose(void *conn_context,
763 				       const sasl_utils_t *utils)
764 {
765 #ifdef _SUN_SDK_
766     if (conn_context == NULL)
767 	return;
768 #ifdef _INTEGRATED_SOLARIS_
769     convert_prompt(utils, &((context_t *)conn_context)->h, NULL);
770 #endif /* _INTEGRATED_SOLARIS_ */
771 #endif /* _SUN_SDK_ */
772 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
773     (void) LOCK_MUTEX(&global_mutex);
774 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
775     sasl_gss_free_context_contents((context_t *)(conn_context));
776 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
777     UNLOCK_MUTEX(&global_mutex);
778 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
779     utils->free(conn_context);
780 }
781 
782 /*****************************  Server Section  *****************************/
783 
784 static int
785 gssapi_server_mech_new(void *glob_context __attribute__((unused)),
786 		       sasl_server_params_t *params,
787 		       const char *challenge __attribute__((unused)),
788 		       unsigned challen __attribute__((unused)),
789 		       void **conn_context)
790 {
791     context_t *text;
792 
793 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
794     if (LOCK_MUTEX(&global_mutex) < 0)
795 	return (SASL_FAIL);
796 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
797     text = gss_new_context(params->utils);
798 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
799     UNLOCK_MUTEX(&global_mutex);
800 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
801     if (text == NULL) {
802 #ifndef _SUN_SDK_
803 	MEMERROR(params->utils);
804 #endif /* !_SUN_SDK_ */
805 	return SASL_NOMEM;
806     }
807 
808     text->gss_ctx = GSS_C_NO_CONTEXT;
809     text->client_name = GSS_C_NO_NAME;
810     text->server_name = GSS_C_NO_NAME;
811     text->server_creds = GSS_C_NO_CREDENTIAL;
812     text->state = SASL_GSSAPI_STATE_AUTHNEG;
813 
814     *conn_context = text;
815 
816     return SASL_OK;
817 }
818 
819 static int
820 gssapi_server_mech_step(void *conn_context,
821 			sasl_server_params_t *params,
822 			const char *clientin,
823 			unsigned clientinlen,
824 			const char **serverout,
825 			unsigned *serveroutlen,
826 			sasl_out_params_t *oparams)
827 {
828     context_t *text = (context_t *)conn_context;
829     gss_buffer_t input_token, output_token;
830     gss_buffer_desc real_input_token, real_output_token;
831     OM_uint32 maj_stat, min_stat;
832 #ifdef _SUN_SDK_
833     OM_uint32 max_input_size;
834     gss_OID_set desired_mechs = GSS_C_NULL_OID_SET;
835 #endif /* _SUN_SDK_ */
836     gss_buffer_desc name_token;
837     int ret;
838 
839     input_token = &real_input_token;
840     output_token = &real_output_token;
841     output_token->value = NULL; output_token->length = 0;
842     input_token->value = NULL; input_token->length = 0;
843 
844     if(!serverout) {
845 	PARAMERROR(text->utils);
846 	return SASL_BADPARAM;
847     }
848 
849     *serverout = NULL;
850     *serveroutlen = 0;
851 
852     switch (text->state) {
853 
854     case SASL_GSSAPI_STATE_AUTHNEG:
855 	if (text->server_name == GSS_C_NO_NAME) { /* only once */
856 	    name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
857 	    name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
858 	    if (name_token.value == NULL) {
859 		MEMERROR(text->utils);
860 		sasl_gss_free_context_contents(text);
861 		return SASL_NOMEM;
862 	    }
863 #ifdef _SUN_SDK_
864 	    snprintf(name_token.value, name_token.length + 1,
865 		"%s@%s", params->service, params->serverFQDN);
866 #else
867 	    sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
868 #endif /* _SUN_SDK_ */
869 
870 	    maj_stat = gss_import_name (&min_stat,
871 					&name_token,
872 					GSS_C_NT_HOSTBASED_SERVICE,
873 					&text->server_name);
874 
875 	    params->utils->free(name_token.value);
876 	    name_token.value = NULL;
877 
878 	    if (GSS_ERROR(maj_stat)) {
879 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
880 		sasl_gss_free_context_contents(text);
881 		return SASL_FAIL;
882 	    }
883 
884 	    if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
885 		maj_stat = gss_release_cred(&min_stat, &text->server_creds);
886 		text->server_creds = GSS_C_NO_CREDENTIAL;
887 	    }
888 
889 #ifdef _SUN_SDK_
890 	    if (text->mech_oid != GSS_C_NULL_OID) {
891 		ret = add_mech_to_set(text, &desired_mechs);
892 		if (ret != SASL_OK)
893 		    return (ret);
894 	    }
895 #endif /* _SUN_SDK_ */
896 
897 	    maj_stat = gss_acquire_cred(&min_stat,
898 					text->server_name,
899 					GSS_C_INDEFINITE,
900 #ifdef _SUN_SDK_
901 					desired_mechs,
902 #else
903 					GSS_C_NO_OID_SET,
904 #endif /* _SUN_SDK_ */
905 					GSS_C_ACCEPT,
906 					&text->server_creds,
907 					NULL,
908 					NULL);
909 
910 #ifdef _SUN_SDK_
911 	    if (desired_mechs != GSS_C_NULL_OID_SET) {
912 		OM_uint32 min_stat2;
913 		(void) gss_release_oid_set(&min_stat2, &desired_mechs);
914 	    }
915 #endif /* _SUN_SDK_ */
916 
917 	    if (GSS_ERROR(maj_stat)) {
918 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
919 		sasl_gss_free_context_contents(text);
920 		return SASL_FAIL;
921 	    }
922 	}
923 
924 	if (clientinlen) {
925 	    real_input_token.value = (void *)clientin;
926 	    real_input_token.length = clientinlen;
927 	}
928 
929 	maj_stat =
930 	    gss_accept_sec_context(&min_stat,
931 				   &(text->gss_ctx),
932 				   text->server_creds,
933 				   input_token,
934 				   GSS_C_NO_CHANNEL_BINDINGS,
935 				   &text->client_name,
936 				   NULL,
937 				   output_token,
938 				   NULL,
939 				   NULL,
940 				   NULL);
941 
942 	if (GSS_ERROR(maj_stat)) {
943 #ifdef _SUN_SDK_
944 	    /* log the local error info, set a more generic error */
945 	    sasl_gss_log(text->utils, maj_stat, min_stat);
946 	    text->utils->seterror(text->utils->conn, SASL_NOLOG,
947 		    gettext("GSSAPI Failure: accept security context error"));
948 	    if (output_token->value) {
949 		gss_release_buffer(&min_stat, output_token);
950 	    }
951 #else
952 	    if (output_token->value) {
953 		gss_release_buffer(&min_stat, output_token);
954 	    }
955 	    text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context");
956 	    text->utils->log(NULL, SASL_LOG_DEBUG, "GSSAPI Failure: gss_accept_sec_context");
957 #endif /* _SUN_SDK_ */
958 	    sasl_gss_free_context_contents(text);
959 	    return SASL_BADAUTH;
960 	}
961 
962 	if (serveroutlen)
963 	    *serveroutlen = output_token->length;
964 	if (output_token->value) {
965 	    if (serverout) {
966 		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
967 				      &(text->out_buf_len), *serveroutlen);
968 		if(ret != SASL_OK) {
969 		    gss_release_buffer(&min_stat, output_token);
970 		    return ret;
971 		}
972 		memcpy(text->out_buf, output_token->value, *serveroutlen);
973 		*serverout = text->out_buf;
974 	    }
975 
976 	    gss_release_buffer(&min_stat, output_token);
977 	} else {
978 	    /* No output token, send an empty string */
979 	    *serverout = GSSAPI_BLANK_STRING;
980 #ifndef _SUN_SDK_
981 	    serveroutlen = 0;
982 #endif /* !_SUN_SDK_ */
983 	}
984 
985 
986 	if (maj_stat == GSS_S_COMPLETE) {
987 	    /* Switch to ssf negotiation */
988 	    text->state = SASL_GSSAPI_STATE_SSFCAP;
989 	}
990 
991 	return SASL_CONTINUE;
992 
993     case SASL_GSSAPI_STATE_SSFCAP: {
994 	unsigned char sasldata[4];
995 	gss_buffer_desc name_token;
996 #ifndef _SUN_SDK_
997 	gss_buffer_desc name_without_realm;
998 	gss_name_t without = NULL;
999 	int equal;
1000 #endif /* !_SUN_SDK_ */
1001 
1002 	name_token.value = NULL;
1003 #ifndef _SUN_SDK_
1004 	name_without_realm.value = NULL;
1005 #endif /* !_SUN_SDK_ */
1006 
1007 	/* We ignore whatever the client sent us at this stage */
1008 
1009 	maj_stat = gss_display_name (&min_stat,
1010 				     text->client_name,
1011 				     &name_token,
1012 				     NULL);
1013 
1014 	if (GSS_ERROR(maj_stat)) {
1015 #ifndef _SUN_SDK_
1016 	    if (name_without_realm.value)
1017 		params->utils->free(name_without_realm.value);
1018 #endif /* !_SUN_SDK_ */
1019 
1020 	    if (name_token.value)
1021 		gss_release_buffer(&min_stat, &name_token);
1022 #ifndef _SUN_SDK_
1023 	    if (without)
1024 		gss_release_name(&min_stat, &without);
1025 #endif /* !_SUN_SDK_ */
1026 #ifdef _INTEGRATED_SOLARIS_
1027 	    SETERROR(text->utils, gettext("GSSAPI Failure"));
1028 #else
1029 	    SETERROR(text->utils, "GSSAPI Failure");
1030 #endif /* _INTEGRATED_SOLARIS_ */
1031 	    sasl_gss_free_context_contents(text);
1032 	    return SASL_BADAUTH;
1033 	}
1034 
1035 #ifndef _SUN_SDK_
1036 	/* If the id contains a realm get the identifier for the user
1037 	   without the realm and see if it's the same id (i.e.
1038 	   tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want
1039 	   to return the id (i.e. just "tmartin" */
1040 	if (strchr((char *) name_token.value, (int) '@') != NULL) {
1041 	    /* NOTE: libc malloc, as it is freed below by a gssapi internal
1042 	     *       function! */
1043 	    name_without_realm.value = malloc(strlen(name_token.value)+1);
1044 	    if (name_without_realm.value == NULL) {
1045 		MEMERROR(text->utils);
1046 		return SASL_NOMEM;
1047 	    }
1048 
1049 	    strcpy(name_without_realm.value, name_token.value);
1050 
1051 	    /* cut off string at '@' */
1052 	    (strchr(name_without_realm.value,'@'))[0] = '\0';
1053 
1054 	    name_without_realm.length = strlen( (char *) name_without_realm.value );
1055 
1056 	    maj_stat = gss_import_name (&min_stat,
1057 					&name_without_realm,
1058 	    /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here,
1059 	       so use GSS_C_NT_USER_NAME instead if available.  */
1060 #ifdef HAVE_GSS_C_NT_USER_NAME
1061 					GSS_C_NT_USER_NAME,
1062 #else
1063 					GSS_C_NULL_OID,
1064 #endif
1065 					&without);
1066 
1067 	    if (GSS_ERROR(maj_stat)) {
1068 		params->utils->free(name_without_realm.value);
1069 		if (name_token.value)
1070 		    gss_release_buffer(&min_stat, &name_token);
1071 		if (without)
1072 		    gss_release_name(&min_stat, &without);
1073 		SETERROR(text->utils, "GSSAPI Failure");
1074 		sasl_gss_free_context_contents(text);
1075 		return SASL_BADAUTH;
1076 	    }
1077 
1078 	    maj_stat = gss_compare_name(&min_stat,
1079 					text->client_name,
1080 					without,
1081 					&equal);
1082 
1083 	    if (GSS_ERROR(maj_stat)) {
1084 		params->utils->free(name_without_realm.value);
1085 		if (name_token.value)
1086 		    gss_release_buffer(&min_stat, &name_token);
1087 		if (without)
1088 		    gss_release_name(&min_stat, &without);
1089 		SETERROR(text->utils, "GSSAPI Failure");
1090 		sasl_gss_free_context_contents(text);
1091 		return SASL_BADAUTH;
1092 	    }
1093 
1094 	    gss_release_name(&min_stat,&without);
1095 	} else {
1096 	    equal = 0;
1097 	}
1098 
1099 	if (equal) {
1100 	    text->authid = strdup(name_without_realm.value);
1101 
1102 	    if (text->authid == NULL) {
1103 		MEMERROR(params->utils);
1104 		return SASL_NOMEM;
1105 	    }
1106 	} else {
1107 	    text->authid = strdup(name_token.value);
1108 
1109 	    if (text->authid == NULL) {
1110 		MEMERROR(params->utils);
1111 		return SASL_NOMEM;
1112 	    }
1113 	}
1114 #else
1115 	{
1116 	    ret = _plug_strdup(params->utils, name_token.value,
1117 		&text->authid, NULL);
1118 	}
1119 #endif /* _SUN_SDK_ */
1120 
1121 	if (name_token.value)
1122 	    gss_release_buffer(&min_stat, &name_token);
1123 
1124 #ifdef _SUN_SDK_
1125 	if (ret != SASL_OK)
1126 	    return (ret);
1127 #else
1128 	if (name_without_realm.value)
1129 	    params->utils->free(name_without_realm.value);
1130 #endif /* _SUN_SDK_ */
1131 
1132 
1133 	/* we have to decide what sort of encryption/integrity/etc.,
1134 	   we support */
1135 	if (params->props.max_ssf < params->external_ssf) {
1136 	    text->limitssf = 0;
1137 	} else {
1138 	    text->limitssf = params->props.max_ssf - params->external_ssf;
1139 	}
1140 	if (params->props.min_ssf < params->external_ssf) {
1141 	    text->requiressf = 0;
1142 	} else {
1143 	    text->requiressf = params->props.min_ssf - params->external_ssf;
1144 	}
1145 
1146 	/* build up our security properties token */
1147         if (params->props.maxbufsize > 0xFFFFFF) {
1148             /* make sure maxbufsize isn't too large */
1149             /* maxbufsize = 0xFFFFFF */
1150             sasldata[1] = sasldata[2] = sasldata[3] = 0xFF;
1151         } else {
1152             sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF;
1153             sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF;
1154             sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF;
1155         }
1156 	sasldata[0] = 0;
1157 	if(text->requiressf != 0 && !params->props.maxbufsize) {
1158 #ifdef _SUN_SDK_
1159 	    params->utils->log(params->utils->conn, SASL_LOG_ERR,
1160 		"GSSAPI needs a security layer but one is forbidden");
1161 #else
1162 	    params->utils->seterror(params->utils->conn, 0,
1163 				    "GSSAPI needs a security layer but one is forbidden");
1164 #endif /* _SUN_SDK_ */
1165 	    return SASL_TOOWEAK;
1166 	}
1167 
1168 	if (text->requiressf == 0) {
1169 	    sasldata[0] |= 1; /* authentication */
1170 	}
1171 	if (text->requiressf <= 1 && text->limitssf >= 1
1172 	    && params->props.maxbufsize) {
1173 	    sasldata[0] |= 2;
1174 	}
1175 	if (text->requiressf <= 56 && text->limitssf >= 56
1176 	    && params->props.maxbufsize) {
1177 	    sasldata[0] |= 4;
1178 	}
1179 
1180 	real_input_token.value = (void *)sasldata;
1181 	real_input_token.length = 4;
1182 
1183 	maj_stat = gss_wrap(&min_stat,
1184 			    text->gss_ctx,
1185 			    0, /* Just integrity checking here */
1186 			    GSS_C_QOP_DEFAULT,
1187 			    input_token,
1188 			    NULL,
1189 			    output_token);
1190 
1191 	if (GSS_ERROR(maj_stat)) {
1192 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
1193 	    if (output_token->value)
1194 		gss_release_buffer(&min_stat, output_token);
1195 	    sasl_gss_free_context_contents(text);
1196 	    return SASL_FAIL;
1197 	}
1198 
1199 
1200 	if (serveroutlen)
1201 	    *serveroutlen = output_token->length;
1202 	if (output_token->value) {
1203 	    if (serverout) {
1204 		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1205 				      &(text->out_buf_len), *serveroutlen);
1206 		if(ret != SASL_OK) {
1207 		    gss_release_buffer(&min_stat, output_token);
1208 		    return ret;
1209 		}
1210 		memcpy(text->out_buf, output_token->value, *serveroutlen);
1211 		*serverout = text->out_buf;
1212 	    }
1213 
1214 	    gss_release_buffer(&min_stat, output_token);
1215 	}
1216 
1217 	/* Wait for ssf request and authid */
1218 	text->state = SASL_GSSAPI_STATE_SSFREQ;
1219 
1220 	return SASL_CONTINUE;
1221     }
1222 
1223     case SASL_GSSAPI_STATE_SSFREQ: {
1224 	int layerchoice;
1225 
1226 	real_input_token.value = (void *)clientin;
1227 	real_input_token.length = clientinlen;
1228 
1229 	maj_stat = gss_unwrap(&min_stat,
1230 			      text->gss_ctx,
1231 			      input_token,
1232 			      output_token,
1233 			      NULL,
1234 			      NULL);
1235 
1236 	if (GSS_ERROR(maj_stat)) {
1237 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
1238 	    sasl_gss_free_context_contents(text);
1239 	    return SASL_FAIL;
1240 	}
1241 
1242 	layerchoice = (int)(((char *)(output_token->value))[0]);
1243 	if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */
1244 	    oparams->encode = NULL;
1245 	    oparams->decode = NULL;
1246 	    oparams->mech_ssf = 0;
1247 	} else if (layerchoice == 2 && text->requiressf <= 1 &&
1248 		   text->limitssf >= 1) { /* integrity */
1249 	    oparams->encode=&gssapi_integrity_encode;
1250 	    oparams->decode=&gssapi_decode;
1251 	    oparams->mech_ssf=1;
1252 	} else if (layerchoice == 4 && text->requiressf <= 56 &&
1253 		   text->limitssf >= 56) { /* privacy */
1254 	    oparams->encode = &gssapi_privacy_encode;
1255 	    oparams->decode = &gssapi_decode;
1256 	    oparams->mech_ssf = 56;
1257 	} else {
1258 	    /* not a supported encryption layer */
1259 #ifdef _SUN_SDK_
1260 	    text->utils->log(text->utils->conn, SASL_LOG_ERR,
1261 		"protocol violation: client requested invalid layer");
1262 #else
1263 	    SETERROR(text->utils,
1264 		     "protocol violation: client requested invalid layer");
1265 #endif /* _SUN_SDK_ */
1266 	    /* Mark that we attempted negotiation */
1267 	    oparams->mech_ssf = 2;
1268 	    if (output_token->value)
1269 		gss_release_buffer(&min_stat, output_token);
1270 	    sasl_gss_free_context_contents(text);
1271 	    return SASL_FAIL;
1272 	}
1273 
1274 	if (output_token->length > 4) {
1275 	    int ret;
1276 
1277 	    ret = params->canon_user(params->utils->conn,
1278 				     ((char *) output_token->value) + 4,
1279 				     (output_token->length - 4) * sizeof(char),
1280 				     SASL_CU_AUTHZID, oparams);
1281 
1282 	    if (ret != SASL_OK) {
1283 		sasl_gss_free_context_contents(text);
1284 		return ret;
1285 	    }
1286 
1287 	    ret = params->canon_user(params->utils->conn,
1288 				     text->authid,
1289 				     0, /* strlen(text->authid) */
1290 				     SASL_CU_AUTHID, oparams);
1291 	    if (ret != SASL_OK) {
1292 		sasl_gss_free_context_contents(text);
1293 		return ret;
1294 	    }
1295 	} else if(output_token->length == 4) {
1296 	    /* null authzid */
1297 	    int ret;
1298 
1299 	    ret = params->canon_user(params->utils->conn,
1300 				     text->authid,
1301 				     0, /* strlen(text->authid) */
1302 				     SASL_CU_AUTHZID | SASL_CU_AUTHID,
1303 				     oparams);
1304 
1305 	    if (ret != SASL_OK) {
1306 		sasl_gss_free_context_contents(text);
1307 		return ret;
1308 	    }
1309 	} else {
1310 #ifdef _SUN_SDK_
1311 	    text->utils->log(text->utils->conn, SASL_LOG_ERR,
1312 	    		     "token too short");
1313 #else
1314 	    SETERROR(text->utils,
1315 		     "token too short");
1316 #endif /* _SUN_SDK_ */
1317 	    gss_release_buffer(&min_stat, output_token);
1318 	    sasl_gss_free_context_contents(text);
1319 	    return SASL_FAIL;
1320 	}
1321 
1322 	/* No matter what, set the rest of the oparams */
1323         oparams->maxoutbuf =
1324 	    (((unsigned char *) output_token->value)[1] << 16) |
1325             (((unsigned char *) output_token->value)[2] << 8) |
1326             (((unsigned char *) output_token->value)[3] << 0);
1327 
1328 #ifdef _SUN_SDK_
1329 	if (oparams->mech_ssf) {
1330 	    oparams->maxoutbuf -= 4;	/* Allow for 4 byte tag */
1331 	    maj_stat = gss_wrap_size_limit(&min_stat,
1332 					text->gss_ctx,
1333 					oparams->mech_ssf > 1,
1334 					GSS_C_QOP_DEFAULT,
1335 					oparams->maxoutbuf,
1336 					&max_input_size);
1337 	    if (GSS_ERROR(maj_stat)) {
1338 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
1339 		(void) gss_release_buffer(&min_stat, output_token);
1340 		sasl_gss_free_context_contents(text);
1341 		return (SASL_FAIL);
1342 	    }
1343 
1344 	    /*
1345 	     * gss_wrap_size_limit will return very big sizes for
1346 	     * small input values
1347 	     */
1348 	    if (max_input_size < oparams->maxoutbuf)
1349  		oparams->maxoutbuf = max_input_size;
1350 	    else {
1351 		oparams->maxoutbuf = 0;
1352 	    }
1353 	}
1354 #else
1355 	if (oparams->mech_ssf) {
1356 	    /* xxx this is probably too big */
1357 	    oparams->maxoutbuf -= 50;
1358 	}
1359 #endif /* _SUN_SDK_ */
1360 
1361 	gss_release_buffer(&min_stat, output_token);
1362 
1363 	text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
1364 
1365 	oparams->doneflag = 1;
1366 
1367 	return SASL_OK;
1368     }
1369 
1370     default:
1371 #ifdef _SUN_SDK_
1372 	params->utils->log(text->utils->conn, SASL_LOG_ERR,
1373 			   "Invalid GSSAPI server step %d", text->state);
1374 #else
1375 	params->utils->log(NULL, SASL_LOG_ERR,
1376 			   "Invalid GSSAPI server step %d\n", text->state);
1377 #endif /* _SUN_SDK_ */
1378 	return SASL_FAIL;
1379     }
1380 
1381 #ifndef _SUN_SDK_
1382     return SASL_FAIL; /* should never get here */
1383 #endif /* !_SUN_SDK_ */
1384 }
1385 
1386 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1387 static int
1388 _gssapi_server_mech_step(void *conn_context,
1389 			sasl_server_params_t *params,
1390 			const char *clientin,
1391 			unsigned clientinlen,
1392 			const char **serverout,
1393 			unsigned *serveroutlen,
1394 			sasl_out_params_t *oparams)
1395 {
1396     int ret;
1397 
1398     if (LOCK_MUTEX(&global_mutex) < 0)
1399 	return (SASL_FAIL);
1400 
1401     ret = gssapi_server_mech_step(conn_context, params, clientin, clientinlen,
1402 	serverout, serveroutlen, oparams);
1403 
1404     UNLOCK_MUTEX(&global_mutex);
1405     return (ret);
1406 }
1407 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1408 
1409 static sasl_server_plug_t gssapi_server_plugins[] =
1410 {
1411     {
1412 	"GSSAPI",			/* mech_name */
1413 	56,				/* max_ssf */
1414 	SASL_SEC_NOPLAINTEXT
1415 	| SASL_SEC_NOACTIVE
1416 	| SASL_SEC_NOANONYMOUS
1417 	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
1418 	SASL_FEAT_WANT_CLIENT_FIRST
1419 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
1420 	NULL,				/* glob_context */
1421 	&gssapi_server_mech_new,	/* mech_new */
1422 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1423 	&_gssapi_server_mech_step,	/* mech_step */
1424 #else
1425 	&gssapi_server_mech_step,	/* mech_step */
1426 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1427 	&gssapi_common_mech_dispose,	/* mech_dispose */
1428 	NULL,				/* mech_free */
1429 	NULL,				/* setpass */
1430 	NULL,				/* user_query */
1431 	NULL,				/* idle */
1432 	NULL,				/* mech_avail */
1433 	NULL				/* spare */
1434     }
1435 };
1436 
1437 int gssapiv2_server_plug_init(
1438 #ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1439     const sasl_utils_t *utils __attribute__((unused)),
1440 #else
1441     const sasl_utils_t *utils,
1442 #endif
1443     int maxversion,
1444     int *out_version,
1445     sasl_server_plug_t **pluglist,
1446     int *plugcount)
1447 {
1448 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1449     const char *keytab = NULL;
1450     char keytab_path[1024];
1451     unsigned int rl;
1452 #endif
1453 
1454     if (maxversion < SASL_SERVER_PLUG_VERSION) {
1455 	return SASL_BADVERS;
1456     }
1457 
1458 #ifndef _SUN_SDK_
1459 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1460     /* unfortunately, we don't check for readability of keytab if it's
1461        the standard one, since we don't know where it is */
1462 
1463     /* FIXME: This code is broken */
1464 
1465     utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl);
1466     if (keytab != NULL) {
1467 	if (access(keytab, R_OK) != 0) {
1468 	    utils->log(NULL, SASL_LOG_ERR,
1469 		       "Could not find keytab file: %s: %m",
1470 		       keytab, errno);
1471 	    return SASL_FAIL;
1472 	}
1473 
1474 	if(strlen(keytab) > 1024) {
1475 	    utils->log(NULL, SASL_LOG_ERR,
1476 		       "path to keytab is > 1024 characters");
1477 	    return SASL_BUFOVER;
1478 	}
1479 
1480 	strncpy(keytab_path, keytab, 1024);
1481 
1482 	gsskrb5_register_acceptor_identity(keytab_path);
1483     }
1484 #endif
1485 #endif /* !_SUN_SDK_ */
1486 
1487 #ifdef _INTEGRATED_SOLARIS_
1488     /*
1489      * Let libsasl know that we are a "Sun" plugin so that privacy
1490      * and integrity will be allowed.
1491      */
1492     REG_PLUG("GSSAPI", gssapi_server_plugins);
1493 #endif /* _INTEGRATED_SOLARIS_ */
1494 
1495     *out_version = SASL_SERVER_PLUG_VERSION;
1496     *pluglist = gssapi_server_plugins;
1497     *plugcount = 1;
1498 
1499     return SASL_OK;
1500 }
1501 
1502 /*****************************  Client Section  *****************************/
1503 
1504 static int gssapi_client_mech_new(void *glob_context __attribute__((unused)),
1505 				  sasl_client_params_t *params,
1506 				  void **conn_context)
1507 {
1508     context_t *text;
1509 #ifdef _SUN_SDK_
1510     const char *use_authid = NULL;
1511 #endif /* _SUN_SDK_ */
1512 
1513     /* holds state are in */
1514 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1515     if (LOCK_MUTEX(&global_mutex) < 0)
1516 	return (SASL_FAIL);
1517 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1518     text = gss_new_context(params->utils);
1519 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1520     UNLOCK_MUTEX(&global_mutex);
1521 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1522     if (text == NULL) {
1523 #ifndef _SUN_SDK_
1524 	MEMERROR(params->utils);
1525 #endif /* !_SUN_SDK_ */
1526 	return SASL_NOMEM;
1527     }
1528 
1529     text->state = SASL_GSSAPI_STATE_AUTHNEG;
1530     text->gss_ctx = GSS_C_NO_CONTEXT;
1531     text->client_name = GSS_C_NO_NAME;
1532     text->server_creds = GSS_C_NO_CREDENTIAL;
1533 
1534 #ifdef _SUN_SDK_
1535     params->utils->getopt(params->utils->getopt_context,
1536 			  "GSSAPI", "use_authid", &use_authid, NULL);
1537     text->use_authid = (use_authid != NULL) &&
1538 	(*use_authid == 'y' || *use_authid == 'Y' || *use_authid == '1');
1539 #endif /* _SUN_SDK_ */
1540 
1541     *conn_context = text;
1542 
1543     return SASL_OK;
1544 }
1545 
1546 static int gssapi_client_mech_step(void *conn_context,
1547 				   sasl_client_params_t *params,
1548 				   const char *serverin,
1549 				   unsigned serverinlen,
1550 				   sasl_interact_t **prompt_need,
1551 				   const char **clientout,
1552 				   unsigned *clientoutlen,
1553 				   sasl_out_params_t *oparams)
1554 {
1555     context_t *text = (context_t *)conn_context;
1556     gss_buffer_t input_token, output_token;
1557     gss_buffer_desc real_input_token, real_output_token;
1558     OM_uint32 maj_stat, min_stat;
1559 #ifdef _SUN_SDK_
1560     OM_uint32 max_input_size;
1561 #endif /* _SUN_SDK_ */
1562     gss_buffer_desc name_token;
1563     int ret;
1564     OM_uint32 req_flags, out_req_flags;
1565     input_token = &real_input_token;
1566     output_token = &real_output_token;
1567     output_token->value = NULL;
1568     input_token->value = NULL;
1569     input_token->length = 0;
1570 
1571     *clientout = NULL;
1572     *clientoutlen = 0;
1573 
1574     switch (text->state) {
1575 
1576     case SASL_GSSAPI_STATE_AUTHNEG:
1577 	/* try to get the userid */
1578 #ifdef _SUN_SDK_
1579 	if (text->user == NULL ||
1580 		(text->use_authid && text->client_authid == NULL)) {
1581 	    int auth_result = SASL_OK;
1582 	    int user_result = SASL_OK;
1583 
1584 	    if (text->use_authid && text->client_authid == NULL) {
1585 		auth_result = _plug_get_authid(params->utils,
1586 					       &text->client_authid,
1587 					       prompt_need);
1588 
1589 		if ((auth_result != SASL_OK) &&
1590 			(auth_result != SASL_INTERACT)) {
1591 		    sasl_gss_free_context_contents(text);
1592 		    return auth_result;
1593 		}
1594 	    }
1595 	    if (text->user == NULL) {
1596 		user_result = _plug_get_userid(params->utils, &text->user,
1597 					       prompt_need);
1598 
1599 		if ((user_result != SASL_OK) &&
1600 			(user_result != SASL_INTERACT)) {
1601 		    sasl_gss_free_context_contents(text);
1602 		    return user_result;
1603 		}
1604 	    }
1605 #else
1606 	if (text->user == NULL) {
1607 	    int user_result = SASL_OK;
1608 
1609 	    user_result = _plug_get_userid(params->utils, &text->user,
1610 					   prompt_need);
1611 
1612 	    if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
1613 		sasl_gss_free_context_contents(text);
1614 		return user_result;
1615 	    }
1616 #endif /* _SUN_SDK_ */
1617 
1618 	    /* free prompts we got */
1619 	    if (prompt_need && *prompt_need) {
1620 		params->utils->free(*prompt_need);
1621 		*prompt_need = NULL;
1622 	    }
1623 
1624 	    /* if there are prompts not filled in */
1625 #ifdef _SUN_SDK_
1626 	    if ((user_result == SASL_INTERACT) ||
1627 			(auth_result == SASL_INTERACT)) {
1628 		/* make the prompt list */
1629 #ifdef _INTEGRATED_SOLARIS_
1630 		int result = _plug_make_prompts(params->utils, &text->h,
1631 			   prompt_need,
1632 			   user_result == SASL_INTERACT ?
1633 			   convert_prompt(params->utils, &text->h,
1634 			    gettext("Please enter your authorization name"))
1635 				: NULL, NULL,
1636 			   auth_result == SASL_INTERACT ?
1637 			   convert_prompt(params->utils, &text->h,
1638 			    gettext("Please enter your authentication name"))
1639 				: NULL, NULL,
1640 			   NULL, NULL,
1641 			   NULL, NULL, NULL,
1642 			   NULL, NULL, NULL);
1643 #else
1644 		int result = _plug_make_prompts(params->utils, prompt_need,
1645 			   user_result == SASL_INTERACT ?
1646 			   	"Please enter your authorization name"
1647 				: NULL, NULL,
1648 			   auth_result == SASL_INTERACT ?
1649 			   	"Please enter your authentication name"
1650 				: NULL, NULL,
1651 			   NULL, NULL,
1652 			   NULL, NULL, NULL,
1653 			   NULL, NULL, NULL);
1654 #endif /* _INTEGRATED_SOLARIS_ */
1655 
1656 		if (result != SASL_OK) return result;
1657 
1658 		return SASL_INTERACT;
1659 	    }
1660 #else
1661 	    if (user_result == SASL_INTERACT) {
1662 		/* make the prompt list */
1663 		int result =
1664 		    _plug_make_prompts(params->utils, prompt_need,
1665 				       user_result == SASL_INTERACT ?
1666 				       "Please enter your authorization name" : NULL, NULL,
1667 				       NULL, NULL,
1668 				       NULL, NULL,
1669 				       NULL, NULL, NULL,
1670 				       NULL, NULL, NULL);
1671 		if (result != SASL_OK) return result;
1672 
1673 		return SASL_INTERACT;
1674 	    }
1675 #endif /* _SUN_SDK_ */
1676 	}
1677 
1678 	if (text->server_name == GSS_C_NO_NAME) { /* only once */
1679 	    name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
1680 	    name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
1681 	    if (name_token.value == NULL) {
1682 		sasl_gss_free_context_contents(text);
1683 		return SASL_NOMEM;
1684 	    }
1685 	    if (params->serverFQDN == NULL
1686 		|| strlen(params->serverFQDN) == 0) {
1687 #ifdef _SUN_SDK_
1688 		text->utils->log(text->utils->conn, SASL_LOG_ERR,
1689 				 "GSSAPI Failure: no serverFQDN");
1690 #else
1691 		SETERROR(text->utils, "GSSAPI Failure: no serverFQDN");
1692 #endif /* _SUN_SDK_ */
1693 		return SASL_FAIL;
1694 	    }
1695 
1696 #ifdef _SUN_SDK_
1697 	    snprintf(name_token.value, name_token.length + 1,
1698 		"%s@%s", params->service, params->serverFQDN);
1699 #else
1700 	    sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
1701 #endif /* _SUN_SDK_ */
1702 
1703 	    maj_stat = gss_import_name (&min_stat,
1704 					&name_token,
1705 					GSS_C_NT_HOSTBASED_SERVICE,
1706 					&text->server_name);
1707 
1708 	    params->utils->free(name_token.value);
1709 	    name_token.value = NULL;
1710 
1711 	    if (GSS_ERROR(maj_stat)) {
1712 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
1713 		sasl_gss_free_context_contents(text);
1714 		return SASL_FAIL;
1715 	    }
1716 	}
1717 
1718 	if (serverinlen == 0)
1719 	    input_token = GSS_C_NO_BUFFER;
1720 
1721 	if (serverinlen) {
1722 	    real_input_token.value = (void *)serverin;
1723 	    real_input_token.length = serverinlen;
1724 	}
1725 	else if (text->gss_ctx != GSS_C_NO_CONTEXT ) {
1726 	    /* This can't happen under GSSAPI: we have a non-null context
1727 	     * and no input from the server.  However, thanks to Imap,
1728 	     * which discards our first output, this happens all the time.
1729 	     * Throw away the context and try again. */
1730 	    maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
1731 	    text->gss_ctx = GSS_C_NO_CONTEXT;
1732 	}
1733 
1734 	/* Setup req_flags properly */
1735 	req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
1736 	if(params->props.max_ssf > params->external_ssf) {
1737 	    /* We are requesting a security layer */
1738 	    req_flags |= GSS_C_INTEG_FLAG;
1739 	    if(params->props.max_ssf - params->external_ssf > 56) {
1740 		/* We want to try for privacy */
1741 		req_flags |= GSS_C_CONF_FLAG;
1742 	    }
1743 	}
1744 
1745 #ifdef _SUN_SDK_
1746 	if (text->use_authid && text->client_creds == GSS_C_NO_CREDENTIAL) {
1747 	    gss_OID_set desired_mechs = GSS_C_NULL_OID_SET;
1748 	    gss_buffer_desc name_token;
1749 
1750 	    name_token.length = strlen(text->client_authid);
1751 	    name_token.value = (char *)text->client_authid;
1752 
1753 	    maj_stat = gss_import_name (&min_stat,
1754 					&name_token,
1755 #ifdef HAVE_GSS_C_NT_USER_NAME
1756 					GSS_C_NT_USER_NAME,
1757 #else
1758 					GSS_C_NULL_OID,
1759 #endif
1760 					&text->client_name);
1761 	    if (GSS_ERROR(maj_stat)) {
1762 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
1763 		sasl_gss_free_context_contents(text);
1764 		return SASL_FAIL;
1765 	    }
1766 
1767 	    if (text->mech_oid != GSS_C_NULL_OID) {
1768 		ret = add_mech_to_set(text, &desired_mechs);
1769 		if (ret != SASL_OK)
1770 		    return (ret);
1771 	    }
1772 
1773 	    maj_stat = gss_acquire_cred(&min_stat,
1774 					text->client_name,
1775 					GSS_C_INDEFINITE,
1776 					desired_mechs,
1777 					GSS_C_INITIATE,
1778 					&text->client_creds,
1779 					NULL,
1780 					NULL);
1781 
1782 	    if (desired_mechs != GSS_C_NULL_OID_SET) {
1783 		OM_uint32 min_stat2;
1784 		(void) gss_release_oid_set(&min_stat2, &desired_mechs);
1785 	    }
1786 
1787 	    if (GSS_ERROR(maj_stat)) {
1788 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
1789 		sasl_gss_free_context_contents(text);
1790 		return SASL_FAIL;
1791 	    }
1792 	}
1793 #endif /* _SUN_SDK_ */
1794 
1795 	maj_stat = gss_init_sec_context(&min_stat,
1796 #ifdef _SUN_SDK_
1797 					text->client_creds,
1798 #else
1799 					GSS_C_NO_CREDENTIAL,
1800 #endif /* _SUN_SDK_ */
1801 					&text->gss_ctx,
1802 					text->server_name,
1803 #ifdef _SUN_SDK_
1804 					text->mech_oid,
1805 #else
1806 					GSS_C_NO_OID,
1807 #endif /* _SUN_SDK_ */
1808 					req_flags,
1809 					0,
1810 					GSS_C_NO_CHANNEL_BINDINGS,
1811 					input_token,
1812 					NULL,
1813 					output_token,
1814 					&out_req_flags,
1815 					NULL);
1816 
1817 	if (GSS_ERROR(maj_stat)) {
1818 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
1819 	    if (output_token->value)
1820 		gss_release_buffer(&min_stat, output_token);
1821 	    sasl_gss_free_context_contents(text);
1822 	    return SASL_FAIL;
1823 	}
1824 
1825 	*clientoutlen = output_token->length;
1826 
1827 	if (output_token->value) {
1828 	    if (clientout) {
1829 		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1830 				      &(text->out_buf_len), *clientoutlen);
1831 		if(ret != SASL_OK) {
1832 		    gss_release_buffer(&min_stat, output_token);
1833 		    return ret;
1834 		}
1835 		memcpy(text->out_buf, output_token->value, *clientoutlen);
1836 		*clientout = text->out_buf;
1837 	    }
1838 
1839 	    gss_release_buffer(&min_stat, output_token);
1840 	}
1841 
1842 	if (maj_stat == GSS_S_COMPLETE) {
1843 	    maj_stat = gss_inquire_context(&min_stat,
1844 					   text->gss_ctx,
1845 					   &text->client_name,
1846 					   NULL,       /* targ_name */
1847 					   NULL,       /* lifetime */
1848 					   NULL,       /* mech */
1849 					   NULL,       /* flags */
1850 					   NULL,       /* local init */
1851 					   NULL);      /* open */
1852 
1853 	    if (GSS_ERROR(maj_stat)) {
1854 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
1855 		sasl_gss_free_context_contents(text);
1856 		return SASL_FAIL;
1857 	    }
1858 
1859 	    name_token.length = 0;
1860 	    maj_stat = gss_display_name(&min_stat,
1861 					text->client_name,
1862 					&name_token,
1863 					NULL);
1864 
1865 	    if (GSS_ERROR(maj_stat)) {
1866 		if (name_token.value)
1867 		    gss_release_buffer(&min_stat, &name_token);
1868 #ifdef _INTEGRATED_SOLARIS_
1869 		SETERROR(text->utils, gettext("GSSAPI Failure"));
1870 #else
1871 		SETERROR(text->utils, "GSSAPI Failure");
1872 #endif /* _INTEGRATED_SOLARIS_ */
1873 		sasl_gss_free_context_contents(text);
1874 		return SASL_FAIL;
1875 	    }
1876 
1877 	    if (text->user && text->user[0]) {
1878 		ret = params->canon_user(params->utils->conn,
1879 					 text->user, 0,
1880 					 SASL_CU_AUTHZID, oparams);
1881 		if (ret == SASL_OK)
1882 		    ret = params->canon_user(params->utils->conn,
1883 					     name_token.value, 0,
1884 					     SASL_CU_AUTHID, oparams);
1885 	    } else {
1886 		ret = params->canon_user(params->utils->conn,
1887 					 name_token.value, 0,
1888 					 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1889 					 oparams);
1890 	    }
1891 	    gss_release_buffer(&min_stat, &name_token);
1892 
1893 	    if (ret != SASL_OK) return ret;
1894 
1895 	    /* Switch to ssf negotiation */
1896 	    text->state = SASL_GSSAPI_STATE_SSFCAP;
1897 	}
1898 
1899 	return SASL_CONTINUE;
1900 
1901     case SASL_GSSAPI_STATE_SSFCAP: {
1902 	sasl_security_properties_t *secprops = &(params->props);
1903 	unsigned int alen, external = params->external_ssf;
1904 	sasl_ssf_t need, allowed;
1905 	char serverhas, mychoice;
1906 
1907 	real_input_token.value = (void *) serverin;
1908 	real_input_token.length = serverinlen;
1909 
1910 	maj_stat = gss_unwrap(&min_stat,
1911 			      text->gss_ctx,
1912 			      input_token,
1913 			      output_token,
1914 			      NULL,
1915 			      NULL);
1916 
1917 	if (GSS_ERROR(maj_stat)) {
1918 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
1919 	    sasl_gss_free_context_contents(text);
1920 	    if (output_token->value)
1921 		gss_release_buffer(&min_stat, output_token);
1922 	    return SASL_FAIL;
1923 	}
1924 
1925 	/* taken from kerberos.c */
1926 	if (secprops->min_ssf > (56 + external)) {
1927 	    return SASL_TOOWEAK;
1928 	} else if (secprops->min_ssf > secprops->max_ssf) {
1929 	    return SASL_BADPARAM;
1930 	}
1931 
1932 	/* need bits of layer -- sasl_ssf_t is unsigned so be careful */
1933 	if (secprops->max_ssf >= external) {
1934 	    allowed = secprops->max_ssf - external;
1935 	} else {
1936 	    allowed = 0;
1937 	}
1938 	if (secprops->min_ssf >= external) {
1939 	    need = secprops->min_ssf - external;
1940 	} else {
1941 	    /* good to go */
1942 	    need = 0;
1943 	}
1944 
1945 	/* bit mask of server support */
1946 	serverhas = ((char *)output_token->value)[0];
1947 
1948 	/* if client didn't set use strongest layer available */
1949 	if (allowed >= 56 && need <= 56 && (serverhas & 4)) {
1950 	    /* encryption */
1951 	    oparams->encode = &gssapi_privacy_encode;
1952 	    oparams->decode = &gssapi_decode;
1953 	    oparams->mech_ssf = 56;
1954 	    mychoice = 4;
1955 	} else if (allowed >= 1 && need <= 1 && (serverhas & 2)) {
1956 	    /* integrity */
1957 	    oparams->encode = &gssapi_integrity_encode;
1958 	    oparams->decode = &gssapi_decode;
1959 	    oparams->mech_ssf = 1;
1960 	    mychoice = 2;
1961 #ifdef _SUN_SDK_
1962 	} else if (need == 0 && (serverhas & 1)) {
1963 #else
1964 	} else if (need <= 0 && (serverhas & 1)) {
1965 #endif /* _SUN_SDK_ */
1966 	    /* no layer */
1967 	    oparams->encode = NULL;
1968 	    oparams->decode = NULL;
1969 	    oparams->mech_ssf = 0;
1970 	    mychoice = 1;
1971 	} else {
1972 	    /* there's no appropriate layering for us! */
1973 	    sasl_gss_free_context_contents(text);
1974 	    return SASL_TOOWEAK;
1975 	}
1976 
1977         oparams->maxoutbuf =
1978 	    (((unsigned char *) output_token->value)[1] << 16) |
1979             (((unsigned char *) output_token->value)[2] << 8) |
1980             (((unsigned char *) output_token->value)[3] << 0);
1981 
1982 #ifdef _SUN_SDK_
1983 	if (oparams->mech_ssf > 0) {
1984 	    oparams->maxoutbuf -= 4;	/* Space for 4 byte length header */
1985 	    maj_stat = gss_wrap_size_limit(&min_stat,
1986 					text->gss_ctx,
1987 					oparams->mech_ssf > 1,
1988 					GSS_C_QOP_DEFAULT,
1989 					oparams->maxoutbuf,
1990 					&max_input_size);
1991 	    if (GSS_ERROR(maj_stat)) {
1992 		sasl_gss_seterror(text->utils, maj_stat, min_stat);
1993 		(void) gss_release_buffer(&min_stat, output_token);
1994 		sasl_gss_free_context_contents(text);
1995 		return (SASL_FAIL);
1996 	    }
1997 
1998 	/*
1999 	 * This is a workaround for a Solaris bug where
2000 	 * gss_wrap_size_limit may return very big sizes for
2001 	 * small input values
2002 	 */
2003 	    if (max_input_size < oparams->maxoutbuf)
2004  		oparams->maxoutbuf = max_input_size;
2005 	    else {
2006 		oparams->maxoutbuf = 0;
2007 	    }
2008 	}
2009 #else
2010 	if(oparams->mech_ssf) {
2011 	    /* xxx probably too large */
2012 	    oparams->maxoutbuf -= 50;
2013 	}
2014 #endif /* _SUN_SDK_ */
2015 
2016 	gss_release_buffer(&min_stat, output_token);
2017 
2018 	/* oparams->user is always set, due to canon_user requirements.
2019 	 * Make sure the client actually requested it though, by checking
2020 	 * if our context was set.
2021 	 */
2022 	if (text->user && text->user[0])
2023 	    alen = strlen(oparams->user);
2024 	else
2025 	    alen = 0;
2026 
2027 	input_token->length = 4 + alen;
2028 	input_token->value =
2029 	    (char *)params->utils->malloc((input_token->length + 1)*sizeof(char));
2030 	if (input_token->value == NULL) {
2031 	    sasl_gss_free_context_contents(text);
2032 	    return SASL_NOMEM;
2033 	}
2034 
2035 	if (alen)
2036 	    memcpy((char *)input_token->value+4,oparams->user,alen);
2037 
2038 	/* build up our security properties token */
2039         if (params->props.maxbufsize > 0xFFFFFF) {
2040             /* make sure maxbufsize isn't too large */
2041             /* maxbufsize = 0xFFFFFF */
2042             ((unsigned char *)input_token->value)[1] = 0xFF;
2043             ((unsigned char *)input_token->value)[2] = 0xFF;
2044             ((unsigned char *)input_token->value)[3] = 0xFF;
2045         } else {
2046             ((unsigned char *)input_token->value)[1] =
2047                 (params->props.maxbufsize >> 16) & 0xFF;
2048             ((unsigned char *)input_token->value)[2] =
2049                 (params->props.maxbufsize >> 8) & 0xFF;
2050             ((unsigned char *)input_token->value)[3] =
2051                 (params->props.maxbufsize >> 0) & 0xFF;
2052         }
2053 	((unsigned char *)input_token->value)[0] = mychoice;
2054 
2055 	maj_stat = gss_wrap (&min_stat,
2056 			     text->gss_ctx,
2057 			     0, /* Just integrity checking here */
2058 			     GSS_C_QOP_DEFAULT,
2059 			     input_token,
2060 			     NULL,
2061 			     output_token);
2062 
2063 	params->utils->free(input_token->value);
2064 	input_token->value = NULL;
2065 
2066 	if (GSS_ERROR(maj_stat)) {
2067 	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
2068 	    if (output_token->value)
2069 		gss_release_buffer(&min_stat, output_token);
2070 	    sasl_gss_free_context_contents(text);
2071 	    return SASL_FAIL;
2072 	}
2073 
2074 	if (clientoutlen)
2075 	    *clientoutlen = output_token->length;
2076 	if (output_token->value) {
2077 	    if (clientout) {
2078 		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
2079 				      &(text->out_buf_len), *clientoutlen);
2080 		if (ret != SASL_OK) {
2081 		    gss_release_buffer(&min_stat, output_token);
2082 		    return ret;
2083 		}
2084 		memcpy(text->out_buf, output_token->value, *clientoutlen);
2085 		*clientout = text->out_buf;
2086 	    }
2087 
2088 	    gss_release_buffer(&min_stat, output_token);
2089 	}
2090 
2091 	text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
2092 
2093 	oparams->doneflag = 1;
2094 
2095 	return SASL_OK;
2096     }
2097 
2098     default:
2099 #ifdef _SUN_SDK_
2100 	params->utils->log(params->utils->conn, SASL_LOG_ERR,
2101 			   "Invalid GSSAPI client step %d", text->state);
2102 #else
2103 	params->utils->log(NULL, SASL_LOG_ERR,
2104 			   "Invalid GSSAPI client step %d\n", text->state);
2105 #endif /* _SUN_SDK_ */
2106 	return SASL_FAIL;
2107     }
2108 
2109 #ifndef _SUN_SDK_
2110     return SASL_FAIL; /* should never get here */
2111 #endif /* !_SUN_SDK_ */
2112 }
2113 
2114 #ifdef _SUN_SDK_
2115 static const unsigned long gssapi_required_prompts[] = {
2116 #else
2117 static const long gssapi_required_prompts[] = {
2118 #endif /* _SUN_SDK_ */
2119     SASL_CB_LIST_END
2120 };
2121 
2122 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
2123 static int _gssapi_client_mech_step(void *conn_context,
2124 				   sasl_client_params_t *params,
2125 				   const char *serverin,
2126 				   unsigned serverinlen,
2127 				   sasl_interact_t **prompt_need,
2128 				   const char **clientout,
2129 				   unsigned *clientoutlen,
2130 				   sasl_out_params_t *oparams)
2131 {
2132     int ret;
2133 
2134     if (LOCK_MUTEX(&global_mutex) < 0)
2135 	return (SASL_FAIL);
2136 
2137     ret = gssapi_client_mech_step(conn_context, params, serverin, serverinlen,
2138 	prompt_need, clientout, clientoutlen, oparams);
2139 
2140     UNLOCK_MUTEX(&global_mutex);
2141     return (ret);
2142 }
2143 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
2144 
2145 static sasl_client_plug_t gssapi_client_plugins[] =
2146 {
2147     {
2148 	"GSSAPI",			/* mech_name */
2149 	56,				/* max_ssf */
2150 	SASL_SEC_NOPLAINTEXT
2151 	| SASL_SEC_NOACTIVE
2152 	| SASL_SEC_NOANONYMOUS
2153 	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
2154 	SASL_FEAT_WANT_CLIENT_FIRST
2155 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
2156 	gssapi_required_prompts,	/* required_prompts */
2157 	NULL,				/* glob_context */
2158 	&gssapi_client_mech_new,	/* mech_new */
2159 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
2160 	&_gssapi_client_mech_step,	/* mech_step */
2161 #else
2162 	&gssapi_client_mech_step,	/* mech_step */
2163 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
2164 	&gssapi_common_mech_dispose,	/* mech_dispose */
2165 	NULL,				/* mech_free */
2166 	NULL,				/* idle */
2167 	NULL,				/* spare */
2168 	NULL				/* spare */
2169     }
2170 };
2171 
2172 int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)),
2173 			      int maxversion,
2174 			      int *out_version,
2175 			      sasl_client_plug_t **pluglist,
2176 			      int *plugcount)
2177 {
2178     if (maxversion < SASL_CLIENT_PLUG_VERSION) {
2179 	SETERROR(utils, "Version mismatch in GSSAPI");
2180 	return SASL_BADVERS;
2181     }
2182 
2183 #ifdef _INTEGRATED_SOLARIS_
2184     /*
2185      * Let libsasl know that we are a "Sun" plugin so that privacy
2186      * and integrity will be allowed.
2187      */
2188     REG_PLUG("GSSAPI", gssapi_client_plugins);
2189 #endif /* _INTEGRATED_SOLARIS_ */
2190 
2191     *out_version = SASL_CLIENT_PLUG_VERSION;
2192     *pluglist = gssapi_client_plugins;
2193     *plugcount = 1;
2194 
2195     return SASL_OK;
2196 }
2197