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