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>
75 #ifdef HAVE_GSSAPI_GSSAPI_EXT_H
76 #include <gssapi/gssapi_ext.h>
84 #include "plugin_common.h"
92 #include "gs2_token.h"
94 #define GS2_CB_FLAG_MASK 0x0F
95 #define GS2_CB_FLAG_N 0x00
96 #define GS2_CB_FLAG_P 0x01
97 #define GS2_CB_FLAG_Y 0x02
98 #define GS2_NONSTD_FLAG 0x10
100 #ifndef GSS_S_PROMPTING_NEEDED
101 #define GSS_S_PROMPTING_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 5))
104 typedef struct context {
105 gss_ctx_id_t gss_ctx;
106 gss_name_t client_name;
107 gss_name_t server_name;
108 gss_cred_id_t server_creds;
109 gss_cred_id_t client_creds;
111 unsigned out_buf_len;
112 const sasl_utils_t *utils;
116 sasl_client_plug_t *client;
117 sasl_server_plug_t *server;
122 struct gss_channel_bindings_struct gss_cbindings;
123 sasl_secret_t *password;
124 unsigned int free_password;
128 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
130 static int gs2_get_init_creds(context_t *context,
131 sasl_client_params_t *params,
132 sasl_interact_t **prompt_need,
133 sasl_out_params_t *oparams);
135 static int gs2_verify_initial_message(context_t *text,
136 sasl_server_params_t *sparams,
141 static int gs2_make_header(context_t *text,
142 sasl_client_params_t *cparams,
147 static int gs2_make_message(context_t *text,
148 sasl_client_params_t *cparams,
149 int initialContextToken,
154 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
156 unsigned int *security_flags,
157 unsigned int *features,
158 const unsigned long **prompts);
160 static int gs2_indicate_mechs(const sasl_utils_t *utils);
162 static int gs2_map_sasl_name(const sasl_utils_t *utils,
166 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
167 const gss_buffer_t src,
170 static int gs2_unescape_authzid(const sasl_utils_t *utils,
175 static int gs2_escape_authzid(const sasl_utils_t *utils,
180 /* sasl_gs_log: only logs status string returned from gss_display_status() */
181 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
182 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
185 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
189 sasl_gs2_new_context(const sasl_utils_t *utils)
193 ret = utils->malloc(sizeof(context_t));
197 memset(ret, 0, sizeof(context_t));
204 sasl_gs2_free_context_contents(context_t *text)
211 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
212 gss_delete_sec_context(&min_stat,&text->gss_ctx,
214 text->gss_ctx = GSS_C_NO_CONTEXT;
217 if (text->client_name != GSS_C_NO_NAME) {
218 gss_release_name(&min_stat,&text->client_name);
219 text->client_name = GSS_C_NO_NAME;
222 if (text->server_name != GSS_C_NO_NAME) {
223 gss_release_name(&min_stat,&text->server_name);
224 text->server_name = GSS_C_NO_NAME;
227 if (text->server_creds != GSS_C_NO_CREDENTIAL) {
228 gss_release_cred(&min_stat, &text->server_creds);
229 text->server_creds = GSS_C_NO_CREDENTIAL;
232 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
233 gss_release_cred(&min_stat, &text->client_creds);
234 text->client_creds = GSS_C_NO_CREDENTIAL;
237 if (text->authid != NULL) {
238 text->utils->free(text->authid);
242 if (text->authzid != NULL) {
243 text->utils->free(text->authzid);
244 text->authzid = NULL;
247 gss_release_buffer(&min_stat, &text->gss_cbindings.application_data);
249 if (text->out_buf != NULL) {
250 text->utils->free(text->out_buf);
251 text->out_buf = NULL;
254 text->out_buf_len = 0;
256 if (text->cbindingname != NULL) {
257 text->utils->free(text->cbindingname);
258 text->cbindingname = NULL;
261 if (text->free_password)
262 _plug_free_secret(text->utils, &text->password);
264 memset(text, 0, sizeof(*text));
270 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
272 sasl_gs2_free_context_contents((context_t *)(conn_context));
273 utils->free(conn_context);
277 gs2_common_mech_free(void *global_context __attribute__((unused)),
278 const sasl_utils_t *utils)
282 if (gs2_mechs != GSS_C_NO_OID_SET) {
283 gss_release_oid_set(&minor, &gs2_mechs);
284 gs2_mechs = GSS_C_NO_OID_SET;
288 /***************************** Server Section *****************************/
291 gs2_server_mech_new(void *glob_context,
292 sasl_server_params_t *params,
293 const char *challenge __attribute__((unused)),
294 unsigned challen __attribute__((unused)),
300 text = sasl_gs2_new_context(params->utils);
302 MEMERROR(params->utils);
306 text->gss_ctx = GSS_C_NO_CONTEXT;
307 text->client_name = GSS_C_NO_NAME;
308 text->server_name = GSS_C_NO_NAME;
309 text->server_creds = GSS_C_NO_CREDENTIAL;
310 text->client_creds = GSS_C_NO_CREDENTIAL;
311 text->plug.server = glob_context;
313 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
315 if (ret != SASL_OK) {
316 gs2_common_mech_dispose(text, params->utils);
320 *conn_context = text;
326 gs2_server_mech_step(void *conn_context,
327 sasl_server_params_t *params,
328 const char *clientin,
329 unsigned clientinlen,
330 const char **serverout,
331 unsigned *serveroutlen,
332 sasl_out_params_t *oparams)
334 context_t *text = (context_t *)conn_context;
335 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
336 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
337 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
338 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
339 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
340 gss_name_t without = GSS_C_NO_NAME;
341 gss_OID_set_desc mechs;
342 OM_uint32 out_flags = 0;
343 int ret = 0, equal = 0;
344 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
347 if (serverout == NULL) {
348 PARAMERROR(text->utils);
349 return SASL_BADPARAM;
355 if (initialContextToken) {
356 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
357 name_buf.value = params->utils->malloc(name_buf.length + 1);
358 if (name_buf.value == NULL) {
359 MEMERROR(text->utils);
363 snprintf(name_buf.value, name_buf.length + 1,
364 "%s@%s", params->service, params->serverFQDN);
365 maj_stat = gss_import_name(&min_stat,
367 GSS_C_NT_HOSTBASED_SERVICE,
369 params->utils->free(name_buf.value);
370 name_buf.value = NULL;
372 if (GSS_ERROR(maj_stat))
375 assert(text->server_creds == GSS_C_NO_CREDENTIAL);
378 mechs.elements = (gss_OID)text->mechanism;
380 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
381 maj_stat = gss_acquire_cred(&min_stat,
389 if (GSS_ERROR(maj_stat))
393 ret = gs2_verify_initial_message(text,
401 input_token.value = (void *)clientin;
402 input_token.length = clientinlen;
405 maj_stat = gss_accept_sec_context(&min_stat,
407 (params->gss_creds != GSS_C_NO_CREDENTIAL)
408 ? (gss_cred_id_t)params->gss_creds
409 : text->server_creds,
411 &text->gss_cbindings,
417 &text->client_creds);
418 if (GSS_ERROR(maj_stat)) {
419 sasl_gs2_log(text->utils, maj_stat, min_stat);
420 text->utils->seterror(text->utils->conn, SASL_NOLOG,
421 "GS2 Failure: gss_accept_sec_context");
422 ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH;
426 *serveroutlen = output_token.length;
427 if (output_token.value != NULL) {
428 ret = _plug_buf_alloc(text->utils, &text->out_buf,
429 &text->out_buf_len, *serveroutlen);
432 memcpy(text->out_buf, output_token.value, *serveroutlen);
433 *serverout = text->out_buf;
435 /* No output token, send an empty string */
440 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
445 assert(maj_stat == GSS_S_COMPLETE);
447 maj_stat = gss_display_name(&min_stat, text->client_name,
449 if (GSS_ERROR(maj_stat))
452 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
456 p = (char *)memchr(name_buf.value, '@', name_buf.length);
458 short_name_buf.length = (p - (char *)name_buf.value);
460 maj_stat = gss_import_name(&min_stat,
464 if (GSS_ERROR(maj_stat)) {
469 maj_stat = gss_compare_name(&min_stat, text->client_name,
471 if (GSS_ERROR(maj_stat)) {
477 ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
480 text->authid = (char *)short_name_buf.value;
481 short_name_buf.value = NULL;
482 short_name_buf.length = 0;
484 if (text->authzid != NULL) {
485 ret = params->canon_user(params->utils->conn,
487 SASL_CU_AUTHZID, oparams);
492 ret = params->canon_user(params->utils->conn,
494 text->authzid == NULL
495 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
501 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
503 oparams->cbindingdisp = SASL_CB_DISP_NONE;
506 oparams->cbindingdisp = SASL_CB_DISP_USED;
507 oparams->cbindingname = text->cbindingname;
510 oparams->cbindingdisp = SASL_CB_DISP_WANT;
514 if (text->client_creds != GSS_C_NO_CREDENTIAL)
515 oparams->client_creds = &text->client_creds;
517 oparams->client_creds = NULL;
519 oparams->gss_peer_name = text->client_name;
520 oparams->gss_local_name = text->server_name;
521 oparams->maxoutbuf = 0xFFFFFF;
522 oparams->encode = NULL;
523 oparams->decode = NULL;
524 oparams->mech_ssf = 0;
525 oparams->doneflag = 1;
530 if (initialContextToken)
531 gss_release_buffer(&min_stat, &input_token);
532 gss_release_buffer(&min_stat, &name_buf);
533 gss_release_buffer(&min_stat, &short_name_buf);
534 gss_release_buffer(&min_stat, &output_token);
535 gss_release_name(&min_stat, &without);
537 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
538 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
542 sasl_gs2_free_context_contents(text);
548 gs2_common_plug_init(const sasl_utils_t *utils,
550 int (*plug_alloc)(const sasl_utils_t *,
557 OM_uint32 major, minor;
564 if (gs2_indicate_mechs(utils) != SASL_OK) {
568 plugs = utils->malloc(gs2_mechs->count * plugsize);
573 memset(plugs, 0, gs2_mechs->count * plugsize);
575 for (i = 0; i < gs2_mechs->count; i++) {
576 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
578 major = gss_inquire_saslname_for_mech(&minor,
579 &gs2_mechs->elements[i],
583 if (GSS_ERROR(major))
586 #define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize))
588 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
589 &gs2_mechs->elements[i]) == SASL_OK)
592 gss_release_buffer(&minor, &sasl_mech_name);
607 gs2_server_plug_alloc(const sasl_utils_t *utils,
609 gss_buffer_t sasl_name,
613 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
616 memset(splug, 0, sizeof(*splug));
618 ret = gs2_get_mech_attrs(utils, mech,
619 &splug->security_flags,
625 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
629 splug->mech_name = (char *)buf.value;
630 splug->glob_context = plug;
631 splug->mech_new = gs2_server_mech_new;
632 splug->mech_step = gs2_server_mech_step;
633 splug->mech_dispose = gs2_common_mech_dispose;
634 splug->mech_free = gs2_common_mech_free;
639 static sasl_server_plug_t *gs2_server_plugins;
640 static int gs2_server_plugcount;
643 gs2_server_plug_init(const sasl_utils_t *utils,
646 sasl_server_plug_t **pluglist,
654 if (maxversion < SASL_SERVER_PLUG_VERSION)
657 *outversion = SASL_SERVER_PLUG_VERSION;
659 if (gs2_server_plugins == NULL) {
660 ret = gs2_common_plug_init(utils,
661 sizeof(sasl_server_plug_t),
662 gs2_server_plug_alloc,
663 (void **)&gs2_server_plugins,
664 &gs2_server_plugcount);
669 *pluglist = gs2_server_plugins;
670 *plugcount = gs2_server_plugcount;
675 /***************************** Client Section *****************************/
677 static int gs2_client_mech_step(void *conn_context,
678 sasl_client_params_t *params,
679 const char *serverin,
680 unsigned serverinlen,
681 sasl_interact_t **prompt_need,
682 const char **clientout,
683 unsigned *clientoutlen,
684 sasl_out_params_t *oparams)
686 context_t *text = (context_t *)conn_context;
687 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
688 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
689 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
690 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
693 int initialContextToken;
698 if (text->gss_ctx == GSS_C_NO_CONTEXT) {
699 ret = gs2_get_init_creds(text, params, prompt_need, oparams);
703 initialContextToken = 1;
705 initialContextToken = 0;
707 if (text->server_name == GSS_C_NO_NAME) { /* only once */
708 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
709 name_buf.value = params->utils->malloc(name_buf.length + 1);
710 if (name_buf.value == NULL) {
714 if (params->serverFQDN == NULL ||
715 strlen(params->serverFQDN) == 0) {
716 SETERROR(text->utils, "GS2 Failure: no serverFQDN");
721 snprintf(name_buf.value, name_buf.length + 1,
722 "%s@%s", params->service, params->serverFQDN);
724 maj_stat = gss_import_name(&min_stat,
726 GSS_C_NT_HOSTBASED_SERVICE,
728 params->utils->free(name_buf.value);
729 name_buf.value = NULL;
731 if (GSS_ERROR(maj_stat))
735 /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
736 if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
737 gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
738 text->gss_ctx = GSS_C_NO_CONTEXT;
741 input_token.value = (void *)serverin;
742 input_token.length = serverinlen;
744 if (initialContextToken) {
745 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
746 text->gs2_flags |= GS2_NONSTD_FLAG;
748 switch (params->cbindingdisp) {
749 case SASL_CB_DISP_NONE:
750 text->gs2_flags |= GS2_CB_FLAG_N;
752 case SASL_CB_DISP_USED:
753 text->gs2_flags |= GS2_CB_FLAG_P;
755 case SASL_CB_DISP_WANT:
756 text->gs2_flags |= GS2_CB_FLAG_Y;
760 ret = gs2_make_header(text, params,
761 strcmp(oparams->user, oparams->authid) ?
762 (char *) oparams->user : NULL,
763 &text->out_buf, &text->out_buf_len);
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 (params->cbindingdisp != SASL_CB_DISP_NONE &&
815 (ret_flags & GSS_C_MUTUAL_FLAG) == 0) {
816 maj_stat = SASL_BADAUTH;
820 maj_stat = gss_display_name(&min_stat,
824 if (GSS_ERROR(maj_stat))
827 oparams->gss_peer_name = text->server_name;
828 oparams->gss_local_name = text->client_name;
829 oparams->encode = NULL;
830 oparams->decode = NULL;
831 oparams->mech_ssf = 0;
832 oparams->maxoutbuf = 0xFFFFFF;
833 oparams->doneflag = 1;
836 gss_release_buffer(&min_stat, &output_token);
837 gss_release_buffer(&min_stat, &name_buf);
839 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
840 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
841 ret = (maj_stat & GSS_S_PROMPTING_NEEDED) ? SASL_INTERACT : SASL_FAIL;
844 sasl_gs2_free_context_contents(text);
849 static int gs2_client_mech_new(void *glob_context,
850 sasl_client_params_t *params,
856 text = sasl_gs2_new_context(params->utils);
858 MEMERROR(params->utils);
862 text->gss_ctx = GSS_C_NO_CONTEXT;
863 text->client_name = GSS_C_NO_NAME;
864 text->server_creds = GSS_C_NO_CREDENTIAL;
865 text->client_creds = GSS_C_NO_CREDENTIAL;
866 text->plug.client = glob_context;
868 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
870 if (ret != SASL_OK) {
871 gs2_common_mech_dispose(text, params->utils);
875 *conn_context = text;
881 gs2_client_plug_alloc(const sasl_utils_t *utils,
883 gss_buffer_t sasl_name,
887 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
890 memset(cplug, 0, sizeof(*cplug));
892 ret = gs2_get_mech_attrs(utils, mech,
893 &cplug->security_flags,
895 &cplug->required_prompts);
899 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
903 cplug->mech_name = (char *)buf.value;
904 cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
905 cplug->glob_context = plug;
906 cplug->mech_new = gs2_client_mech_new;
907 cplug->mech_step = gs2_client_mech_step;
908 cplug->mech_dispose = gs2_common_mech_dispose;
909 cplug->mech_free = gs2_common_mech_free;
914 static sasl_client_plug_t *gs2_client_plugins;
915 static int gs2_client_plugcount;
918 gs2_client_plug_init(const sasl_utils_t *utils,
921 sasl_client_plug_t **pluglist,
929 if (maxversion < SASL_CLIENT_PLUG_VERSION)
932 *outversion = SASL_CLIENT_PLUG_VERSION;
934 if (gs2_client_plugins == NULL) {
935 ret = gs2_common_plug_init(utils,
936 sizeof(sasl_client_plug_t),
937 gs2_client_plug_alloc,
938 (void **)&gs2_client_plugins,
939 &gs2_client_plugcount);
944 *pluglist = gs2_client_plugins;
945 *plugcount = gs2_client_plugcount;
951 * Copy header and application channel bindings to GSS channel bindings
952 * structure in context.
955 gs2_save_cbindings(context_t *text,
957 const sasl_channel_binding_t *cbinding)
959 gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data;
963 assert(gss_cbindings->value == NULL);
966 * The application-data field MUST be set to the gs2-header, excluding
967 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
968 * gs2-cb-flag of "p" is used, the application's channel binding data.
970 len = header->length;
971 if (text->gs2_flags & GS2_NONSTD_FLAG) {
975 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
977 len += cbinding->len;
980 gss_cbindings->length = len;
981 gss_cbindings->value = text->utils->malloc(len);
982 if (gss_cbindings->value == NULL)
985 p = (unsigned char *)gss_cbindings->value;
986 if (text->gs2_flags & GS2_NONSTD_FLAG) {
987 memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
988 p += header->length - 2;
990 memcpy(p, header->value, header->length);
994 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
996 memcpy(p, cbinding->data, cbinding->len);
1002 #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0)
1005 * Verify gs2-header, save authzid and channel bindings to context.
1008 gs2_verify_initial_message(context_t *text,
1009 sasl_server_params_t *sparams,
1014 OM_uint32 major, minor;
1015 char *p = (char *)in;
1016 unsigned remain = inlen;
1018 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1020 assert(text->cbindingname == NULL);
1021 assert(text->authzid == NULL);
1024 token->value = NULL;
1026 /* minimum header includes CB flag and non-zero GSS token */
1027 CHECK_REMAIN(4); /* [pny],,. */
1029 /* non-standard GSS framing flag */
1030 if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1031 text->gs2_flags |= GS2_NONSTD_FLAG;
1036 /* SASL channel bindings */
1037 CHECK_REMAIN(1); /* [pny] */
1041 CHECK_REMAIN(1); /* = */
1044 return SASL_BADPROT;
1046 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname);
1050 text->gs2_flags |= GS2_CB_FLAG_P;
1053 text->gs2_flags |= GS2_CB_FLAG_N;
1056 text->gs2_flags |= GS2_CB_FLAG_Y;
1060 CHECK_REMAIN(1); /* , */
1063 return SASL_BADPROT;
1065 /* authorization identity */
1066 if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1071 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1077 CHECK_REMAIN(1); /* , */
1080 return SASL_BADPROT;
1082 buf.length = inlen - remain;
1083 buf.value = (void *)in;
1085 /* stash channel bindings to pass into gss_accept_sec_context() */
1086 ret = gs2_save_cbindings(text, &buf, sparams->cbinding);
1090 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1091 buf.length = remain;
1094 gss_buffer_desc tmp;
1096 tmp.length = remain;
1099 major = gss_encapsulate_token(&tmp, text->mechanism, &buf);
1100 if (GSS_ERROR(major))
1104 token->value = text->utils->malloc(buf.length);
1105 if (token->value == NULL)
1108 token->length = buf.length;
1109 memcpy(token->value, buf.value, buf.length);
1111 if ((text->gs2_flags & GS2_NONSTD_FLAG) == 0)
1112 gss_release_buffer(&minor, &buf);
1118 * Create gs2-header, save channel bindings to context.
1121 gs2_make_header(context_t *text,
1122 sasl_client_params_t *cparams,
1123 const char *authzid,
1127 size_t required = 0;
1128 size_t wire_authzid_len = 0, cbnamelen = 0;
1129 char *wire_authzid = NULL;
1132 gss_buffer_desc buf;
1137 /* non-standard GSS framing flag */
1138 if (text->gs2_flags & GS2_NONSTD_FLAG)
1139 required += 2; /* F, */
1141 /* SASL channel bindings */
1142 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1144 if (!SASL_CB_PRESENT(cparams))
1145 return SASL_BADPARAM;
1146 cbnamelen = strlen(cparams->cbinding->name);
1147 required += 1 /*=*/ + cbnamelen;
1151 required += 2; /* [pny], */
1154 return SASL_BADPARAM;
1157 /* authorization identity */
1158 if (authzid != NULL) {
1159 ret = gs2_escape_authzid(text->utils, authzid,
1160 strlen(authzid), &wire_authzid);
1164 wire_authzid_len = strlen(wire_authzid);
1165 required += 2 /* a= */ + wire_authzid_len;
1168 required += 1; /* trailing comma */
1170 ret = _plug_buf_alloc(text->utils, out, outlen, required);
1171 if (ret != SASL_OK) {
1172 text->utils->free(wire_authzid);
1176 *out = text->out_buf;
1179 p = (char *)text->out_buf;
1180 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1184 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1187 memcpy(p + 2, cparams->cbinding->name, cbnamelen);
1198 if (wire_authzid != NULL) {
1200 memcpy(p + 2, wire_authzid, wire_authzid_len);
1201 text->utils->free(wire_authzid);
1202 p += 2 + wire_authzid_len;
1206 assert(p == (char *)text->out_buf + required);
1208 buf.length = required;
1211 ret = gs2_save_cbindings(text, &buf, cparams->cbinding);
1219 * Convert a GSS token to a GS2 one
1222 gs2_make_message(context_t *text,
1223 sasl_client_params_t *cparams __attribute__((unused)),
1224 int initialContextToken,
1229 OM_uint32 major, minor;
1231 unsigned header_len = 0;
1232 gss_buffer_desc decap_token = GSS_C_EMPTY_BUFFER;
1234 if (initialContextToken) {
1235 header_len = *outlen;
1237 major = gss_decapsulate_token(token, text->mechanism, &decap_token);
1238 if ((major == GSS_S_DEFECTIVE_TOKEN &&
1239 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1243 token = &decap_token;
1246 ret = _plug_buf_alloc(text->utils, out, outlen,
1247 header_len + token->length);
1251 memcpy(*out + header_len, token->value, token->length);
1252 *outlen = header_len + token->length;
1254 if (initialContextToken)
1255 gss_release_buffer(&minor, &decap_token);
1260 static const unsigned long gs2_required_prompts[] = {
1265 * Map GSS mechanism attributes to SASL ones
1268 gs2_get_mech_attrs(const sasl_utils_t *utils,
1270 unsigned int *security_flags,
1271 unsigned int *features,
1272 const unsigned long **prompts)
1274 OM_uint32 major, minor;
1276 gss_OID_set attrs = GSS_C_NO_OID_SET;
1278 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1279 if (GSS_ERROR(major)) {
1280 utils->seterror(utils->conn, SASL_NOLOG,
1281 "GS2 Failure: gss_inquire_attrs_for_mech");
1285 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1286 *features = SASL_FEAT_WANT_CLIENT_FIRST;
1287 if (prompts != NULL)
1288 *prompts = gs2_required_prompts;
1290 #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1291 attrs, &present) == GSS_S_COMPLETE && \
1294 if (MA_PRESENT(GSS_C_MA_PFS))
1295 *security_flags |= SASL_SEC_FORWARD_SECRECY;
1296 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1297 *security_flags |= SASL_SEC_NOANONYMOUS;
1298 if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1299 *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1300 if (MA_PRESENT(GSS_C_MA_AUTH_TARG)) {
1301 *features |= SASL_FEAT_CHANNEL_BINDING;
1302 *security_flags |= SASL_SEC_MUTUAL_AUTH;
1304 if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL)
1306 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1307 *features |= SASL_FEAT_GSS_FRAMING;
1309 gss_release_oid_set(&minor, &attrs);
1315 * Enumerate GSS mechanisms that can be used for GS2
1317 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1319 OM_uint32 major, minor;
1320 gss_OID_desc desired_oids[2];
1321 gss_OID_set_desc desired_attrs;
1322 gss_OID_desc except_oids[3];
1323 gss_OID_set_desc except_attrs;
1325 if (gs2_mechs != GSS_C_NO_OID_SET)
1328 desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1329 desired_oids[1] = *GSS_C_MA_CBINDINGS;
1330 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1331 desired_attrs.elements = desired_oids;
1333 except_oids[0] = *GSS_C_MA_MECH_NEGO;
1334 except_oids[1] = *GSS_C_MA_NOT_MECH;
1335 except_oids[2] = *GSS_C_MA_DEPRECATED;
1337 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1338 except_attrs.elements = except_oids;
1340 major = gss_indicate_mechs_by_attrs(&minor,
1345 if (GSS_ERROR(major)) {
1346 utils->seterror(utils->conn, SASL_NOLOG,
1347 "GS2 Failure: gss_indicate_mechs_by_attrs");
1351 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1355 * Map SASL mechanism name to OID
1358 gs2_map_sasl_name(const sasl_utils_t *utils,
1362 OM_uint32 major, minor;
1363 gss_buffer_desc buf;
1365 buf.length = strlen(mech);
1366 buf.value = (void *)mech;
1368 major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1369 if (GSS_ERROR(major)) {
1370 utils->seterror(utils->conn, SASL_NOLOG,
1371 "GS2 Failure: gss_inquire_mech_for_saslname");
1379 gs2_duplicate_buffer(const sasl_utils_t *utils,
1380 const gss_buffer_t src,
1383 dst->value = utils->malloc(src->length + 1);
1384 if (dst->value == NULL)
1387 memcpy(dst->value, src->value, src->length);
1388 ((char *)dst->value)[src->length] = '\0';
1389 dst->length = src->length;
1395 gs2_unescape_authzid(const sasl_utils_t *utils,
1401 size_t i, len, inlen = *remain;
1406 for (i = 0, len = 0; i < inlen; i++) {
1411 } else if (in[i] == '=') {
1413 return SASL_BADPROT;
1419 if (len == 0 || *endp == NULL)
1420 return SASL_BADPROT;
1422 p = *authzid = utils->malloc(len + 1);
1423 if (*authzid == NULL)
1426 for (i = 0; i < inlen; i++) {
1429 else if (in[i] == '=') {
1430 if (memcmp(&in[i + 1], "2C", 2) == 0)
1432 else if (memcmp(&in[i + 1], "3D", 2) == 0)
1435 utils->free(*authzid);
1437 return SASL_BADPROT;
1450 gs2_escape_authzid(const sasl_utils_t *utils,
1458 p = *authzid = utils->malloc((inlen * 3) + 1);
1459 if (*authzid == NULL)
1462 for (i = 0; i < inlen; i++) {
1464 memcpy(p, "=2C", 3);
1466 } else if (in[i] == '=') {
1467 memcpy(p, "=3D", 3);
1479 #define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL)
1480 #define CRED_ERROR(status) ((status) == GSS_S_CRED_UNAVAIL || (status) == GSS_S_NO_CRED)
1483 * Determine the authentication identity from the application supplied
1484 * GSS credential, the application supplied identity, and the default
1485 * GSS credential, in that order. Then, acquire credentials.
1488 gs2_get_init_creds(context_t *text,
1489 sasl_client_params_t *params,
1490 sasl_interact_t **prompt_need,
1491 sasl_out_params_t *oparams)
1493 int result = SASL_OK;
1494 const char *authid = NULL, *userid = NULL;
1495 int user_result = SASL_OK;
1496 int auth_result = SASL_OK;
1497 int pass_result = SASL_OK;
1498 OM_uint32 maj_stat = GSS_S_COMPLETE, min_stat = 0;
1499 gss_OID_set_desc mechs;
1500 gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER;
1501 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
1504 mechs.elements = (gss_OID)text->mechanism;
1507 * Get the authentication identity from the application.
1509 if (oparams->authid == NULL) {
1510 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1511 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1512 result = auth_result;
1518 * Get the authorization identity from the application.
1520 if (oparams->user == NULL) {
1521 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1522 if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1523 result = user_result;
1529 * Canonicalize the authentication and authorization identities before
1530 * calling GSS_Import_name.
1532 if (auth_result == SASL_OK && user_result == SASL_OK &&
1533 oparams->authid == NULL) {
1534 if (userid == NULL || userid[0] == '\0') {
1535 result = params->canon_user(params->utils->conn, authid, 0,
1536 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1539 result = params->canon_user(params->utils->conn,
1540 authid, 0, SASL_CU_AUTHID, oparams);
1541 if (result != SASL_OK)
1544 result = params->canon_user(params->utils->conn,
1545 userid, 0, SASL_CU_AUTHZID, oparams);
1546 if (result != SASL_OK)
1550 if (oparams->authid != NULL) {
1551 name_buf.length = strlen(oparams->authid);
1552 name_buf.value = (void *)oparams->authid;
1554 assert(text->client_name == GSS_C_NO_NAME);
1556 maj_stat = gss_import_name(&min_stat,
1559 &text->client_name);
1560 if (GSS_ERROR(maj_stat))
1563 /* The authid may have changed after prompting, so free any creds */
1564 gss_release_cred(&min_stat, &text->client_creds);
1569 * If application didn't provide an authid, then use the default
1570 * credential. If that doesn't work, give up.
1572 if (!GOT_CREDS(text, params) && oparams->authid == NULL) {
1573 maj_stat = gss_acquire_cred(&min_stat,
1578 &text->client_creds,
1581 if (GSS_ERROR(maj_stat))
1584 assert(text->client_name == GSS_C_NO_NAME);
1586 maj_stat = gss_inquire_cred(&min_stat,
1588 ? (gss_cred_id_t)params->gss_creds
1589 : text->client_creds,
1594 if (GSS_ERROR(maj_stat)) {
1595 /* Maybe there was no default credential */
1596 auth_result = SASL_INTERACT;
1600 maj_stat = gss_display_name(&min_stat,
1604 if (GSS_ERROR(maj_stat))
1607 if (userid == NULL || userid[0] == '\0') {
1608 result = params->canon_user(params->utils->conn,
1609 cred_authid.value, cred_authid.length,
1610 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1613 result = params->canon_user(params->utils->conn,
1614 cred_authid.value, cred_authid.length,
1615 SASL_CU_AUTHID, oparams);
1616 if (result != SASL_OK)
1619 result = params->canon_user(params->utils->conn,
1620 cred_authid.value, cred_authid.length,
1621 SASL_CU_AUTHZID, oparams);
1622 if (result != SASL_OK)
1628 * Armed with the authentication identity, try to get a credential without
1631 if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) {
1632 maj_stat = gss_acquire_cred(&min_stat,
1637 &text->client_creds,
1640 if (GSS_ERROR(maj_stat) && !CRED_ERROR(maj_stat))
1645 * If that failed, try to get a credential with a password.
1647 if (!GOT_CREDS(text, params)) {
1648 if (text->password == NULL) {
1649 pass_result = _plug_get_password(params->utils, &text->password,
1650 &text->free_password, prompt_need);
1651 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1652 result = pass_result;
1657 if (text->password != NULL) {
1658 gss_buffer_desc password_buf;
1660 password_buf.length = text->password->len;
1661 password_buf.value = text->password->data;
1663 maj_stat = gss_acquire_cred_with_password(&min_stat,
1669 &text->client_creds,
1672 if (GSS_ERROR(maj_stat))
1677 maj_stat = GSS_S_COMPLETE;
1681 /* free prompts we got */
1682 if (prompt_need && *prompt_need) {
1683 params->utils->free(*prompt_need);
1684 *prompt_need = NULL;
1687 /* if there are prompts not filled in */
1688 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1689 pass_result == SASL_INTERACT) {
1690 /* make the prompt list */
1692 _plug_make_prompts(params->utils, prompt_need,
1693 user_result == SASL_INTERACT ?
1694 "Please enter your authorization name" : NULL,
1696 auth_result == SASL_INTERACT ?
1697 "Please enter your authentication name" : NULL,
1699 pass_result == SASL_INTERACT ?
1700 "Please enter your password" : NULL, NULL,
1704 if (result == SASL_OK)
1705 result = SASL_INTERACT;
1709 if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) {
1710 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
1711 result = (maj_stat & GSS_S_PROMPTING_NEEDED) ? SASL_INTERACT : SASL_FAIL;
1714 gss_release_buffer(&min_stat, &cred_authid);
1720 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1723 OM_uint32 maj_stat, min_stat;
1724 gss_buffer_desc msg;
1728 unsigned int len, curlen = 0;
1729 const char prefix[] = "GS2 Error: ";
1731 len = sizeof(prefix);
1732 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1736 strcpy(out, prefix);
1740 maj_stat = gss_display_status(&min_stat, maj,
1741 GSS_C_GSS_CODE, GSS_C_NULL_OID,
1744 if (GSS_ERROR(maj_stat)) {
1746 utils->log(utils->conn, SASL_LOG_FAIL,
1747 "GS2 Failure: (could not get major error message)");
1749 utils->seterror(utils->conn, 0,
1751 "(could not get major error message)");
1757 len += len + msg.length;
1758 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1759 if (ret != SASL_OK) {
1764 strcat(out, msg.value);
1766 gss_release_buffer(&min_stat, &msg);
1772 /* Now get the minor status */
1775 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1776 if (ret != SASL_OK) {
1785 maj_stat = gss_display_status(&min_stat, min,
1786 GSS_C_MECH_CODE, GSS_C_NULL_OID,
1789 if (GSS_ERROR(maj_stat)) {
1791 utils->log(utils->conn, SASL_LOG_FAIL,
1792 "GS2 Failure: (could not get minor error message)");
1794 utils->seterror(utils->conn, 0,
1796 "(could not get minor error message)");
1802 len += len + msg.length;
1804 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1805 if (ret != SASL_OK) {
1810 strcat(out, msg.value);
1812 gss_release_buffer(&min_stat, &msg);
1819 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1820 if (ret != SASL_OK) {
1828 utils->log(utils->conn, SASL_LOG_FAIL, out);
1830 utils->seterror(utils->conn, 0, out);