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