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