fefa9af0b92ea4bb902431d179f584f6a363b05a
[cyrus-sasl.git] / plugins / gs2.c
1 /*
2  * Copyright 2010 PADL Software Pty Ltd. All rights reserved.
3  */
4 /*
5  * Portions Copyright (c) 1998-2003 Carnegie Mellon University.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The name "Carnegie Mellon University" must not be used to
21  *    endorse or promote products derived from this software without
22  *    prior written permission. For permission or any other legal
23  *    details, please contact
24  *      Office of Technology Transfer
25  *      Carnegie Mellon University
26  *      5000 Forbes Avenue
27  *      Pittsburgh, PA  15213-3890
28  *      (412) 268-4387, fax: (412) 268-7395
29  *      tech-transfer@andrew.cmu.edu
30  *
31  * 4. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by Computing Services
34  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35  *
36  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43  */
44
45 #include <config.h>
46 #include <gssapi/gssapi.h>
47 #include <gssapi/gssapi_ext.h>
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <sasl.h>
51 #include <saslutil.h>
52 #include <saslplug.h>
53
54 #include "plugin_common.h"
55
56 #ifdef HAVE_UNISTD_H
57 #include <unistd.h>
58 #endif
59
60 #include <errno.h>
61 #include <assert.h>
62 #include "gs2_token.h"
63
64 #define GS2_CB_FLAG_MASK    0x0F
65 #define GS2_CB_FLAG_P       SASL_CB_FLAG_USED
66 #define GS2_CB_FLAG_N       SASL_CB_FLAG_NONE
67 #define GS2_CB_FLAG_Y       SASL_CB_FLAG_WANT
68 #define GS2_NONSTD_FLAG     0x10
69
70 typedef struct context {
71     gss_ctx_id_t gss_ctx;
72     gss_name_t client_name;
73     gss_name_t server_name;
74     gss_cred_id_t server_creds;
75     gss_cred_id_t client_creds;
76     char *out_buf;
77     unsigned out_buf_len;
78     const sasl_utils_t *utils;
79     char *authid;
80     char *authzid;
81     union {
82         sasl_client_plug_t *client;
83         sasl_server_plug_t *server;
84     } plug;
85     gss_OID mechanism;
86     int gs2_flags;
87     char *cb_name;
88     struct gss_channel_bindings_struct bindings;
89     sasl_secret_t *password;
90     unsigned int free_password;
91     OM_uint32 lifetime;
92 } context_t;
93
94 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
95
96 static int gs2_ask_user_info(context_t *context,
97                              sasl_client_params_t *params,
98                              char **realms, int nrealm,
99                              sasl_interact_t **prompt_need,
100                              sasl_out_params_t *oparams);
101
102 static int gs2_verify_initial_message(context_t *text,
103                                       sasl_server_params_t *sparams,
104                                       const char *in,
105                                       unsigned inlen,
106                                       gss_buffer_t token);
107
108 static int gs2_make_header(context_t *text,
109                            sasl_client_params_t *cparams,
110                            const char *authzid,
111                            char **out,
112                            unsigned *outlen);
113
114 static int gs2_make_message(context_t *text,
115                             sasl_client_params_t *cparams,
116                             int initialContextToken,
117                             gss_buffer_t token,
118                             char **out,
119                             unsigned *outlen);
120
121 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
122                               const gss_OID mech,
123                               unsigned int *security_flags,
124                               unsigned int *features);
125
126 static int gs2_indicate_mechs(const sasl_utils_t *utils);
127
128 static int gs2_map_sasl_name(const sasl_utils_t *utils,
129                              const char *mech,
130                              gss_OID *oid);
131
132 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
133                                 const gss_buffer_t src,
134                                 gss_buffer_t dst);
135
136 static int gs2_unescape_authzid(const sasl_utils_t *utils,
137                                 char **in,
138                                 unsigned *inlen,
139                                 char **authzid);
140
141 static int gs2_escape_authzid(const sasl_utils_t *utils,
142                               const char *in,
143                               unsigned inlen,
144                               char **authzid);
145
146 /* sasl_gs_log: only logs status string returned from gss_display_status() */
147 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
148 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
149
150 static int
151 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
152                    int logonly);
153
154 static context_t *
155 sasl_gs2_new_context(const sasl_utils_t *utils)
156 {
157     context_t *ret;
158
159     ret = utils->malloc(sizeof(context_t));
160     if (!ret)
161         return NULL;
162
163     memset(ret, 0, sizeof(context_t));
164     ret->utils = utils;
165
166     return ret;
167 }
168
169 static int
170 sasl_gs2_free_context_contents(context_t *text)
171 {
172     OM_uint32 min_stat;
173
174     if (text == NULL)
175         return SASL_OK;
176
177     if (text->gss_ctx != GSS_C_NO_CONTEXT) {
178         gss_delete_sec_context(&min_stat,&text->gss_ctx,
179                                GSS_C_NO_BUFFER);
180         text->gss_ctx = GSS_C_NO_CONTEXT;
181     }
182
183     if (text->client_name != GSS_C_NO_NAME) {
184         gss_release_name(&min_stat,&text->client_name);
185         text->client_name = GSS_C_NO_NAME;
186     }
187
188     if (text->server_name != GSS_C_NO_NAME) {
189         gss_release_name(&min_stat,&text->server_name);
190         text->server_name = GSS_C_NO_NAME;
191     }
192
193     if (text->server_creds != GSS_C_NO_CREDENTIAL) {
194         gss_release_cred(&min_stat, &text->server_creds);
195         text->server_creds = GSS_C_NO_CREDENTIAL;
196     }
197
198     if (text->client_creds != GSS_C_NO_CREDENTIAL) {
199         gss_release_cred(&min_stat, &text->client_creds);
200         text->client_creds = GSS_C_NO_CREDENTIAL;
201     }
202
203     if (text->authid != NULL) {
204         text->utils->free(text->authid);
205         text->authid = NULL;
206     }
207
208     if (text->authzid != NULL) {
209         text->utils->free(text->authzid);
210         text->authzid = NULL;
211     }
212
213     if (text->mechanism != NULL) {
214         gss_release_oid(&min_stat, &text->mechanism);
215         text->mechanism = GSS_C_NO_OID;
216     }
217
218     gss_release_buffer(&min_stat, &text->bindings.application_data);
219
220     if (text->out_buf != NULL) {
221         text->utils->free(text->out_buf);
222         text->out_buf = NULL;
223     }
224
225     text->out_buf_len = 0;
226
227     if (text->cb_name != NULL) {
228         text->utils->free(text->cb_name);
229         text->cb_name = NULL;
230     }
231
232     if (text->free_password)
233         _plug_free_secret(text->utils, &text->password);
234
235     memset(text, 0, sizeof(*text));
236
237     return SASL_OK;
238 }
239
240 static void
241 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
242 {
243     sasl_gs2_free_context_contents((context_t *)(conn_context));
244     utils->free(conn_context);
245 }
246
247 static void
248 gs2_common_mech_free(void *global_context __attribute__((unused)),
249                      const sasl_utils_t *utils)
250 {
251     OM_uint32 minor;
252
253     if (gs2_mechs != GSS_C_NO_OID_SET) {
254         gss_release_oid_set(&minor, &gs2_mechs);
255         gs2_mechs = GSS_C_NO_OID_SET;
256     }
257 }
258
259 /*****************************  Server Section  *****************************/
260
261 static int
262 gs2_server_mech_new(void *glob_context,
263                     sasl_server_params_t *params,
264                     const char *challenge __attribute__((unused)),
265                     unsigned challen __attribute__((unused)),
266                     void **conn_context)
267 {
268     context_t *text;
269     int ret;
270
271     text = sasl_gs2_new_context(params->utils);
272     if (text == NULL) {
273         MEMERROR(params->utils);
274         return SASL_NOMEM;
275     }
276
277     text->gss_ctx = GSS_C_NO_CONTEXT;
278     text->client_name = GSS_C_NO_NAME;
279     text->server_name = GSS_C_NO_NAME;
280     text->server_creds = GSS_C_NO_CREDENTIAL;
281     text->client_creds = GSS_C_NO_CREDENTIAL;
282     text->plug.server = glob_context;
283
284     ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
285                             &text->mechanism);
286     if (ret != SASL_OK) {
287         gs2_common_mech_dispose(text, params->utils);
288         return ret;
289     }
290
291     *conn_context = text;
292
293     return SASL_OK;
294 }
295
296 static int
297 gs2_server_mech_step(void *conn_context,
298                      sasl_server_params_t *params,
299                      const char *clientin,
300                      unsigned clientinlen,
301                      const char **serverout,
302                      unsigned *serveroutlen,
303                      sasl_out_params_t *oparams)
304 {
305     context_t *text = (context_t *)conn_context;
306     gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
307     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
308     OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
309     gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
310     gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
311     gss_name_t without = GSS_C_NO_NAME;
312     gss_OID_set_desc mechs;
313     gss_OID actual_mech = GSS_C_NO_OID;
314     OM_uint32 out_flags = 0;
315     int ret = 0, equal = 0;
316     int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
317     char *p;
318
319     if (serverout == NULL) {
320         PARAMERROR(text->utils);
321         return SASL_BADPARAM;
322     }
323
324     *serverout = NULL;
325     *serveroutlen = 0;
326
327     if (initialContextToken) {
328         name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
329         name_buf.value = params->utils->malloc(name_buf.length + 1);
330         if (name_buf.value == NULL) {
331             MEMERROR(text->utils);
332             ret = SASL_NOMEM;
333             goto cleanup;
334         }
335         snprintf(name_buf.value, name_buf.length + 1,
336                  "%s@%s", params->service, params->serverFQDN);
337         maj_stat = gss_import_name(&min_stat,
338                                    &name_buf,
339                                    GSS_C_NT_HOSTBASED_SERVICE,
340                                    &text->server_name);
341         params->utils->free(name_buf.value);
342         name_buf.value = NULL;
343
344         if (GSS_ERROR(maj_stat))
345             goto cleanup;
346
347         assert(text->server_creds == GSS_C_NO_CREDENTIAL);
348
349         mechs.count = 1;
350         mechs.elements = (gss_OID)text->mechanism;
351
352         if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
353             maj_stat = gss_acquire_cred(&min_stat,
354                                         text->server_name,
355                                         GSS_C_INDEFINITE,
356                                         &mechs,
357                                         GSS_C_ACCEPT,
358                                         &text->server_creds,
359                                         NULL,
360                                         &text->lifetime);
361             if (GSS_ERROR(maj_stat))
362                 goto cleanup;
363         }
364
365         ret = gs2_verify_initial_message(text,
366                                          params,
367                                          clientin,
368                                          clientinlen,
369                                          &input_token);
370         if (ret != SASL_OK)
371             goto cleanup;
372     } else {
373         input_token.value = (void *)clientin;
374         input_token.length = clientinlen;
375     }
376
377     maj_stat = gss_accept_sec_context(&min_stat,
378                                       &text->gss_ctx,
379                                       (params->gss_creds != GSS_C_NO_CREDENTIAL)
380                                         ? params->gss_creds
381                                         : text->server_creds,
382                                       &input_token,
383                                       &text->bindings,
384                                       &text->client_name,
385                                       &actual_mech,
386                                       &output_token,
387                                       &out_flags,
388                                       &text->lifetime,
389                                       &text->client_creds);
390     if (GSS_ERROR(maj_stat)) {
391         sasl_gs2_log(text->utils, maj_stat, min_stat);
392         text->utils->seterror(text->utils->conn, SASL_NOLOG,
393                               "GS2 Failure: gss_accept_sec_context");
394         ret = SASL_BADAUTH;
395         goto cleanup;
396     }
397
398     *serveroutlen = output_token.length;
399     if (output_token.value != NULL) {
400         ret = _plug_buf_alloc(text->utils, &text->out_buf,
401                               &text->out_buf_len, *serveroutlen);
402         if (ret != SASL_OK)
403             goto cleanup;
404         memcpy(text->out_buf, output_token.value, *serveroutlen);
405         *serverout = text->out_buf;
406     } else {
407         /* No output token, send an empty string */
408         *serverout = "";
409         serveroutlen = 0;
410     }
411
412     if (maj_stat == GSS_S_CONTINUE_NEEDED) {
413         ret = SASL_CONTINUE;
414         goto cleanup;
415     }
416
417     assert(maj_stat == GSS_S_COMPLETE);
418
419     if (!g_OID_equal(text->mechanism, actual_mech)) {
420         ret = SASL_WRONGMECH;
421         goto cleanup;
422     }
423     if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0)  {
424         ret = SASL_BADAUTH;
425         goto cleanup;
426     }
427
428     maj_stat = gss_display_name(&min_stat, text->client_name,
429                                 &name_buf, NULL);
430     if (GSS_ERROR(maj_stat))
431         goto cleanup;
432
433     ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
434     if (ret != 0)
435         goto cleanup;
436
437     p = (char *)memchr(name_buf.value, '@', name_buf.length);
438     if (p != NULL) {
439         short_name_buf.length = (p - (char *)name_buf.value);
440
441         maj_stat = gss_import_name(&min_stat,
442                                    &short_name_buf,
443                                    GSS_C_NT_USER_NAME,
444                                    &without);
445         if (GSS_ERROR(maj_stat)) {
446             ret = SASL_BADAUTH;
447             goto cleanup;
448         }
449
450         maj_stat = gss_compare_name(&min_stat, text->client_name,
451                                     without, &equal);
452         if (GSS_ERROR(maj_stat)) {
453             ret = SASL_BADAUTH;
454             goto cleanup;
455         }
456
457         if (equal)
458             ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
459     }
460
461     text->authid = (char *)short_name_buf.value;
462     short_name_buf.value = NULL;
463     short_name_buf.length = 0;
464
465     if (text->authzid != NULL) {
466         ret = params->canon_user(params->utils->conn,
467                                  text->authzid, 0,
468                                  SASL_CU_AUTHZID, oparams);
469         if (ret != SASL_OK)
470             goto cleanup;
471     }
472
473     ret = params->canon_user(params->utils->conn,
474                              text->authid, 0,
475                              text->authzid == NULL
476                                 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
477                                 : SASL_CU_AUTHID,
478                              oparams);
479     if (ret != SASL_OK)
480         goto cleanup;
481
482     switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
483     case GS2_CB_FLAG_N:
484         oparams->chanbindingflag = SASL_CB_FLAG_NONE;
485         break;
486     case GS2_CB_FLAG_P:
487         oparams->chanbindingflag = SASL_CB_FLAG_USED;
488         break;
489     case GS2_CB_FLAG_Y:
490         oparams->chanbindingflag == SASL_CB_FLAG_WANT;
491         break;
492     }
493
494     if (text->client_creds != GSS_C_NO_CREDENTIAL)
495         oparams->client_creds = &text->client_creds;
496     else
497         oparams->client_creds = NULL;
498
499     oparams->gss_peer_name = text->client_name;
500     oparams->gss_local_name = text->server_name;
501     oparams->maxoutbuf = 0xFFFFFF;
502     oparams->encode = NULL;
503     oparams->decode = NULL;
504     oparams->mech_ssf = 0;
505     oparams->doneflag = 1;
506
507     ret = SASL_OK;
508
509 cleanup:
510     if (initialContextToken)
511         gss_release_buffer(&min_stat, &input_token);
512     gss_release_buffer(&min_stat, &name_buf);
513     gss_release_buffer(&min_stat, &short_name_buf);
514     gss_release_buffer(&min_stat, &output_token);
515     gss_release_name(&min_stat, &without);
516     gss_release_oid(&min_stat, &actual_mech);
517
518     if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
519         sasl_gs2_seterror(text->utils, maj_stat, min_stat);
520         ret = SASL_FAIL;
521     }
522     if (ret != SASL_OK && ret != SASL_CONTINUE)
523         sasl_gs2_free_context_contents(text);
524
525     return ret;
526 }
527
528 static int
529 gs2_common_plug_init(const sasl_utils_t *utils,
530                      size_t plugsize,
531                      int (*plug_alloc)(const sasl_utils_t *,
532                                        void *,
533                                        const gss_buffer_t,
534                                        const gss_OID),
535                      void **pluglist,
536                      int *plugcount)
537 {
538     OM_uint32 major, minor;
539     size_t i, count = 0;
540     void *plugs = NULL;
541
542     *pluglist = NULL;
543     *plugcount = 0;
544
545     if (gs2_indicate_mechs(utils) != SASL_OK) {
546         return SASL_NOMECH;
547     }
548
549     plugs = utils->malloc(2 * gs2_mechs->count * plugsize);
550     if (plugs == NULL) {
551         MEMERROR(utils);
552         return SASL_NOMEM;
553     }
554     memset(plugs, 0, 2 * gs2_mechs->count * plugsize);
555
556     for (i = 0; i < gs2_mechs->count; i++) {
557         gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
558
559         major = gss_inquire_saslname_for_mech(&minor,
560                                               &gs2_mechs->elements[i],
561                                               &sasl_mech_name,
562                                               GSS_C_NO_BUFFER,
563                                               GSS_C_NO_BUFFER);
564         if (GSS_ERROR(major))
565             continue;
566
567 #define PLUG_AT(index)      (void *)((unsigned char *)plugs + (count * plugsize))
568
569         if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
570                        &gs2_mechs->elements[i]) == SASL_OK)
571             count++;
572
573         gss_release_buffer(&minor, &sasl_mech_name);
574     }
575
576     if (count == 0) {
577         utils->free(plugs);
578         return SASL_NOMECH;
579     }
580
581     *pluglist = plugs;
582     *plugcount = count;
583
584     return SASL_OK;
585 }
586
587 static int
588 gs2_server_plug_alloc(const sasl_utils_t *utils,
589                       void *plug,
590                       gss_buffer_t sasl_name,
591                       gss_OID mech)
592 {
593     int ret;
594     sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
595     gss_buffer_desc buf;
596
597     memset(splug, 0, sizeof(*splug));
598
599     ret = gs2_get_mech_attrs(utils, mech,
600                              &splug->security_flags,
601                              &splug->features);
602     if (ret != SASL_OK)
603         return ret;
604
605     ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
606     if (ret != SASL_OK)
607         return ret;
608
609     splug->mech_name = (char *)buf.value;
610     splug->glob_context = plug;
611     splug->mech_new = gs2_server_mech_new;
612     splug->mech_step = gs2_server_mech_step;
613     splug->mech_dispose = gs2_common_mech_dispose;
614     splug->mech_free = gs2_common_mech_free;
615
616     return SASL_OK;
617 }
618
619 static sasl_server_plug_t *gs2_server_plugins;
620 static int gs2_server_plugcount;
621
622 int
623 gs2_server_plug_init(const sasl_utils_t *utils,
624                      int maxversion,
625                      int *outversion,
626                      sasl_server_plug_t **pluglist,
627                      int *plugcount)
628 {
629     int ret;
630
631     *pluglist = NULL;
632     *plugcount = 0;
633
634     if (maxversion < SASL_SERVER_PLUG_VERSION)
635         return SASL_BADVERS;
636
637     *outversion = SASL_SERVER_PLUG_VERSION;
638
639     if (gs2_server_plugins == NULL) {
640         ret = gs2_common_plug_init(utils,
641                                    sizeof(sasl_server_plug_t),
642                                    gs2_server_plug_alloc,
643                                    (void **)&gs2_server_plugins,
644                                    &gs2_server_plugcount);
645         if (ret != SASL_OK)
646             return ret;
647     }
648
649     *pluglist = gs2_server_plugins;
650     *plugcount = gs2_server_plugcount;
651
652     return SASL_OK;
653 }
654
655 /*****************************  Client Section  *****************************/
656
657 static int gs2_client_mech_step(void *conn_context,
658                                 sasl_client_params_t *params,
659                                 const char *serverin,
660                                 unsigned serverinlen,
661                                 sasl_interact_t **prompt_need,
662                                 const char **clientout,
663                                 unsigned *clientoutlen,
664                                 sasl_out_params_t *oparams)
665 {
666     context_t *text = (context_t *)conn_context;
667     gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
668     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
669     gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
670     OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
671     OM_uint32 req_flags, ret_flags;
672     gss_OID actual_mech = GSS_C_NO_OID;
673     int ret = SASL_FAIL;
674     int initialContextToken;
675
676     *clientout = NULL;
677     *clientoutlen = 0;
678
679     if (text->gss_ctx == GSS_C_NO_CONTEXT) {
680         ret = gs2_ask_user_info(text, params, NULL, 0, prompt_need, oparams);
681         if (ret != SASL_OK)
682             goto cleanup;
683
684         if (params->gss_creds == GSS_C_NO_CREDENTIAL && 
685             text->password != NULL && text->password->len != 0) {
686             gss_buffer_desc password_buf;
687             gss_buffer_desc name_buf;
688             gss_OID_set_desc mechs;
689
690             name_buf.length = strlen(oparams->authid);
691             name_buf.value = (void *)oparams->authid;
692
693             password_buf.length = text->password->len;
694             password_buf.value = text->password->data;
695
696             mechs.count = 1;
697             mechs.elements = (gss_OID)text->mechanism;
698
699             maj_stat = gss_import_name(&min_stat,
700                                        &name_buf,
701                                        GSS_C_NT_USER_NAME,
702                                        &text->client_name);
703             if (GSS_ERROR(maj_stat))
704                 goto cleanup;
705
706             maj_stat = gss_acquire_cred_with_password(&min_stat,
707                                                       text->client_name,
708                                                       &password_buf,
709                                                       GSS_C_INDEFINITE,
710                                                       &mechs,
711                                                       GSS_C_INITIATE,
712                                                       &text->client_creds,
713                                                       NULL,
714                                                       &text->lifetime);
715             if (GSS_ERROR(maj_stat))
716                 goto cleanup;
717         }
718
719         initialContextToken = 1;
720     } else
721         initialContextToken = 0;
722
723     if (text->server_name == GSS_C_NO_NAME) { /* only once */
724         name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
725         name_buf.value = params->utils->malloc(name_buf.length + 1);
726         if (name_buf.value == NULL) {
727             ret = SASL_NOMEM;
728             goto cleanup;
729         }
730         if (params->serverFQDN == NULL ||
731             strlen(params->serverFQDN) == 0) {
732             SETERROR(text->utils, "GS2 Failure: no serverFQDN");
733             ret = SASL_FAIL;
734             goto cleanup;
735         }
736
737         snprintf(name_buf.value, name_buf.length + 1,
738                  "%s@%s", params->service, params->serverFQDN);
739
740         maj_stat = gss_import_name (&min_stat,
741                                     &name_buf,
742                                     GSS_C_NT_HOSTBASED_SERVICE,
743                                     &text->server_name);
744         params->utils->free(name_buf.value);
745         name_buf.value = NULL;
746
747         if (GSS_ERROR(maj_stat))
748             goto cleanup;
749     }
750
751     /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
752     if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
753         gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
754         text->gss_ctx = GSS_C_NO_CONTEXT;
755     }
756
757     input_token.value = (void *)serverin;
758     input_token.length = serverinlen;
759
760     if (initialContextToken) {
761         if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
762             text->gs2_flags |= GS2_NONSTD_FLAG;
763
764         switch (params->chanbindingflag) {
765         case SASL_CB_FLAG_NONE:
766             text->gs2_flags |= GS2_CB_FLAG_N;
767             break;
768         case SASL_CB_FLAG_USED:
769             text->gs2_flags |= GS2_CB_FLAG_P;
770             break;
771         case SASL_CB_FLAG_WANT:
772             text->gs2_flags |= GS2_CB_FLAG_Y;
773             break;
774         }
775
776         ret = gs2_make_header(text, params,
777                               strcmp(oparams->user, oparams->authid) ?
778                                      (char *) oparams->user : NULL,
779                               &text->out_buf, &text->out_buf_len);
780         if (ret != 0)
781             goto cleanup;
782     }
783
784     req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
785
786     maj_stat = gss_init_sec_context(&min_stat,
787                                     (params->gss_creds != GSS_C_NO_CREDENTIAL)
788                                         ? params->gss_creds
789                                         : text->client_creds,
790                                     &text->gss_ctx,
791                                     text->server_name,
792                                     (gss_OID)text->mechanism,
793                                     req_flags,
794                                     GSS_C_INDEFINITE,
795                                     &text->bindings,
796                                     serverinlen ? &input_token : GSS_C_NO_BUFFER,
797                                     NULL,
798                                     &output_token,
799                                     &ret_flags,
800                                     &text->lifetime);
801     if (GSS_ERROR(maj_stat))
802         goto cleanup;
803
804     ret = gs2_make_message(text, params, initialContextToken, &output_token,
805                            &text->out_buf, &text->out_buf_len);
806     if (ret != 0)
807         goto cleanup;
808
809     *clientout = text->out_buf;
810     *clientoutlen = text->out_buf_len;
811
812     if (maj_stat == GSS_S_CONTINUE_NEEDED) {
813         ret = SASL_CONTINUE;
814         goto cleanup;
815     }
816
817     if (text->client_name != GSS_C_NO_NAME)
818         gss_release_name(&min_stat, &text->client_name);
819
820     maj_stat = gss_inquire_context(&min_stat,
821                                    text->gss_ctx,
822                                    &text->client_name,
823                                    NULL,
824                                    &text->lifetime,
825                                    &actual_mech,
826                                    &ret_flags, /* flags */
827                                    NULL,
828                                    NULL);
829     if (GSS_ERROR(maj_stat))
830         goto cleanup;
831
832     if (!g_OID_equal(text->mechanism, actual_mech)) {
833         ret = SASL_WRONGMECH;
834         goto cleanup;
835     }
836     if ((ret_flags & req_flags) != req_flags) {
837         maj_stat = SASL_BADAUTH;
838         goto cleanup;
839     }
840
841     maj_stat = gss_display_name(&min_stat,
842                                 text->client_name,
843                                 &name_buf,
844                                 NULL);
845     if (GSS_ERROR(maj_stat))
846         goto cleanup;
847
848     oparams->gss_peer_name = text->server_name;
849     oparams->gss_local_name = text->client_name;
850     oparams->encode = NULL;
851     oparams->decode = NULL;
852     oparams->mech_ssf = 0;
853     oparams->maxoutbuf = 0xFFFFFF;
854     oparams->doneflag = 1;
855
856 cleanup:
857     gss_release_buffer(&min_stat, &output_token);
858     gss_release_buffer(&min_stat, &name_buf);
859     gss_release_oid(&min_stat, &actual_mech);
860
861     if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
862         sasl_gs2_seterror(text->utils, maj_stat, min_stat);
863         ret = SASL_FAIL;
864     }
865     if (ret != SASL_OK && ret != SASL_CONTINUE)
866         sasl_gs2_free_context_contents(text);
867
868     return ret;
869 }
870
871 static int gs2_client_mech_new(void *glob_context,
872                                sasl_client_params_t *params,
873                                void **conn_context)
874 {
875     context_t *text;
876     int ret;
877
878     text = sasl_gs2_new_context(params->utils);
879     if (text == NULL) {
880         MEMERROR(params->utils);
881         return SASL_NOMEM;
882     }
883
884     text->gss_ctx = GSS_C_NO_CONTEXT;
885     text->client_name = GSS_C_NO_NAME;
886     text->server_creds = GSS_C_NO_CREDENTIAL;
887     text->client_creds  = GSS_C_NO_CREDENTIAL;
888     text->plug.client = glob_context;
889
890     ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
891                             &text->mechanism);
892     if (ret != SASL_OK) {
893         gs2_common_mech_dispose(text, params->utils);
894         return ret;
895     } 
896
897     *conn_context = text;
898
899     return SASL_OK;
900 }
901
902 static const unsigned long gs2_required_prompts[] = {
903     SASL_CB_LIST_END
904 };
905
906 static int
907 gs2_client_plug_alloc(const sasl_utils_t *utils,
908                       void *plug,
909                       gss_buffer_t sasl_name,
910                       gss_OID mech)
911 {
912     int ret;
913     sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
914     gss_buffer_desc buf;
915
916     memset(cplug, 0, sizeof(*cplug));
917
918     ret = gs2_get_mech_attrs(utils, mech,
919                              &cplug->security_flags,
920                              &cplug->features);
921     if (ret != SASL_OK)
922         return ret;
923
924     ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
925     if (ret != SASL_OK)
926         return ret;
927
928     cplug->mech_name = (char *)buf.value;
929     cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
930     cplug->glob_context = plug;
931     cplug->mech_new = gs2_client_mech_new;
932     cplug->mech_step = gs2_client_mech_step;
933     cplug->mech_dispose = gs2_common_mech_dispose;
934     cplug->mech_free = gs2_common_mech_free;
935     cplug->required_prompts = gs2_required_prompts;
936
937     return SASL_OK;
938 }
939
940 static sasl_client_plug_t *gs2_client_plugins;
941 static int gs2_client_plugcount;
942
943 int
944 gs2_client_plug_init(const sasl_utils_t *utils,
945                      int maxversion,
946                      int *outversion,
947                      sasl_client_plug_t **pluglist,
948                      int *plugcount)
949 {
950     int ret;
951
952     *pluglist = NULL;
953     *plugcount = 0;
954
955     if (maxversion < SASL_CLIENT_PLUG_VERSION)
956         return SASL_BADVERS;
957
958     *outversion = SASL_CLIENT_PLUG_VERSION;
959
960     if (gs2_client_plugins == NULL) {
961         ret = gs2_common_plug_init(utils,
962                                    sizeof(sasl_client_plug_t),
963                                    gs2_client_plug_alloc,
964                                    (void **)&gs2_client_plugins,
965                                    &gs2_client_plugcount);
966         if (ret != SASL_OK)
967             return ret;
968     }
969
970     *pluglist = gs2_client_plugins;
971     *plugcount = gs2_client_plugcount;
972
973     return SASL_OK;
974 }
975
976 /*
977  * Copy header and application channel bindings to GSS channel bindings
978  * structure in context.
979  */
980 static int
981 gs2_save_cbindings(context_t *text,
982                    gss_buffer_t header,
983                    const char *chanbindingdata,
984                    unsigned int chanbindinglen)
985 {
986     gss_buffer_t gss_bindings = &text->bindings.application_data;
987     size_t len;
988     unsigned char *p;
989
990     assert(gss_bindings->value == NULL);
991
992     /*
993      * The application-data field MUST be set to the gs2-header, excluding
994      * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
995      * gs2-cb-flag of "p" is used, the application's channel binding data.
996      */
997     len = header->length;
998     if (text->gs2_flags & GS2_NONSTD_FLAG) {
999         assert(len > 2);
1000         len -= 2;
1001     }
1002     if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P)
1003         len += chanbindinglen;
1004
1005     gss_bindings->length = len;
1006     gss_bindings->value = text->utils->malloc(len);
1007     if (gss_bindings->value == NULL)
1008         return SASL_NOMEM;
1009
1010     p = (unsigned char *)gss_bindings->value;
1011     if (text->gs2_flags & GS2_NONSTD_FLAG) {
1012         memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
1013         p += header->length - 2;
1014     } else {
1015         memcpy(p, header->value, header->length);
1016         p += header->length;
1017     }
1018
1019     if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
1020         chanbindinglen != 0) {
1021         memcpy(p, chanbindingdata, chanbindinglen);
1022     }
1023
1024     return SASL_OK;
1025 }
1026
1027 #define CHECK_REMAIN(n)     do { if (remain < (n)) return SASL_BADAUTH; } while (0)
1028
1029 /*
1030  * Verify gs2-header, save authzid and channel bindings to context.
1031  */
1032 static int
1033 gs2_verify_initial_message(context_t *text,
1034                            sasl_server_params_t *sparams,
1035                            const char *in,
1036                            unsigned inlen,
1037                            gss_buffer_t token)
1038 {
1039     char *p = (char *)in;
1040     unsigned remain = inlen;
1041     int ret;
1042     gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1043
1044     assert(text->cb_name == NULL);
1045     assert(text->authzid == NULL);
1046
1047     token->length = 0;
1048     token->value = NULL;
1049
1050     /* minimum header includes CB flag and non-zero GSS token */
1051     CHECK_REMAIN(4); /* [pny],,. */
1052
1053     /* non-standard GSS framing flag */
1054     if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1055         text->gs2_flags |= GS2_NONSTD_FLAG;
1056         remain -= 2;
1057         p += 2;
1058     }
1059
1060     /* SASL channel bindings */
1061     CHECK_REMAIN(1); /* [pny] */
1062     remain--;
1063     switch (*p++) {
1064     case 'p':
1065         CHECK_REMAIN(1); /* = */
1066         remain--;
1067         if (*p++ != '=')
1068             return SASL_BADAUTH;
1069
1070         ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cb_name);
1071         if (ret != SASL_OK)
1072             return ret;
1073
1074         text->gs2_flags |= GS2_CB_FLAG_P;
1075         break;
1076     case 'n':
1077         text->gs2_flags |= GS2_CB_FLAG_N;
1078         break;
1079     case 'y':
1080         text->gs2_flags |= GS2_CB_FLAG_Y;
1081         break;
1082     }
1083
1084     CHECK_REMAIN(1); /* , */
1085     remain--;
1086     if (*p++ != ',')
1087         return SASL_BADAUTH;
1088
1089     /* authorization identity */
1090     if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1091         CHECK_REMAIN(2);
1092         remain -= 2;
1093         p += 2;
1094
1095         ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1096         if (ret != SASL_OK)
1097             return ret;
1098     }
1099
1100     /* end of header */ 
1101     CHECK_REMAIN(1); /* , */
1102     remain--;
1103     if (*p++ != ',')
1104         return SASL_BADAUTH;
1105
1106     buf.length = inlen - remain;
1107     buf.value = (void *)in;
1108
1109     /* stash channel bindings to pass into gss_accept_sec_context() */
1110     ret = gs2_save_cbindings(text, &buf, sparams->chanbindingdata,
1111                              sparams->chanbindinglen);
1112     if (ret != SASL_OK)
1113         return ret;
1114
1115     buf.length = remain;
1116     buf.value = p;
1117
1118     if (text->gs2_flags & GS2_NONSTD_FLAG) {
1119         token->value = text->utils->malloc(buf.length);
1120         if (token->value == NULL)
1121             return SASL_NOMEM;
1122
1123         token->length = buf.length;
1124         memcpy(token->value, buf.value, buf.length);
1125     } else {
1126         unsigned int token_size;
1127
1128         /* create a properly formed GSS token */ 
1129         token_size = gs2_token_size(text->mechanism, buf.length);
1130         token->value = text->utils->malloc(token_size);
1131         if (token->value == NULL)
1132             return SASL_NOMEM;
1133
1134         token->length = token_size;
1135
1136         p = (char *)token->value;
1137         gs2_make_token_header(text->mechanism, buf.length,
1138                               (unsigned char **)&p);
1139         memcpy(p, buf.value, buf.length);
1140     }
1141
1142     return SASL_OK;
1143 }
1144
1145 /*
1146  * Create gs2-header, save channel bindings to context.
1147  */
1148 static int
1149 gs2_make_header(context_t *text,
1150                 sasl_client_params_t *cparams,
1151                 const char *authzid,
1152                 char **out,
1153                 unsigned *outlen)
1154 {
1155     size_t required = 0;
1156     size_t wire_authzid_len = 0, cb_name_len = 0;
1157     char *wire_authzid = NULL;
1158     char *p;
1159     int ret;
1160     gss_buffer_desc buf;
1161
1162     *out = NULL;
1163     *outlen = 0;
1164
1165     /* non-standard GSS framing flag */
1166     if (text->gs2_flags & GS2_NONSTD_FLAG)
1167         required += 2; /* F, */
1168
1169     /* SASL channel bindings */
1170     switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1171     case GS2_CB_FLAG_P:
1172         if (cparams->chanbindingtype == NULL)
1173             return SASL_BADPARAM;
1174         cb_name_len = strlen(cparams->chanbindingtype);
1175         required += 1 /*=*/ + cb_name_len;
1176         /* fallthrough */
1177     case GS2_CB_FLAG_N:
1178     case GS2_CB_FLAG_Y:
1179         required += 2; /* [pny], */
1180         break;
1181     default:
1182         return SASL_BADPARAM;
1183     }
1184
1185     /* authorization identity */
1186     if (authzid != NULL) {
1187         ret = gs2_escape_authzid(text->utils, authzid,
1188                                  strlen(authzid), &wire_authzid);
1189         if (ret != SASL_OK)
1190             return ret;
1191
1192         wire_authzid_len = strlen(wire_authzid);
1193         required += 2 /* a= */ + wire_authzid_len;
1194     }
1195
1196     required += 1; /* trailing comma */
1197
1198     ret = _plug_buf_alloc(text->utils, out, outlen, required);
1199     if (ret != SASL_OK) {
1200         text->utils->free(wire_authzid);
1201         return ret;
1202     }
1203
1204     *out = text->out_buf;
1205     *outlen = required;
1206
1207     p = (char *)text->out_buf;
1208     if (text->gs2_flags & GS2_NONSTD_FLAG) {
1209         *p++ = 'F';
1210         *p++ = ',';
1211     }
1212     switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1213     case GS2_CB_FLAG_P:
1214         memcpy(p, "p=", 2);
1215         memcpy(p + 2, cparams->chanbindingtype, cb_name_len);
1216         p += 2 + cb_name_len;
1217         break;
1218     case GS2_CB_FLAG_N:
1219         *p++ = 'n';
1220         break;
1221     case GS2_CB_FLAG_Y:
1222         *p++ = 'y';
1223         break;
1224     }
1225     *p++ = ',';
1226     if (wire_authzid != NULL) {
1227         memcpy(p, "a=", 2);
1228         memcpy(p + 2, wire_authzid, wire_authzid_len);
1229         text->utils->free(wire_authzid);
1230         p += 2 + wire_authzid_len;
1231     }
1232     *p++ = ',';
1233
1234     assert(p == (char *)text->out_buf + required);
1235
1236     buf.length = required;
1237     buf.value = *out;
1238
1239     ret = gs2_save_cbindings(text, &buf, cparams->chanbindingdata,
1240                              cparams->chanbindinglen);
1241
1242     return ret;
1243 }
1244
1245 /*
1246  * Convert a GSS token to a GS2 one
1247  */
1248 static int
1249 gs2_make_message(context_t *text,
1250                  sasl_client_params_t *cparams __attribute__((unused)),
1251                  int initialContextToken,
1252                  gss_buffer_t token,
1253                  char **out,
1254                  unsigned *outlen)
1255 {
1256     OM_uint32 major, minor;
1257     unsigned char *mech_token_data;
1258     size_t mech_token_size;
1259     char *p;
1260     unsigned header_len = 0;
1261     int ret;
1262
1263     mech_token_size = token->length;
1264     mech_token_data = (unsigned char *)token->value;
1265
1266     if (initialContextToken) {
1267         header_len = *outlen;
1268
1269         major = gs2_verify_token_header(&minor, text->mechanism,
1270                                         &mech_token_size, &mech_token_data,
1271                                         token->length);
1272         if ((major == GSS_S_DEFECTIVE_TOKEN &&
1273              (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1274             GSS_ERROR(major))
1275             return SASL_FAIL;
1276     }
1277
1278     ret = _plug_buf_alloc(text->utils, out, outlen,
1279                           header_len + mech_token_size);
1280     if (ret != 0)
1281         return ret;
1282
1283     p = *out + header_len;
1284     memcpy(p, mech_token_data, mech_token_size);
1285
1286     *outlen = header_len + mech_token_size;
1287
1288     return SASL_OK;
1289 }
1290
1291 /*
1292  * Map GSS mechanism attributes to SASL ones
1293  */
1294 static int
1295 gs2_get_mech_attrs(const sasl_utils_t *utils,
1296                    const gss_OID mech,
1297                    unsigned int *security_flags,
1298                    unsigned int *features)
1299 {
1300     OM_uint32 major, minor;
1301     int present, ret;
1302     gss_OID_set attrs = GSS_C_NO_OID_SET;
1303
1304     major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1305     if (GSS_ERROR(major)) {
1306         utils->seterror(utils->conn, SASL_NOLOG,
1307                         "GS2 Failure: gss_inquire_attrs_for_mech");
1308         return SASL_FAIL;
1309     }
1310
1311     *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1312     *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1313
1314 #define MA_PRESENT(a)   (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1315                                                  attrs, &present) == GSS_S_COMPLETE && \
1316                          present)
1317
1318     ret = SASL_OK;
1319
1320     if (MA_PRESENT(GSS_C_MA_PFS))
1321         *security_flags |= SASL_SEC_FORWARD_SECRECY;
1322     if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1323         *security_flags |= SASL_SEC_NOANONYMOUS;
1324     if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1325         *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1326     if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1327         *security_flags |= SASL_SEC_MUTUAL_AUTH;
1328     if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1329         *features |= SASL_FEAT_GSS_FRAMING;
1330
1331     gss_release_oid_set(&minor, &attrs);
1332     return ret;
1333 }
1334
1335 /*
1336  * Enumerate GSS mechanisms that can be used for GS2
1337  */
1338 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1339 {
1340     OM_uint32 major, minor;
1341     gss_OID_desc desired_oids[3];
1342     gss_OID_set_desc desired_attrs;
1343     gss_OID_desc except_oids[3];
1344     gss_OID_set_desc except_attrs;
1345
1346     if (gs2_mechs != GSS_C_NO_OID_SET)
1347         return SASL_OK;
1348
1349     desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1350     desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1351     desired_oids[2] = *GSS_C_MA_CBINDINGS;
1352     desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1353     desired_attrs.elements = desired_oids;
1354
1355     except_oids[0] = *GSS_C_MA_MECH_NEGO;
1356     except_oids[1] = *GSS_C_MA_NOT_MECH;
1357     except_oids[2] = *GSS_C_MA_DEPRECATED;
1358
1359     except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1360     except_attrs.elements = except_oids;
1361
1362     major = gss_indicate_mechs_by_attrs(&minor,
1363                                         &desired_attrs,
1364                                         &except_attrs,
1365                                         GSS_C_NO_OID_SET,
1366                                         &gs2_mechs);
1367     if (GSS_ERROR(major)) {
1368         utils->seterror(utils->conn, SASL_NOLOG,
1369                         "GS2 Failure: gss_indicate_mechs_by_attrs");
1370         return SASL_FAIL;
1371     }
1372
1373     return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1374 }
1375
1376 /*
1377  * Map SASL mechanism name to OID
1378  */
1379 static int
1380 gs2_map_sasl_name(const sasl_utils_t *utils,
1381                   const char *mech,
1382                   gss_OID *oid)
1383 {
1384     OM_uint32 major, minor;
1385     gss_buffer_desc buf;
1386
1387     buf.length = strlen(mech);
1388     buf.value = (void *)mech;
1389
1390     major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1391     if (GSS_ERROR(major)) {
1392         utils->seterror(utils->conn, SASL_NOLOG,
1393                         "GS2 Failure: gss_inquire_mech_for_saslname");
1394         return SASL_FAIL;
1395     }
1396
1397     return SASL_OK;
1398 }
1399
1400 static int
1401 gs2_duplicate_buffer(const sasl_utils_t *utils,
1402                      const gss_buffer_t src,
1403                      gss_buffer_t dst)
1404 {
1405     dst->value = utils->malloc(src->length + 1);
1406     if (dst->value == NULL)
1407         return SASL_NOMEM;
1408
1409     memcpy(dst->value, src->value, src->length);
1410     ((char *)dst->value)[src->length] = '\0';
1411     dst->length = src->length;
1412
1413     return SASL_OK;
1414 }
1415
1416 static int
1417 gs2_unescape_authzid(const sasl_utils_t *utils,
1418                      char **endp,
1419                      unsigned *remain,
1420                      char **authzid)
1421 {
1422     char *in = *endp;
1423     size_t i, len, inlen = *remain;
1424     char *p;
1425
1426     *endp = NULL;
1427
1428     for (i = 0, len = 0; i < inlen; i++) {
1429         if (in[i] == ',') {
1430             *endp = &in[i];
1431             *remain -= i;
1432             break;
1433         } else if (in[i] == '=') {
1434             if (inlen <= i + 2)
1435                 return SASL_BADPROT;
1436             i += 2;
1437         }
1438         len++;
1439     }
1440
1441     if (len == 0 || *endp == NULL)
1442         return SASL_BADPROT;
1443
1444     p = *authzid = utils->malloc(len + 1);
1445     if (*authzid == NULL)
1446         return SASL_NOMEM;
1447
1448     for (i = 0; i < inlen; i++) {
1449         if (in[i] == ',')
1450             break;
1451         else if (in[i] == '=') {
1452             if (memcmp(&in[i + 1], "2C", 2) == 0)
1453                 *p++ = ',';
1454             else if (memcmp(&in[i + 1], "3D", 2) == 0)
1455                 *p++ = '=';
1456             else {
1457                 utils->free(*authzid);
1458                 *authzid = NULL;
1459                 return SASL_BADPROT;
1460             }
1461             i += 2;
1462         } else
1463             *p++ = in[i];
1464     }
1465
1466     *p = '\0';
1467
1468     return SASL_OK;
1469 }
1470
1471 static int
1472 gs2_escape_authzid(const sasl_utils_t *utils,
1473                    const char *in,
1474                    unsigned inlen,
1475                    char **authzid)
1476 {
1477     size_t i;
1478     char *p;
1479
1480     p = *authzid = utils->malloc((inlen * 3) + 1);
1481     if (*authzid == NULL)
1482         return SASL_NOMEM;
1483
1484     for (i = 0; i < inlen; i++) {
1485         if (in[i] == ',') {
1486             memcpy(p, "=2C", 3);
1487             p += 3;
1488         } else if (in[i] == '=') {
1489             memcpy(p, "=3D", 3);
1490             p += 3;
1491         } else {
1492             *p++ = in[i];
1493         }
1494     }
1495
1496     *p = '\0';
1497
1498     return SASL_OK;
1499 }
1500
1501 static int
1502 gs2_ask_user_info(context_t *text,
1503                   sasl_client_params_t *params,
1504                   char **realms __attribute__((unused)),
1505                   int nrealm __attribute__((unused)),
1506                   sasl_interact_t **prompt_need,
1507                   sasl_out_params_t *oparams)
1508 {
1509     int result = SASL_OK;
1510     const char *authid = NULL, *userid = NULL;
1511     int user_result = SASL_OK;
1512     int auth_result = SASL_OK;
1513     int pass_result = SASL_OK;
1514
1515     /* try to get the authid */
1516     if (oparams->authid == NULL) {
1517         auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1518
1519         if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1520             return auth_result;
1521         }
1522     }
1523
1524     /* try to get the userid */
1525     if (oparams->user == NULL) {
1526         user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1527
1528         if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1529             return user_result;
1530         }
1531     }
1532
1533     /* try to get the password */
1534     if (text->password == NULL) {
1535         pass_result = _plug_get_password(params->utils, &text->password,
1536                                          &text->free_password, prompt_need);
1537         if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1538             return pass_result;
1539         }
1540     }
1541
1542     /* free prompts we got */
1543     if (prompt_need && *prompt_need) {
1544         params->utils->free(*prompt_need);
1545         *prompt_need = NULL;
1546     }
1547
1548     /* if there are prompts not filled in */
1549     if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1550         pass_result == SASL_INTERACT) {
1551
1552         /* make the prompt list */
1553         result =
1554             _plug_make_prompts(params->utils, prompt_need,
1555                                user_result == SASL_INTERACT ?
1556                                "Please enter your authorization name" : NULL,
1557                                NULL,
1558                                auth_result == SASL_INTERACT ?
1559                                "Please enter your authentication name" : NULL,
1560                                NULL,
1561                                pass_result == SASL_INTERACT ?
1562                                "Please enter your password" : NULL, NULL,
1563                                NULL, NULL, NULL,
1564                                NULL,
1565                                NULL, NULL);
1566         if (result == SASL_OK) return SASL_INTERACT;
1567
1568         return result;
1569     }
1570
1571     if (oparams->authid == NULL) {
1572         if (userid == NULL || userid[0] == '\0') {
1573             result = params->canon_user(params->utils->conn, authid, 0,
1574                                         SASL_CU_AUTHID | SASL_CU_AUTHZID,
1575                                         oparams);
1576         } else {
1577             result = params->canon_user(params->utils->conn,
1578                                         authid, 0, SASL_CU_AUTHID, oparams);
1579             if (result != SASL_OK) return result;
1580
1581             result = params->canon_user(params->utils->conn,
1582                                         userid, 0, SASL_CU_AUTHZID, oparams);
1583         }
1584         if (result != SASL_OK)
1585             return result;
1586     }
1587
1588     return result;
1589 }
1590
1591 static int
1592 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1593                    int logonly)
1594 {
1595     OM_uint32 maj_stat, min_stat;
1596     gss_buffer_desc msg;
1597     OM_uint32 msg_ctx;
1598     int ret;
1599     char *out = NULL;
1600     unsigned int len, curlen = 0;
1601     const char prefix[] = "GSSAPI Error: ";
1602
1603     len = sizeof(prefix);
1604     ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1605     if (ret != SASL_OK)
1606         return SASL_OK;
1607
1608     strcpy(out, prefix);
1609
1610     msg_ctx = 0;
1611     while (1) {
1612         maj_stat = gss_display_status(&min_stat, maj,
1613                                       GSS_C_GSS_CODE, GSS_C_NULL_OID,
1614                                       &msg_ctx, &msg);
1615
1616         if (GSS_ERROR(maj_stat)) {
1617             if (logonly) {
1618                 utils->log(utils->conn, SASL_LOG_FAIL,
1619                         "GS2 Failure: (could not get major error message)");
1620             } else {
1621                 utils->seterror(utils->conn, 0,
1622                                 "GS2 Failure "
1623                                 "(could not get major error message)");
1624             }
1625             utils->free(out);
1626             return SASL_OK;
1627         }
1628
1629         len += len + msg.length;
1630         ret = _plug_buf_alloc(utils, &out, &curlen, len);
1631         if (ret != SASL_OK) {
1632             utils->free(out);
1633             return SASL_OK;
1634         }
1635
1636         strcat(out, msg.value);
1637
1638         gss_release_buffer(&min_stat, &msg);
1639
1640         if (!msg_ctx)
1641             break;
1642     }
1643
1644     /* Now get the minor status */
1645
1646     len += 2;
1647     ret = _plug_buf_alloc(utils, &out, &curlen, len);
1648     if (ret != SASL_OK) {
1649         utils->free(out);
1650         return SASL_NOMEM;
1651     }
1652
1653     strcat(out, " (");
1654
1655     msg_ctx = 0;
1656     while (1) {
1657         maj_stat = gss_display_status(&min_stat, min,
1658                                       GSS_C_MECH_CODE, GSS_C_NULL_OID,
1659                                       &msg_ctx, &msg);
1660
1661         if (GSS_ERROR(maj_stat)) {
1662             if (logonly) {
1663                 utils->log(utils->conn, SASL_LOG_FAIL,
1664                         "GS2 Failure: (could not get minor error message)");
1665             } else {
1666                 utils->seterror(utils->conn, 0,
1667                                 "GS2 Failure "
1668                                 "(could not get minor error message)");
1669             }
1670             utils->free(out);
1671             return SASL_OK;
1672         }
1673
1674         len += len + msg.length;
1675
1676         ret = _plug_buf_alloc(utils, &out, &curlen, len);
1677         if (ret != SASL_OK) {
1678             utils->free(out);
1679             return SASL_NOMEM;
1680         }
1681
1682         strcat(out, msg.value);
1683
1684         gss_release_buffer(&min_stat, &msg);
1685
1686         if (!msg_ctx)
1687             break;
1688     }
1689
1690     len += 1;
1691     ret = _plug_buf_alloc(utils, &out, &curlen, len);
1692     if (ret != SASL_OK) {
1693         utils->free(out);
1694         return SASL_NOMEM;
1695     }
1696
1697     strcat(out, ")");
1698
1699     if (logonly) {
1700         utils->log(utils->conn, SASL_LOG_FAIL, out);
1701     } else {
1702         utils->seterror(utils->conn, 0, out);
1703     }
1704     utils->free(out);
1705
1706     return SASL_OK;
1707 }