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