From: Luke Howard Date: Fri, 28 Dec 2018 12:13:05 +0000 (+1100) Subject: Process SPNEGO error tokens through mech X-Git-Tag: krb5-1.18-beta1~189 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F885%2Fhead;p=thirdparty%2Fkrb5.git Process SPNEGO error tokens through mech In the SPNEGO initiator code, if the acceptor returns a token with negState=REJCET and a mechanism token, process the token through the mech to get a better error status. [ghudson@mit.edu: modified approach for clarity and to prevent some edge cases; rewrote commit message] ticket: 8775 (new) --- diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index 4d7506e698..01d413522b 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -129,7 +129,7 @@ init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID, gss_buffer_t *, gss_buffer_t *, send_token_flag *); static OM_uint32 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t, - gss_name_t, OM_uint32, OM_uint32, gss_buffer_t, + OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t, gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *, send_token_flag *); @@ -724,11 +724,20 @@ init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, &supportedMech, responseToken, mechListMIC); if (ret != GSS_S_COMPLETE) goto cleanup; - if (*acc_negState == REJECT) { - *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; - map_errcode(minor_status); + + /* Bail out now on a reject with no error token. If we have an error + * token, keep going and get a better error status from the mech. */ + if (*acc_negState == REJECT && *responseToken == GSS_C_NO_BUFFER) { + if (!sc->nego_done) { + /* RFC 4178 says to return GSS_S_BAD_MECH on a + * mechanism negotiation failure. */ + *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; + map_errcode(minor_status); + ret = GSS_S_BAD_MECH; + } else { + ret = GSS_S_FAILURE; + } *tokflag = NO_TOKEN_SEND; - ret = GSS_S_FAILURE; goto cleanup; } /* @@ -886,6 +895,7 @@ static OM_uint32 init_ctx_call_init(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, spnego_gss_cred_id_t spcred, + OM_uint32 acc_negState, gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req, @@ -918,6 +928,14 @@ init_ctx_call_init(OM_uint32 *minor_status, mechtok_out, &sc->ctx_flags, time_rec); + + /* Bail out if the acceptor gave us an error token but the mech didn't + * see it as an error. */ + if (acc_negState == REJECT && !GSS_ERROR(ret)) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + if (ret == GSS_S_COMPLETE) { sc->mech_complete = 1; if (ret_flags != NULL) @@ -959,10 +977,10 @@ init_ctx_call_init(OM_uint32 *minor_status, gss_release_buffer(&tmpmin, &sc->DER_mechTypes); if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) goto fail; - tmpret = init_ctx_call_init(&tmpmin, sc, spcred, target_name, - req_flags, time_req, mechtok_in, - actual_mech, mechtok_out, ret_flags, - time_rec, send_token); + tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState, + target_name, req_flags, time_req, + mechtok_in, actual_mech, mechtok_out, + ret_flags, time_rec, send_token); if (HARD_ERROR(tmpret)) goto fail; *minor_status = tmpmin; @@ -1060,13 +1078,11 @@ spnego_gss_init_sec_context( /* Step 2: invoke the selected or optimistic mechanism's * gss_init_sec_context function, if it didn't complete previously. */ if (!spnego_ctx->mech_complete) { - ret = init_ctx_call_init( - minor_status, spnego_ctx, spcred, - target_name, req_flags, - time_req, mechtok_in, - actual_mech, &mechtok_out, - ret_flags, time_rec, - &send_token); + ret = init_ctx_call_init(minor_status, spnego_ctx, spcred, + acc_negState, target_name, req_flags, + time_req, mechtok_in, actual_mech, + &mechtok_out, ret_flags, time_rec, + &send_token); if (ret != GSS_S_COMPLETE) goto cleanup; diff --git a/src/tests/gssapi/t_err.c b/src/tests/gssapi/t_err.c index b7c32b463f..3a9c47bdb1 100644 --- a/src/tests/gssapi/t_err.c +++ b/src/tests/gssapi/t_err.c @@ -74,11 +74,16 @@ main(int argc, char *argv[]) gss_buffer_desc itok, atok; gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT; - if (argc != 2) { - fprintf(stderr, "Usage: %s targetname\n", argv[0]); + argv++; + if (*argv != NULL && strcmp(*argv, "--spnego") == 0) { + mech = &mech_spnego; + argv++; + } + if (*argv == NULL || argv[1] != NULL) { + fprintf(stderr, "Usage: t_err targetname\n"); return 1; } - tname = import_name(argv[1]); + tname = import_name(*argv); /* Get the initial context token. */ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG; diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py index 8428e821a1..54d5cf5492 100755 --- a/src/tests/gssapi/t_gssapi.py +++ b/src/tests/gssapi/t_gssapi.py @@ -178,6 +178,7 @@ if krb5_mech not in out or spnego_mech not in out: # Test that accept_sec_context can produce an error token and # init_sec_context can interpret it. realm.run(['./t_err', 'p:' + realm.host_princ]) +realm.run(['./t_err', '--spnego', 'p:' + realm.host_princ]) # Test the GSS_KRB5_CRED_NO_CI_FLAGS_X cred option. realm.run(['./t_ciflags', 'p:' + realm.host_princ])