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