2 * Copyright (c) 2010, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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
33 * Copyright (c) 1998-2003 Carnegie Mellon University.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
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
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
55 * Pittsburgh, PA 15213-3890
56 * (412) 268-4387, fax: (412) 268-7395
57 * tech-transfer@andrew.cmu.edu
59 * 4. Redistributions of any form whatsoever must retain the following
61 * "This product includes software developed by Computing Services
62 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
74 #include <gssapi/gssapi.h>
81 #include "plugin_common.h"
89 #include "gs2_token.h"
91 #define GS2_CB_FLAG_MASK 0x0F
92 #define GS2_CB_FLAG_N 0x00
93 #define GS2_CB_FLAG_P 0x01
94 #define GS2_CB_FLAG_Y 0x02
95 #define GS2_NONSTD_FLAG 0x10
97 typedef struct context {
99 gss_name_t client_name;
100 gss_name_t server_name;
101 gss_cred_id_t server_creds;
102 gss_cred_id_t client_creds;
104 unsigned out_buf_len;
105 const sasl_utils_t *utils;
109 sasl_client_plug_t *client;
110 sasl_server_plug_t *server;
115 struct gss_channel_bindings_struct gss_cbindings;
116 sasl_secret_t *password;
117 unsigned int free_password;
121 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
123 static int gs2_get_init_creds(context_t *context,
124 sasl_client_params_t *params,
125 sasl_interact_t **prompt_need,
126 sasl_out_params_t *oparams);
128 static int gs2_verify_initial_message(context_t *text,
129 sasl_server_params_t *sparams,
134 static int gs2_make_header(context_t *text,
135 sasl_client_params_t *cparams,
140 static int gs2_make_message(context_t *text,
141 sasl_client_params_t *cparams,
142 int initialContextToken,
147 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
149 unsigned int *security_flags,
150 unsigned int *features,
151 const unsigned long **prompts);
153 static int gs2_indicate_mechs(const sasl_utils_t *utils);
155 static int gs2_map_sasl_name(const sasl_utils_t *utils,
159 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
160 const gss_buffer_t src,
163 static int gs2_unescape_authzid(const sasl_utils_t *utils,
168 static int gs2_escape_authzid(const sasl_utils_t *utils,
173 /* sasl_gs_log: only logs status string returned from gss_display_status() */
174 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
175 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
178 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
182 sasl_gs2_new_context(const sasl_utils_t *utils)
186 ret = utils->malloc(sizeof(context_t));
190 memset(ret, 0, sizeof(context_t));
197 sasl_gs2_free_context_contents(context_t *text)
204 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
205 gss_delete_sec_context(&min_stat,&text->gss_ctx,
207 text->gss_ctx = GSS_C_NO_CONTEXT;
210 if (text->client_name != GSS_C_NO_NAME) {
211 gss_release_name(&min_stat,&text->client_name);
212 text->client_name = GSS_C_NO_NAME;
215 if (text->server_name != GSS_C_NO_NAME) {
216 gss_release_name(&min_stat,&text->server_name);
217 text->server_name = GSS_C_NO_NAME;
220 if (text->server_creds != GSS_C_NO_CREDENTIAL) {
221 gss_release_cred(&min_stat, &text->server_creds);
222 text->server_creds = GSS_C_NO_CREDENTIAL;
225 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
226 gss_release_cred(&min_stat, &text->client_creds);
227 text->client_creds = GSS_C_NO_CREDENTIAL;
230 if (text->authid != NULL) {
231 text->utils->free(text->authid);
235 if (text->authzid != NULL) {
236 text->utils->free(text->authzid);
237 text->authzid = NULL;
240 gss_release_buffer(&min_stat, &text->gss_cbindings.application_data);
242 if (text->out_buf != NULL) {
243 text->utils->free(text->out_buf);
244 text->out_buf = NULL;
247 text->out_buf_len = 0;
249 if (text->cbindingname != NULL) {
250 text->utils->free(text->cbindingname);
251 text->cbindingname = NULL;
254 if (text->free_password)
255 _plug_free_secret(text->utils, &text->password);
257 memset(text, 0, sizeof(*text));
263 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
265 sasl_gs2_free_context_contents((context_t *)(conn_context));
266 utils->free(conn_context);
270 gs2_common_mech_free(void *global_context __attribute__((unused)),
271 const sasl_utils_t *utils)
275 if (gs2_mechs != GSS_C_NO_OID_SET) {
276 gss_release_oid_set(&minor, &gs2_mechs);
277 gs2_mechs = GSS_C_NO_OID_SET;
281 /***************************** Server Section *****************************/
284 gs2_server_mech_new(void *glob_context,
285 sasl_server_params_t *params,
286 const char *challenge __attribute__((unused)),
287 unsigned challen __attribute__((unused)),
293 text = sasl_gs2_new_context(params->utils);
295 MEMERROR(params->utils);
299 text->gss_ctx = GSS_C_NO_CONTEXT;
300 text->client_name = GSS_C_NO_NAME;
301 text->server_name = GSS_C_NO_NAME;
302 text->server_creds = GSS_C_NO_CREDENTIAL;
303 text->client_creds = GSS_C_NO_CREDENTIAL;
304 text->plug.server = glob_context;
306 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
308 if (ret != SASL_OK) {
309 gs2_common_mech_dispose(text, params->utils);
313 *conn_context = text;
319 gs2_server_mech_step(void *conn_context,
320 sasl_server_params_t *params,
321 const char *clientin,
322 unsigned clientinlen,
323 const char **serverout,
324 unsigned *serveroutlen,
325 sasl_out_params_t *oparams)
327 context_t *text = (context_t *)conn_context;
328 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
329 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
330 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
331 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
332 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
333 gss_name_t without = GSS_C_NO_NAME;
334 gss_OID_set_desc mechs;
335 OM_uint32 out_flags = 0;
336 int ret = 0, equal = 0;
337 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
340 if (serverout == NULL) {
341 PARAMERROR(text->utils);
342 return SASL_BADPARAM;
348 if (initialContextToken) {
349 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
350 name_buf.value = params->utils->malloc(name_buf.length + 1);
351 if (name_buf.value == NULL) {
352 MEMERROR(text->utils);
356 snprintf(name_buf.value, name_buf.length + 1,
357 "%s@%s", params->service, params->serverFQDN);
358 maj_stat = gss_import_name(&min_stat,
360 GSS_C_NT_HOSTBASED_SERVICE,
362 params->utils->free(name_buf.value);
363 name_buf.value = NULL;
365 if (GSS_ERROR(maj_stat))
368 assert(text->server_creds == GSS_C_NO_CREDENTIAL);
371 mechs.elements = (gss_OID)text->mechanism;
373 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
374 maj_stat = gss_acquire_cred(&min_stat,
382 if (GSS_ERROR(maj_stat))
386 ret = gs2_verify_initial_message(text,
394 input_token.value = (void *)clientin;
395 input_token.length = clientinlen;
398 maj_stat = gss_accept_sec_context(&min_stat,
400 (params->gss_creds != GSS_C_NO_CREDENTIAL)
401 ? (gss_cred_id_t)params->gss_creds
402 : text->server_creds,
404 &text->gss_cbindings,
410 &text->client_creds);
411 if (GSS_ERROR(maj_stat)) {
412 sasl_gs2_log(text->utils, maj_stat, min_stat);
413 text->utils->seterror(text->utils->conn, SASL_NOLOG,
414 "GS2 Failure: gss_accept_sec_context");
415 ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH;
419 *serveroutlen = output_token.length;
420 if (output_token.value != NULL) {
421 ret = _plug_buf_alloc(text->utils, &text->out_buf,
422 &text->out_buf_len, *serveroutlen);
425 memcpy(text->out_buf, output_token.value, *serveroutlen);
426 *serverout = text->out_buf;
428 /* No output token, send an empty string */
433 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
438 assert(maj_stat == GSS_S_COMPLETE);
440 if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) {
445 maj_stat = gss_display_name(&min_stat, text->client_name,
447 if (GSS_ERROR(maj_stat))
450 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
454 p = (char *)memchr(name_buf.value, '@', name_buf.length);
456 short_name_buf.length = (p - (char *)name_buf.value);
458 maj_stat = gss_import_name(&min_stat,
462 if (GSS_ERROR(maj_stat)) {
467 maj_stat = gss_compare_name(&min_stat, text->client_name,
469 if (GSS_ERROR(maj_stat)) {
475 ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
478 text->authid = (char *)short_name_buf.value;
479 short_name_buf.value = NULL;
480 short_name_buf.length = 0;
482 if (text->authzid != NULL) {
483 ret = params->canon_user(params->utils->conn,
485 SASL_CU_AUTHZID, oparams);
490 ret = params->canon_user(params->utils->conn,
492 text->authzid == NULL
493 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
499 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
501 oparams->cbindingdisp = SASL_CB_DISP_NONE;
504 oparams->cbindingdisp = SASL_CB_DISP_USED;
505 oparams->cbindingname = text->cbindingname;
508 oparams->cbindingdisp = SASL_CB_DISP_WANT;
512 if (text->client_creds != GSS_C_NO_CREDENTIAL)
513 oparams->client_creds = &text->client_creds;
515 oparams->client_creds = NULL;
517 oparams->gss_peer_name = text->client_name;
518 oparams->gss_local_name = text->server_name;
519 oparams->maxoutbuf = 0xFFFFFF;
520 oparams->encode = NULL;
521 oparams->decode = NULL;
522 oparams->mech_ssf = 0;
523 oparams->doneflag = 1;
528 if (initialContextToken)
529 gss_release_buffer(&min_stat, &input_token);
530 gss_release_buffer(&min_stat, &name_buf);
531 gss_release_buffer(&min_stat, &short_name_buf);
532 gss_release_buffer(&min_stat, &output_token);
533 gss_release_name(&min_stat, &without);
535 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
536 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
539 if (ret != SASL_OK && ret != SASL_CONTINUE)
540 sasl_gs2_free_context_contents(text);
546 gs2_common_plug_init(const sasl_utils_t *utils,
548 int (*plug_alloc)(const sasl_utils_t *,
555 OM_uint32 major, minor;
562 if (gs2_indicate_mechs(utils) != SASL_OK) {
566 plugs = utils->malloc(gs2_mechs->count * plugsize);
571 memset(plugs, 0, gs2_mechs->count * plugsize);
573 for (i = 0; i < gs2_mechs->count; i++) {
574 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
576 major = gss_inquire_saslname_for_mech(&minor,
577 &gs2_mechs->elements[i],
581 if (GSS_ERROR(major))
584 #define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize))
586 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
587 &gs2_mechs->elements[i]) == SASL_OK)
590 gss_release_buffer(&minor, &sasl_mech_name);
605 gs2_server_plug_alloc(const sasl_utils_t *utils,
607 gss_buffer_t sasl_name,
611 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
614 memset(splug, 0, sizeof(*splug));
616 ret = gs2_get_mech_attrs(utils, mech,
617 &splug->security_flags,
623 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
627 splug->mech_name = (char *)buf.value;
628 splug->glob_context = plug;
629 splug->mech_new = gs2_server_mech_new;
630 splug->mech_step = gs2_server_mech_step;
631 splug->mech_dispose = gs2_common_mech_dispose;
632 splug->mech_free = gs2_common_mech_free;
637 static sasl_server_plug_t *gs2_server_plugins;
638 static int gs2_server_plugcount;
641 gs2_server_plug_init(const sasl_utils_t *utils,
644 sasl_server_plug_t **pluglist,
652 if (maxversion < SASL_SERVER_PLUG_VERSION)
655 *outversion = SASL_SERVER_PLUG_VERSION;
657 if (gs2_server_plugins == NULL) {
658 ret = gs2_common_plug_init(utils,
659 sizeof(sasl_server_plug_t),
660 gs2_server_plug_alloc,
661 (void **)&gs2_server_plugins,
662 &gs2_server_plugcount);
667 *pluglist = gs2_server_plugins;
668 *plugcount = gs2_server_plugcount;
673 /***************************** Client Section *****************************/
675 static int gs2_client_mech_step(void *conn_context,
676 sasl_client_params_t *params,
677 const char *serverin,
678 unsigned serverinlen,
679 sasl_interact_t **prompt_need,
680 const char **clientout,
681 unsigned *clientoutlen,
682 sasl_out_params_t *oparams)
684 context_t *text = (context_t *)conn_context;
685 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
686 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
687 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
688 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
689 OM_uint32 req_flags, ret_flags;
691 int initialContextToken;
696 if (text->gss_ctx == GSS_C_NO_CONTEXT) {
697 ret = gs2_get_init_creds(text, params, prompt_need, oparams);
701 initialContextToken = 1;
703 initialContextToken = 0;
705 if (text->server_name == GSS_C_NO_NAME) { /* only once */
706 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
707 name_buf.value = params->utils->malloc(name_buf.length + 1);
708 if (name_buf.value == NULL) {
712 if (params->serverFQDN == NULL ||
713 strlen(params->serverFQDN) == 0) {
714 SETERROR(text->utils, "GS2 Failure: no serverFQDN");
719 snprintf(name_buf.value, name_buf.length + 1,
720 "%s@%s", params->service, params->serverFQDN);
722 maj_stat = gss_import_name(&min_stat,
724 GSS_C_NT_HOSTBASED_SERVICE,
726 params->utils->free(name_buf.value);
727 name_buf.value = NULL;
729 if (GSS_ERROR(maj_stat))
733 /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
734 if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
735 gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
736 text->gss_ctx = GSS_C_NO_CONTEXT;
739 input_token.value = (void *)serverin;
740 input_token.length = serverinlen;
742 if (initialContextToken) {
743 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
744 text->gs2_flags |= GS2_NONSTD_FLAG;
746 switch (params->cbindingdisp) {
747 case SASL_CB_DISP_NONE:
748 text->gs2_flags |= GS2_CB_FLAG_N;
750 case SASL_CB_DISP_USED:
751 text->gs2_flags |= GS2_CB_FLAG_P;
753 case SASL_CB_DISP_WANT:
754 text->gs2_flags |= GS2_CB_FLAG_Y;
758 ret = gs2_make_header(text, params,
759 strcmp(oparams->user, oparams->authid) ?
760 (char *) oparams->user : NULL,
761 &text->out_buf, &text->out_buf_len);
766 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
768 maj_stat = gss_init_sec_context(&min_stat,
769 (params->gss_creds != GSS_C_NO_CREDENTIAL)
770 ? (gss_cred_id_t)params->gss_creds
771 : text->client_creds,
774 (gss_OID)text->mechanism,
777 &text->gss_cbindings,
778 serverinlen ? &input_token : GSS_C_NO_BUFFER,
783 if (GSS_ERROR(maj_stat))
786 ret = gs2_make_message(text, params, initialContextToken, &output_token,
787 &text->out_buf, &text->out_buf_len);
791 *clientout = text->out_buf;
792 *clientoutlen = text->out_buf_len;
794 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
799 if (text->client_name != GSS_C_NO_NAME)
800 gss_release_name(&min_stat, &text->client_name);
802 maj_stat = gss_inquire_context(&min_stat,
808 &ret_flags, /* flags */
811 if (GSS_ERROR(maj_stat))
814 if ((ret_flags & req_flags) != req_flags) {
815 maj_stat = SASL_BADAUTH;
819 maj_stat = gss_display_name(&min_stat,
823 if (GSS_ERROR(maj_stat))
826 oparams->gss_peer_name = text->server_name;
827 oparams->gss_local_name = text->client_name;
828 oparams->encode = NULL;
829 oparams->decode = NULL;
830 oparams->mech_ssf = 0;
831 oparams->maxoutbuf = 0xFFFFFF;
832 oparams->doneflag = 1;
835 gss_release_buffer(&min_stat, &output_token);
836 gss_release_buffer(&min_stat, &name_buf);
838 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
839 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
842 if (ret != SASL_OK && ret != SASL_CONTINUE)
843 sasl_gs2_free_context_contents(text);
848 static int gs2_client_mech_new(void *glob_context,
849 sasl_client_params_t *params,
855 text = sasl_gs2_new_context(params->utils);
857 MEMERROR(params->utils);
861 text->gss_ctx = GSS_C_NO_CONTEXT;
862 text->client_name = GSS_C_NO_NAME;
863 text->server_creds = GSS_C_NO_CREDENTIAL;
864 text->client_creds = GSS_C_NO_CREDENTIAL;
865 text->plug.client = glob_context;
867 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
869 if (ret != SASL_OK) {
870 gs2_common_mech_dispose(text, params->utils);
874 *conn_context = text;
880 gs2_client_plug_alloc(const sasl_utils_t *utils,
882 gss_buffer_t sasl_name,
886 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
889 memset(cplug, 0, sizeof(*cplug));
891 ret = gs2_get_mech_attrs(utils, mech,
892 &cplug->security_flags,
894 &cplug->required_prompts);
898 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
902 cplug->mech_name = (char *)buf.value;
903 cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
904 cplug->glob_context = plug;
905 cplug->mech_new = gs2_client_mech_new;
906 cplug->mech_step = gs2_client_mech_step;
907 cplug->mech_dispose = gs2_common_mech_dispose;
908 cplug->mech_free = gs2_common_mech_free;
913 static sasl_client_plug_t *gs2_client_plugins;
914 static int gs2_client_plugcount;
917 gs2_client_plug_init(const sasl_utils_t *utils,
920 sasl_client_plug_t **pluglist,
928 if (maxversion < SASL_CLIENT_PLUG_VERSION)
931 *outversion = SASL_CLIENT_PLUG_VERSION;
933 if (gs2_client_plugins == NULL) {
934 ret = gs2_common_plug_init(utils,
935 sizeof(sasl_client_plug_t),
936 gs2_client_plug_alloc,
937 (void **)&gs2_client_plugins,
938 &gs2_client_plugcount);
943 *pluglist = gs2_client_plugins;
944 *plugcount = gs2_client_plugcount;
950 * Copy header and application channel bindings to GSS channel bindings
951 * structure in context.
954 gs2_save_cbindings(context_t *text,
956 const sasl_channel_binding_t *cbinding)
958 gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data;
962 assert(gss_cbindings->value == NULL);
965 * The application-data field MUST be set to the gs2-header, excluding
966 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
967 * gs2-cb-flag of "p" is used, the application's channel binding data.
969 len = header->length;
970 if (text->gs2_flags & GS2_NONSTD_FLAG) {
974 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
976 len += cbinding->len;
979 gss_cbindings->length = len;
980 gss_cbindings->value = text->utils->malloc(len);
981 if (gss_cbindings->value == NULL)
984 p = (unsigned char *)gss_cbindings->value;
985 if (text->gs2_flags & GS2_NONSTD_FLAG) {
986 memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
987 p += header->length - 2;
989 memcpy(p, header->value, header->length);
993 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
995 memcpy(p, cbinding->data, cbinding->len);
1001 #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0)
1004 * Verify gs2-header, save authzid and channel bindings to context.
1007 gs2_verify_initial_message(context_t *text,
1008 sasl_server_params_t *sparams,
1013 char *p = (char *)in;
1014 unsigned remain = inlen;
1016 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1018 assert(text->cbindingname == NULL);
1019 assert(text->authzid == NULL);
1022 token->value = NULL;
1024 /* minimum header includes CB flag and non-zero GSS token */
1025 CHECK_REMAIN(4); /* [pny],,. */
1027 /* non-standard GSS framing flag */
1028 if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1029 text->gs2_flags |= GS2_NONSTD_FLAG;
1034 /* SASL channel bindings */
1035 CHECK_REMAIN(1); /* [pny] */
1039 CHECK_REMAIN(1); /* = */
1042 return SASL_BADPROT;
1044 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname);
1048 text->gs2_flags |= GS2_CB_FLAG_P;
1051 text->gs2_flags |= GS2_CB_FLAG_N;
1054 text->gs2_flags |= GS2_CB_FLAG_Y;
1058 CHECK_REMAIN(1); /* , */
1061 return SASL_BADPROT;
1063 /* authorization identity */
1064 if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1069 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1075 CHECK_REMAIN(1); /* , */
1078 return SASL_BADPROT;
1080 buf.length = inlen - remain;
1081 buf.value = (void *)in;
1083 /* stash channel bindings to pass into gss_accept_sec_context() */
1084 ret = gs2_save_cbindings(text, &buf, sparams->cbinding);
1088 buf.length = remain;
1091 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1092 token->value = text->utils->malloc(buf.length);
1093 if (token->value == NULL)
1096 token->length = buf.length;
1097 memcpy(token->value, buf.value, buf.length);
1099 unsigned int token_size;
1101 /* create a properly formed GSS token */
1102 token_size = gs2_token_size(text->mechanism, buf.length);
1103 token->value = text->utils->malloc(token_size);
1104 if (token->value == NULL)
1107 token->length = token_size;
1109 p = (char *)token->value;
1110 gs2_make_token_header(text->mechanism, buf.length,
1111 (unsigned char **)&p);
1112 memcpy(p, buf.value, buf.length);
1119 * Create gs2-header, save channel bindings to context.
1122 gs2_make_header(context_t *text,
1123 sasl_client_params_t *cparams,
1124 const char *authzid,
1128 size_t required = 0;
1129 size_t wire_authzid_len = 0, cbnamelen = 0;
1130 char *wire_authzid = NULL;
1133 gss_buffer_desc buf;
1138 /* non-standard GSS framing flag */
1139 if (text->gs2_flags & GS2_NONSTD_FLAG)
1140 required += 2; /* F, */
1142 /* SASL channel bindings */
1143 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1145 if (!SASL_CB_PRESENT(cparams))
1146 return SASL_BADPARAM;
1147 cbnamelen = strlen(cparams->cbinding->name);
1148 required += 1 /*=*/ + cbnamelen;
1152 required += 2; /* [pny], */
1155 return SASL_BADPARAM;
1158 /* authorization identity */
1159 if (authzid != NULL) {
1160 ret = gs2_escape_authzid(text->utils, authzid,
1161 strlen(authzid), &wire_authzid);
1165 wire_authzid_len = strlen(wire_authzid);
1166 required += 2 /* a= */ + wire_authzid_len;
1169 required += 1; /* trailing comma */
1171 ret = _plug_buf_alloc(text->utils, out, outlen, required);
1172 if (ret != SASL_OK) {
1173 text->utils->free(wire_authzid);
1177 *out = text->out_buf;
1180 p = (char *)text->out_buf;
1181 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1185 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1188 memcpy(p + 2, cparams->cbinding->name, cbnamelen);
1199 if (wire_authzid != NULL) {
1201 memcpy(p + 2, wire_authzid, wire_authzid_len);
1202 text->utils->free(wire_authzid);
1203 p += 2 + wire_authzid_len;
1207 assert(p == (char *)text->out_buf + required);
1209 buf.length = required;
1212 ret = gs2_save_cbindings(text, &buf, cparams->cbinding);
1220 * Convert a GSS token to a GS2 one
1223 gs2_make_message(context_t *text,
1224 sasl_client_params_t *cparams __attribute__((unused)),
1225 int initialContextToken,
1230 OM_uint32 major, minor;
1231 unsigned char *mech_token_data;
1232 size_t mech_token_size;
1234 unsigned header_len = 0;
1237 mech_token_size = token->length;
1238 mech_token_data = (unsigned char *)token->value;
1240 if (initialContextToken) {
1241 header_len = *outlen;
1243 major = gs2_verify_token_header(&minor, text->mechanism,
1244 &mech_token_size, &mech_token_data,
1246 if ((major == GSS_S_DEFECTIVE_TOKEN &&
1247 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1252 ret = _plug_buf_alloc(text->utils, out, outlen,
1253 header_len + mech_token_size);
1257 p = *out + header_len;
1258 memcpy(p, mech_token_data, mech_token_size);
1260 *outlen = header_len + mech_token_size;
1265 static const unsigned long gs2_required_prompts[] = {
1270 * Map GSS mechanism attributes to SASL ones
1273 gs2_get_mech_attrs(const sasl_utils_t *utils,
1275 unsigned int *security_flags,
1276 unsigned int *features,
1277 const unsigned long **prompts)
1279 OM_uint32 major, minor;
1281 gss_OID_set attrs = GSS_C_NO_OID_SET;
1283 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1284 if (GSS_ERROR(major)) {
1285 utils->seterror(utils->conn, SASL_NOLOG,
1286 "GS2 Failure: gss_inquire_attrs_for_mech");
1290 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1291 *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1292 if (prompts != NULL)
1293 *prompts = gs2_required_prompts;
1295 #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1296 attrs, &present) == GSS_S_COMPLETE && \
1299 if (MA_PRESENT(GSS_C_MA_PFS))
1300 *security_flags |= SASL_SEC_FORWARD_SECRECY;
1301 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1302 *security_flags |= SASL_SEC_NOANONYMOUS;
1303 if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1304 *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1305 if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1306 *security_flags |= SASL_SEC_MUTUAL_AUTH;
1307 if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL)
1309 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1310 *features |= SASL_FEAT_GSS_FRAMING;
1312 gss_release_oid_set(&minor, &attrs);
1318 * Enumerate GSS mechanisms that can be used for GS2
1320 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1322 OM_uint32 major, minor;
1323 gss_OID_desc desired_oids[3];
1324 gss_OID_set_desc desired_attrs;
1325 gss_OID_desc except_oids[3];
1326 gss_OID_set_desc except_attrs;
1328 if (gs2_mechs != GSS_C_NO_OID_SET)
1331 desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1332 desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1333 desired_oids[2] = *GSS_C_MA_CBINDINGS;
1334 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1335 desired_attrs.elements = desired_oids;
1337 except_oids[0] = *GSS_C_MA_MECH_NEGO;
1338 except_oids[1] = *GSS_C_MA_NOT_MECH;
1339 except_oids[2] = *GSS_C_MA_DEPRECATED;
1341 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1342 except_attrs.elements = except_oids;
1344 major = gss_indicate_mechs_by_attrs(&minor,
1349 if (GSS_ERROR(major)) {
1350 utils->seterror(utils->conn, SASL_NOLOG,
1351 "GS2 Failure: gss_indicate_mechs_by_attrs");
1355 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1359 * Map SASL mechanism name to OID
1362 gs2_map_sasl_name(const sasl_utils_t *utils,
1366 OM_uint32 major, minor;
1367 gss_buffer_desc buf;
1369 buf.length = strlen(mech);
1370 buf.value = (void *)mech;
1372 major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1373 if (GSS_ERROR(major)) {
1374 utils->seterror(utils->conn, SASL_NOLOG,
1375 "GS2 Failure: gss_inquire_mech_for_saslname");
1383 gs2_duplicate_buffer(const sasl_utils_t *utils,
1384 const gss_buffer_t src,
1387 dst->value = utils->malloc(src->length + 1);
1388 if (dst->value == NULL)
1391 memcpy(dst->value, src->value, src->length);
1392 ((char *)dst->value)[src->length] = '\0';
1393 dst->length = src->length;
1399 gs2_unescape_authzid(const sasl_utils_t *utils,
1405 size_t i, len, inlen = *remain;
1410 for (i = 0, len = 0; i < inlen; i++) {
1415 } else if (in[i] == '=') {
1417 return SASL_BADPROT;
1423 if (len == 0 || *endp == NULL)
1424 return SASL_BADPROT;
1426 p = *authzid = utils->malloc(len + 1);
1427 if (*authzid == NULL)
1430 for (i = 0; i < inlen; i++) {
1433 else if (in[i] == '=') {
1434 if (memcmp(&in[i + 1], "2C", 2) == 0)
1436 else if (memcmp(&in[i + 1], "3D", 2) == 0)
1439 utils->free(*authzid);
1441 return SASL_BADPROT;
1454 gs2_escape_authzid(const sasl_utils_t *utils,
1462 p = *authzid = utils->malloc((inlen * 3) + 1);
1463 if (*authzid == NULL)
1466 for (i = 0; i < inlen; i++) {
1468 memcpy(p, "=2C", 3);
1470 } else if (in[i] == '=') {
1471 memcpy(p, "=3D", 3);
1483 #define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL)
1486 gs2_get_init_creds(context_t *text,
1487 sasl_client_params_t *params,
1488 sasl_interact_t **prompt_need,
1489 sasl_out_params_t *oparams)
1491 int result = SASL_OK;
1492 const char *authid = NULL, *userid = NULL;
1493 int user_result = SASL_OK;
1494 int auth_result = SASL_OK;
1495 int pass_result = SASL_OK;
1496 OM_uint32 maj_stat, min_stat;
1497 gss_OID_set_desc mechs;
1498 gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER;
1501 mechs.elements = (gss_OID)text->mechanism;
1504 * Determine the authentication identity from the application supplied
1505 * GSS credential, the default GSS credential, and the application
1506 * supplied identity, in that order.
1508 if (oparams->authid == NULL) {
1509 assert(text->client_name == GSS_C_NO_NAME);
1511 if (!GOT_CREDS(text, params)) {
1512 maj_stat = gss_acquire_cred(&min_stat,
1517 &text->client_creds,
1521 maj_stat = GSS_S_COMPLETE;
1523 if (maj_stat == GSS_S_COMPLETE) {
1524 maj_stat = gss_inquire_cred(&min_stat,
1526 ? (gss_cred_id_t)params->gss_creds
1527 : text->client_creds,
1532 if (GSS_ERROR(maj_stat))
1534 } else if (maj_stat != GSS_S_CRED_UNAVAIL)
1537 if (text->client_name != GSS_C_NO_NAME) {
1538 maj_stat = gss_display_name(&min_stat,
1542 if (GSS_ERROR(maj_stat))
1545 authid = cred_authid.value;
1547 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1548 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1549 result = auth_result;
1556 * Get the authorization identity.
1558 if (oparams->user == NULL) {
1559 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1560 if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1561 result = user_result;
1567 * Canonicalize the authentication and authorization identities before
1568 * calling GSS_Import_name.
1570 if (auth_result == SASL_OK && user_result == SASL_OK &&
1571 oparams->authid == NULL) {
1572 if (userid == NULL || userid[0] == '\0') {
1573 result = params->canon_user(params->utils->conn, authid, 0,
1574 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1577 result = params->canon_user(params->utils->conn,
1578 authid, 0, SASL_CU_AUTHID, oparams);
1579 if (result != SASL_OK)
1582 result = params->canon_user(params->utils->conn,
1583 userid, 0, SASL_CU_AUTHZID, oparams);
1584 if (result != SASL_OK)
1590 * If the application has provided an authentication identity, parse it.
1592 if (text->client_name == GSS_C_NO_NAME &&
1593 oparams->authid != NULL && oparams->authid[0] != '\0') {
1594 gss_buffer_desc name_buf;
1596 name_buf.length = strlen(oparams->authid);
1597 name_buf.value = (void *)oparams->authid;
1599 maj_stat = gss_import_name(&min_stat,
1602 &text->client_name);
1603 if (GSS_ERROR(maj_stat))
1608 * Armed with the authentication identity, try to get a credential without
1611 if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) {
1612 maj_stat = gss_acquire_cred(&min_stat,
1617 &text->client_creds,
1620 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CRED_UNAVAIL)
1625 * If that failed, try to get a credential with a password.
1627 if (!GOT_CREDS(text, params)) {
1628 if (text->password == NULL) {
1629 pass_result = _plug_get_password(params->utils, &text->password,
1630 &text->free_password, prompt_need);
1631 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1632 result = pass_result;
1637 if (text->password != NULL) {
1638 gss_buffer_desc password_buf;
1640 password_buf.length = text->password->len;
1641 password_buf.value = text->password->data;
1643 maj_stat = gss_acquire_cred_with_password(&min_stat,
1649 &text->client_creds,
1652 if (GSS_ERROR(maj_stat))
1657 maj_stat = GSS_S_COMPLETE;
1659 /* free prompts we got */
1660 if (prompt_need && *prompt_need) {
1661 params->utils->free(*prompt_need);
1662 *prompt_need = NULL;
1665 /* if there are prompts not filled in */
1666 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1667 pass_result == SASL_INTERACT) {
1668 /* make the prompt list */
1670 _plug_make_prompts(params->utils, prompt_need,
1671 user_result == SASL_INTERACT ?
1672 "Please enter your authorization name" : NULL,
1674 auth_result == SASL_INTERACT ?
1675 "Please enter your authentication name" : NULL,
1677 pass_result == SASL_INTERACT ?
1678 "Please enter your password" : NULL, NULL,
1682 if (result == SASL_OK)
1683 result = SASL_INTERACT;
1687 if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) {
1688 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
1692 gss_release_buffer(&min_stat, &cred_authid);
1698 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1701 OM_uint32 maj_stat, min_stat;
1702 gss_buffer_desc msg;
1706 unsigned int len, curlen = 0;
1707 const char prefix[] = "GS2 Error: ";
1709 len = sizeof(prefix);
1710 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1714 strcpy(out, prefix);
1718 maj_stat = gss_display_status(&min_stat, maj,
1719 GSS_C_GSS_CODE, GSS_C_NULL_OID,
1722 if (GSS_ERROR(maj_stat)) {
1724 utils->log(utils->conn, SASL_LOG_FAIL,
1725 "GS2 Failure: (could not get major error message)");
1727 utils->seterror(utils->conn, 0,
1729 "(could not get major error message)");
1735 len += len + msg.length;
1736 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1737 if (ret != SASL_OK) {
1742 strcat(out, msg.value);
1744 gss_release_buffer(&min_stat, &msg);
1750 /* Now get the minor status */
1753 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1754 if (ret != SASL_OK) {
1763 maj_stat = gss_display_status(&min_stat, min,
1764 GSS_C_MECH_CODE, GSS_C_NULL_OID,
1767 if (GSS_ERROR(maj_stat)) {
1769 utils->log(utils->conn, SASL_LOG_FAIL,
1770 "GS2 Failure: (could not get minor error message)");
1772 utils->seterror(utils->conn, 0,
1774 "(could not get minor error message)");
1780 len += len + msg.length;
1782 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1783 if (ret != SASL_OK) {
1788 strcat(out, msg.value);
1790 gss_release_buffer(&min_stat, &msg);
1797 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1798 if (ret != SASL_OK) {
1806 utils->log(utils->conn, SASL_LOG_FAIL, out);
1808 utils->seterror(utils->conn, 0, out);