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 typedef struct context {
101 gss_ctx_id_t gss_ctx;
102 gss_name_t client_name;
103 gss_name_t server_name;
104 gss_cred_id_t server_creds;
105 gss_cred_id_t client_creds;
107 unsigned out_buf_len;
108 const sasl_utils_t *utils;
112 sasl_client_plug_t *client;
113 sasl_server_plug_t *server;
118 struct gss_channel_bindings_struct gss_cbindings;
119 sasl_secret_t *password;
120 unsigned int free_password;
124 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
126 static int gs2_get_init_creds(context_t *context,
127 sasl_client_params_t *params,
128 sasl_interact_t **prompt_need,
129 sasl_out_params_t *oparams);
131 static int gs2_verify_initial_message(context_t *text,
132 sasl_server_params_t *sparams,
137 static int gs2_make_header(context_t *text,
138 sasl_client_params_t *cparams,
143 static int gs2_make_message(context_t *text,
144 sasl_client_params_t *cparams,
145 int initialContextToken,
150 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
152 unsigned int *security_flags,
153 unsigned int *features,
154 const unsigned long **prompts);
156 static int gs2_indicate_mechs(const sasl_utils_t *utils);
158 static int gs2_map_sasl_name(const sasl_utils_t *utils,
162 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
163 const gss_buffer_t src,
166 static int gs2_unescape_authzid(const sasl_utils_t *utils,
171 static int gs2_escape_authzid(const sasl_utils_t *utils,
176 /* sasl_gs_log: only logs status string returned from gss_display_status() */
177 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
178 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
181 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
185 sasl_gs2_new_context(const sasl_utils_t *utils)
189 ret = utils->malloc(sizeof(context_t));
193 memset(ret, 0, sizeof(context_t));
200 sasl_gs2_free_context_contents(context_t *text)
207 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
208 gss_delete_sec_context(&min_stat,&text->gss_ctx,
210 text->gss_ctx = GSS_C_NO_CONTEXT;
213 if (text->client_name != GSS_C_NO_NAME) {
214 gss_release_name(&min_stat,&text->client_name);
215 text->client_name = GSS_C_NO_NAME;
218 if (text->server_name != GSS_C_NO_NAME) {
219 gss_release_name(&min_stat,&text->server_name);
220 text->server_name = GSS_C_NO_NAME;
223 if (text->server_creds != GSS_C_NO_CREDENTIAL) {
224 gss_release_cred(&min_stat, &text->server_creds);
225 text->server_creds = GSS_C_NO_CREDENTIAL;
228 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
229 gss_release_cred(&min_stat, &text->client_creds);
230 text->client_creds = GSS_C_NO_CREDENTIAL;
233 if (text->authid != NULL) {
234 text->utils->free(text->authid);
238 if (text->authzid != NULL) {
239 text->utils->free(text->authzid);
240 text->authzid = NULL;
243 gss_release_buffer(&min_stat, &text->gss_cbindings.application_data);
245 if (text->out_buf != NULL) {
246 text->utils->free(text->out_buf);
247 text->out_buf = NULL;
250 text->out_buf_len = 0;
252 if (text->cbindingname != NULL) {
253 text->utils->free(text->cbindingname);
254 text->cbindingname = NULL;
257 if (text->free_password)
258 _plug_free_secret(text->utils, &text->password);
260 memset(text, 0, sizeof(*text));
266 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
268 sasl_gs2_free_context_contents((context_t *)(conn_context));
269 utils->free(conn_context);
273 gs2_common_mech_free(void *global_context __attribute__((unused)),
274 const sasl_utils_t *utils)
278 if (gs2_mechs != GSS_C_NO_OID_SET) {
279 gss_release_oid_set(&minor, &gs2_mechs);
280 gs2_mechs = GSS_C_NO_OID_SET;
284 /***************************** Server Section *****************************/
287 gs2_server_mech_new(void *glob_context,
288 sasl_server_params_t *params,
289 const char *challenge __attribute__((unused)),
290 unsigned challen __attribute__((unused)),
296 text = sasl_gs2_new_context(params->utils);
298 MEMERROR(params->utils);
302 text->gss_ctx = GSS_C_NO_CONTEXT;
303 text->client_name = GSS_C_NO_NAME;
304 text->server_name = GSS_C_NO_NAME;
305 text->server_creds = GSS_C_NO_CREDENTIAL;
306 text->client_creds = GSS_C_NO_CREDENTIAL;
307 text->plug.server = glob_context;
309 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
311 if (ret != SASL_OK) {
312 gs2_common_mech_dispose(text, params->utils);
316 *conn_context = text;
322 gs2_server_mech_step(void *conn_context,
323 sasl_server_params_t *params,
324 const char *clientin,
325 unsigned clientinlen,
326 const char **serverout,
327 unsigned *serveroutlen,
328 sasl_out_params_t *oparams)
330 context_t *text = (context_t *)conn_context;
331 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
332 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
333 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
334 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
335 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
336 gss_name_t without = GSS_C_NO_NAME;
337 gss_OID_set_desc mechs;
338 OM_uint32 out_flags = 0;
339 int ret = 0, equal = 0;
340 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
343 if (serverout == NULL) {
344 PARAMERROR(text->utils);
345 return SASL_BADPARAM;
351 if (initialContextToken) {
352 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
353 name_buf.value = params->utils->malloc(name_buf.length + 1);
354 if (name_buf.value == NULL) {
355 MEMERROR(text->utils);
359 snprintf(name_buf.value, name_buf.length + 1,
360 "%s@%s", params->service, params->serverFQDN);
361 maj_stat = gss_import_name(&min_stat,
363 GSS_C_NT_HOSTBASED_SERVICE,
365 params->utils->free(name_buf.value);
366 name_buf.value = NULL;
368 if (GSS_ERROR(maj_stat))
371 assert(text->server_creds == GSS_C_NO_CREDENTIAL);
374 mechs.elements = (gss_OID)text->mechanism;
376 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
377 maj_stat = gss_acquire_cred(&min_stat,
385 if (GSS_ERROR(maj_stat))
389 ret = gs2_verify_initial_message(text,
397 input_token.value = (void *)clientin;
398 input_token.length = clientinlen;
401 maj_stat = gss_accept_sec_context(&min_stat,
403 (params->gss_creds != GSS_C_NO_CREDENTIAL)
404 ? (gss_cred_id_t)params->gss_creds
405 : text->server_creds,
407 &text->gss_cbindings,
413 &text->client_creds);
414 if (GSS_ERROR(maj_stat)) {
415 sasl_gs2_log(text->utils, maj_stat, min_stat);
416 text->utils->seterror(text->utils->conn, SASL_NOLOG,
417 "GS2 Failure: gss_accept_sec_context");
418 ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH;
422 *serveroutlen = output_token.length;
423 if (output_token.value != NULL) {
424 ret = _plug_buf_alloc(text->utils, &text->out_buf,
425 &text->out_buf_len, *serveroutlen);
428 memcpy(text->out_buf, output_token.value, *serveroutlen);
429 *serverout = text->out_buf;
431 /* No output token, send an empty string */
436 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
441 assert(maj_stat == GSS_S_COMPLETE);
443 if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) {
448 maj_stat = gss_display_name(&min_stat, text->client_name,
450 if (GSS_ERROR(maj_stat))
453 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
457 p = (char *)memchr(name_buf.value, '@', name_buf.length);
459 short_name_buf.length = (p - (char *)name_buf.value);
461 maj_stat = gss_import_name(&min_stat,
465 if (GSS_ERROR(maj_stat)) {
470 maj_stat = gss_compare_name(&min_stat, text->client_name,
472 if (GSS_ERROR(maj_stat)) {
478 ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
481 text->authid = (char *)short_name_buf.value;
482 short_name_buf.value = NULL;
483 short_name_buf.length = 0;
485 if (text->authzid != NULL) {
486 ret = params->canon_user(params->utils->conn,
488 SASL_CU_AUTHZID, oparams);
493 ret = params->canon_user(params->utils->conn,
495 text->authzid == NULL
496 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
502 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
504 oparams->cbindingdisp = SASL_CB_DISP_NONE;
507 oparams->cbindingdisp = SASL_CB_DISP_USED;
508 oparams->cbindingname = text->cbindingname;
511 oparams->cbindingdisp = SASL_CB_DISP_WANT;
515 if (text->client_creds != GSS_C_NO_CREDENTIAL)
516 oparams->client_creds = &text->client_creds;
518 oparams->client_creds = NULL;
520 oparams->gss_peer_name = text->client_name;
521 oparams->gss_local_name = text->server_name;
522 oparams->maxoutbuf = 0xFFFFFF;
523 oparams->encode = NULL;
524 oparams->decode = NULL;
525 oparams->mech_ssf = 0;
526 oparams->doneflag = 1;
531 if (initialContextToken)
532 gss_release_buffer(&min_stat, &input_token);
533 gss_release_buffer(&min_stat, &name_buf);
534 gss_release_buffer(&min_stat, &short_name_buf);
535 gss_release_buffer(&min_stat, &output_token);
536 gss_release_name(&min_stat, &without);
538 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
539 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
542 if (ret != SASL_OK && ret != SASL_CONTINUE)
543 sasl_gs2_free_context_contents(text);
549 gs2_common_plug_init(const sasl_utils_t *utils,
551 int (*plug_alloc)(const sasl_utils_t *,
558 OM_uint32 major, minor;
565 if (gs2_indicate_mechs(utils) != SASL_OK) {
569 plugs = utils->malloc(gs2_mechs->count * plugsize);
574 memset(plugs, 0, gs2_mechs->count * plugsize);
576 for (i = 0; i < gs2_mechs->count; i++) {
577 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
579 major = gss_inquire_saslname_for_mech(&minor,
580 &gs2_mechs->elements[i],
584 if (GSS_ERROR(major))
587 #define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize))
589 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
590 &gs2_mechs->elements[i]) == SASL_OK)
593 gss_release_buffer(&minor, &sasl_mech_name);
608 gs2_server_plug_alloc(const sasl_utils_t *utils,
610 gss_buffer_t sasl_name,
614 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
617 memset(splug, 0, sizeof(*splug));
619 ret = gs2_get_mech_attrs(utils, mech,
620 &splug->security_flags,
626 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
630 splug->mech_name = (char *)buf.value;
631 splug->glob_context = plug;
632 splug->mech_new = gs2_server_mech_new;
633 splug->mech_step = gs2_server_mech_step;
634 splug->mech_dispose = gs2_common_mech_dispose;
635 splug->mech_free = gs2_common_mech_free;
640 static sasl_server_plug_t *gs2_server_plugins;
641 static int gs2_server_plugcount;
644 gs2_server_plug_init(const sasl_utils_t *utils,
647 sasl_server_plug_t **pluglist,
655 if (maxversion < SASL_SERVER_PLUG_VERSION)
658 *outversion = SASL_SERVER_PLUG_VERSION;
660 if (gs2_server_plugins == NULL) {
661 ret = gs2_common_plug_init(utils,
662 sizeof(sasl_server_plug_t),
663 gs2_server_plug_alloc,
664 (void **)&gs2_server_plugins,
665 &gs2_server_plugcount);
670 *pluglist = gs2_server_plugins;
671 *plugcount = gs2_server_plugcount;
676 /***************************** Client Section *****************************/
678 static int gs2_client_mech_step(void *conn_context,
679 sasl_client_params_t *params,
680 const char *serverin,
681 unsigned serverinlen,
682 sasl_interact_t **prompt_need,
683 const char **clientout,
684 unsigned *clientoutlen,
685 sasl_out_params_t *oparams)
687 context_t *text = (context_t *)conn_context;
688 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
689 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
690 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
691 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
692 OM_uint32 req_flags, ret_flags;
694 int initialContextToken;
699 if (text->gss_ctx == GSS_C_NO_CONTEXT) {
700 ret = gs2_get_init_creds(text, params, prompt_need, oparams);
704 initialContextToken = 1;
706 initialContextToken = 0;
708 if (text->server_name == GSS_C_NO_NAME) { /* only once */
709 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
710 name_buf.value = params->utils->malloc(name_buf.length + 1);
711 if (name_buf.value == NULL) {
715 if (params->serverFQDN == NULL ||
716 strlen(params->serverFQDN) == 0) {
717 SETERROR(text->utils, "GS2 Failure: no serverFQDN");
722 snprintf(name_buf.value, name_buf.length + 1,
723 "%s@%s", params->service, params->serverFQDN);
725 maj_stat = gss_import_name(&min_stat,
727 GSS_C_NT_HOSTBASED_SERVICE,
729 params->utils->free(name_buf.value);
730 name_buf.value = NULL;
732 if (GSS_ERROR(maj_stat))
736 /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
737 if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
738 gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
739 text->gss_ctx = GSS_C_NO_CONTEXT;
742 input_token.value = (void *)serverin;
743 input_token.length = serverinlen;
745 if (initialContextToken) {
746 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
747 text->gs2_flags |= GS2_NONSTD_FLAG;
749 switch (params->cbindingdisp) {
750 case SASL_CB_DISP_NONE:
751 text->gs2_flags |= GS2_CB_FLAG_N;
753 case SASL_CB_DISP_USED:
754 text->gs2_flags |= GS2_CB_FLAG_P;
756 case SASL_CB_DISP_WANT:
757 text->gs2_flags |= GS2_CB_FLAG_Y;
761 ret = gs2_make_header(text, params,
762 strcmp(oparams->user, oparams->authid) ?
763 (char *) oparams->user : NULL,
764 &text->out_buf, &text->out_buf_len);
769 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
771 maj_stat = gss_init_sec_context(&min_stat,
772 (params->gss_creds != GSS_C_NO_CREDENTIAL)
773 ? (gss_cred_id_t)params->gss_creds
774 : text->client_creds,
777 (gss_OID)text->mechanism,
780 &text->gss_cbindings,
781 serverinlen ? &input_token : GSS_C_NO_BUFFER,
786 if (GSS_ERROR(maj_stat))
789 ret = gs2_make_message(text, params, initialContextToken, &output_token,
790 &text->out_buf, &text->out_buf_len);
794 *clientout = text->out_buf;
795 *clientoutlen = text->out_buf_len;
797 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
802 if (text->client_name != GSS_C_NO_NAME)
803 gss_release_name(&min_stat, &text->client_name);
805 maj_stat = gss_inquire_context(&min_stat,
811 &ret_flags, /* flags */
814 if (GSS_ERROR(maj_stat))
817 if ((ret_flags & req_flags) != req_flags) {
818 maj_stat = SASL_BADAUTH;
822 maj_stat = gss_display_name(&min_stat,
826 if (GSS_ERROR(maj_stat))
829 oparams->gss_peer_name = text->server_name;
830 oparams->gss_local_name = text->client_name;
831 oparams->encode = NULL;
832 oparams->decode = NULL;
833 oparams->mech_ssf = 0;
834 oparams->maxoutbuf = 0xFFFFFF;
835 oparams->doneflag = 1;
838 gss_release_buffer(&min_stat, &output_token);
839 gss_release_buffer(&min_stat, &name_buf);
841 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
842 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
845 if (ret != SASL_OK && ret != SASL_CONTINUE)
846 sasl_gs2_free_context_contents(text);
851 static int gs2_client_mech_new(void *glob_context,
852 sasl_client_params_t *params,
858 text = sasl_gs2_new_context(params->utils);
860 MEMERROR(params->utils);
864 text->gss_ctx = GSS_C_NO_CONTEXT;
865 text->client_name = GSS_C_NO_NAME;
866 text->server_creds = GSS_C_NO_CREDENTIAL;
867 text->client_creds = GSS_C_NO_CREDENTIAL;
868 text->plug.client = glob_context;
870 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
872 if (ret != SASL_OK) {
873 gs2_common_mech_dispose(text, params->utils);
877 *conn_context = text;
883 gs2_client_plug_alloc(const sasl_utils_t *utils,
885 gss_buffer_t sasl_name,
889 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
892 memset(cplug, 0, sizeof(*cplug));
894 ret = gs2_get_mech_attrs(utils, mech,
895 &cplug->security_flags,
897 &cplug->required_prompts);
901 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
905 cplug->mech_name = (char *)buf.value;
906 cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
907 cplug->glob_context = plug;
908 cplug->mech_new = gs2_client_mech_new;
909 cplug->mech_step = gs2_client_mech_step;
910 cplug->mech_dispose = gs2_common_mech_dispose;
911 cplug->mech_free = gs2_common_mech_free;
916 static sasl_client_plug_t *gs2_client_plugins;
917 static int gs2_client_plugcount;
920 gs2_client_plug_init(const sasl_utils_t *utils,
923 sasl_client_plug_t **pluglist,
931 if (maxversion < SASL_CLIENT_PLUG_VERSION)
934 *outversion = SASL_CLIENT_PLUG_VERSION;
936 if (gs2_client_plugins == NULL) {
937 ret = gs2_common_plug_init(utils,
938 sizeof(sasl_client_plug_t),
939 gs2_client_plug_alloc,
940 (void **)&gs2_client_plugins,
941 &gs2_client_plugcount);
946 *pluglist = gs2_client_plugins;
947 *plugcount = gs2_client_plugcount;
953 * Copy header and application channel bindings to GSS channel bindings
954 * structure in context.
957 gs2_save_cbindings(context_t *text,
959 const sasl_channel_binding_t *cbinding)
961 gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data;
965 assert(gss_cbindings->value == NULL);
968 * The application-data field MUST be set to the gs2-header, excluding
969 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
970 * gs2-cb-flag of "p" is used, the application's channel binding data.
972 len = header->length;
973 if (text->gs2_flags & GS2_NONSTD_FLAG) {
977 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
979 len += cbinding->len;
982 gss_cbindings->length = len;
983 gss_cbindings->value = text->utils->malloc(len);
984 if (gss_cbindings->value == NULL)
987 p = (unsigned char *)gss_cbindings->value;
988 if (text->gs2_flags & GS2_NONSTD_FLAG) {
989 memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
990 p += header->length - 2;
992 memcpy(p, header->value, header->length);
996 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
998 memcpy(p, cbinding->data, cbinding->len);
1004 #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0)
1007 * Verify gs2-header, save authzid and channel bindings to context.
1010 gs2_verify_initial_message(context_t *text,
1011 sasl_server_params_t *sparams,
1016 char *p = (char *)in;
1017 unsigned remain = inlen;
1019 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1021 assert(text->cbindingname == NULL);
1022 assert(text->authzid == NULL);
1025 token->value = NULL;
1027 /* minimum header includes CB flag and non-zero GSS token */
1028 CHECK_REMAIN(4); /* [pny],,. */
1030 /* non-standard GSS framing flag */
1031 if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1032 text->gs2_flags |= GS2_NONSTD_FLAG;
1037 /* SASL channel bindings */
1038 CHECK_REMAIN(1); /* [pny] */
1042 CHECK_REMAIN(1); /* = */
1045 return SASL_BADPROT;
1047 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname);
1051 text->gs2_flags |= GS2_CB_FLAG_P;
1054 text->gs2_flags |= GS2_CB_FLAG_N;
1057 text->gs2_flags |= GS2_CB_FLAG_Y;
1061 CHECK_REMAIN(1); /* , */
1064 return SASL_BADPROT;
1066 /* authorization identity */
1067 if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1072 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1078 CHECK_REMAIN(1); /* , */
1081 return SASL_BADPROT;
1083 buf.length = inlen - remain;
1084 buf.value = (void *)in;
1086 /* stash channel bindings to pass into gss_accept_sec_context() */
1087 ret = gs2_save_cbindings(text, &buf, sparams->cbinding);
1091 buf.length = remain;
1094 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1095 token->value = text->utils->malloc(buf.length);
1096 if (token->value == NULL)
1099 token->length = buf.length;
1100 memcpy(token->value, buf.value, buf.length);
1102 unsigned int token_size;
1104 /* create a properly formed GSS token */
1105 token_size = gs2_token_size(text->mechanism, buf.length);
1106 token->value = text->utils->malloc(token_size);
1107 if (token->value == NULL)
1110 token->length = token_size;
1112 p = (char *)token->value;
1113 gs2_make_token_header(text->mechanism, buf.length,
1114 (unsigned char **)&p);
1115 memcpy(p, buf.value, buf.length);
1122 * Create gs2-header, save channel bindings to context.
1125 gs2_make_header(context_t *text,
1126 sasl_client_params_t *cparams,
1127 const char *authzid,
1131 size_t required = 0;
1132 size_t wire_authzid_len = 0, cbnamelen = 0;
1133 char *wire_authzid = NULL;
1136 gss_buffer_desc buf;
1141 /* non-standard GSS framing flag */
1142 if (text->gs2_flags & GS2_NONSTD_FLAG)
1143 required += 2; /* F, */
1145 /* SASL channel bindings */
1146 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1148 if (!SASL_CB_PRESENT(cparams))
1149 return SASL_BADPARAM;
1150 cbnamelen = strlen(cparams->cbinding->name);
1151 required += 1 /*=*/ + cbnamelen;
1155 required += 2; /* [pny], */
1158 return SASL_BADPARAM;
1161 /* authorization identity */
1162 if (authzid != NULL) {
1163 ret = gs2_escape_authzid(text->utils, authzid,
1164 strlen(authzid), &wire_authzid);
1168 wire_authzid_len = strlen(wire_authzid);
1169 required += 2 /* a= */ + wire_authzid_len;
1172 required += 1; /* trailing comma */
1174 ret = _plug_buf_alloc(text->utils, out, outlen, required);
1175 if (ret != SASL_OK) {
1176 text->utils->free(wire_authzid);
1180 *out = text->out_buf;
1183 p = (char *)text->out_buf;
1184 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1188 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1191 memcpy(p + 2, cparams->cbinding->name, cbnamelen);
1202 if (wire_authzid != NULL) {
1204 memcpy(p + 2, wire_authzid, wire_authzid_len);
1205 text->utils->free(wire_authzid);
1206 p += 2 + wire_authzid_len;
1210 assert(p == (char *)text->out_buf + required);
1212 buf.length = required;
1215 ret = gs2_save_cbindings(text, &buf, cparams->cbinding);
1223 * Convert a GSS token to a GS2 one
1226 gs2_make_message(context_t *text,
1227 sasl_client_params_t *cparams __attribute__((unused)),
1228 int initialContextToken,
1233 OM_uint32 major, minor;
1234 unsigned char *mech_token_data;
1235 size_t mech_token_size;
1237 unsigned header_len = 0;
1240 mech_token_size = token->length;
1241 mech_token_data = (unsigned char *)token->value;
1243 if (initialContextToken) {
1244 header_len = *outlen;
1246 major = gs2_verify_token_header(&minor, text->mechanism,
1247 &mech_token_size, &mech_token_data,
1249 if ((major == GSS_S_DEFECTIVE_TOKEN &&
1250 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1255 ret = _plug_buf_alloc(text->utils, out, outlen,
1256 header_len + mech_token_size);
1260 p = *out + header_len;
1261 memcpy(p, mech_token_data, mech_token_size);
1263 *outlen = header_len + mech_token_size;
1268 static const unsigned long gs2_required_prompts[] = {
1273 * Map GSS mechanism attributes to SASL ones
1276 gs2_get_mech_attrs(const sasl_utils_t *utils,
1278 unsigned int *security_flags,
1279 unsigned int *features,
1280 const unsigned long **prompts)
1282 OM_uint32 major, minor;
1284 gss_OID_set attrs = GSS_C_NO_OID_SET;
1286 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1287 if (GSS_ERROR(major)) {
1288 utils->seterror(utils->conn, SASL_NOLOG,
1289 "GS2 Failure: gss_inquire_attrs_for_mech");
1293 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1294 *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1295 if (prompts != NULL)
1296 *prompts = gs2_required_prompts;
1298 #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1299 attrs, &present) == GSS_S_COMPLETE && \
1302 if (MA_PRESENT(GSS_C_MA_PFS))
1303 *security_flags |= SASL_SEC_FORWARD_SECRECY;
1304 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1305 *security_flags |= SASL_SEC_NOANONYMOUS;
1306 if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1307 *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1308 if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1309 *security_flags |= SASL_SEC_MUTUAL_AUTH;
1310 if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL)
1312 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1313 *features |= SASL_FEAT_GSS_FRAMING;
1315 gss_release_oid_set(&minor, &attrs);
1321 * Enumerate GSS mechanisms that can be used for GS2
1323 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1325 OM_uint32 major, minor;
1326 gss_OID_desc desired_oids[3];
1327 gss_OID_set_desc desired_attrs;
1328 gss_OID_desc except_oids[3];
1329 gss_OID_set_desc except_attrs;
1331 if (gs2_mechs != GSS_C_NO_OID_SET)
1334 desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1335 desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1336 desired_oids[2] = *GSS_C_MA_CBINDINGS;
1337 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1338 desired_attrs.elements = desired_oids;
1340 except_oids[0] = *GSS_C_MA_MECH_NEGO;
1341 except_oids[1] = *GSS_C_MA_NOT_MECH;
1342 except_oids[2] = *GSS_C_MA_DEPRECATED;
1344 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1345 except_attrs.elements = except_oids;
1347 major = gss_indicate_mechs_by_attrs(&minor,
1352 if (GSS_ERROR(major)) {
1353 utils->seterror(utils->conn, SASL_NOLOG,
1354 "GS2 Failure: gss_indicate_mechs_by_attrs");
1358 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1362 * Map SASL mechanism name to OID
1365 gs2_map_sasl_name(const sasl_utils_t *utils,
1369 OM_uint32 major, minor;
1370 gss_buffer_desc buf;
1372 buf.length = strlen(mech);
1373 buf.value = (void *)mech;
1375 major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1376 if (GSS_ERROR(major)) {
1377 utils->seterror(utils->conn, SASL_NOLOG,
1378 "GS2 Failure: gss_inquire_mech_for_saslname");
1386 gs2_duplicate_buffer(const sasl_utils_t *utils,
1387 const gss_buffer_t src,
1390 dst->value = utils->malloc(src->length + 1);
1391 if (dst->value == NULL)
1394 memcpy(dst->value, src->value, src->length);
1395 ((char *)dst->value)[src->length] = '\0';
1396 dst->length = src->length;
1402 gs2_unescape_authzid(const sasl_utils_t *utils,
1408 size_t i, len, inlen = *remain;
1413 for (i = 0, len = 0; i < inlen; i++) {
1418 } else if (in[i] == '=') {
1420 return SASL_BADPROT;
1426 if (len == 0 || *endp == NULL)
1427 return SASL_BADPROT;
1429 p = *authzid = utils->malloc(len + 1);
1430 if (*authzid == NULL)
1433 for (i = 0; i < inlen; i++) {
1436 else if (in[i] == '=') {
1437 if (memcmp(&in[i + 1], "2C", 2) == 0)
1439 else if (memcmp(&in[i + 1], "3D", 2) == 0)
1442 utils->free(*authzid);
1444 return SASL_BADPROT;
1457 gs2_escape_authzid(const sasl_utils_t *utils,
1465 p = *authzid = utils->malloc((inlen * 3) + 1);
1466 if (*authzid == NULL)
1469 for (i = 0; i < inlen; i++) {
1471 memcpy(p, "=2C", 3);
1473 } else if (in[i] == '=') {
1474 memcpy(p, "=3D", 3);
1486 #define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL)
1487 #define CRED_ERROR(status) ((status) == GSS_S_CRED_UNAVAIL || (status) == GSS_S_NO_CRED)
1490 * Determine the authentication identity from the application supplied
1491 * GSS credential, the application supplied identity, and the default
1492 * GSS credential, in that order. Then, acquire credentials.
1495 gs2_get_init_creds(context_t *text,
1496 sasl_client_params_t *params,
1497 sasl_interact_t **prompt_need,
1498 sasl_out_params_t *oparams)
1500 int result = SASL_OK;
1501 const char *authid = NULL, *userid = NULL;
1502 int user_result = SASL_OK;
1503 int auth_result = SASL_OK;
1504 int pass_result = SASL_OK;
1505 OM_uint32 maj_stat = GSS_S_COMPLETE, min_stat = 0;
1506 gss_OID_set_desc mechs;
1507 gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER;
1510 mechs.elements = (gss_OID)text->mechanism;
1513 * Get the authentication identity from the application.
1515 if (oparams->authid == NULL) {
1516 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1517 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1518 result = auth_result;
1524 * Get the authorization identity from the application.
1526 if (oparams->user == NULL) {
1527 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1528 if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1529 result = user_result;
1535 * Canonicalize the authentication and authorization identities before
1536 * calling GSS_Import_name.
1538 if (auth_result == SASL_OK && user_result == SASL_OK &&
1539 oparams->authid == NULL) {
1540 if (userid == NULL || userid[0] == '\0') {
1541 result = params->canon_user(params->utils->conn, authid, 0,
1542 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1545 result = params->canon_user(params->utils->conn,
1546 authid, 0, SASL_CU_AUTHID, oparams);
1547 if (result != SASL_OK)
1550 result = params->canon_user(params->utils->conn,
1551 userid, 0, SASL_CU_AUTHZID, oparams);
1552 if (result != SASL_OK)
1556 if (oparams->authid != NULL) {
1557 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
1560 * If no realm in authid, use server FQDN; we have no mechanism-
1561 * agnostic way of determing a realm from a service name.
1563 if (strchr(oparams->authid, '@') == NULL &&
1564 params->serverFQDN != NULL) {
1565 name_buf.length = strlen(oparams->authid) + 1 + strlen(params->serverFQDN);
1567 name_buf.value = params->utils->malloc(name_buf.length + 1);
1568 if (name_buf.value == NULL) {
1569 MEMERROR(text->utils);
1570 result = SASL_NOMEM;
1573 snprintf(name_buf.value, name_buf.length + 1,
1574 "%s@%s", oparams->authid, params->serverFQDN);
1576 name_buf.length = strlen(oparams->authid);
1577 name_buf.value = oparams->authid;
1580 assert(text->client_name == GSS_C_NO_NAME);
1582 maj_stat = gss_import_name(&min_stat,
1585 &text->client_name);
1586 if (name_buf.value != oparams->authid)
1587 params->utils->free(name_buf.value);
1588 if (GSS_ERROR(maj_stat))
1594 * If application didn't provide an authid, then use the default
1595 * credential. If that doesn't work, give up.
1597 if (!GOT_CREDS(text, params) && oparams->authid == NULL) {
1598 maj_stat = gss_acquire_cred(&min_stat,
1603 &text->client_creds,
1606 if (GSS_ERROR(maj_stat))
1609 assert(text->client_name == GSS_C_NO_NAME);
1611 maj_stat = gss_inquire_cred(&min_stat,
1613 ? (gss_cred_id_t)params->gss_creds
1614 : text->client_creds,
1619 if (GSS_ERROR(maj_stat))
1622 maj_stat = gss_display_name(&min_stat,
1626 if (GSS_ERROR(maj_stat))
1629 if (userid == NULL || userid[0] == '\0') {
1630 result = params->canon_user(params->utils->conn,
1631 cred_authid.value, cred_authid.length,
1632 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1635 result = params->canon_user(params->utils->conn,
1636 cred_authid.value, cred_authid.length,
1637 SASL_CU_AUTHID, oparams);
1638 if (result != SASL_OK)
1641 result = params->canon_user(params->utils->conn,
1642 cred_authid.value, cred_authid.length,
1643 SASL_CU_AUTHZID, oparams);
1644 if (result != SASL_OK)
1650 * Armed with the authentication identity, try to get a credential without
1653 if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) {
1654 maj_stat = gss_acquire_cred(&min_stat,
1659 &text->client_creds,
1662 if (GSS_ERROR(maj_stat) && !CRED_ERROR(maj_stat))
1667 * If that failed, try to get a credential with a password.
1669 if (!GOT_CREDS(text, params)) {
1670 if (text->password == NULL) {
1671 pass_result = _plug_get_password(params->utils, &text->password,
1672 &text->free_password, prompt_need);
1673 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1674 result = pass_result;
1679 if (text->password != NULL) {
1680 gss_buffer_desc password_buf;
1682 password_buf.length = text->password->len;
1683 password_buf.value = text->password->data;
1685 maj_stat = gss_acquire_cred_with_password(&min_stat,
1691 &text->client_creds,
1694 if (GSS_ERROR(maj_stat))
1699 maj_stat = GSS_S_COMPLETE;
1701 /* free prompts we got */
1702 if (prompt_need && *prompt_need) {
1703 params->utils->free(*prompt_need);
1704 *prompt_need = NULL;
1707 /* if there are prompts not filled in */
1708 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1709 pass_result == SASL_INTERACT) {
1710 /* make the prompt list */
1712 _plug_make_prompts(params->utils, prompt_need,
1713 user_result == SASL_INTERACT ?
1714 "Please enter your authorization name" : NULL,
1716 auth_result == SASL_INTERACT ?
1717 "Please enter your authentication name" : NULL,
1719 pass_result == SASL_INTERACT ?
1720 "Please enter your password" : NULL, NULL,
1724 if (result == SASL_OK)
1725 result = SASL_INTERACT;
1729 if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) {
1730 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
1734 gss_release_buffer(&min_stat, &cred_authid);
1740 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1743 OM_uint32 maj_stat, min_stat;
1744 gss_buffer_desc msg;
1748 unsigned int len, curlen = 0;
1749 const char prefix[] = "GS2 Error: ";
1751 len = sizeof(prefix);
1752 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1756 strcpy(out, prefix);
1760 maj_stat = gss_display_status(&min_stat, maj,
1761 GSS_C_GSS_CODE, GSS_C_NULL_OID,
1764 if (GSS_ERROR(maj_stat)) {
1766 utils->log(utils->conn, SASL_LOG_FAIL,
1767 "GS2 Failure: (could not get major error message)");
1769 utils->seterror(utils->conn, 0,
1771 "(could not get major error message)");
1777 len += len + msg.length;
1778 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1779 if (ret != SASL_OK) {
1784 strcat(out, msg.value);
1786 gss_release_buffer(&min_stat, &msg);
1792 /* Now get the minor status */
1795 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1796 if (ret != SASL_OK) {
1805 maj_stat = gss_display_status(&min_stat, min,
1806 GSS_C_MECH_CODE, GSS_C_NULL_OID,
1809 if (GSS_ERROR(maj_stat)) {
1811 utils->log(utils->conn, SASL_LOG_FAIL,
1812 "GS2 Failure: (could not get minor error message)");
1814 utils->seterror(utils->conn, 0,
1816 "(could not get minor error message)");
1822 len += len + msg.length;
1824 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1825 if (ret != SASL_OK) {
1830 strcat(out, msg.value);
1832 gss_release_buffer(&min_stat, &msg);
1839 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1840 if (ret != SASL_OK) {
1848 utils->log(utils->conn, SASL_LOG_FAIL, out);
1850 utils->seterror(utils->conn, 0, out);