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