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