X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=plugins%2Fgs2.c;h=c04f99f85b36a275cf0c319f18f463bc54d30d12;hb=69332d4cce1cfb8d094e1cb3b82d9030907c5eb7;hp=85f2c302947931f4a822ac941744d590b98d8f93;hpb=83ab9112023c5e09098e7eb7687e545d56c43b08;p=cyrus-sasl.git diff --git a/plugins/gs2.c b/plugins/gs2.c index 85f2c30..c04f99f 100644 --- a/plugins/gs2.c +++ b/plugins/gs2.c @@ -1,8 +1,36 @@ /* - * Copyright 2010 PADL Software Pty Ltd. All rights reserved. + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ /* - * Portions Copyright (c) 1998-2003 Carnegie Mellon University. + * Copyright (c) 1998-2003 Carnegie Mellon University. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,7 +72,9 @@ #include #include +#ifdef HAVE_GSSAPI_GSSAPI_EXT_H #include +#endif #include #include #include @@ -62,11 +92,15 @@ #include "gs2_token.h" #define GS2_CB_FLAG_MASK 0x0F +#define GS2_CB_FLAG_N 0x00 #define GS2_CB_FLAG_P 0x01 -#define GS2_CB_FLAG_N 0x02 -#define GS2_CB_FLAG_Y 0x03 +#define GS2_CB_FLAG_Y 0x02 #define GS2_NONSTD_FLAG 0x10 +#ifndef GSS_S_PROMPTING_NEEDED +#define GSS_S_PROMPTING_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 5)) +#endif + typedef struct context { gss_ctx_id_t gss_ctx; gss_name_t client_name; @@ -84,8 +118,8 @@ typedef struct context { } plug; gss_OID mechanism; int gs2_flags; - char *cb_name; - struct gss_channel_bindings_struct bindings; + char *cbindingname; + struct gss_channel_bindings_struct gss_cbindings; sasl_secret_t *password; unsigned int free_password; OM_uint32 lifetime; @@ -93,11 +127,10 @@ typedef struct context { static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET; -static int gs2_ask_user_info(context_t *context, - sasl_client_params_t *params, - char **realms, int nrealm, - sasl_interact_t **prompt_need, - sasl_out_params_t *oparams); +static int gs2_get_init_creds(context_t *context, + sasl_client_params_t *params, + sasl_interact_t **prompt_need, + sasl_out_params_t *oparams); static int gs2_verify_initial_message(context_t *text, sasl_server_params_t *sparams, @@ -121,7 +154,8 @@ static int gs2_make_message(context_t *text, static int gs2_get_mech_attrs(const sasl_utils_t *utils, const gss_OID mech, unsigned int *security_flags, - unsigned int *features); + unsigned int *features, + const unsigned long **prompts); static int gs2_indicate_mechs(const sasl_utils_t *utils); @@ -157,7 +191,7 @@ sasl_gs2_new_context(const sasl_utils_t *utils) context_t *ret; ret = utils->malloc(sizeof(context_t)); - if (!ret) + if (ret == NULL) return NULL; memset(ret, 0, sizeof(context_t)); @@ -210,12 +244,7 @@ sasl_gs2_free_context_contents(context_t *text) text->authzid = NULL; } - if (text->mechanism != NULL) { - gss_release_oid(&min_stat, &text->mechanism); - text->mechanism = GSS_C_NO_OID; - } - - gss_release_buffer(&min_stat, &text->bindings.application_data); + gss_release_buffer(&min_stat, &text->gss_cbindings.application_data); if (text->out_buf != NULL) { text->utils->free(text->out_buf); @@ -224,9 +253,9 @@ sasl_gs2_free_context_contents(context_t *text) text->out_buf_len = 0; - if (text->cb_name != NULL) { - text->utils->free(text->cb_name); - text->cb_name = NULL; + if (text->cbindingname != NULL) { + text->utils->free(text->cbindingname); + text->cbindingname = NULL; } if (text->free_password) @@ -310,7 +339,6 @@ gs2_server_mech_step(void *conn_context, gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER; gss_name_t without = GSS_C_NO_NAME; gss_OID_set_desc mechs; - gss_OID actual_mech = GSS_C_NO_OID; OM_uint32 out_flags = 0; int ret = 0, equal = 0; int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT); @@ -377,12 +405,12 @@ gs2_server_mech_step(void *conn_context, maj_stat = gss_accept_sec_context(&min_stat, &text->gss_ctx, (params->gss_creds != GSS_C_NO_CREDENTIAL) - ? params->gss_creds + ? (gss_cred_id_t)params->gss_creds : text->server_creds, &input_token, - &text->bindings, + &text->gss_cbindings, &text->client_name, - &actual_mech, + NULL, &output_token, &out_flags, &text->lifetime, @@ -391,7 +419,7 @@ gs2_server_mech_step(void *conn_context, sasl_gs2_log(text->utils, maj_stat, min_stat); text->utils->seterror(text->utils->conn, SASL_NOLOG, "GS2 Failure: gss_accept_sec_context"); - ret = SASL_BADAUTH; + ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH; goto cleanup; } @@ -416,15 +444,6 @@ gs2_server_mech_step(void *conn_context, assert(maj_stat == GSS_S_COMPLETE); - if (!g_OID_equal(text->mechanism, actual_mech)) { - ret = SASL_WRONGMECH; - goto cleanup; - } - if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) { - ret = SASL_BADAUTH; - goto cleanup; - } - maj_stat = gss_display_name(&min_stat, text->client_name, &name_buf, NULL); if (GSS_ERROR(maj_stat)) @@ -443,14 +462,14 @@ gs2_server_mech_step(void *conn_context, GSS_C_NT_USER_NAME, &without); if (GSS_ERROR(maj_stat)) { - ret = SASL_BADAUTH; + ret = SASL_FAIL; goto cleanup; } maj_stat = gss_compare_name(&min_stat, text->client_name, without, &equal); if (GSS_ERROR(maj_stat)) { - ret = SASL_BADAUTH; + ret = SASL_FAIL; goto cleanup; } @@ -481,13 +500,14 @@ gs2_server_mech_step(void *conn_context, switch (text->gs2_flags & GS2_CB_FLAG_MASK) { case GS2_CB_FLAG_N: - oparams->chanbindingflag = SASL_CB_FLAG_NONE; + oparams->cbindingdisp = SASL_CB_DISP_NONE; break; case GS2_CB_FLAG_P: - oparams->chanbindingflag = SASL_CB_FLAG_USED; + oparams->cbindingdisp = SASL_CB_DISP_USED; + oparams->cbindingname = text->cbindingname; break; case GS2_CB_FLAG_Y: - oparams->chanbindingflag = SASL_CB_FLAG_WANT; + oparams->cbindingdisp = SASL_CB_DISP_WANT; break; } @@ -513,13 +533,12 @@ cleanup: gss_release_buffer(&min_stat, &short_name_buf); gss_release_buffer(&min_stat, &output_token); gss_release_name(&min_stat, &without); - gss_release_oid(&min_stat, &actual_mech); if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) { sasl_gs2_seterror(text->utils, maj_stat, min_stat); ret = SASL_FAIL; } - if (ret != SASL_OK && ret != SASL_CONTINUE) + if (ret < SASL_OK) sasl_gs2_free_context_contents(text); return ret; @@ -546,12 +565,12 @@ gs2_common_plug_init(const sasl_utils_t *utils, return SASL_NOMECH; } - plugs = utils->malloc(2 * gs2_mechs->count * plugsize); + plugs = utils->malloc(gs2_mechs->count * plugsize); if (plugs == NULL) { MEMERROR(utils); return SASL_NOMEM; } - memset(plugs, 0, 2 * gs2_mechs->count * plugsize); + memset(plugs, 0, gs2_mechs->count * plugsize); for (i = 0; i < gs2_mechs->count; i++) { gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER; @@ -598,7 +617,8 @@ gs2_server_plug_alloc(const sasl_utils_t *utils, ret = gs2_get_mech_attrs(utils, mech, &splug->security_flags, - &splug->features); + &splug->features, + NULL); if (ret != SASL_OK) return ret; @@ -668,8 +688,7 @@ static int gs2_client_mech_step(void *conn_context, gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER; OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0; - OM_uint32 req_flags, ret_flags; - gss_OID actual_mech = GSS_C_NO_OID; + OM_uint32 ret_flags; int ret = SASL_FAIL; int initialContextToken; @@ -677,45 +696,10 @@ static int gs2_client_mech_step(void *conn_context, *clientoutlen = 0; if (text->gss_ctx == GSS_C_NO_CONTEXT) { - ret = gs2_ask_user_info(text, params, NULL, 0, prompt_need, oparams); + ret = gs2_get_init_creds(text, params, prompt_need, oparams); if (ret != SASL_OK) goto cleanup; - if (params->gss_creds == GSS_C_NO_CREDENTIAL && - text->password != NULL && text->password->len != 0) { - gss_buffer_desc password_buf; - gss_buffer_desc name_buf; - gss_OID_set_desc mechs; - - name_buf.length = strlen(oparams->authid); - name_buf.value = (void *)oparams->authid; - - password_buf.length = text->password->len; - password_buf.value = text->password->data; - - mechs.count = 1; - mechs.elements = (gss_OID)text->mechanism; - - maj_stat = gss_import_name(&min_stat, - &name_buf, - GSS_C_NT_USER_NAME, - &text->client_name); - if (GSS_ERROR(maj_stat)) - goto cleanup; - - maj_stat = gss_acquire_cred_with_password(&min_stat, - text->client_name, - &password_buf, - GSS_C_INDEFINITE, - &mechs, - GSS_C_INITIATE, - &text->client_creds, - NULL, - &text->lifetime); - if (GSS_ERROR(maj_stat)) - goto cleanup; - } - initialContextToken = 1; } else initialContextToken = 0; @@ -737,10 +721,10 @@ static int gs2_client_mech_step(void *conn_context, snprintf(name_buf.value, name_buf.length + 1, "%s@%s", params->service, params->serverFQDN); - maj_stat = gss_import_name (&min_stat, - &name_buf, - GSS_C_NT_HOSTBASED_SERVICE, - &text->server_name); + maj_stat = gss_import_name(&min_stat, + &name_buf, + GSS_C_NT_HOSTBASED_SERVICE, + &text->server_name); params->utils->free(name_buf.value); name_buf.value = NULL; @@ -761,14 +745,14 @@ static int gs2_client_mech_step(void *conn_context, if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0) text->gs2_flags |= GS2_NONSTD_FLAG; - switch (params->chanbindingflag) { - case SASL_CB_FLAG_NONE: + switch (params->cbindingdisp) { + case SASL_CB_DISP_NONE: text->gs2_flags |= GS2_CB_FLAG_N; break; - case SASL_CB_FLAG_USED: + case SASL_CB_DISP_USED: text->gs2_flags |= GS2_CB_FLAG_P; break; - case SASL_CB_FLAG_WANT: + case SASL_CB_DISP_WANT: text->gs2_flags |= GS2_CB_FLAG_Y; break; } @@ -781,18 +765,16 @@ static int gs2_client_mech_step(void *conn_context, goto cleanup; } - req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; - maj_stat = gss_init_sec_context(&min_stat, (params->gss_creds != GSS_C_NO_CREDENTIAL) - ? params->gss_creds + ? (gss_cred_id_t)params->gss_creds : text->client_creds, &text->gss_ctx, text->server_name, (gss_OID)text->mechanism, - req_flags, + GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, - &text->bindings, + &text->gss_cbindings, serverinlen ? &input_token : GSS_C_NO_BUFFER, NULL, &output_token, @@ -822,18 +804,15 @@ static int gs2_client_mech_step(void *conn_context, &text->client_name, NULL, &text->lifetime, - &actual_mech, + NULL, &ret_flags, /* flags */ NULL, NULL); if (GSS_ERROR(maj_stat)) goto cleanup; - if (!g_OID_equal(text->mechanism, actual_mech)) { - ret = SASL_WRONGMECH; - goto cleanup; - } - if ((ret_flags & req_flags) != req_flags) { + if (params->cbindingdisp != SASL_CB_DISP_NONE && + (ret_flags & GSS_C_MUTUAL_FLAG) == 0) { maj_stat = SASL_BADAUTH; goto cleanup; } @@ -856,13 +835,12 @@ static int gs2_client_mech_step(void *conn_context, cleanup: gss_release_buffer(&min_stat, &output_token); gss_release_buffer(&min_stat, &name_buf); - gss_release_oid(&min_stat, &actual_mech); if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) { sasl_gs2_seterror(text->utils, maj_stat, min_stat); - ret = SASL_FAIL; + ret = (maj_stat == GSS_S_PROMPTING_NEEDED) ? SASL_INTERACT : SASL_FAIL; } - if (ret != SASL_OK && ret != SASL_CONTINUE) + if (ret < SASL_OK) sasl_gs2_free_context_contents(text); return ret; @@ -892,17 +870,13 @@ static int gs2_client_mech_new(void *glob_context, if (ret != SASL_OK) { gs2_common_mech_dispose(text, params->utils); return ret; - } + } *conn_context = text; return SASL_OK; } -static const unsigned long gs2_required_prompts[] = { - SASL_CB_LIST_END -}; - static int gs2_client_plug_alloc(const sasl_utils_t *utils, void *plug, @@ -917,7 +891,8 @@ gs2_client_plug_alloc(const sasl_utils_t *utils, ret = gs2_get_mech_attrs(utils, mech, &cplug->security_flags, - &cplug->features); + &cplug->features, + &cplug->required_prompts); if (ret != SASL_OK) return ret; @@ -932,7 +907,6 @@ gs2_client_plug_alloc(const sasl_utils_t *utils, cplug->mech_step = gs2_client_mech_step; cplug->mech_dispose = gs2_common_mech_dispose; cplug->mech_free = gs2_common_mech_free; - cplug->required_prompts = gs2_required_prompts; return SASL_OK; } @@ -980,14 +954,13 @@ gs2_client_plug_init(const sasl_utils_t *utils, static int gs2_save_cbindings(context_t *text, gss_buffer_t header, - const char *chanbindingdata, - unsigned int chanbindinglen) + const sasl_channel_binding_t *cbinding) { - gss_buffer_t gss_bindings = &text->bindings.application_data; + gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data; size_t len; unsigned char *p; - assert(gss_bindings->value == NULL); + assert(gss_cbindings->value == NULL); /* * The application-data field MUST be set to the gs2-header, excluding @@ -999,15 +972,17 @@ gs2_save_cbindings(context_t *text, assert(len > 2); len -= 2; } - if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P) - len += chanbindinglen; + if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P && + cbinding != NULL) { + len += cbinding->len; + } - gss_bindings->length = len; - gss_bindings->value = text->utils->malloc(len); - if (gss_bindings->value == NULL) + gss_cbindings->length = len; + gss_cbindings->value = text->utils->malloc(len); + if (gss_cbindings->value == NULL) return SASL_NOMEM; - p = (unsigned char *)gss_bindings->value; + p = (unsigned char *)gss_cbindings->value; if (text->gs2_flags & GS2_NONSTD_FLAG) { memcpy(p, (unsigned char *)header->value + 2, header->length - 2); p += header->length - 2; @@ -1017,14 +992,14 @@ gs2_save_cbindings(context_t *text, } if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P && - chanbindinglen != 0) { - memcpy(p, chanbindingdata, chanbindinglen); + cbinding != NULL) { + memcpy(p, cbinding->data, cbinding->len); } return SASL_OK; } -#define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADAUTH; } while (0) +#define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0) /* * Verify gs2-header, save authzid and channel bindings to context. @@ -1036,12 +1011,13 @@ gs2_verify_initial_message(context_t *text, unsigned inlen, gss_buffer_t token) { + OM_uint32 major, minor; char *p = (char *)in; unsigned remain = inlen; int ret; gss_buffer_desc buf = GSS_C_EMPTY_BUFFER; - assert(text->cb_name == NULL); + assert(text->cbindingname == NULL); assert(text->authzid == NULL); token->length = 0; @@ -1065,9 +1041,9 @@ gs2_verify_initial_message(context_t *text, CHECK_REMAIN(1); /* = */ remain--; if (*p++ != '=') - return SASL_BADAUTH; + return SASL_BADPROT; - ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cb_name); + ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname); if (ret != SASL_OK) return ret; @@ -1084,7 +1060,7 @@ gs2_verify_initial_message(context_t *text, CHECK_REMAIN(1); /* , */ remain--; if (*p++ != ',') - return SASL_BADAUTH; + return SASL_BADPROT; /* authorization identity */ if (remain > 1 && memcmp(p, "a=", 2) == 0) { @@ -1097,47 +1073,43 @@ gs2_verify_initial_message(context_t *text, return ret; } - /* end of header */ + /* end of header */ CHECK_REMAIN(1); /* , */ remain--; if (*p++ != ',') - return SASL_BADAUTH; + return SASL_BADPROT; buf.length = inlen - remain; buf.value = (void *)in; /* stash channel bindings to pass into gss_accept_sec_context() */ - ret = gs2_save_cbindings(text, &buf, sparams->chanbindingdata, - sparams->chanbindinglen); + ret = gs2_save_cbindings(text, &buf, sparams->cbinding); if (ret != SASL_OK) return ret; - buf.length = remain; - buf.value = p; - if (text->gs2_flags & GS2_NONSTD_FLAG) { - token->value = text->utils->malloc(buf.length); - if (token->value == NULL) - return SASL_NOMEM; - - token->length = buf.length; - memcpy(token->value, buf.value, buf.length); + buf.length = remain; + buf.value = p; } else { - unsigned int token_size; + gss_buffer_desc tmp; - /* create a properly formed GSS token */ - token_size = gs2_token_size(text->mechanism, buf.length); - token->value = text->utils->malloc(token_size); - if (token->value == NULL) + tmp.length = remain; + tmp.value = p; + + major = gss_encapsulate_token(&tmp, text->mechanism, &buf); + if (GSS_ERROR(major)) return SASL_NOMEM; + } - token->length = token_size; + token->value = text->utils->malloc(buf.length); + if (token->value == NULL) + return SASL_NOMEM; - p = (char *)token->value; - gs2_make_token_header(text->mechanism, buf.length, - (unsigned char **)&p); - memcpy(p, buf.value, buf.length); - } + token->length = buf.length; + memcpy(token->value, buf.value, buf.length); + + if ((text->gs2_flags & GS2_NONSTD_FLAG) == 0) + gss_release_buffer(&minor, &buf); return SASL_OK; } @@ -1153,7 +1125,7 @@ gs2_make_header(context_t *text, unsigned *outlen) { size_t required = 0; - size_t wire_authzid_len = 0, cb_name_len = 0; + size_t wire_authzid_len = 0, cbnamelen = 0; char *wire_authzid = NULL; char *p; int ret; @@ -1169,10 +1141,10 @@ gs2_make_header(context_t *text, /* SASL channel bindings */ switch (text->gs2_flags & GS2_CB_FLAG_MASK) { case GS2_CB_FLAG_P: - if (cparams->chanbindingtype == NULL) + if (!SASL_CB_PRESENT(cparams)) return SASL_BADPARAM; - cb_name_len = strlen(cparams->chanbindingtype); - required += 1 /*=*/ + cb_name_len; + cbnamelen = strlen(cparams->cbinding->name); + required += 1 /*=*/ + cbnamelen; /* fallthrough */ case GS2_CB_FLAG_N: case GS2_CB_FLAG_Y: @@ -1212,8 +1184,8 @@ gs2_make_header(context_t *text, switch (text->gs2_flags & GS2_CB_FLAG_MASK) { case GS2_CB_FLAG_P: memcpy(p, "p=", 2); - memcpy(p + 2, cparams->chanbindingtype, cb_name_len); - p += 2 + cb_name_len; + memcpy(p + 2, cparams->cbinding->name, cbnamelen); + p += 2 + cbnamelen; break; case GS2_CB_FLAG_N: *p++ = 'n'; @@ -1236,10 +1208,11 @@ gs2_make_header(context_t *text, buf.length = required; buf.value = *out; - ret = gs2_save_cbindings(text, &buf, cparams->chanbindingdata, - cparams->chanbindinglen); + ret = gs2_save_cbindings(text, &buf, cparams->cbinding); + if (ret != SASL_OK) + return ret; - return ret; + return SASL_OK; } /* @@ -1254,40 +1227,40 @@ gs2_make_message(context_t *text, unsigned *outlen) { OM_uint32 major, minor; - unsigned char *mech_token_data; - size_t mech_token_size; - char *p; - unsigned header_len = 0; int ret; - - mech_token_size = token->length; - mech_token_data = (unsigned char *)token->value; + unsigned header_len = 0; + gss_buffer_desc decap_token = GSS_C_EMPTY_BUFFER; if (initialContextToken) { header_len = *outlen; - major = gs2_verify_token_header(&minor, text->mechanism, - &mech_token_size, &mech_token_data, - token->length); + major = gss_decapsulate_token(token, text->mechanism, &decap_token); if ((major == GSS_S_DEFECTIVE_TOKEN && (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) || GSS_ERROR(major)) return SASL_FAIL; + + token = &decap_token; } ret = _plug_buf_alloc(text->utils, out, outlen, - header_len + mech_token_size); + header_len + token->length); if (ret != 0) return ret; - p = *out + header_len; - memcpy(p, mech_token_data, mech_token_size); + memcpy(*out + header_len, token->value, token->length); + *outlen = header_len + token->length; - *outlen = header_len + mech_token_size; + if (initialContextToken) + gss_release_buffer(&minor, &decap_token); return SASL_OK; } +static const unsigned long gs2_required_prompts[] = { + SASL_CB_LIST_END +}; + /* * Map GSS mechanism attributes to SASL ones */ @@ -1295,10 +1268,11 @@ static int gs2_get_mech_attrs(const sasl_utils_t *utils, const gss_OID mech, unsigned int *security_flags, - unsigned int *features) + unsigned int *features, + const unsigned long **prompts) { OM_uint32 major, minor; - int present, ret; + int present; gss_OID_set attrs = GSS_C_NO_OID_SET; major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL); @@ -1309,27 +1283,32 @@ gs2_get_mech_attrs(const sasl_utils_t *utils, } *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE; - *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING; + *features = SASL_FEAT_WANT_CLIENT_FIRST; + if (prompts != NULL) + *prompts = gs2_required_prompts; #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \ attrs, &present) == GSS_S_COMPLETE && \ present) - ret = SASL_OK; - if (MA_PRESENT(GSS_C_MA_PFS)) *security_flags |= SASL_SEC_FORWARD_SECRECY; if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON)) *security_flags |= SASL_SEC_NOANONYMOUS; if (MA_PRESENT(GSS_C_MA_DELEG_CRED)) *security_flags |= SASL_SEC_PASS_CREDENTIALS; - if (MA_PRESENT(GSS_C_MA_AUTH_TARG)) + if (MA_PRESENT(GSS_C_MA_AUTH_TARG)) { + *features |= SASL_FEAT_CHANNEL_BINDING; *security_flags |= SASL_SEC_MUTUAL_AUTH; + } + if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL) + *prompts = NULL; if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED)) *features |= SASL_FEAT_GSS_FRAMING; gss_release_oid_set(&minor, &attrs); - return ret; + + return SASL_OK; } /* @@ -1338,7 +1317,7 @@ gs2_get_mech_attrs(const sasl_utils_t *utils, static int gs2_indicate_mechs(const sasl_utils_t *utils) { OM_uint32 major, minor; - gss_OID_desc desired_oids[3]; + gss_OID_desc desired_oids[2]; gss_OID_set_desc desired_attrs; gss_OID_desc except_oids[3]; gss_OID_set_desc except_attrs; @@ -1347,8 +1326,7 @@ static int gs2_indicate_mechs(const sasl_utils_t *utils) return SASL_OK; desired_oids[0] = *GSS_C_MA_AUTH_INIT; - desired_oids[1] = *GSS_C_MA_AUTH_TARG; - desired_oids[2] = *GSS_C_MA_CBINDINGS; + desired_oids[1] = *GSS_C_MA_CBINDINGS; desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]); desired_attrs.elements = desired_oids; @@ -1498,47 +1476,208 @@ gs2_escape_authzid(const sasl_utils_t *utils, return SASL_OK; } +#define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL) +#define CRED_ERROR(status) ((status) == GSS_S_CRED_UNAVAIL || (status) == GSS_S_NO_CRED) + +/* + * Determine the authentication identity from the application supplied + * GSS credential, the application supplied identity, and the default + * GSS credential, in that order. Then, acquire credentials. + */ static int -gs2_ask_user_info(context_t *text, - sasl_client_params_t *params, - char **realms __attribute__((unused)), - int nrealm __attribute__((unused)), - sasl_interact_t **prompt_need, - sasl_out_params_t *oparams) +gs2_get_init_creds(context_t *text, + sasl_client_params_t *params, + sasl_interact_t **prompt_need, + sasl_out_params_t *oparams) { int result = SASL_OK; const char *authid = NULL, *userid = NULL; int user_result = SASL_OK; int auth_result = SASL_OK; int pass_result = SASL_OK; + OM_uint32 maj_stat = GSS_S_COMPLETE, min_stat = 0; + gss_OID_set_desc mechs; + gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER; + gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER; - /* try to get the authid */ + mechs.count = 1; + mechs.elements = (gss_OID)text->mechanism; + + /* + * Get the authentication identity from the application. + */ if (oparams->authid == NULL) { auth_result = _plug_get_authid(params->utils, &authid, prompt_need); - if (auth_result != SASL_OK && auth_result != SASL_INTERACT) { - return auth_result; + result = auth_result; + goto cleanup; } } - /* try to get the userid */ + /* + * Get the authorization identity from the application. + */ if (oparams->user == NULL) { user_result = _plug_get_userid(params->utils, &userid, prompt_need); - if (user_result != SASL_OK && user_result != SASL_INTERACT) { - return user_result; + result = user_result; + goto cleanup; + } + } + + /* + * Canonicalize the authentication and authorization identities before + * calling GSS_Import_name. + */ + if (auth_result == SASL_OK && user_result == SASL_OK && + oparams->authid == NULL) { + if (userid == NULL || userid[0] == '\0') { + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + } else { + result = params->canon_user(params->utils->conn, + authid, 0, SASL_CU_AUTHID, oparams); + if (result != SASL_OK) + goto cleanup; + + result = params->canon_user(params->utils->conn, + userid, 0, SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) + goto cleanup; + } + + if (oparams->authid != NULL) { + name_buf.length = strlen(oparams->authid); + name_buf.value = (void *)oparams->authid; + + assert(text->client_name == GSS_C_NO_NAME); + + maj_stat = gss_import_name(&min_stat, + &name_buf, + GSS_C_NT_USER_NAME, + &text->client_name); + if (GSS_ERROR(maj_stat)) + goto cleanup; + + /* The authid may have changed after prompting, so free any creds */ + gss_release_cred(&min_stat, &text->client_creds); + } + } + + /* + * If application didn't provide an authid, then use the default + * credential. If that doesn't work, give up. + */ + if (!GOT_CREDS(text, params) && oparams->authid == NULL) { + maj_stat = gss_acquire_cred(&min_stat, + GSS_C_NO_NAME, + GSS_C_INDEFINITE, + &mechs, + GSS_C_INITIATE, + &text->client_creds, + NULL, + &text->lifetime); + if (GSS_ERROR(maj_stat)) + goto cleanup; + + assert(text->client_name == GSS_C_NO_NAME); + + maj_stat = gss_inquire_cred(&min_stat, + params->gss_creds + ? (gss_cred_id_t)params->gss_creds + : text->client_creds, + &text->client_name, + NULL, + NULL, + NULL); + if (GSS_ERROR(maj_stat)) { + /* Maybe there was no default credential */ + auth_result = SASL_INTERACT; + goto interact; + } + + maj_stat = gss_display_name(&min_stat, + text->client_name, + &cred_authid, + NULL); + if (GSS_ERROR(maj_stat)) + goto cleanup; + + if (userid == NULL || userid[0] == '\0') { + result = params->canon_user(params->utils->conn, + cred_authid.value, cred_authid.length, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + } else { + result = params->canon_user(params->utils->conn, + cred_authid.value, cred_authid.length, + SASL_CU_AUTHID, oparams); + if (result != SASL_OK) + goto cleanup; + + result = params->canon_user(params->utils->conn, + cred_authid.value, cred_authid.length, + SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) + goto cleanup; } } - /* try to get the password */ - if (text->password == NULL) { - pass_result = _plug_get_password(params->utils, &text->password, - &text->free_password, prompt_need); - if (pass_result != SASL_OK && pass_result != SASL_INTERACT) { - return pass_result; + /* + * Armed with the authentication identity, try to get a credential without + * a password. + */ + if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) { + maj_stat = gss_acquire_cred(&min_stat, + text->client_name, + GSS_C_INDEFINITE, + &mechs, + GSS_C_INITIATE, + &text->client_creds, + NULL, + &text->lifetime); + if (GSS_ERROR(maj_stat) && !CRED_ERROR(maj_stat)) + goto cleanup; + } + + /* + * If that failed, try to get a credential with a password. + */ + if (!GOT_CREDS(text, params)) { + if (text->password == NULL) { + pass_result = _plug_get_password(params->utils, &text->password, + &text->free_password, prompt_need); + if (pass_result != SASL_OK && pass_result != SASL_INTERACT) { + result = pass_result; + goto cleanup; + } + } + + if (text->password != NULL) { + gss_buffer_desc password_buf; + + password_buf.length = text->password->len; + password_buf.value = text->password->data; + + maj_stat = gss_acquire_cred_with_password(&min_stat, + text->client_name, + &password_buf, + GSS_C_INDEFINITE, + &mechs, + GSS_C_INITIATE, + &text->client_creds, + NULL, + &text->lifetime); + if (GSS_ERROR(maj_stat)) + goto cleanup; } } + maj_stat = GSS_S_COMPLETE; + +interact: + /* free prompts we got */ if (prompt_need && *prompt_need) { params->utils->free(*prompt_need); @@ -1548,7 +1687,6 @@ gs2_ask_user_info(context_t *text, /* if there are prompts not filled in */ if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT || pass_result == SASL_INTERACT) { - /* make the prompt list */ result = _plug_make_prompts(params->utils, prompt_need, @@ -1563,28 +1701,18 @@ gs2_ask_user_info(context_t *text, NULL, NULL, NULL, NULL, NULL, NULL); - if (result == SASL_OK) return SASL_INTERACT; - - return result; + if (result == SASL_OK) + result = SASL_INTERACT; } - if (oparams->authid == NULL) { - if (userid == NULL || userid[0] == '\0') { - result = params->canon_user(params->utils->conn, authid, 0, - SASL_CU_AUTHID | SASL_CU_AUTHZID, - oparams); - } else { - result = params->canon_user(params->utils->conn, - authid, 0, SASL_CU_AUTHID, oparams); - if (result != SASL_OK) return result; - - result = params->canon_user(params->utils->conn, - userid, 0, SASL_CU_AUTHZID, oparams); - } - if (result != SASL_OK) - return result; +cleanup: + if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) { + sasl_gs2_seterror(text->utils, maj_stat, min_stat); + result = (maj_stat == GSS_S_PROMPTING_NEEDED) ? SASL_INTERACT : SASL_FAIL; } + gss_release_buffer(&min_stat, &cred_authid); + return result; } @@ -1598,7 +1726,7 @@ sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min, int ret; char *out = NULL; unsigned int len, curlen = 0; - const char prefix[] = "GSSAPI Error: "; + const char prefix[] = "GS2 Error: "; len = sizeof(prefix); ret = _plug_buf_alloc(utils, &out, &curlen, 256);