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