2 * Copyright 2010 PADL Software Pty Ltd. All rights reserved.
5 * Portions Copyright (c) 1998-2003 Carnegie Mellon University.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * 3. The name "Carnegie Mellon University" must not be used to
21 * endorse or promote products derived from this software without
22 * prior written permission. For permission or any other legal
23 * details, please contact
24 * Office of Technology Transfer
25 * Carnegie Mellon University
27 * Pittsburgh, PA 15213-3890
28 * (412) 268-4387, fax: (412) 268-7395
29 * tech-transfer@andrew.cmu.edu
31 * 4. Redistributions of any form whatsoever must retain the following
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46 #include <gssapi/gssapi.h>
47 #include <gssapi/gssapi_ext.h>
54 #include "plugin_common.h"
62 #include "gs2_token.h"
64 #define GS2_CB_FLAG_MASK 0x0F
65 #define GS2_CB_FLAG_P SASL_CB_FLAG_USED
66 #define GS2_CB_FLAG_N SASL_CB_FLAG_NONE
67 #define GS2_CB_FLAG_Y SASL_CB_FLAG_WANT
68 #define GS2_NONSTD_FLAG 0x10
70 typedef struct context {
72 gss_name_t client_name;
73 gss_name_t server_name;
74 gss_cred_id_t server_creds;
75 gss_cred_id_t client_creds;
78 const sasl_utils_t *utils;
82 sasl_client_plug_t *client;
83 sasl_server_plug_t *server;
88 struct gss_channel_bindings_struct bindings;
89 sasl_secret_t *password;
90 unsigned int free_password;
94 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
96 static int gs2_ask_user_info(context_t *context,
97 sasl_client_params_t *params,
98 char **realms, int nrealm,
99 sasl_interact_t **prompt_need,
100 sasl_out_params_t *oparams);
102 static int gs2_verify_initial_message(context_t *text,
103 sasl_server_params_t *sparams,
108 static int gs2_make_header(context_t *text,
109 sasl_client_params_t *cparams,
114 static int gs2_make_message(context_t *text,
115 sasl_client_params_t *cparams,
116 int initialContextToken,
121 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
123 unsigned int *security_flags,
124 unsigned int *features);
126 static int gs2_indicate_mechs(const sasl_utils_t *utils);
128 static int gs2_map_sasl_name(const sasl_utils_t *utils,
132 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
133 const gss_buffer_t src,
136 static int gs2_unescape_authzid(const sasl_utils_t *utils,
141 static int gs2_escape_authzid(const sasl_utils_t *utils,
146 /* sasl_gs_log: only logs status string returned from gss_display_status() */
147 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
148 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
151 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
155 sasl_gs2_new_context(const sasl_utils_t *utils)
159 ret = utils->malloc(sizeof(context_t));
163 memset(ret, 0, sizeof(context_t));
170 sasl_gs2_free_context_contents(context_t *text)
177 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
178 gss_delete_sec_context(&min_stat,&text->gss_ctx,
180 text->gss_ctx = GSS_C_NO_CONTEXT;
183 if (text->client_name != GSS_C_NO_NAME) {
184 gss_release_name(&min_stat,&text->client_name);
185 text->client_name = GSS_C_NO_NAME;
188 if (text->server_name != GSS_C_NO_NAME) {
189 gss_release_name(&min_stat,&text->server_name);
190 text->server_name = GSS_C_NO_NAME;
193 if (text->server_creds != GSS_C_NO_CREDENTIAL) {
194 gss_release_cred(&min_stat, &text->server_creds);
195 text->server_creds = GSS_C_NO_CREDENTIAL;
198 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
199 gss_release_cred(&min_stat, &text->client_creds);
200 text->client_creds = GSS_C_NO_CREDENTIAL;
203 if (text->authid != NULL) {
204 text->utils->free(text->authid);
208 if (text->authzid != NULL) {
209 text->utils->free(text->authzid);
210 text->authzid = NULL;
213 if (text->mechanism != NULL) {
214 gss_release_oid(&min_stat, &text->mechanism);
215 text->mechanism = GSS_C_NO_OID;
218 gss_release_buffer(&min_stat, &text->bindings.application_data);
220 if (text->out_buf != NULL) {
221 text->utils->free(text->out_buf);
222 text->out_buf = NULL;
225 text->out_buf_len = 0;
227 if (text->cb_name != NULL) {
228 text->utils->free(text->cb_name);
229 text->cb_name = NULL;
232 if (text->free_password)
233 _plug_free_secret(text->utils, &text->password);
235 memset(text, 0, sizeof(*text));
241 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
243 sasl_gs2_free_context_contents((context_t *)(conn_context));
244 utils->free(conn_context);
248 gs2_common_mech_free(void *global_context __attribute__((unused)),
249 const sasl_utils_t *utils)
253 if (gs2_mechs != GSS_C_NO_OID_SET) {
254 gss_release_oid_set(&minor, &gs2_mechs);
255 gs2_mechs = GSS_C_NO_OID_SET;
259 /***************************** Server Section *****************************/
262 gs2_server_mech_new(void *glob_context,
263 sasl_server_params_t *params,
264 const char *challenge __attribute__((unused)),
265 unsigned challen __attribute__((unused)),
271 text = sasl_gs2_new_context(params->utils);
273 MEMERROR(params->utils);
277 text->gss_ctx = GSS_C_NO_CONTEXT;
278 text->client_name = GSS_C_NO_NAME;
279 text->server_name = GSS_C_NO_NAME;
280 text->server_creds = GSS_C_NO_CREDENTIAL;
281 text->client_creds = GSS_C_NO_CREDENTIAL;
282 text->plug.server = glob_context;
284 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
286 if (ret != SASL_OK) {
287 gs2_common_mech_dispose(text, params->utils);
291 *conn_context = text;
297 gs2_server_mech_step(void *conn_context,
298 sasl_server_params_t *params,
299 const char *clientin,
300 unsigned clientinlen,
301 const char **serverout,
302 unsigned *serveroutlen,
303 sasl_out_params_t *oparams)
305 context_t *text = (context_t *)conn_context;
306 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
307 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
308 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
309 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
310 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
311 gss_name_t without = GSS_C_NO_NAME;
312 gss_OID_set_desc mechs;
313 gss_OID actual_mech = GSS_C_NO_OID;
314 OM_uint32 out_flags = 0;
315 int ret = 0, equal = 0;
316 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
319 if (serverout == NULL) {
320 PARAMERROR(text->utils);
321 return SASL_BADPARAM;
327 if (initialContextToken) {
328 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
329 name_buf.value = params->utils->malloc(name_buf.length + 1);
330 if (name_buf.value == NULL) {
331 MEMERROR(text->utils);
335 snprintf(name_buf.value, name_buf.length + 1,
336 "%s@%s", params->service, params->serverFQDN);
337 maj_stat = gss_import_name(&min_stat,
339 GSS_C_NT_HOSTBASED_SERVICE,
341 params->utils->free(name_buf.value);
342 name_buf.value = NULL;
344 if (GSS_ERROR(maj_stat))
347 assert(text->server_creds == GSS_C_NO_CREDENTIAL);
350 mechs.elements = (gss_OID)text->mechanism;
352 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
353 maj_stat = gss_acquire_cred(&min_stat,
361 if (GSS_ERROR(maj_stat))
365 ret = gs2_verify_initial_message(text,
373 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_N) {
374 if (params->chanbindingcrit != 0)
376 } else if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_Y) {
377 if (SASL_CB_PRESENT(params))
383 input_token.value = (void *)clientin;
384 input_token.length = clientinlen;
387 maj_stat = gss_accept_sec_context(&min_stat,
389 (params->gss_creds != GSS_C_NO_CREDENTIAL)
391 : text->server_creds,
399 &text->client_creds);
400 if (GSS_ERROR(maj_stat)) {
401 sasl_gs2_log(text->utils, maj_stat, min_stat);
402 text->utils->seterror(text->utils->conn, SASL_NOLOG,
403 "GS2 Failure: gss_accept_sec_context");
408 *serveroutlen = output_token.length;
409 if (output_token.value != NULL) {
410 ret = _plug_buf_alloc(text->utils, &text->out_buf,
411 &text->out_buf_len, *serveroutlen);
414 memcpy(text->out_buf, output_token.value, *serveroutlen);
415 *serverout = text->out_buf;
417 /* No output token, send an empty string */
422 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
427 assert(maj_stat == GSS_S_COMPLETE);
429 if (!g_OID_equal(text->mechanism, actual_mech)) {
430 ret = SASL_WRONGMECH;
433 if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) {
438 maj_stat = gss_display_name(&min_stat, text->client_name,
440 if (GSS_ERROR(maj_stat))
443 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
447 p = (char *)memchr(name_buf.value, '@', name_buf.length);
449 short_name_buf.length = (p - (char *)name_buf.value);
451 maj_stat = gss_import_name(&min_stat,
455 if (GSS_ERROR(maj_stat)) {
460 maj_stat = gss_compare_name(&min_stat, text->client_name,
462 if (GSS_ERROR(maj_stat)) {
468 ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
471 text->authid = (char *)short_name_buf.value;
472 short_name_buf.value = NULL;
473 short_name_buf.length = 0;
475 if (text->authzid != NULL) {
476 ret = params->canon_user(params->utils->conn,
478 SASL_CU_AUTHZID, oparams);
483 ret = params->canon_user(params->utils->conn,
485 text->authzid == NULL
486 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
492 if (text->client_creds != GSS_C_NO_CREDENTIAL)
493 oparams->client_creds = &text->client_creds;
495 oparams->client_creds = NULL;
497 oparams->gss_peer_name = text->client_name;
498 oparams->gss_local_name = text->server_name;
499 oparams->maxoutbuf = 0xFFFFFF;
500 oparams->encode = NULL;
501 oparams->decode = NULL;
502 oparams->mech_ssf = 0;
503 oparams->doneflag = 1;
508 if (initialContextToken)
509 gss_release_buffer(&min_stat, &input_token);
510 gss_release_buffer(&min_stat, &name_buf);
511 gss_release_buffer(&min_stat, &short_name_buf);
512 gss_release_buffer(&min_stat, &output_token);
513 gss_release_name(&min_stat, &without);
514 gss_release_oid(&min_stat, &actual_mech);
516 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
517 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
520 if (ret != SASL_OK && ret != SASL_CONTINUE)
521 sasl_gs2_free_context_contents(text);
527 gs2_common_plug_init(const sasl_utils_t *utils,
529 int (*plug_alloc)(const sasl_utils_t *,
536 OM_uint32 major, minor;
543 if (gs2_indicate_mechs(utils) != SASL_OK) {
547 plugs = utils->malloc(2 * gs2_mechs->count * plugsize);
552 memset(plugs, 0, 2 * gs2_mechs->count * plugsize);
554 for (i = 0; i < gs2_mechs->count; i++) {
555 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
557 major = gss_inquire_saslname_for_mech(&minor,
558 &gs2_mechs->elements[i],
562 if (GSS_ERROR(major))
565 #define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize))
567 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
568 &gs2_mechs->elements[i]) == SASL_OK)
571 gss_release_buffer(&minor, &sasl_mech_name);
586 gs2_server_plug_alloc(const sasl_utils_t *utils,
588 gss_buffer_t sasl_name,
592 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
595 memset(splug, 0, sizeof(*splug));
597 ret = gs2_get_mech_attrs(utils, mech,
598 &splug->security_flags,
603 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
607 splug->mech_name = (char *)buf.value;
608 splug->glob_context = plug;
609 splug->mech_new = gs2_server_mech_new;
610 splug->mech_step = gs2_server_mech_step;
611 splug->mech_dispose = gs2_common_mech_dispose;
612 splug->mech_free = gs2_common_mech_free;
617 static sasl_server_plug_t *gs2_server_plugins;
618 static int gs2_server_plugcount;
621 gs2_server_plug_init(const sasl_utils_t *utils,
624 sasl_server_plug_t **pluglist,
632 if (maxversion < SASL_SERVER_PLUG_VERSION)
635 *outversion = SASL_SERVER_PLUG_VERSION;
637 if (gs2_server_plugins == NULL) {
638 ret = gs2_common_plug_init(utils,
639 sizeof(sasl_server_plug_t),
640 gs2_server_plug_alloc,
641 (void **)&gs2_server_plugins,
642 &gs2_server_plugcount);
647 *pluglist = gs2_server_plugins;
648 *plugcount = gs2_server_plugcount;
653 /***************************** Client Section *****************************/
655 static int gs2_client_mech_step(void *conn_context,
656 sasl_client_params_t *params,
657 const char *serverin,
658 unsigned serverinlen,
659 sasl_interact_t **prompt_need,
660 const char **clientout,
661 unsigned *clientoutlen,
662 sasl_out_params_t *oparams)
664 context_t *text = (context_t *)conn_context;
665 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
666 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
667 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
668 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
669 OM_uint32 req_flags, ret_flags;
670 gss_OID actual_mech = GSS_C_NO_OID;
672 int initialContextToken;
677 if (text->gss_ctx == GSS_C_NO_CONTEXT) {
678 ret = gs2_ask_user_info(text, params, NULL, 0, prompt_need, oparams);
682 if (params->gss_creds == GSS_C_NO_CREDENTIAL &&
683 text->password != NULL && text->password->len != 0) {
684 gss_buffer_desc password_buf;
685 gss_buffer_desc name_buf;
686 gss_OID_set_desc mechs;
688 name_buf.length = strlen(oparams->authid);
689 name_buf.value = (void *)oparams->authid;
691 password_buf.length = text->password->len;
692 password_buf.value = text->password->data;
695 mechs.elements = (gss_OID)text->mechanism;
697 maj_stat = gss_import_name(&min_stat,
701 if (GSS_ERROR(maj_stat))
704 maj_stat = gss_acquire_cred_with_password(&min_stat,
713 if (GSS_ERROR(maj_stat))
717 initialContextToken = 1;
719 initialContextToken = 0;
721 if (text->server_name == GSS_C_NO_NAME) { /* only once */
722 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
723 name_buf.value = params->utils->malloc(name_buf.length + 1);
724 if (name_buf.value == NULL) {
728 if (params->serverFQDN == NULL ||
729 strlen(params->serverFQDN) == 0) {
730 SETERROR(text->utils, "GS2 Failure: no serverFQDN");
735 snprintf(name_buf.value, name_buf.length + 1,
736 "%s@%s", params->service, params->serverFQDN);
738 maj_stat = gss_import_name (&min_stat,
740 GSS_C_NT_HOSTBASED_SERVICE,
742 params->utils->free(name_buf.value);
743 name_buf.value = NULL;
745 if (GSS_ERROR(maj_stat))
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;
755 input_token.value = (void *)serverin;
756 input_token.length = serverinlen;
758 if (initialContextToken) {
759 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
760 text->gs2_flags |= GS2_NONSTD_FLAG;
762 switch (params->chanbindingflag) {
763 case SASL_CB_FLAG_NONE:
764 text->gs2_flags |= GS2_CB_FLAG_N;
766 case SASL_CB_FLAG_USED:
767 text->gs2_flags |= GS2_CB_FLAG_P;
769 case SASL_CB_FLAG_WANT:
770 text->gs2_flags |= GS2_CB_FLAG_Y;
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);
782 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
784 maj_stat = gss_init_sec_context(&min_stat,
785 (params->gss_creds != GSS_C_NO_CREDENTIAL)
787 : text->client_creds,
790 (gss_OID)text->mechanism,
794 serverinlen ? &input_token : GSS_C_NO_BUFFER,
799 if (GSS_ERROR(maj_stat))
802 ret = gs2_make_message(text, params, initialContextToken, &output_token,
803 &text->out_buf, &text->out_buf_len);
807 *clientout = text->out_buf;
808 *clientoutlen = text->out_buf_len;
810 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
815 if (text->client_name != GSS_C_NO_NAME)
816 gss_release_name(&min_stat, &text->client_name);
818 maj_stat = gss_inquire_context(&min_stat,
824 &ret_flags, /* flags */
827 if (GSS_ERROR(maj_stat))
830 if (!g_OID_equal(text->mechanism, actual_mech)) {
831 ret = SASL_WRONGMECH;
834 if ((ret_flags & req_flags) != req_flags) {
835 maj_stat = SASL_BADAUTH;
839 maj_stat = gss_display_name(&min_stat,
843 if (GSS_ERROR(maj_stat))
846 oparams->gss_peer_name = text->server_name;
847 oparams->gss_local_name = text->client_name;
848 oparams->encode = NULL;
849 oparams->decode = NULL;
850 oparams->mech_ssf = 0;
851 oparams->maxoutbuf = 0xFFFFFF;
852 oparams->doneflag = 1;
855 gss_release_buffer(&min_stat, &output_token);
856 gss_release_buffer(&min_stat, &name_buf);
857 gss_release_oid(&min_stat, &actual_mech);
859 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
860 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
863 if (ret != SASL_OK && ret != SASL_CONTINUE)
864 sasl_gs2_free_context_contents(text);
869 static int gs2_client_mech_new(void *glob_context,
870 sasl_client_params_t *params,
876 text = sasl_gs2_new_context(params->utils);
878 MEMERROR(params->utils);
882 text->gss_ctx = GSS_C_NO_CONTEXT;
883 text->client_name = GSS_C_NO_NAME;
884 text->server_creds = GSS_C_NO_CREDENTIAL;
885 text->client_creds = GSS_C_NO_CREDENTIAL;
886 text->plug.client = glob_context;
888 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
890 if (ret != SASL_OK) {
891 gs2_common_mech_dispose(text, params->utils);
895 *conn_context = text;
900 static const unsigned long gs2_required_prompts[] = {
905 gs2_client_plug_alloc(const sasl_utils_t *utils,
907 gss_buffer_t sasl_name,
911 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
914 memset(cplug, 0, sizeof(*cplug));
916 ret = gs2_get_mech_attrs(utils, mech,
917 &cplug->security_flags,
922 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
926 cplug->mech_name = (char *)buf.value;
927 cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
928 cplug->glob_context = plug;
929 cplug->mech_new = gs2_client_mech_new;
930 cplug->mech_step = gs2_client_mech_step;
931 cplug->mech_dispose = gs2_common_mech_dispose;
932 cplug->mech_free = gs2_common_mech_free;
933 cplug->required_prompts = gs2_required_prompts;
938 static sasl_client_plug_t *gs2_client_plugins;
939 static int gs2_client_plugcount;
942 gs2_client_plug_init(const sasl_utils_t *utils,
945 sasl_client_plug_t **pluglist,
953 if (maxversion < SASL_CLIENT_PLUG_VERSION)
956 *outversion = SASL_CLIENT_PLUG_VERSION;
958 if (gs2_client_plugins == NULL) {
959 ret = gs2_common_plug_init(utils,
960 sizeof(sasl_client_plug_t),
961 gs2_client_plug_alloc,
962 (void **)&gs2_client_plugins,
963 &gs2_client_plugcount);
968 *pluglist = gs2_client_plugins;
969 *plugcount = gs2_client_plugcount;
975 * Copy header and application channel bindings to GSS channel bindings
976 * structure in context.
979 gs2_save_cbindings(context_t *text,
981 const char *chanbindingdata,
982 unsigned int chanbindinglen)
984 gss_buffer_t gss_bindings = &text->bindings.application_data;
988 assert(gss_bindings->value == NULL);
991 * The application-data field MUST be set to the gs2-header, excluding
992 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
993 * gs2-cb-flag of "p" is used, the application's channel binding data.
995 len = header->length;
996 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1000 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P)
1001 len += chanbindinglen;
1003 gss_bindings->length = len;
1004 gss_bindings->value = text->utils->malloc(len);
1005 if (gss_bindings->value == NULL)
1008 p = (unsigned char *)gss_bindings->value;
1009 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1010 memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
1011 p += header->length - 2;
1013 memcpy(p, header->value, header->length);
1014 p += header->length;
1017 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
1018 chanbindinglen != 0) {
1019 memcpy(p, chanbindingdata, chanbindinglen);
1025 #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADAUTH; } while (0)
1028 * Verify gs2-header, save authzid and channel bindings to context.
1031 gs2_verify_initial_message(context_t *text,
1032 sasl_server_params_t *sparams,
1037 char *p = (char *)in;
1038 unsigned remain = inlen;
1040 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1042 assert(text->cb_name == NULL);
1043 assert(text->authzid == NULL);
1046 token->value = NULL;
1048 /* minimum header includes CB flag and non-zero GSS token */
1049 CHECK_REMAIN(4); /* [pny],,. */
1051 /* non-standard GSS framing flag */
1052 if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1053 text->gs2_flags |= GS2_NONSTD_FLAG;
1058 /* SASL channel bindings */
1059 CHECK_REMAIN(1); /* [pny] */
1063 CHECK_REMAIN(1); /* = */
1066 return SASL_BADAUTH;
1068 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cb_name);
1072 text->gs2_flags |= GS2_CB_FLAG_P;
1075 text->gs2_flags |= GS2_CB_FLAG_N;
1078 text->gs2_flags |= GS2_CB_FLAG_Y;
1082 CHECK_REMAIN(1); /* , */
1085 return SASL_BADAUTH;
1087 /* authorization identity */
1088 if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1093 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1099 CHECK_REMAIN(1); /* , */
1102 return SASL_BADAUTH;
1104 buf.length = inlen - remain;
1105 buf.value = (void *)in;
1107 /* stash channel bindings to pass into gss_accept_sec_context() */
1108 ret = gs2_save_cbindings(text, &buf, sparams->chanbindingdata,
1109 sparams->chanbindinglen);
1113 buf.length = remain;
1116 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1117 token->value = text->utils->malloc(buf.length);
1118 if (token->value == NULL)
1121 token->length = buf.length;
1122 memcpy(token->value, buf.value, buf.length);
1124 unsigned int token_size;
1126 /* create a properly formed GSS token */
1127 token_size = gs2_token_size(text->mechanism, buf.length);
1128 token->value = text->utils->malloc(token_size);
1129 if (token->value == NULL)
1132 token->length = token_size;
1134 p = (char *)token->value;
1135 gs2_make_token_header(text->mechanism, buf.length,
1136 (unsigned char **)&p);
1137 memcpy(p, buf.value, buf.length);
1144 * Create gs2-header, save channel bindings to context.
1147 gs2_make_header(context_t *text,
1148 sasl_client_params_t *cparams,
1149 const char *authzid,
1153 size_t required = 0;
1154 size_t wire_authzid_len = 0, cb_name_len = 0;
1155 char *wire_authzid = NULL;
1158 gss_buffer_desc buf;
1163 /* non-standard GSS framing flag */
1164 if (text->gs2_flags & GS2_NONSTD_FLAG)
1165 required += 2; /* F, */
1167 /* SASL channel bindings */
1168 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1170 if (cparams->chanbindingtype == NULL)
1171 return SASL_BADPARAM;
1172 cb_name_len = strlen(cparams->chanbindingtype);
1173 required += 1 /*=*/ + cb_name_len;
1177 required += 2; /* [pny], */
1180 return SASL_BADPARAM;
1183 /* authorization identity */
1184 if (authzid != NULL) {
1185 ret = gs2_escape_authzid(text->utils, authzid,
1186 strlen(authzid), &wire_authzid);
1190 wire_authzid_len = strlen(wire_authzid);
1191 required += 2 /* a= */ + wire_authzid_len;
1194 required += 1; /* trailing comma */
1196 ret = _plug_buf_alloc(text->utils, out, outlen, required);
1197 if (ret != SASL_OK) {
1198 text->utils->free(wire_authzid);
1202 *out = text->out_buf;
1205 p = (char *)text->out_buf;
1206 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1210 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1213 memcpy(p + 2, cparams->chanbindingtype, cb_name_len);
1214 p += 2 + cb_name_len;
1224 if (wire_authzid != NULL) {
1226 memcpy(p + 2, wire_authzid, wire_authzid_len);
1227 text->utils->free(wire_authzid);
1228 p += 2 + wire_authzid_len;
1232 assert(p == (char *)text->out_buf + required);
1234 buf.length = required;
1237 ret = gs2_save_cbindings(text, &buf, cparams->chanbindingdata,
1238 cparams->chanbindinglen);
1244 * Convert a GSS token to a GS2 one
1247 gs2_make_message(context_t *text,
1248 sasl_client_params_t *cparams __attribute__((unused)),
1249 int initialContextToken,
1254 OM_uint32 major, minor;
1255 unsigned char *mech_token_data;
1256 size_t mech_token_size;
1258 unsigned header_len = 0;
1261 mech_token_size = token->length;
1262 mech_token_data = (unsigned char *)token->value;
1264 if (initialContextToken) {
1265 header_len = *outlen;
1267 major = gs2_verify_token_header(&minor, text->mechanism,
1268 &mech_token_size, &mech_token_data,
1270 if ((major == GSS_S_DEFECTIVE_TOKEN &&
1271 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1276 ret = _plug_buf_alloc(text->utils, out, outlen,
1277 header_len + mech_token_size);
1281 p = *out + header_len;
1282 memcpy(p, mech_token_data, mech_token_size);
1284 *outlen = header_len + mech_token_size;
1290 * Map GSS mechanism attributes to SASL ones
1293 gs2_get_mech_attrs(const sasl_utils_t *utils,
1295 unsigned int *security_flags,
1296 unsigned int *features)
1298 OM_uint32 major, minor;
1300 gss_OID_set attrs = GSS_C_NO_OID_SET;
1302 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1303 if (GSS_ERROR(major)) {
1304 utils->seterror(utils->conn, SASL_NOLOG,
1305 "GS2 Failure: gss_inquire_attrs_for_mech");
1309 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1310 *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1312 #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1313 attrs, &present) == GSS_S_COMPLETE && \
1318 if (MA_PRESENT(GSS_C_MA_PFS))
1319 *security_flags |= SASL_SEC_FORWARD_SECRECY;
1320 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1321 *security_flags |= SASL_SEC_NOANONYMOUS;
1322 if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1323 *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1324 if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1325 *security_flags |= SASL_SEC_MUTUAL_AUTH;
1326 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1327 *features |= SASL_FEAT_GSS_FRAMING;
1329 gss_release_oid_set(&minor, &attrs);
1334 * Enumerate GSS mechanisms that can be used for GS2
1336 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1338 OM_uint32 major, minor;
1339 gss_OID_desc desired_oids[3];
1340 gss_OID_set_desc desired_attrs;
1341 gss_OID_desc except_oids[3];
1342 gss_OID_set_desc except_attrs;
1344 if (gs2_mechs != GSS_C_NO_OID_SET)
1347 desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1348 desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1349 desired_oids[2] = *GSS_C_MA_CBINDINGS;
1350 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1351 desired_attrs.elements = desired_oids;
1353 except_oids[0] = *GSS_C_MA_MECH_NEGO;
1354 except_oids[1] = *GSS_C_MA_NOT_MECH;
1355 except_oids[2] = *GSS_C_MA_DEPRECATED;
1357 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1358 except_attrs.elements = except_oids;
1360 major = gss_indicate_mechs_by_attrs(&minor,
1365 if (GSS_ERROR(major)) {
1366 utils->seterror(utils->conn, SASL_NOLOG,
1367 "GS2 Failure: gss_indicate_mechs_by_attrs");
1371 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1375 * Map SASL mechanism name to OID
1378 gs2_map_sasl_name(const sasl_utils_t *utils,
1382 OM_uint32 major, minor;
1383 gss_buffer_desc buf;
1385 buf.length = strlen(mech);
1386 buf.value = (void *)mech;
1388 major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1389 if (GSS_ERROR(major)) {
1390 utils->seterror(utils->conn, SASL_NOLOG,
1391 "GS2 Failure: gss_inquire_mech_for_saslname");
1399 gs2_duplicate_buffer(const sasl_utils_t *utils,
1400 const gss_buffer_t src,
1403 dst->value = utils->malloc(src->length + 1);
1404 if (dst->value == NULL)
1407 memcpy(dst->value, src->value, src->length);
1408 ((char *)dst->value)[src->length] = '\0';
1409 dst->length = src->length;
1415 gs2_unescape_authzid(const sasl_utils_t *utils,
1421 size_t i, len, inlen = *remain;
1426 for (i = 0, len = 0; i < inlen; i++) {
1431 } else if (in[i] == '=') {
1433 return SASL_BADPROT;
1439 if (len == 0 || *endp == NULL)
1440 return SASL_BADPROT;
1442 p = *authzid = utils->malloc(len + 1);
1443 if (*authzid == NULL)
1446 for (i = 0; i < inlen; i++) {
1449 else if (in[i] == '=') {
1450 if (memcmp(&in[i + 1], "2C", 2) == 0)
1452 else if (memcmp(&in[i + 1], "3D", 2) == 0)
1455 utils->free(*authzid);
1457 return SASL_BADPROT;
1470 gs2_escape_authzid(const sasl_utils_t *utils,
1478 p = *authzid = utils->malloc((inlen * 3) + 1);
1479 if (*authzid == NULL)
1482 for (i = 0; i < inlen; i++) {
1484 memcpy(p, "=2C", 3);
1486 } else if (in[i] == '=') {
1487 memcpy(p, "=3D", 3);
1500 gs2_ask_user_info(context_t *text,
1501 sasl_client_params_t *params,
1502 char **realms __attribute__((unused)),
1503 int nrealm __attribute__((unused)),
1504 sasl_interact_t **prompt_need,
1505 sasl_out_params_t *oparams)
1507 int result = SASL_OK;
1508 const char *authid = NULL, *userid = NULL;
1509 int user_result = SASL_OK;
1510 int auth_result = SASL_OK;
1511 int pass_result = SASL_OK;
1513 /* try to get the authid */
1514 if (oparams->authid == NULL) {
1515 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1517 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1522 /* try to get the userid */
1523 if (oparams->user == NULL) {
1524 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1526 if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1531 /* try to get the password */
1532 if (text->password == NULL) {
1533 pass_result = _plug_get_password(params->utils, &text->password,
1534 &text->free_password, prompt_need);
1535 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1540 /* free prompts we got */
1541 if (prompt_need && *prompt_need) {
1542 params->utils->free(*prompt_need);
1543 *prompt_need = NULL;
1546 /* if there are prompts not filled in */
1547 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1548 pass_result == SASL_INTERACT) {
1550 /* make the prompt list */
1552 _plug_make_prompts(params->utils, prompt_need,
1553 user_result == SASL_INTERACT ?
1554 "Please enter your authorization name" : NULL,
1556 auth_result == SASL_INTERACT ?
1557 "Please enter your authentication name" : NULL,
1559 pass_result == SASL_INTERACT ?
1560 "Please enter your password" : NULL, NULL,
1564 if (result == SASL_OK) return SASL_INTERACT;
1569 if (oparams->authid == NULL) {
1570 if (userid == NULL || userid[0] == '\0') {
1571 result = params->canon_user(params->utils->conn, authid, 0,
1572 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1575 result = params->canon_user(params->utils->conn,
1576 authid, 0, SASL_CU_AUTHID, oparams);
1577 if (result != SASL_OK) return result;
1579 result = params->canon_user(params->utils->conn,
1580 userid, 0, SASL_CU_AUTHZID, oparams);
1582 if (result != SASL_OK)
1590 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1593 OM_uint32 maj_stat, min_stat;
1594 gss_buffer_desc msg;
1598 unsigned int len, curlen = 0;
1599 const char prefix[] = "GSSAPI Error: ";
1601 len = sizeof(prefix);
1602 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1606 strcpy(out, prefix);
1610 maj_stat = gss_display_status(&min_stat, maj,
1611 GSS_C_GSS_CODE, GSS_C_NULL_OID,
1614 if (GSS_ERROR(maj_stat)) {
1616 utils->log(utils->conn, SASL_LOG_FAIL,
1617 "GS2 Failure: (could not get major error message)");
1619 utils->seterror(utils->conn, 0,
1621 "(could not get major error message)");
1627 len += len + msg.length;
1628 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1629 if (ret != SASL_OK) {
1634 strcat(out, msg.value);
1636 gss_release_buffer(&min_stat, &msg);
1642 /* Now get the minor status */
1645 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1646 if (ret != SASL_OK) {
1655 maj_stat = gss_display_status(&min_stat, min,
1656 GSS_C_MECH_CODE, GSS_C_NULL_OID,
1659 if (GSS_ERROR(maj_stat)) {
1661 utils->log(utils->conn, SASL_LOG_FAIL,
1662 "GS2 Failure: (could not get minor error message)");
1664 utils->seterror(utils->conn, 0,
1666 "(could not get minor error message)");
1672 len += len + msg.length;
1674 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1675 if (ret != SASL_OK) {
1680 strcat(out, msg.value);
1682 gss_release_buffer(&min_stat, &msg);
1689 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1690 if (ret != SASL_OK) {
1698 utils->log(utils->conn, SASL_LOG_FAIL, out);
1700 utils->seterror(utils->conn, 0, out);