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