2 * Copyright (c) 2010 PADL Software Pty Ltd.
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. Redistributions in any form must be accompanied by information on
17 * how to obtain complete source code for the GS2 software and any
18 * accompanying software that uses the GS2 software. The source code
19 * must either be included in the distribution or be available for no
20 * more than the cost of distribution plus a nominal fee, and must be
21 * freely redistributable under reasonable conditions. For an
22 * executable file, complete source code means the source code for all
23 * modules it contains. It does not include source code for modules or
24 * files that typically accompany the major components of the operating
25 * system on which the executable file runs.
27 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE ``AS IS'' AND ANY EXPRESS
28 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
30 * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
37 * THE POSSIBILITY OF SUCH DAMAGE.
40 * Copyright (c) 1998-2003 Carnegie Mellon University.
41 * All rights reserved.
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
47 * 1. Redistributions of source code must retain the above copyright
48 * notice, this list of conditions and the following disclaimer.
50 * 2. Redistributions in binary form must reproduce the above copyright
51 * notice, this list of conditions and the following disclaimer in
52 * the documentation and/or other materials provided with the
55 * 3. The name "Carnegie Mellon University" must not be used to
56 * endorse or promote products derived from this software without
57 * prior written permission. For permission or any other legal
58 * details, please contact
59 * Office of Technology Transfer
60 * Carnegie Mellon University
62 * Pittsburgh, PA 15213-3890
63 * (412) 268-4387, fax: (412) 268-7395
64 * tech-transfer@andrew.cmu.edu
66 * 4. Redistributions of any form whatsoever must retain the following
68 * "This product includes software developed by Computing Services
69 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
71 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
72 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
73 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
74 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
75 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
76 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
77 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
81 #include <gssapi/gssapi.h>
82 #include <gssapi/gssapi_ext.h>
89 #include "plugin_common.h"
97 #include "gs2_token.h"
99 #define GS2_CB_FLAG_MASK 0x0F
100 #define GS2_CB_FLAG_N 0x00
101 #define GS2_CB_FLAG_P 0x01
102 #define GS2_CB_FLAG_Y 0x02
103 #define GS2_NONSTD_FLAG 0x10
105 typedef struct context {
106 gss_ctx_id_t gss_ctx;
107 gss_name_t client_name;
108 gss_name_t server_name;
109 gss_cred_id_t server_creds;
110 gss_cred_id_t client_creds;
112 unsigned out_buf_len;
113 const sasl_utils_t *utils;
117 sasl_client_plug_t *client;
118 sasl_server_plug_t *server;
123 struct gss_channel_bindings_struct gss_cbindings;
124 sasl_secret_t *password;
125 unsigned int free_password;
129 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
131 static int gs2_get_init_creds(context_t *context,
132 sasl_client_params_t *params,
133 sasl_interact_t **prompt_need,
134 sasl_out_params_t *oparams);
136 static int gs2_verify_initial_message(context_t *text,
137 sasl_server_params_t *sparams,
142 static int gs2_make_header(context_t *text,
143 sasl_client_params_t *cparams,
148 static int gs2_make_message(context_t *text,
149 sasl_client_params_t *cparams,
150 int initialContextToken,
155 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
157 unsigned int *security_flags,
158 unsigned int *features,
159 const unsigned long **prompts);
161 static int gs2_indicate_mechs(const sasl_utils_t *utils);
163 static int gs2_map_sasl_name(const sasl_utils_t *utils,
167 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
168 const gss_buffer_t src,
171 static int gs2_unescape_authzid(const sasl_utils_t *utils,
176 static int gs2_escape_authzid(const sasl_utils_t *utils,
181 /* sasl_gs_log: only logs status string returned from gss_display_status() */
182 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
183 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
186 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
190 sasl_gs2_new_context(const sasl_utils_t *utils)
194 ret = utils->malloc(sizeof(context_t));
198 memset(ret, 0, sizeof(context_t));
205 sasl_gs2_free_context_contents(context_t *text)
212 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
213 gss_delete_sec_context(&min_stat,&text->gss_ctx,
215 text->gss_ctx = GSS_C_NO_CONTEXT;
218 if (text->client_name != GSS_C_NO_NAME) {
219 gss_release_name(&min_stat,&text->client_name);
220 text->client_name = GSS_C_NO_NAME;
223 if (text->server_name != GSS_C_NO_NAME) {
224 gss_release_name(&min_stat,&text->server_name);
225 text->server_name = GSS_C_NO_NAME;
228 if (text->server_creds != GSS_C_NO_CREDENTIAL) {
229 gss_release_cred(&min_stat, &text->server_creds);
230 text->server_creds = GSS_C_NO_CREDENTIAL;
233 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
234 gss_release_cred(&min_stat, &text->client_creds);
235 text->client_creds = GSS_C_NO_CREDENTIAL;
238 if (text->authid != NULL) {
239 text->utils->free(text->authid);
243 if (text->authzid != NULL) {
244 text->utils->free(text->authzid);
245 text->authzid = NULL;
248 gss_release_buffer(&min_stat, &text->gss_cbindings.application_data);
250 if (text->out_buf != NULL) {
251 text->utils->free(text->out_buf);
252 text->out_buf = NULL;
255 text->out_buf_len = 0;
257 if (text->cbindingname != NULL) {
258 text->utils->free(text->cbindingname);
259 text->cbindingname = NULL;
262 if (text->free_password)
263 _plug_free_secret(text->utils, &text->password);
265 memset(text, 0, sizeof(*text));
271 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
273 sasl_gs2_free_context_contents((context_t *)(conn_context));
274 utils->free(conn_context);
278 gs2_common_mech_free(void *global_context __attribute__((unused)),
279 const sasl_utils_t *utils)
283 if (gs2_mechs != GSS_C_NO_OID_SET) {
284 gss_release_oid_set(&minor, &gs2_mechs);
285 gs2_mechs = GSS_C_NO_OID_SET;
289 /***************************** Server Section *****************************/
292 gs2_server_mech_new(void *glob_context,
293 sasl_server_params_t *params,
294 const char *challenge __attribute__((unused)),
295 unsigned challen __attribute__((unused)),
301 text = sasl_gs2_new_context(params->utils);
303 MEMERROR(params->utils);
307 text->gss_ctx = GSS_C_NO_CONTEXT;
308 text->client_name = GSS_C_NO_NAME;
309 text->server_name = GSS_C_NO_NAME;
310 text->server_creds = GSS_C_NO_CREDENTIAL;
311 text->client_creds = GSS_C_NO_CREDENTIAL;
312 text->plug.server = glob_context;
314 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
316 if (ret != SASL_OK) {
317 gs2_common_mech_dispose(text, params->utils);
321 *conn_context = text;
327 gs2_server_mech_step(void *conn_context,
328 sasl_server_params_t *params,
329 const char *clientin,
330 unsigned clientinlen,
331 const char **serverout,
332 unsigned *serveroutlen,
333 sasl_out_params_t *oparams)
335 context_t *text = (context_t *)conn_context;
336 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
337 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
338 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
339 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
340 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
341 gss_name_t without = GSS_C_NO_NAME;
342 gss_OID_set_desc mechs;
343 OM_uint32 out_flags = 0;
344 int ret = 0, equal = 0;
345 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
348 if (serverout == NULL) {
349 PARAMERROR(text->utils);
350 return SASL_BADPARAM;
356 if (initialContextToken) {
357 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
358 name_buf.value = params->utils->malloc(name_buf.length + 1);
359 if (name_buf.value == NULL) {
360 MEMERROR(text->utils);
364 snprintf(name_buf.value, name_buf.length + 1,
365 "%s@%s", params->service, params->serverFQDN);
366 maj_stat = gss_import_name(&min_stat,
368 GSS_C_NT_HOSTBASED_SERVICE,
370 params->utils->free(name_buf.value);
371 name_buf.value = NULL;
373 if (GSS_ERROR(maj_stat))
376 assert(text->server_creds == GSS_C_NO_CREDENTIAL);
379 mechs.elements = (gss_OID)text->mechanism;
381 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
382 maj_stat = gss_acquire_cred(&min_stat,
390 if (GSS_ERROR(maj_stat))
394 ret = gs2_verify_initial_message(text,
402 input_token.value = (void *)clientin;
403 input_token.length = clientinlen;
406 maj_stat = gss_accept_sec_context(&min_stat,
408 (params->gss_creds != GSS_C_NO_CREDENTIAL)
409 ? (gss_cred_id_t)params->gss_creds
410 : text->server_creds,
412 &text->gss_cbindings,
418 &text->client_creds);
419 if (GSS_ERROR(maj_stat)) {
420 sasl_gs2_log(text->utils, maj_stat, min_stat);
421 text->utils->seterror(text->utils->conn, SASL_NOLOG,
422 "GS2 Failure: gss_accept_sec_context");
423 ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH;
427 *serveroutlen = output_token.length;
428 if (output_token.value != NULL) {
429 ret = _plug_buf_alloc(text->utils, &text->out_buf,
430 &text->out_buf_len, *serveroutlen);
433 memcpy(text->out_buf, output_token.value, *serveroutlen);
434 *serverout = text->out_buf;
436 /* No output token, send an empty string */
441 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
446 assert(maj_stat == GSS_S_COMPLETE);
448 if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) {
453 maj_stat = gss_display_name(&min_stat, text->client_name,
455 if (GSS_ERROR(maj_stat))
458 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
462 p = (char *)memchr(name_buf.value, '@', name_buf.length);
464 short_name_buf.length = (p - (char *)name_buf.value);
466 maj_stat = gss_import_name(&min_stat,
470 if (GSS_ERROR(maj_stat)) {
475 maj_stat = gss_compare_name(&min_stat, text->client_name,
477 if (GSS_ERROR(maj_stat)) {
483 ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
486 text->authid = (char *)short_name_buf.value;
487 short_name_buf.value = NULL;
488 short_name_buf.length = 0;
490 if (text->authzid != NULL) {
491 ret = params->canon_user(params->utils->conn,
493 SASL_CU_AUTHZID, oparams);
498 ret = params->canon_user(params->utils->conn,
500 text->authzid == NULL
501 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
507 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
509 oparams->cbindingdisp = SASL_CB_DISP_NONE;
512 oparams->cbindingdisp = SASL_CB_DISP_USED;
513 oparams->cbindingname = text->cbindingname;
516 oparams->cbindingdisp = SASL_CB_DISP_WANT;
520 if (text->client_creds != GSS_C_NO_CREDENTIAL)
521 oparams->client_creds = &text->client_creds;
523 oparams->client_creds = NULL;
525 oparams->gss_peer_name = text->client_name;
526 oparams->gss_local_name = text->server_name;
527 oparams->maxoutbuf = 0xFFFFFF;
528 oparams->encode = NULL;
529 oparams->decode = NULL;
530 oparams->mech_ssf = 0;
531 oparams->doneflag = 1;
536 if (initialContextToken)
537 gss_release_buffer(&min_stat, &input_token);
538 gss_release_buffer(&min_stat, &name_buf);
539 gss_release_buffer(&min_stat, &short_name_buf);
540 gss_release_buffer(&min_stat, &output_token);
541 gss_release_name(&min_stat, &without);
543 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
544 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
547 if (ret != SASL_OK && ret != SASL_CONTINUE)
548 sasl_gs2_free_context_contents(text);
554 gs2_common_plug_init(const sasl_utils_t *utils,
556 int (*plug_alloc)(const sasl_utils_t *,
563 OM_uint32 major, minor;
570 if (gs2_indicate_mechs(utils) != SASL_OK) {
574 plugs = utils->malloc(gs2_mechs->count * plugsize);
579 memset(plugs, 0, gs2_mechs->count * plugsize);
581 for (i = 0; i < gs2_mechs->count; i++) {
582 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
584 major = gss_inquire_saslname_for_mech(&minor,
585 &gs2_mechs->elements[i],
589 if (GSS_ERROR(major))
592 #define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize))
594 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
595 &gs2_mechs->elements[i]) == SASL_OK)
598 gss_release_buffer(&minor, &sasl_mech_name);
613 gs2_server_plug_alloc(const sasl_utils_t *utils,
615 gss_buffer_t sasl_name,
619 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
622 memset(splug, 0, sizeof(*splug));
624 ret = gs2_get_mech_attrs(utils, mech,
625 &splug->security_flags,
631 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
635 splug->mech_name = (char *)buf.value;
636 splug->glob_context = plug;
637 splug->mech_new = gs2_server_mech_new;
638 splug->mech_step = gs2_server_mech_step;
639 splug->mech_dispose = gs2_common_mech_dispose;
640 splug->mech_free = gs2_common_mech_free;
645 static sasl_server_plug_t *gs2_server_plugins;
646 static int gs2_server_plugcount;
649 gs2_server_plug_init(const sasl_utils_t *utils,
652 sasl_server_plug_t **pluglist,
660 if (maxversion < SASL_SERVER_PLUG_VERSION)
663 *outversion = SASL_SERVER_PLUG_VERSION;
665 if (gs2_server_plugins == NULL) {
666 ret = gs2_common_plug_init(utils,
667 sizeof(sasl_server_plug_t),
668 gs2_server_plug_alloc,
669 (void **)&gs2_server_plugins,
670 &gs2_server_plugcount);
675 *pluglist = gs2_server_plugins;
676 *plugcount = gs2_server_plugcount;
681 /***************************** Client Section *****************************/
683 static int gs2_client_mech_step(void *conn_context,
684 sasl_client_params_t *params,
685 const char *serverin,
686 unsigned serverinlen,
687 sasl_interact_t **prompt_need,
688 const char **clientout,
689 unsigned *clientoutlen,
690 sasl_out_params_t *oparams)
692 context_t *text = (context_t *)conn_context;
693 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
694 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
695 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
696 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
697 OM_uint32 req_flags, ret_flags;
699 int initialContextToken;
704 if (text->gss_ctx == GSS_C_NO_CONTEXT) {
705 ret = gs2_get_init_creds(text, params, prompt_need, oparams);
709 initialContextToken = 1;
711 initialContextToken = 0;
713 if (text->server_name == GSS_C_NO_NAME) { /* only once */
714 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
715 name_buf.value = params->utils->malloc(name_buf.length + 1);
716 if (name_buf.value == NULL) {
720 if (params->serverFQDN == NULL ||
721 strlen(params->serverFQDN) == 0) {
722 SETERROR(text->utils, "GS2 Failure: no serverFQDN");
727 snprintf(name_buf.value, name_buf.length + 1,
728 "%s@%s", params->service, params->serverFQDN);
730 maj_stat = gss_import_name(&min_stat,
732 GSS_C_NT_HOSTBASED_SERVICE,
734 params->utils->free(name_buf.value);
735 name_buf.value = NULL;
737 if (GSS_ERROR(maj_stat))
741 /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
742 if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
743 gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
744 text->gss_ctx = GSS_C_NO_CONTEXT;
747 input_token.value = (void *)serverin;
748 input_token.length = serverinlen;
750 if (initialContextToken) {
751 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
752 text->gs2_flags |= GS2_NONSTD_FLAG;
754 switch (params->cbindingdisp) {
755 case SASL_CB_DISP_NONE:
756 text->gs2_flags |= GS2_CB_FLAG_N;
758 case SASL_CB_DISP_USED:
759 text->gs2_flags |= GS2_CB_FLAG_P;
761 case SASL_CB_DISP_WANT:
762 text->gs2_flags |= GS2_CB_FLAG_Y;
766 ret = gs2_make_header(text, params,
767 strcmp(oparams->user, oparams->authid) ?
768 (char *) oparams->user : NULL,
769 &text->out_buf, &text->out_buf_len);
774 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
776 maj_stat = gss_init_sec_context(&min_stat,
777 (params->gss_creds != GSS_C_NO_CREDENTIAL)
778 ? (gss_cred_id_t)params->gss_creds
779 : text->client_creds,
782 (gss_OID)text->mechanism,
785 &text->gss_cbindings,
786 serverinlen ? &input_token : GSS_C_NO_BUFFER,
791 if (GSS_ERROR(maj_stat))
794 ret = gs2_make_message(text, params, initialContextToken, &output_token,
795 &text->out_buf, &text->out_buf_len);
799 *clientout = text->out_buf;
800 *clientoutlen = text->out_buf_len;
802 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
807 if (text->client_name != GSS_C_NO_NAME)
808 gss_release_name(&min_stat, &text->client_name);
810 maj_stat = gss_inquire_context(&min_stat,
816 &ret_flags, /* flags */
819 if (GSS_ERROR(maj_stat))
822 if ((ret_flags & req_flags) != req_flags) {
823 maj_stat = SASL_BADAUTH;
827 maj_stat = gss_display_name(&min_stat,
831 if (GSS_ERROR(maj_stat))
834 oparams->gss_peer_name = text->server_name;
835 oparams->gss_local_name = text->client_name;
836 oparams->encode = NULL;
837 oparams->decode = NULL;
838 oparams->mech_ssf = 0;
839 oparams->maxoutbuf = 0xFFFFFF;
840 oparams->doneflag = 1;
843 gss_release_buffer(&min_stat, &output_token);
844 gss_release_buffer(&min_stat, &name_buf);
846 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
847 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
850 if (ret != SASL_OK && ret != SASL_CONTINUE)
851 sasl_gs2_free_context_contents(text);
856 static int gs2_client_mech_new(void *glob_context,
857 sasl_client_params_t *params,
863 text = sasl_gs2_new_context(params->utils);
865 MEMERROR(params->utils);
869 text->gss_ctx = GSS_C_NO_CONTEXT;
870 text->client_name = GSS_C_NO_NAME;
871 text->server_creds = GSS_C_NO_CREDENTIAL;
872 text->client_creds = GSS_C_NO_CREDENTIAL;
873 text->plug.client = glob_context;
875 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
877 if (ret != SASL_OK) {
878 gs2_common_mech_dispose(text, params->utils);
882 *conn_context = text;
888 gs2_client_plug_alloc(const sasl_utils_t *utils,
890 gss_buffer_t sasl_name,
894 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
897 memset(cplug, 0, sizeof(*cplug));
899 ret = gs2_get_mech_attrs(utils, mech,
900 &cplug->security_flags,
902 &cplug->required_prompts);
906 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
910 cplug->mech_name = (char *)buf.value;
911 cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
912 cplug->glob_context = plug;
913 cplug->mech_new = gs2_client_mech_new;
914 cplug->mech_step = gs2_client_mech_step;
915 cplug->mech_dispose = gs2_common_mech_dispose;
916 cplug->mech_free = gs2_common_mech_free;
921 static sasl_client_plug_t *gs2_client_plugins;
922 static int gs2_client_plugcount;
925 gs2_client_plug_init(const sasl_utils_t *utils,
928 sasl_client_plug_t **pluglist,
936 if (maxversion < SASL_CLIENT_PLUG_VERSION)
939 *outversion = SASL_CLIENT_PLUG_VERSION;
941 if (gs2_client_plugins == NULL) {
942 ret = gs2_common_plug_init(utils,
943 sizeof(sasl_client_plug_t),
944 gs2_client_plug_alloc,
945 (void **)&gs2_client_plugins,
946 &gs2_client_plugcount);
951 *pluglist = gs2_client_plugins;
952 *plugcount = gs2_client_plugcount;
958 * Copy header and application channel bindings to GSS channel bindings
959 * structure in context.
962 gs2_save_cbindings(context_t *text,
964 const sasl_channel_binding_t *cbinding)
966 gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data;
970 assert(gss_cbindings->value == NULL);
973 * The application-data field MUST be set to the gs2-header, excluding
974 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
975 * gs2-cb-flag of "p" is used, the application's channel binding data.
977 len = header->length;
978 if (text->gs2_flags & GS2_NONSTD_FLAG) {
982 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
984 len += cbinding->len;
987 gss_cbindings->length = len;
988 gss_cbindings->value = text->utils->malloc(len);
989 if (gss_cbindings->value == NULL)
992 p = (unsigned char *)gss_cbindings->value;
993 if (text->gs2_flags & GS2_NONSTD_FLAG) {
994 memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
995 p += header->length - 2;
997 memcpy(p, header->value, header->length);
1001 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
1003 memcpy(p, cbinding->data, cbinding->len);
1009 #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0)
1012 * Verify gs2-header, save authzid and channel bindings to context.
1015 gs2_verify_initial_message(context_t *text,
1016 sasl_server_params_t *sparams,
1021 char *p = (char *)in;
1022 unsigned remain = inlen;
1024 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1026 assert(text->cbindingname == NULL);
1027 assert(text->authzid == NULL);
1030 token->value = NULL;
1032 /* minimum header includes CB flag and non-zero GSS token */
1033 CHECK_REMAIN(4); /* [pny],,. */
1035 /* non-standard GSS framing flag */
1036 if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1037 text->gs2_flags |= GS2_NONSTD_FLAG;
1042 /* SASL channel bindings */
1043 CHECK_REMAIN(1); /* [pny] */
1047 CHECK_REMAIN(1); /* = */
1050 return SASL_BADPROT;
1052 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname);
1056 text->gs2_flags |= GS2_CB_FLAG_P;
1059 text->gs2_flags |= GS2_CB_FLAG_N;
1062 text->gs2_flags |= GS2_CB_FLAG_Y;
1066 CHECK_REMAIN(1); /* , */
1069 return SASL_BADPROT;
1071 /* authorization identity */
1072 if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1077 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1083 CHECK_REMAIN(1); /* , */
1086 return SASL_BADPROT;
1088 buf.length = inlen - remain;
1089 buf.value = (void *)in;
1091 /* stash channel bindings to pass into gss_accept_sec_context() */
1092 ret = gs2_save_cbindings(text, &buf, sparams->cbinding);
1096 buf.length = remain;
1099 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1100 token->value = text->utils->malloc(buf.length);
1101 if (token->value == NULL)
1104 token->length = buf.length;
1105 memcpy(token->value, buf.value, buf.length);
1107 unsigned int token_size;
1109 /* create a properly formed GSS token */
1110 token_size = gs2_token_size(text->mechanism, buf.length);
1111 token->value = text->utils->malloc(token_size);
1112 if (token->value == NULL)
1115 token->length = token_size;
1117 p = (char *)token->value;
1118 gs2_make_token_header(text->mechanism, buf.length,
1119 (unsigned char **)&p);
1120 memcpy(p, buf.value, buf.length);
1127 * Create gs2-header, save channel bindings to context.
1130 gs2_make_header(context_t *text,
1131 sasl_client_params_t *cparams,
1132 const char *authzid,
1136 size_t required = 0;
1137 size_t wire_authzid_len = 0, cbnamelen = 0;
1138 char *wire_authzid = NULL;
1141 gss_buffer_desc buf;
1146 /* non-standard GSS framing flag */
1147 if (text->gs2_flags & GS2_NONSTD_FLAG)
1148 required += 2; /* F, */
1150 /* SASL channel bindings */
1151 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1153 if (!SASL_CB_PRESENT(cparams))
1154 return SASL_BADPARAM;
1155 cbnamelen = strlen(cparams->cbinding->name);
1156 required += 1 /*=*/ + cbnamelen;
1160 required += 2; /* [pny], */
1163 return SASL_BADPARAM;
1166 /* authorization identity */
1167 if (authzid != NULL) {
1168 ret = gs2_escape_authzid(text->utils, authzid,
1169 strlen(authzid), &wire_authzid);
1173 wire_authzid_len = strlen(wire_authzid);
1174 required += 2 /* a= */ + wire_authzid_len;
1177 required += 1; /* trailing comma */
1179 ret = _plug_buf_alloc(text->utils, out, outlen, required);
1180 if (ret != SASL_OK) {
1181 text->utils->free(wire_authzid);
1185 *out = text->out_buf;
1188 p = (char *)text->out_buf;
1189 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1193 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1196 memcpy(p + 2, cparams->cbinding->name, cbnamelen);
1207 if (wire_authzid != NULL) {
1209 memcpy(p + 2, wire_authzid, wire_authzid_len);
1210 text->utils->free(wire_authzid);
1211 p += 2 + wire_authzid_len;
1215 assert(p == (char *)text->out_buf + required);
1217 buf.length = required;
1220 ret = gs2_save_cbindings(text, &buf, cparams->cbinding);
1228 * Convert a GSS token to a GS2 one
1231 gs2_make_message(context_t *text,
1232 sasl_client_params_t *cparams __attribute__((unused)),
1233 int initialContextToken,
1238 OM_uint32 major, minor;
1239 unsigned char *mech_token_data;
1240 size_t mech_token_size;
1242 unsigned header_len = 0;
1245 mech_token_size = token->length;
1246 mech_token_data = (unsigned char *)token->value;
1248 if (initialContextToken) {
1249 header_len = *outlen;
1251 major = gs2_verify_token_header(&minor, text->mechanism,
1252 &mech_token_size, &mech_token_data,
1254 if ((major == GSS_S_DEFECTIVE_TOKEN &&
1255 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1260 ret = _plug_buf_alloc(text->utils, out, outlen,
1261 header_len + mech_token_size);
1265 p = *out + header_len;
1266 memcpy(p, mech_token_data, mech_token_size);
1268 *outlen = header_len + mech_token_size;
1273 static const unsigned long gs2_required_prompts[] = {
1278 * Map GSS mechanism attributes to SASL ones
1281 gs2_get_mech_attrs(const sasl_utils_t *utils,
1283 unsigned int *security_flags,
1284 unsigned int *features,
1285 const unsigned long **prompts)
1287 OM_uint32 major, minor;
1289 gss_OID_set attrs = GSS_C_NO_OID_SET;
1291 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1292 if (GSS_ERROR(major)) {
1293 utils->seterror(utils->conn, SASL_NOLOG,
1294 "GS2 Failure: gss_inquire_attrs_for_mech");
1298 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1299 *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1300 if (prompts != NULL)
1301 *prompts = gs2_required_prompts;
1303 #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1304 attrs, &present) == GSS_S_COMPLETE && \
1307 if (MA_PRESENT(GSS_C_MA_PFS))
1308 *security_flags |= SASL_SEC_FORWARD_SECRECY;
1309 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1310 *security_flags |= SASL_SEC_NOANONYMOUS;
1311 if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1312 *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1313 if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1314 *security_flags |= SASL_SEC_MUTUAL_AUTH;
1315 if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL)
1317 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1318 *features |= SASL_FEAT_GSS_FRAMING;
1320 gss_release_oid_set(&minor, &attrs);
1326 * Enumerate GSS mechanisms that can be used for GS2
1328 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1330 OM_uint32 major, minor;
1331 gss_OID_desc desired_oids[3];
1332 gss_OID_set_desc desired_attrs;
1333 gss_OID_desc except_oids[3];
1334 gss_OID_set_desc except_attrs;
1336 if (gs2_mechs != GSS_C_NO_OID_SET)
1339 desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1340 desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1341 desired_oids[2] = *GSS_C_MA_CBINDINGS;
1342 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1343 desired_attrs.elements = desired_oids;
1345 except_oids[0] = *GSS_C_MA_MECH_NEGO;
1346 except_oids[1] = *GSS_C_MA_NOT_MECH;
1347 except_oids[2] = *GSS_C_MA_DEPRECATED;
1349 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1350 except_attrs.elements = except_oids;
1352 major = gss_indicate_mechs_by_attrs(&minor,
1357 if (GSS_ERROR(major)) {
1358 utils->seterror(utils->conn, SASL_NOLOG,
1359 "GS2 Failure: gss_indicate_mechs_by_attrs");
1363 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1367 * Map SASL mechanism name to OID
1370 gs2_map_sasl_name(const sasl_utils_t *utils,
1374 OM_uint32 major, minor;
1375 gss_buffer_desc buf;
1377 buf.length = strlen(mech);
1378 buf.value = (void *)mech;
1380 major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1381 if (GSS_ERROR(major)) {
1382 utils->seterror(utils->conn, SASL_NOLOG,
1383 "GS2 Failure: gss_inquire_mech_for_saslname");
1391 gs2_duplicate_buffer(const sasl_utils_t *utils,
1392 const gss_buffer_t src,
1395 dst->value = utils->malloc(src->length + 1);
1396 if (dst->value == NULL)
1399 memcpy(dst->value, src->value, src->length);
1400 ((char *)dst->value)[src->length] = '\0';
1401 dst->length = src->length;
1407 gs2_unescape_authzid(const sasl_utils_t *utils,
1413 size_t i, len, inlen = *remain;
1418 for (i = 0, len = 0; i < inlen; i++) {
1423 } else if (in[i] == '=') {
1425 return SASL_BADPROT;
1431 if (len == 0 || *endp == NULL)
1432 return SASL_BADPROT;
1434 p = *authzid = utils->malloc(len + 1);
1435 if (*authzid == NULL)
1438 for (i = 0; i < inlen; i++) {
1441 else if (in[i] == '=') {
1442 if (memcmp(&in[i + 1], "2C", 2) == 0)
1444 else if (memcmp(&in[i + 1], "3D", 2) == 0)
1447 utils->free(*authzid);
1449 return SASL_BADPROT;
1462 gs2_escape_authzid(const sasl_utils_t *utils,
1470 p = *authzid = utils->malloc((inlen * 3) + 1);
1471 if (*authzid == NULL)
1474 for (i = 0; i < inlen; i++) {
1476 memcpy(p, "=2C", 3);
1478 } else if (in[i] == '=') {
1479 memcpy(p, "=3D", 3);
1491 #define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL)
1494 gs2_get_init_creds(context_t *text,
1495 sasl_client_params_t *params,
1496 sasl_interact_t **prompt_need,
1497 sasl_out_params_t *oparams)
1499 int result = SASL_OK;
1500 const char *authid = NULL, *userid = NULL;
1501 int user_result = SASL_OK;
1502 int auth_result = SASL_OK;
1503 int pass_result = SASL_OK;
1504 OM_uint32 maj_stat, min_stat;
1505 gss_OID_set_desc mechs;
1506 gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER;
1509 mechs.elements = (gss_OID)text->mechanism;
1512 * Determine the authentication identity from the application supplied
1513 * GSS credential, the default GSS credential, and the application
1514 * supplied identity, in that order.
1516 if (oparams->authid == NULL) {
1517 assert(text->client_name == GSS_C_NO_NAME);
1519 if (!GOT_CREDS(text, params)) {
1520 maj_stat = gss_acquire_cred(&min_stat,
1525 &text->client_creds,
1529 maj_stat = GSS_S_COMPLETE;
1531 if (maj_stat == GSS_S_COMPLETE) {
1532 maj_stat = gss_inquire_cred(&min_stat,
1534 ? (gss_cred_id_t)params->gss_creds
1535 : text->client_creds,
1540 if (GSS_ERROR(maj_stat))
1542 } else if (maj_stat != GSS_S_CRED_UNAVAIL)
1545 if (text->client_name != GSS_C_NO_NAME) {
1546 maj_stat = gss_display_name(&min_stat,
1550 if (GSS_ERROR(maj_stat))
1553 authid = cred_authid.value;
1555 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1556 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1557 result = auth_result;
1564 * Get the authorization identity.
1566 if (oparams->user == NULL) {
1567 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1568 if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1569 result = user_result;
1575 * Canonicalize the authentication and authorization identities before
1576 * calling GSS_Import_name.
1578 if (auth_result == SASL_OK && user_result == SASL_OK &&
1579 oparams->authid == NULL) {
1580 if (userid == NULL || userid[0] == '\0') {
1581 result = params->canon_user(params->utils->conn, authid, 0,
1582 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1585 result = params->canon_user(params->utils->conn,
1586 authid, 0, SASL_CU_AUTHID, oparams);
1587 if (result != SASL_OK)
1590 result = params->canon_user(params->utils->conn,
1591 userid, 0, SASL_CU_AUTHZID, oparams);
1592 if (result != SASL_OK)
1598 * If the application has provided an authentication identity, parse it.
1600 if (text->client_name == GSS_C_NO_NAME &&
1601 oparams->authid != NULL && oparams->authid[0] != '\0') {
1602 gss_buffer_desc name_buf;
1604 name_buf.length = strlen(oparams->authid);
1605 name_buf.value = (void *)oparams->authid;
1607 maj_stat = gss_import_name(&min_stat,
1610 &text->client_name);
1611 if (GSS_ERROR(maj_stat))
1616 * Armed with the authentication identity, try to get a credential without
1619 if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) {
1620 maj_stat = gss_acquire_cred(&min_stat,
1625 &text->client_creds,
1628 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CRED_UNAVAIL)
1633 * If that failed, try to get a credential with a password.
1635 if (!GOT_CREDS(text, params)) {
1636 if (text->password == NULL) {
1637 pass_result = _plug_get_password(params->utils, &text->password,
1638 &text->free_password, prompt_need);
1639 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1640 result = pass_result;
1645 if (text->password != NULL) {
1646 gss_buffer_desc password_buf;
1648 password_buf.length = text->password->len;
1649 password_buf.value = text->password->data;
1651 maj_stat = gss_acquire_cred_with_password(&min_stat,
1657 &text->client_creds,
1660 if (GSS_ERROR(maj_stat))
1665 maj_stat = GSS_S_COMPLETE;
1667 /* free prompts we got */
1668 if (prompt_need && *prompt_need) {
1669 params->utils->free(*prompt_need);
1670 *prompt_need = NULL;
1673 /* if there are prompts not filled in */
1674 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1675 pass_result == SASL_INTERACT) {
1676 /* make the prompt list */
1678 _plug_make_prompts(params->utils, prompt_need,
1679 user_result == SASL_INTERACT ?
1680 "Please enter your authorization name" : NULL,
1682 auth_result == SASL_INTERACT ?
1683 "Please enter your authentication name" : NULL,
1685 pass_result == SASL_INTERACT ?
1686 "Please enter your password" : NULL, NULL,
1690 if (result == SASL_OK)
1691 result = SASL_INTERACT;
1695 if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) {
1696 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
1700 gss_release_buffer(&min_stat, &cred_authid);
1706 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1709 OM_uint32 maj_stat, min_stat;
1710 gss_buffer_desc msg;
1714 unsigned int len, curlen = 0;
1715 const char prefix[] = "GS2 Error: ";
1717 len = sizeof(prefix);
1718 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1722 strcpy(out, prefix);
1726 maj_stat = gss_display_status(&min_stat, maj,
1727 GSS_C_GSS_CODE, GSS_C_NULL_OID,
1730 if (GSS_ERROR(maj_stat)) {
1732 utils->log(utils->conn, SASL_LOG_FAIL,
1733 "GS2 Failure: (could not get major error message)");
1735 utils->seterror(utils->conn, 0,
1737 "(could not get major error message)");
1743 len += len + msg.length;
1744 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1745 if (ret != SASL_OK) {
1750 strcat(out, msg.value);
1752 gss_release_buffer(&min_stat, &msg);
1758 /* Now get the minor status */
1761 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1762 if (ret != SASL_OK) {
1771 maj_stat = gss_display_status(&min_stat, min,
1772 GSS_C_MECH_CODE, GSS_C_NULL_OID,
1775 if (GSS_ERROR(maj_stat)) {
1777 utils->log(utils->conn, SASL_LOG_FAIL,
1778 "GS2 Failure: (could not get minor error message)");
1780 utils->seterror(utils->conn, 0,
1782 "(could not get minor error message)");
1788 len += len + msg.length;
1790 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1791 if (ret != SASL_OK) {
1796 strcat(out, msg.value);
1798 gss_release_buffer(&min_stat, &msg);
1805 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1806 if (ret != SASL_OK) {
1814 utils->log(utils->conn, SASL_LOG_FAIL, out);
1816 utils->seterror(utils->conn, 0, out);