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_
96static const char plugin_id[] = "$Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $";
97#endif /* !_SUN_SDK_ */
98
99static const char * GSSAPI_BLANK_STRING = "";
100
101#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
102extern gss_OID gss_nt_service_name;
103#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
104#endif
105
106#ifdef _SUN_SDK_
107static int
108get_oid(const sasl_utils_t *utils, gss_OID *oid);
109#ifdef GSSAPI_PROTECT
110DEFINE_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
124typedef 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
178enum {
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)
189static void
190sasl_gss_seterror_(const context_t *text, OM_uint32 maj, OM_uint32 min,
191	int logonly)
192#else
193static void
194sasl_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
353static int
354sasl_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
445static 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
452static 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
461static 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
603static 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
619static 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
645static 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
726static int
727get_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
738static int
739add_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
762static 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
784static int
785gssapi_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
819static int
820gssapi_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
1387static 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
1409static 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
1437int 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
1504static 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
1546static 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_
2115static const unsigned long gssapi_required_prompts[] = {
2116#else
2117static const long gssapi_required_prompts[] = {
2118#endif /* _SUN_SDK_ */
2119    SASL_CB_LIST_END
2120};
2121
2122#if defined _SUN_SDK_ && defined GSSAPI_PROTECT
2123static 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
2145static 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
2172int 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