Add GS2 mech code
[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
373         if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_N) {
374             if (params->chanbindingcrit != 0)
375                 ret = SASL_BADAUTH;
376         } else if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_Y) {
377             if (SASL_CB_PRESENT(params))
378                 ret = SASL_BADAUTH;
379         }
380         if (ret != SASL_OK)
381             goto cleanup;
382     } else {
383         input_token.value = (void *)clientin;
384         input_token.length = clientinlen;
385     }
386
387     maj_stat = gss_accept_sec_context(&min_stat,
388                                       &text->gss_ctx,
389                                       (params->gss_creds != GSS_C_NO_CREDENTIAL)
390                                         ? params->gss_creds
391                                         : text->server_creds,
392                                       &input_token,
393                                       &text->bindings,
394                                       &text->client_name,
395                                       &actual_mech,
396                                       &output_token,
397                                       &out_flags,
398                                       &text->lifetime,
399                                       &text->client_creds);
400     if (GSS_ERROR(maj_stat)) {
401         sasl_gs2_log(text->utils, maj_stat, min_stat);
402         text->utils->seterror(text->utils->conn, SASL_NOLOG,
403                               "GS2 Failure: gss_accept_sec_context");
404         ret = SASL_BADAUTH;
405         goto cleanup;
406     }
407
408     *serveroutlen = output_token.length;
409     if (output_token.value != NULL) {
410         ret = _plug_buf_alloc(text->utils, &text->out_buf,
411                               &text->out_buf_len, *serveroutlen);
412         if (ret != SASL_OK)
413             goto cleanup;
414         memcpy(text->out_buf, output_token.value, *serveroutlen);
415         *serverout = text->out_buf;
416     } else {
417         /* No output token, send an empty string */
418         *serverout = "";
419         serveroutlen = 0;
420     }
421
422     if (maj_stat == GSS_S_CONTINUE_NEEDED) {
423         ret = SASL_CONTINUE;
424         goto cleanup;
425     }
426
427     assert(maj_stat == GSS_S_COMPLETE);
428
429     if (!g_OID_equal(text->mechanism, actual_mech)) {
430         ret = SASL_WRONGMECH;
431         goto cleanup;
432     }
433     if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0)  {
434         ret = SASL_BADAUTH;
435         goto cleanup;
436     }
437
438     maj_stat = gss_display_name(&min_stat, text->client_name,
439                                 &name_buf, NULL);
440     if (GSS_ERROR(maj_stat))
441         goto cleanup;
442
443     ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
444     if (ret != 0)
445         goto cleanup;
446
447     p = (char *)memchr(name_buf.value, '@', name_buf.length);
448     if (p != NULL) {
449         short_name_buf.length = (p - (char *)name_buf.value);
450
451         maj_stat = gss_import_name(&min_stat,
452                                    &short_name_buf,
453                                    GSS_C_NT_USER_NAME,
454                                    &without);
455         if (GSS_ERROR(maj_stat)) {
456             ret = SASL_BADAUTH;
457             goto cleanup;
458         }
459
460         maj_stat = gss_compare_name(&min_stat, text->client_name,
461                                     without, &equal);
462         if (GSS_ERROR(maj_stat)) {
463             ret = SASL_BADAUTH;
464             goto cleanup;
465         }
466
467         if (equal)
468             ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
469     }
470
471     text->authid = (char *)short_name_buf.value;
472     short_name_buf.value = NULL;
473     short_name_buf.length = 0;
474
475     if (text->authzid != NULL) {
476         ret = params->canon_user(params->utils->conn,
477                                  text->authzid, 0,
478                                  SASL_CU_AUTHZID, oparams);
479         if (ret != SASL_OK)
480             goto cleanup;
481     }
482
483     ret = params->canon_user(params->utils->conn,
484                              text->authid, 0,
485                              text->authzid == NULL
486                                 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
487                                 : SASL_CU_AUTHID,
488                              oparams);
489     if (ret != SASL_OK)
490         goto cleanup;
491
492     if (text->client_creds != GSS_C_NO_CREDENTIAL)
493         oparams->client_creds = &text->client_creds;
494     else
495         oparams->client_creds = NULL;
496
497     oparams->gss_peer_name = text->client_name;
498     oparams->gss_local_name = text->server_name;
499     oparams->maxoutbuf = 0xFFFFFF;
500     oparams->encode = NULL;
501     oparams->decode = NULL;
502     oparams->mech_ssf = 0;
503     oparams->doneflag = 1;
504
505     ret = SASL_OK;
506
507 cleanup:
508     if (initialContextToken)
509         gss_release_buffer(&min_stat, &input_token);
510     gss_release_buffer(&min_stat, &name_buf);
511     gss_release_buffer(&min_stat, &short_name_buf);
512     gss_release_buffer(&min_stat, &output_token);
513     gss_release_name(&min_stat, &without);
514     gss_release_oid(&min_stat, &actual_mech);
515
516     if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
517         sasl_gs2_seterror(text->utils, maj_stat, min_stat);
518         ret = SASL_FAIL;
519     }
520     if (ret != SASL_OK && ret != SASL_CONTINUE)
521         sasl_gs2_free_context_contents(text);
522
523     return ret;
524 }
525
526 static int
527 gs2_common_plug_init(const sasl_utils_t *utils,
528                      size_t plugsize,
529                      int (*plug_alloc)(const sasl_utils_t *,
530                                        void *,
531                                        const gss_buffer_t,
532                                        const gss_OID),
533                      void **pluglist,
534                      int *plugcount)
535 {
536     OM_uint32 major, minor;
537     size_t i, count = 0;
538     void *plugs = NULL;
539
540     *pluglist = NULL;
541     *plugcount = 0;
542
543     if (gs2_indicate_mechs(utils) != SASL_OK) {
544         return SASL_NOMECH;
545     }
546
547     plugs = utils->malloc(2 * gs2_mechs->count * plugsize);
548     if (plugs == NULL) {
549         MEMERROR(utils);
550         return SASL_NOMEM;
551     }
552     memset(plugs, 0, 2 * gs2_mechs->count * plugsize);
553
554     for (i = 0; i < gs2_mechs->count; i++) {
555         gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
556
557         major = gss_inquire_saslname_for_mech(&minor,
558                                               &gs2_mechs->elements[i],
559                                               &sasl_mech_name,
560                                               GSS_C_NO_BUFFER,
561                                               GSS_C_NO_BUFFER);
562         if (GSS_ERROR(major))
563             continue;
564
565 #define PLUG_AT(index)      (void *)((unsigned char *)plugs + (count * plugsize))
566
567         if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
568                        &gs2_mechs->elements[i]) == SASL_OK)
569             count++;
570
571         gss_release_buffer(&minor, &sasl_mech_name);
572     }
573
574     if (count == 0) {
575         utils->free(plugs);
576         return SASL_NOMECH;
577     }
578
579     *pluglist = plugs;
580     *plugcount = count;
581
582     return SASL_OK;
583 }
584
585 static int
586 gs2_server_plug_alloc(const sasl_utils_t *utils,
587                       void *plug,
588                       gss_buffer_t sasl_name,
589                       gss_OID mech)
590 {
591     int ret;
592     sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
593     gss_buffer_desc buf;
594
595     memset(splug, 0, sizeof(*splug));
596
597     ret = gs2_get_mech_attrs(utils, mech,
598                              &splug->security_flags,
599                              &splug->features);
600     if (ret != SASL_OK)
601         return ret;
602
603     ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
604     if (ret != SASL_OK)
605         return ret;
606
607     splug->mech_name = (char *)buf.value;
608     splug->glob_context = plug;
609     splug->mech_new = gs2_server_mech_new;
610     splug->mech_step = gs2_server_mech_step;
611     splug->mech_dispose = gs2_common_mech_dispose;
612     splug->mech_free = gs2_common_mech_free;
613
614     return SASL_OK;
615 }
616
617 static sasl_server_plug_t *gs2_server_plugins;
618 static int gs2_server_plugcount;
619
620 int
621 gs2_server_plug_init(const sasl_utils_t *utils,
622                      int maxversion,
623                      int *outversion,
624                      sasl_server_plug_t **pluglist,
625                      int *plugcount)
626 {
627     int ret;
628
629     *pluglist = NULL;
630     *plugcount = 0;
631
632     if (maxversion < SASL_SERVER_PLUG_VERSION)
633         return SASL_BADVERS;
634
635     *outversion = SASL_SERVER_PLUG_VERSION;
636
637     if (gs2_server_plugins == NULL) {
638         ret = gs2_common_plug_init(utils,
639                                    sizeof(sasl_server_plug_t),
640                                    gs2_server_plug_alloc,
641                                    (void **)&gs2_server_plugins,
642                                    &gs2_server_plugcount);
643         if (ret != SASL_OK)
644             return ret;
645     }
646
647     *pluglist = gs2_server_plugins;
648     *plugcount = gs2_server_plugcount;
649
650     return SASL_OK;
651 }
652
653 /*****************************  Client Section  *****************************/
654
655 static int gs2_client_mech_step(void *conn_context,
656                                 sasl_client_params_t *params,
657                                 const char *serverin,
658                                 unsigned serverinlen,
659                                 sasl_interact_t **prompt_need,
660                                 const char **clientout,
661                                 unsigned *clientoutlen,
662                                 sasl_out_params_t *oparams)
663 {
664     context_t *text = (context_t *)conn_context;
665     gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
666     gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
667     gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
668     OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
669     OM_uint32 req_flags, ret_flags;
670     gss_OID actual_mech = GSS_C_NO_OID;
671     int ret = SASL_FAIL;
672     int initialContextToken;
673
674     *clientout = NULL;
675     *clientoutlen = 0;
676
677     if (text->gss_ctx == GSS_C_NO_CONTEXT) {
678         ret = gs2_ask_user_info(text, params, NULL, 0, prompt_need, oparams);
679         if (ret != SASL_OK)
680             goto cleanup;
681
682         if (params->gss_creds == GSS_C_NO_CREDENTIAL && 
683             text->password != NULL && text->password->len != 0) {
684             gss_buffer_desc password_buf;
685             gss_buffer_desc name_buf;
686             gss_OID_set_desc mechs;
687
688             name_buf.length = strlen(oparams->authid);
689             name_buf.value = (void *)oparams->authid;
690
691             password_buf.length = text->password->len;
692             password_buf.value = text->password->data;
693
694             mechs.count = 1;
695             mechs.elements = (gss_OID)text->mechanism;
696
697             maj_stat = gss_import_name(&min_stat,
698                                        &name_buf,
699                                        GSS_C_NT_USER_NAME,
700                                        &text->client_name);
701             if (GSS_ERROR(maj_stat))
702                 goto cleanup;
703
704             maj_stat = gss_acquire_cred_with_password(&min_stat,
705                                                       text->client_name,
706                                                       &password_buf,
707                                                       GSS_C_INDEFINITE,
708                                                       &mechs,
709                                                       GSS_C_INITIATE,
710                                                       &text->client_creds,
711                                                       NULL,
712                                                       &text->lifetime);
713             if (GSS_ERROR(maj_stat))
714                 goto cleanup;
715         }
716
717         initialContextToken = 1;
718     } else
719         initialContextToken = 0;
720
721     if (text->server_name == GSS_C_NO_NAME) { /* only once */
722         name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
723         name_buf.value = params->utils->malloc(name_buf.length + 1);
724         if (name_buf.value == NULL) {
725             ret = SASL_NOMEM;
726             goto cleanup;
727         }
728         if (params->serverFQDN == NULL ||
729             strlen(params->serverFQDN) == 0) {
730             SETERROR(text->utils, "GS2 Failure: no serverFQDN");
731             ret = SASL_FAIL;
732             goto cleanup;
733         }
734
735         snprintf(name_buf.value, name_buf.length + 1,
736                  "%s@%s", params->service, params->serverFQDN);
737
738         maj_stat = gss_import_name (&min_stat,
739                                     &name_buf,
740                                     GSS_C_NT_HOSTBASED_SERVICE,
741                                     &text->server_name);
742         params->utils->free(name_buf.value);
743         name_buf.value = NULL;
744
745         if (GSS_ERROR(maj_stat))
746             goto cleanup;
747     }
748
749     /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
750     if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
751         gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
752         text->gss_ctx = GSS_C_NO_CONTEXT;
753     }
754
755     input_token.value = (void *)serverin;
756     input_token.length = serverinlen;
757
758     if (initialContextToken) {
759         if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
760             text->gs2_flags |= GS2_NONSTD_FLAG;
761
762         switch (params->chanbindingflag) {
763         case SASL_CB_FLAG_NONE:
764             text->gs2_flags |= GS2_CB_FLAG_N;
765             break;
766         case SASL_CB_FLAG_USED:
767             text->gs2_flags |= GS2_CB_FLAG_P;
768             break;
769         case SASL_CB_FLAG_WANT:
770             text->gs2_flags |= GS2_CB_FLAG_Y;
771             break;
772         }
773
774         ret = gs2_make_header(text, params,
775                               strcmp(oparams->user, oparams->authid) ?
776                                      (char *) oparams->user : NULL,
777                               &text->out_buf, &text->out_buf_len);
778         if (ret != 0)
779             goto cleanup;
780     }
781
782     req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
783
784     maj_stat = gss_init_sec_context(&min_stat,
785                                     (params->gss_creds != GSS_C_NO_CREDENTIAL)
786                                         ? params->gss_creds
787                                         : text->client_creds,
788                                     &text->gss_ctx,
789                                     text->server_name,
790                                     (gss_OID)text->mechanism,
791                                     req_flags,
792                                     GSS_C_INDEFINITE,
793                                     &text->bindings,
794                                     serverinlen ? &input_token : GSS_C_NO_BUFFER,
795                                     NULL,
796                                     &output_token,
797                                     &ret_flags,
798                                     &text->lifetime);
799     if (GSS_ERROR(maj_stat))
800         goto cleanup;
801
802     ret = gs2_make_message(text, params, initialContextToken, &output_token,
803                            &text->out_buf, &text->out_buf_len);
804     if (ret != 0)
805         goto cleanup;
806
807     *clientout = text->out_buf;
808     *clientoutlen = text->out_buf_len;
809
810     if (maj_stat == GSS_S_CONTINUE_NEEDED) {
811         ret = SASL_CONTINUE;
812         goto cleanup;
813     }
814
815     if (text->client_name != GSS_C_NO_NAME)
816         gss_release_name(&min_stat, &text->client_name);
817
818     maj_stat = gss_inquire_context(&min_stat,
819                                    text->gss_ctx,
820                                    &text->client_name,
821                                    NULL,
822                                    &text->lifetime,
823                                    &actual_mech,
824                                    &ret_flags, /* flags */
825                                    NULL,
826                                    NULL);
827     if (GSS_ERROR(maj_stat))
828         goto cleanup;
829
830     if (!g_OID_equal(text->mechanism, actual_mech)) {
831         ret = SASL_WRONGMECH;
832         goto cleanup;
833     }
834     if ((ret_flags & req_flags) != req_flags) {
835         maj_stat = SASL_BADAUTH;
836         goto cleanup;
837     }
838
839     maj_stat = gss_display_name(&min_stat,
840                                 text->client_name,
841                                 &name_buf,
842                                 NULL);
843     if (GSS_ERROR(maj_stat))
844         goto cleanup;
845
846     oparams->gss_peer_name = text->server_name;
847     oparams->gss_local_name = text->client_name;
848     oparams->encode = NULL;
849     oparams->decode = NULL;
850     oparams->mech_ssf = 0;
851     oparams->maxoutbuf = 0xFFFFFF;
852     oparams->doneflag = 1;
853
854 cleanup:
855     gss_release_buffer(&min_stat, &output_token);
856     gss_release_buffer(&min_stat, &name_buf);
857     gss_release_oid(&min_stat, &actual_mech);
858
859     if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
860         sasl_gs2_seterror(text->utils, maj_stat, min_stat);
861         ret = SASL_FAIL;
862     }
863     if (ret != SASL_OK && ret != SASL_CONTINUE)
864         sasl_gs2_free_context_contents(text);
865
866     return ret;
867 }
868
869 static int gs2_client_mech_new(void *glob_context,
870                                sasl_client_params_t *params,
871                                void **conn_context)
872 {
873     context_t *text;
874     int ret;
875
876     text = sasl_gs2_new_context(params->utils);
877     if (text == NULL) {
878         MEMERROR(params->utils);
879         return SASL_NOMEM;
880     }
881
882     text->gss_ctx = GSS_C_NO_CONTEXT;
883     text->client_name = GSS_C_NO_NAME;
884     text->server_creds = GSS_C_NO_CREDENTIAL;
885     text->client_creds  = GSS_C_NO_CREDENTIAL;
886     text->plug.client = glob_context;
887
888     ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
889                             &text->mechanism);
890     if (ret != SASL_OK) {
891         gs2_common_mech_dispose(text, params->utils);
892         return ret;
893     } 
894
895     *conn_context = text;
896
897     return SASL_OK;
898 }
899
900 static const unsigned long gs2_required_prompts[] = {
901     SASL_CB_LIST_END
902 };
903
904 static int
905 gs2_client_plug_alloc(const sasl_utils_t *utils,
906                       void *plug,
907                       gss_buffer_t sasl_name,
908                       gss_OID mech)
909 {
910     int ret;
911     sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
912     gss_buffer_desc buf;
913
914     memset(cplug, 0, sizeof(*cplug));
915
916     ret = gs2_get_mech_attrs(utils, mech,
917                              &cplug->security_flags,
918                              &cplug->features);
919     if (ret != SASL_OK)
920         return ret;
921
922     ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
923     if (ret != SASL_OK)
924         return ret;
925
926     cplug->mech_name = (char *)buf.value;
927     cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
928     cplug->glob_context = plug;
929     cplug->mech_new = gs2_client_mech_new;
930     cplug->mech_step = gs2_client_mech_step;
931     cplug->mech_dispose = gs2_common_mech_dispose;
932     cplug->mech_free = gs2_common_mech_free;
933     cplug->required_prompts = gs2_required_prompts;
934
935     return SASL_OK;
936 }
937
938 static sasl_client_plug_t *gs2_client_plugins;
939 static int gs2_client_plugcount;
940
941 int
942 gs2_client_plug_init(const sasl_utils_t *utils,
943                      int maxversion,
944                      int *outversion,
945                      sasl_client_plug_t **pluglist,
946                      int *plugcount)
947 {
948     int ret;
949
950     *pluglist = NULL;
951     *plugcount = 0;
952
953     if (maxversion < SASL_CLIENT_PLUG_VERSION)
954         return SASL_BADVERS;
955
956     *outversion = SASL_CLIENT_PLUG_VERSION;
957
958     if (gs2_client_plugins == NULL) {
959         ret = gs2_common_plug_init(utils,
960                                    sizeof(sasl_client_plug_t),
961                                    gs2_client_plug_alloc,
962                                    (void **)&gs2_client_plugins,
963                                    &gs2_client_plugcount);
964         if (ret != SASL_OK)
965             return ret;
966     }
967
968     *pluglist = gs2_client_plugins;
969     *plugcount = gs2_client_plugcount;
970
971     return SASL_OK;
972 }
973
974 /*
975  * Copy header and application channel bindings to GSS channel bindings
976  * structure in context.
977  */
978 static int
979 gs2_save_cbindings(context_t *text,
980                    gss_buffer_t header,
981                    const char *chanbindingdata,
982                    unsigned int chanbindinglen)
983 {
984     gss_buffer_t gss_bindings = &text->bindings.application_data;
985     size_t len;
986     unsigned char *p;
987
988     assert(gss_bindings->value == NULL);
989
990     /*
991      * The application-data field MUST be set to the gs2-header, excluding
992      * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
993      * gs2-cb-flag of "p" is used, the application's channel binding data.
994      */
995     len = header->length;
996     if (text->gs2_flags & GS2_NONSTD_FLAG) {
997         assert(len > 2);
998         len -= 2;
999     }
1000     if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P)
1001         len += chanbindinglen;
1002
1003     gss_bindings->length = len;
1004     gss_bindings->value = text->utils->malloc(len);
1005     if (gss_bindings->value == NULL)
1006         return SASL_NOMEM;
1007
1008     p = (unsigned char *)gss_bindings->value;
1009     if (text->gs2_flags & GS2_NONSTD_FLAG) {
1010         memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
1011         p += header->length - 2;
1012     } else {
1013         memcpy(p, header->value, header->length);
1014         p += header->length;
1015     }
1016
1017     if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
1018         chanbindinglen != 0) {
1019         memcpy(p, chanbindingdata, chanbindinglen);
1020     }
1021
1022     return SASL_OK;
1023 }
1024
1025 #define CHECK_REMAIN(n)     do { if (remain < (n)) return SASL_BADAUTH; } while (0)
1026
1027 /*
1028  * Verify gs2-header, save authzid and channel bindings to context.
1029  */
1030 static int
1031 gs2_verify_initial_message(context_t *text,
1032                            sasl_server_params_t *sparams,
1033                            const char *in,
1034                            unsigned inlen,
1035                            gss_buffer_t token)
1036 {
1037     char *p = (char *)in;
1038     unsigned remain = inlen;
1039     int ret;
1040     gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1041
1042     assert(text->cb_name == NULL);
1043     assert(text->authzid == NULL);
1044
1045     token->length = 0;
1046     token->value = NULL;
1047
1048     /* minimum header includes CB flag and non-zero GSS token */
1049     CHECK_REMAIN(4); /* [pny],,. */
1050
1051     /* non-standard GSS framing flag */
1052     if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1053         text->gs2_flags |= GS2_NONSTD_FLAG;
1054         remain -= 2;
1055         p += 2;
1056     }
1057
1058     /* SASL channel bindings */
1059     CHECK_REMAIN(1); /* [pny] */
1060     remain--;
1061     switch (*p++) {
1062     case 'p':
1063         CHECK_REMAIN(1); /* = */
1064         remain--;
1065         if (*p++ != '=')
1066             return SASL_BADAUTH;
1067
1068         ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cb_name);
1069         if (ret != SASL_OK)
1070             return ret;
1071
1072         text->gs2_flags |= GS2_CB_FLAG_P;
1073         break;
1074     case 'n':
1075         text->gs2_flags |= GS2_CB_FLAG_N;
1076         break;
1077     case 'y':
1078         text->gs2_flags |= GS2_CB_FLAG_Y;
1079         break;
1080     }
1081
1082     CHECK_REMAIN(1); /* , */
1083     remain--;
1084     if (*p++ != ',')
1085         return SASL_BADAUTH;
1086
1087     /* authorization identity */
1088     if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1089         CHECK_REMAIN(2);
1090         remain -= 2;
1091         p += 2;
1092
1093         ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1094         if (ret != SASL_OK)
1095             return ret;
1096     }
1097
1098     /* end of header */ 
1099     CHECK_REMAIN(1); /* , */
1100     remain--;
1101     if (*p++ != ',')
1102         return SASL_BADAUTH;
1103
1104     buf.length = inlen - remain;
1105     buf.value = (void *)in;
1106
1107     /* stash channel bindings to pass into gss_accept_sec_context() */
1108     ret = gs2_save_cbindings(text, &buf, sparams->chanbindingdata,
1109                              sparams->chanbindinglen);
1110     if (ret != SASL_OK)
1111         return ret;
1112
1113     buf.length = remain;
1114     buf.value = p;
1115
1116     if (text->gs2_flags & GS2_NONSTD_FLAG) {
1117         token->value = text->utils->malloc(buf.length);
1118         if (token->value == NULL)
1119             return SASL_NOMEM;
1120
1121         token->length = buf.length;
1122         memcpy(token->value, buf.value, buf.length);
1123     } else {
1124         unsigned int token_size;
1125
1126         /* create a properly formed GSS token */ 
1127         token_size = gs2_token_size(text->mechanism, buf.length);
1128         token->value = text->utils->malloc(token_size);
1129         if (token->value == NULL)
1130             return SASL_NOMEM;
1131
1132         token->length = token_size;
1133
1134         p = (char *)token->value;
1135         gs2_make_token_header(text->mechanism, buf.length,
1136                               (unsigned char **)&p);
1137         memcpy(p, buf.value, buf.length);
1138     }
1139
1140     return SASL_OK;
1141 }
1142
1143 /*
1144  * Create gs2-header, save channel bindings to context.
1145  */
1146 static int
1147 gs2_make_header(context_t *text,
1148                 sasl_client_params_t *cparams,
1149                 const char *authzid,
1150                 char **out,
1151                 unsigned *outlen)
1152 {
1153     size_t required = 0;
1154     size_t wire_authzid_len = 0, cb_name_len = 0;
1155     char *wire_authzid = NULL;
1156     char *p;
1157     int ret;
1158     gss_buffer_desc buf;
1159
1160     *out = NULL;
1161     *outlen = 0;
1162
1163     /* non-standard GSS framing flag */
1164     if (text->gs2_flags & GS2_NONSTD_FLAG)
1165         required += 2; /* F, */
1166
1167     /* SASL channel bindings */
1168     switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1169     case GS2_CB_FLAG_P:
1170         if (cparams->chanbindingtype == NULL)
1171             return SASL_BADPARAM;
1172         cb_name_len = strlen(cparams->chanbindingtype);
1173         required += 1 /*=*/ + cb_name_len;
1174         /* fallthrough */
1175     case GS2_CB_FLAG_N:
1176     case GS2_CB_FLAG_Y:
1177         required += 2; /* [pny], */
1178         break;
1179     default:
1180         return SASL_BADPARAM;
1181     }
1182
1183     /* authorization identity */
1184     if (authzid != NULL) {
1185         ret = gs2_escape_authzid(text->utils, authzid,
1186                                  strlen(authzid), &wire_authzid);
1187         if (ret != SASL_OK)
1188             return ret;
1189
1190         wire_authzid_len = strlen(wire_authzid);
1191         required += 2 /* a= */ + wire_authzid_len;
1192     }
1193
1194     required += 1; /* trailing comma */
1195
1196     ret = _plug_buf_alloc(text->utils, out, outlen, required);
1197     if (ret != SASL_OK) {
1198         text->utils->free(wire_authzid);
1199         return ret;
1200     }
1201
1202     *out = text->out_buf;
1203     *outlen = required;
1204
1205     p = (char *)text->out_buf;
1206     if (text->gs2_flags & GS2_NONSTD_FLAG) {
1207         *p++ = 'F';
1208         *p++ = ',';
1209     }
1210     switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1211     case GS2_CB_FLAG_P:
1212         memcpy(p, "p=", 2);
1213         memcpy(p + 2, cparams->chanbindingtype, cb_name_len);
1214         p += 2 + cb_name_len;
1215         break;
1216     case GS2_CB_FLAG_N:
1217         *p++ = 'n';
1218         break;
1219     case GS2_CB_FLAG_Y:
1220         *p++ = 'y';
1221         break;
1222     }
1223     *p++ = ',';
1224     if (wire_authzid != NULL) {
1225         memcpy(p, "a=", 2);
1226         memcpy(p + 2, wire_authzid, wire_authzid_len);
1227         text->utils->free(wire_authzid);
1228         p += 2 + wire_authzid_len;
1229     }
1230     *p++ = ',';
1231
1232     assert(p == (char *)text->out_buf + required);
1233
1234     buf.length = required;
1235     buf.value = *out;
1236
1237     ret = gs2_save_cbindings(text, &buf, cparams->chanbindingdata,
1238                              cparams->chanbindinglen);
1239
1240     return ret;
1241 }
1242
1243 /*
1244  * Convert a GSS token to a GS2 one
1245  */
1246 static int
1247 gs2_make_message(context_t *text,
1248                  sasl_client_params_t *cparams __attribute__((unused)),
1249                  int initialContextToken,
1250                  gss_buffer_t token,
1251                  char **out,
1252                  unsigned *outlen)
1253 {
1254     OM_uint32 major, minor;
1255     unsigned char *mech_token_data;
1256     size_t mech_token_size;
1257     char *p;
1258     unsigned header_len = 0;
1259     int ret;
1260
1261     mech_token_size = token->length;
1262     mech_token_data = (unsigned char *)token->value;
1263
1264     if (initialContextToken) {
1265         header_len = *outlen;
1266
1267         major = gs2_verify_token_header(&minor, text->mechanism,
1268                                         &mech_token_size, &mech_token_data,
1269                                         token->length);
1270         if ((major == GSS_S_DEFECTIVE_TOKEN &&
1271              (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1272             GSS_ERROR(major))
1273             return SASL_FAIL;
1274     }
1275
1276     ret = _plug_buf_alloc(text->utils, out, outlen,
1277                           header_len + mech_token_size);
1278     if (ret != 0)
1279         return ret;
1280
1281     p = *out + header_len;
1282     memcpy(p, mech_token_data, mech_token_size);
1283
1284     *outlen = header_len + mech_token_size;
1285
1286     return SASL_OK;
1287 }
1288
1289 /*
1290  * Map GSS mechanism attributes to SASL ones
1291  */
1292 static int
1293 gs2_get_mech_attrs(const sasl_utils_t *utils,
1294                    const gss_OID mech,
1295                    unsigned int *security_flags,
1296                    unsigned int *features)
1297 {
1298     OM_uint32 major, minor;
1299     int present, ret;
1300     gss_OID_set attrs = GSS_C_NO_OID_SET;
1301
1302     major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1303     if (GSS_ERROR(major)) {
1304         utils->seterror(utils->conn, SASL_NOLOG,
1305                         "GS2 Failure: gss_inquire_attrs_for_mech");
1306         return SASL_FAIL;
1307     }
1308
1309     *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1310     *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1311
1312 #define MA_PRESENT(a)   (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1313                                                  attrs, &present) == GSS_S_COMPLETE && \
1314                          present)
1315
1316     ret = SASL_OK;
1317
1318     if (MA_PRESENT(GSS_C_MA_PFS))
1319         *security_flags |= SASL_SEC_FORWARD_SECRECY;
1320     if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1321         *security_flags |= SASL_SEC_NOANONYMOUS;
1322     if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1323         *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1324     if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1325         *security_flags |= SASL_SEC_MUTUAL_AUTH;
1326     if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1327         *features |= SASL_FEAT_GSS_FRAMING;
1328
1329     gss_release_oid_set(&minor, &attrs);
1330     return ret;
1331 }
1332
1333 /*
1334  * Enumerate GSS mechanisms that can be used for GS2
1335  */
1336 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1337 {
1338     OM_uint32 major, minor;
1339     gss_OID_desc desired_oids[3];
1340     gss_OID_set_desc desired_attrs;
1341     gss_OID_desc except_oids[3];
1342     gss_OID_set_desc except_attrs;
1343
1344     if (gs2_mechs != GSS_C_NO_OID_SET)
1345         return SASL_OK;
1346
1347     desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1348     desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1349     desired_oids[2] = *GSS_C_MA_CBINDINGS;
1350     desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1351     desired_attrs.elements = desired_oids;
1352
1353     except_oids[0] = *GSS_C_MA_MECH_NEGO;
1354     except_oids[1] = *GSS_C_MA_NOT_MECH;
1355     except_oids[2] = *GSS_C_MA_DEPRECATED;
1356
1357     except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1358     except_attrs.elements = except_oids;
1359
1360     major = gss_indicate_mechs_by_attrs(&minor,
1361                                         &desired_attrs,
1362                                         &except_attrs,
1363                                         GSS_C_NO_OID_SET,
1364                                         &gs2_mechs);
1365     if (GSS_ERROR(major)) {
1366         utils->seterror(utils->conn, SASL_NOLOG,
1367                         "GS2 Failure: gss_indicate_mechs_by_attrs");
1368         return SASL_FAIL;
1369     }
1370
1371     return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1372 }
1373
1374 /*
1375  * Map SASL mechanism name to OID
1376  */
1377 static int
1378 gs2_map_sasl_name(const sasl_utils_t *utils,
1379                   const char *mech,
1380                   gss_OID *oid)
1381 {
1382     OM_uint32 major, minor;
1383     gss_buffer_desc buf;
1384
1385     buf.length = strlen(mech);
1386     buf.value = (void *)mech;
1387
1388     major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1389     if (GSS_ERROR(major)) {
1390         utils->seterror(utils->conn, SASL_NOLOG,
1391                         "GS2 Failure: gss_inquire_mech_for_saslname");
1392         return SASL_FAIL;
1393     }
1394
1395     return SASL_OK;
1396 }
1397
1398 static int
1399 gs2_duplicate_buffer(const sasl_utils_t *utils,
1400                      const gss_buffer_t src,
1401                      gss_buffer_t dst)
1402 {
1403     dst->value = utils->malloc(src->length + 1);
1404     if (dst->value == NULL)
1405         return SASL_NOMEM;
1406
1407     memcpy(dst->value, src->value, src->length);
1408     ((char *)dst->value)[src->length] = '\0';
1409     dst->length = src->length;
1410
1411     return SASL_OK;
1412 }
1413
1414 static int
1415 gs2_unescape_authzid(const sasl_utils_t *utils,
1416                      char **endp,
1417                      unsigned *remain,
1418                      char **authzid)
1419 {
1420     char *in = *endp;
1421     size_t i, len, inlen = *remain;
1422     char *p;
1423
1424     *endp = NULL;
1425
1426     for (i = 0, len = 0; i < inlen; i++) {
1427         if (in[i] == ',') {
1428             *endp = &in[i];
1429             *remain -= i;
1430             break;
1431         } else if (in[i] == '=') {
1432             if (inlen <= i + 2)
1433                 return SASL_BADPROT;
1434             i += 2;
1435         }
1436         len++;
1437     }
1438
1439     if (len == 0 || *endp == NULL)
1440         return SASL_BADPROT;
1441
1442     p = *authzid = utils->malloc(len + 1);
1443     if (*authzid == NULL)
1444         return SASL_NOMEM;
1445
1446     for (i = 0; i < inlen; i++) {
1447         if (in[i] == ',')
1448             break;
1449         else if (in[i] == '=') {
1450             if (memcmp(&in[i + 1], "2C", 2) == 0)
1451                 *p++ = ',';
1452             else if (memcmp(&in[i + 1], "3D", 2) == 0)
1453                 *p++ = '=';
1454             else {
1455                 utils->free(*authzid);
1456                 *authzid = NULL;
1457                 return SASL_BADPROT;
1458             }
1459             i += 2;
1460         } else
1461             *p++ = in[i];
1462     }
1463
1464     *p = '\0';
1465
1466     return SASL_OK;
1467 }
1468
1469 static int
1470 gs2_escape_authzid(const sasl_utils_t *utils,
1471                    const char *in,
1472                    unsigned inlen,
1473                    char **authzid)
1474 {
1475     size_t i;
1476     char *p;
1477
1478     p = *authzid = utils->malloc((inlen * 3) + 1);
1479     if (*authzid == NULL)
1480         return SASL_NOMEM;
1481
1482     for (i = 0; i < inlen; i++) {
1483         if (in[i] == ',') {
1484             memcpy(p, "=2C", 3);
1485             p += 3;
1486         } else if (in[i] == '=') {
1487             memcpy(p, "=3D", 3);
1488             p += 3;
1489         } else {
1490             *p++ = in[i];
1491         }
1492     }
1493
1494     *p = '\0';
1495
1496     return SASL_OK;
1497 }
1498
1499 static int
1500 gs2_ask_user_info(context_t *text,
1501                   sasl_client_params_t *params,
1502                   char **realms __attribute__((unused)),
1503                   int nrealm __attribute__((unused)),
1504                   sasl_interact_t **prompt_need,
1505                   sasl_out_params_t *oparams)
1506 {
1507     int result = SASL_OK;
1508     const char *authid = NULL, *userid = NULL;
1509     int user_result = SASL_OK;
1510     int auth_result = SASL_OK;
1511     int pass_result = SASL_OK;
1512
1513     /* try to get the authid */
1514     if (oparams->authid == NULL) {
1515         auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1516
1517         if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1518             return auth_result;
1519         }
1520     }
1521
1522     /* try to get the userid */
1523     if (oparams->user == NULL) {
1524         user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1525
1526         if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1527             return user_result;
1528         }
1529     }
1530
1531     /* try to get the password */
1532     if (text->password == NULL) {
1533         pass_result = _plug_get_password(params->utils, &text->password,
1534                                          &text->free_password, prompt_need);
1535         if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1536             return pass_result;
1537         }
1538     }
1539
1540     /* free prompts we got */
1541     if (prompt_need && *prompt_need) {
1542         params->utils->free(*prompt_need);
1543         *prompt_need = NULL;
1544     }
1545
1546     /* if there are prompts not filled in */
1547     if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1548         pass_result == SASL_INTERACT) {
1549
1550         /* make the prompt list */
1551         result =
1552             _plug_make_prompts(params->utils, prompt_need,
1553                                user_result == SASL_INTERACT ?
1554                                "Please enter your authorization name" : NULL,
1555                                NULL,
1556                                auth_result == SASL_INTERACT ?
1557                                "Please enter your authentication name" : NULL,
1558                                NULL,
1559                                pass_result == SASL_INTERACT ?
1560                                "Please enter your password" : NULL, NULL,
1561                                NULL, NULL, NULL,
1562                                NULL,
1563                                NULL, NULL);
1564         if (result == SASL_OK) return SASL_INTERACT;
1565
1566         return result;
1567     }
1568
1569     if (oparams->authid == NULL) {
1570         if (userid == NULL || userid[0] == '\0') {
1571             result = params->canon_user(params->utils->conn, authid, 0,
1572                                         SASL_CU_AUTHID | SASL_CU_AUTHZID,
1573                                         oparams);
1574         } else {
1575             result = params->canon_user(params->utils->conn,
1576                                         authid, 0, SASL_CU_AUTHID, oparams);
1577             if (result != SASL_OK) return result;
1578
1579             result = params->canon_user(params->utils->conn,
1580                                         userid, 0, SASL_CU_AUTHZID, oparams);
1581         }
1582         if (result != SASL_OK)
1583             return result;
1584     }
1585
1586     return result;
1587 }
1588
1589 static int
1590 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1591                    int logonly)
1592 {
1593     OM_uint32 maj_stat, min_stat;
1594     gss_buffer_desc msg;
1595     OM_uint32 msg_ctx;
1596     int ret;
1597     char *out = NULL;
1598     unsigned int len, curlen = 0;
1599     const char prefix[] = "GSSAPI Error: ";
1600
1601     len = sizeof(prefix);
1602     ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1603     if (ret != SASL_OK)
1604         return SASL_OK;
1605
1606     strcpy(out, prefix);
1607
1608     msg_ctx = 0;
1609     while (1) {
1610         maj_stat = gss_display_status(&min_stat, maj,
1611                                       GSS_C_GSS_CODE, GSS_C_NULL_OID,
1612                                       &msg_ctx, &msg);
1613
1614         if (GSS_ERROR(maj_stat)) {
1615             if (logonly) {
1616                 utils->log(utils->conn, SASL_LOG_FAIL,
1617                         "GS2 Failure: (could not get major error message)");
1618             } else {
1619                 utils->seterror(utils->conn, 0,
1620                                 "GS2 Failure "
1621                                 "(could not get major error message)");
1622             }
1623             utils->free(out);
1624             return SASL_OK;
1625         }
1626
1627         len += len + msg.length;
1628         ret = _plug_buf_alloc(utils, &out, &curlen, len);
1629         if (ret != SASL_OK) {
1630             utils->free(out);
1631             return SASL_OK;
1632         }
1633
1634         strcat(out, msg.value);
1635
1636         gss_release_buffer(&min_stat, &msg);
1637
1638         if (!msg_ctx)
1639             break;
1640     }
1641
1642     /* Now get the minor status */
1643
1644     len += 2;
1645     ret = _plug_buf_alloc(utils, &out, &curlen, len);
1646     if (ret != SASL_OK) {
1647         utils->free(out);
1648         return SASL_NOMEM;
1649     }
1650
1651     strcat(out, " (");
1652
1653     msg_ctx = 0;
1654     while (1) {
1655         maj_stat = gss_display_status(&min_stat, min,
1656                                       GSS_C_MECH_CODE, GSS_C_NULL_OID,
1657                                       &msg_ctx, &msg);
1658
1659         if (GSS_ERROR(maj_stat)) {
1660             if (logonly) {
1661                 utils->log(utils->conn, SASL_LOG_FAIL,
1662                         "GS2 Failure: (could not get minor error message)");
1663             } else {
1664                 utils->seterror(utils->conn, 0,
1665                                 "GS2 Failure "
1666                                 "(could not get minor error message)");
1667             }
1668             utils->free(out);
1669             return SASL_OK;
1670         }
1671
1672         len += len + msg.length;
1673
1674         ret = _plug_buf_alloc(utils, &out, &curlen, len);
1675         if (ret != SASL_OK) {
1676             utils->free(out);
1677             return SASL_NOMEM;
1678         }
1679
1680         strcat(out, msg.value);
1681
1682         gss_release_buffer(&min_stat, &msg);
1683
1684         if (!msg_ctx)
1685             break;
1686     }
1687
1688     len += 1;
1689     ret = _plug_buf_alloc(utils, &out, &curlen, len);
1690     if (ret != SASL_OK) {
1691         utils->free(out);
1692         return SASL_NOMEM;
1693     }
1694
1695     strcat(out, ")");
1696
1697     if (logonly) {
1698         utils->log(utils->conn, SASL_LOG_FAIL, out);
1699     } else {
1700         utils->seterror(utils->conn, 0, out);
1701     }
1702     utils->free(out);
1703
1704     return SASL_OK;
1705 }