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