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