From f802075f08fe0d86f3d176f2302236aeb5834f3d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 2 Dec 2010 00:39:23 +0100 Subject: s3:ntlm_auth: support clients which offer a spnego mechs we don't support Before we rejected the authentication if we don't support the first spnego mech the client offered. We now negotiate the first mech we support. This fix works arround problems, when a client sends the NEGOEX (1.3.6.1.4.1.311.2.2.30) oid, which we don't support. metze --- source3/utils/ntlm_auth.c | 279 ++++++++++++++++++++++++++++------------------ 1 file changed, 169 insertions(+), 110 deletions(-) (limited to 'source3/utils') diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c index 8b6e3c5387..43af0fd638 100644 --- a/source3/utils/ntlm_auth.c +++ b/source3/utils/ntlm_auth.c @@ -82,6 +82,8 @@ struct ntlm_auth_state { struct ntlmssp_state *ntlmssp_state; uint32_t neg_flags; char *want_feature_list; + char *spnego_mech; + char *spnego_mech_oid; bool have_session_key; DATA_BLOB session_key; DATA_BLOB initial_message; @@ -1192,11 +1194,12 @@ static void offer_gss_spnego_mechs(void) { /* Server negTokenInit (mech offerings) */ spnego.type = SPNEGO_NEG_TOKEN_INIT; - spnego.negTokenInit.mechTypes = talloc_array(ctx, const char *, 3); + spnego.negTokenInit.mechTypes = talloc_array(ctx, const char *, 4); #ifdef HAVE_KRB5 spnego.negTokenInit.mechTypes[0] = talloc_strdup(ctx, OID_KERBEROS5_OLD); - spnego.negTokenInit.mechTypes[1] = talloc_strdup(ctx, OID_NTLMSSP); - spnego.negTokenInit.mechTypes[2] = NULL; + spnego.negTokenInit.mechTypes[1] = talloc_strdup(ctx, OID_KERBEROS5); + spnego.negTokenInit.mechTypes[2] = talloc_strdup(ctx, OID_NTLMSSP); + spnego.negTokenInit.mechTypes[3] = NULL; #else spnego.negTokenInit.mechTypes[0] = talloc_strdup(ctx, OID_NTLMSSP); spnego.negTokenInit.mechTypes[1] = NULL; @@ -1266,9 +1269,10 @@ bool spnego_parse_krb5_wrap(TALLOC_CTX *ctx, DATA_BLOB blob, DATA_BLOB *ticket, static void manage_gss_spnego_request(struct ntlm_auth_state *state, char *buf, int length) { - static struct ntlmssp_state *ntlmssp_state = NULL; struct spnego_data request, response; DATA_BLOB token; + DATA_BLOB raw_in_token = data_blob_null; + DATA_BLOB raw_out_token = data_blob_null; NTSTATUS status; ssize_t len; TALLOC_CTX *ctx = talloc_tos(); @@ -1279,6 +1283,7 @@ static void manage_gss_spnego_request(struct ntlm_auth_state *state, const char *reply_code; char *reply_base64; char *reply_argument = NULL; + char *supportedMech = NULL; if (strlen(buf) < 2) { DEBUG(1, ("SPENGO query [%s] invalid\n", buf)); @@ -1287,7 +1292,9 @@ static void manage_gss_spnego_request(struct ntlm_auth_state *state, } if (strncmp(buf, "YR", 2) == 0) { - TALLOC_FREE(ntlmssp_state); + TALLOC_FREE(state->ntlmssp_state); + TALLOC_FREE(state->spnego_mech); + TALLOC_FREE(state->spnego_mech_oid); } else if (strncmp(buf, "KK", 2) == 0) { ; } else { @@ -1341,6 +1348,7 @@ static void manage_gss_spnego_request(struct ntlm_auth_state *state, return; } + ZERO_STRUCT(request); len = spnego_read_data(ctx, token, &request); data_blob_free(&token); @@ -1351,6 +1359,20 @@ static void manage_gss_spnego_request(struct ntlm_auth_state *state, } if (request.type == SPNEGO_NEG_TOKEN_INIT) { +#ifdef HAVE_KRB5 + int krb5_idx = -1; +#endif + int ntlm_idx = -1; + int used_idx = -1; + int i; + + if (state->spnego_mech) { + DEBUG(1, ("Client restarted SPNEGO with NegTokenInit " + "while mech[%s] was already negotiated\n", + state->spnego_mech)); + x_fprintf(x_stdout, "BH Client send NegTokenInit twice\n"); + return; + } /* Second request from Client. This is where the client offers its mechanism to use. */ @@ -1364,159 +1386,198 @@ static void manage_gss_spnego_request(struct ntlm_auth_state *state, } status = NT_STATUS_UNSUCCESSFUL; - if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) { + for (i = 0; request.negTokenInit.mechTypes[i] != NULL; i++) { + DEBUG(10,("got mech[%d][%s]\n", + i, request.negTokenInit.mechTypes[i])); +#ifdef HAVE_KRB5 + if (strcmp(request.negTokenInit.mechTypes[i], OID_KERBEROS5_OLD) == 0) { + krb5_idx = i; + break; + } + if (strcmp(request.negTokenInit.mechTypes[i], OID_KERBEROS5) == 0) { + krb5_idx = i; + break; + } +#endif + if (strcmp(request.negTokenInit.mechTypes[i], OID_NTLMSSP) == 0) { + ntlm_idx = i; + break; + } + } - if ( request.negTokenInit.mechToken.data == NULL ) { - DEBUG(1, ("Client did not provide NTLMSSP data\n")); - x_fprintf(x_stdout, "BH Client did not provide " - "NTLMSSP data\n"); + used_idx = ntlm_idx; +#ifdef HAVE_KRB5 + if (krb5_idx != -1) { + ntlm_idx = -1; + used_idx = krb5_idx; + } +#endif + if (ntlm_idx > -1) { + state->spnego_mech = talloc_strdup(state, "ntlmssp"); + if (state->spnego_mech == NULL) { + x_fprintf(x_stdout, "BH Out of memory\n"); return; } - if ( ntlmssp_state != NULL ) { + if (state->ntlmssp_state) { DEBUG(1, ("Client wants a new NTLMSSP challenge, but " "already got one\n")); x_fprintf(x_stdout, "BH Client wants a new " "NTLMSSP challenge, but " "already got one\n"); - TALLOC_FREE(ntlmssp_state); + TALLOC_FREE(state->ntlmssp_state); return; } - if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) { + status = ntlm_auth_start_ntlmssp_server(&state->ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { x_fprintf(x_stdout, "BH %s\n", nt_errstr(status)); return; } - - DEBUG(10, ("got NTLMSSP packet:\n")); - dump_data(10, request.negTokenInit.mechToken.data, - request.negTokenInit.mechToken.length); - - response.type = SPNEGO_NEG_TOKEN_TARG; - response.negTokenTarg.supportedMech = talloc_strdup(ctx, OID_NTLMSSP); - response.negTokenTarg.mechListMIC = data_blob_talloc(ctx, NULL, 0); - - status = ntlmssp_update(ntlmssp_state, - request.negTokenInit.mechToken, - &response.negTokenTarg.responseToken); } #ifdef HAVE_KRB5 - if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) { - - TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request"); - char *principal; - DATA_BLOB ap_rep; - DATA_BLOB session_key = data_blob_null; - struct PAC_LOGON_INFO *logon_info = NULL; - DATA_BLOB ticket; - uint8_t tok_id[2]; - - if ( request.negTokenInit.mechToken.data == NULL ) { - DEBUG(1, ("Client did not provide Kerberos data\n")); - x_fprintf(x_stdout, "BH Client did not provide " - "Kerberos data\n"); + if (krb5_idx > -1) { + state->spnego_mech = talloc_strdup(state, "krb5"); + if (state->spnego_mech == NULL) { + x_fprintf(x_stdout, "BH Out of memory\n"); return; } - - dump_data(10, request.negTokenInit.mechToken.data, - request.negTokenInit.mechToken.length); - - if (!spnego_parse_krb5_wrap(ctx, request.negTokenInit.mechToken, - &ticket, tok_id)) { - DEBUG(1, ("spnego_parse_krb5_wrap failed\n")); - x_fprintf(x_stdout, "BH spnego_parse_krb5_wrap failed\n"); + } +#endif + if (used_idx > -1) { + state->spnego_mech_oid = talloc_strdup(state, + request.negTokenInit.mechTypes[used_idx]); + if (state->spnego_mech_oid == NULL) { + x_fprintf(x_stdout, "BH Out of memory\n"); return; } - - response.type = SPNEGO_NEG_TOKEN_TARG; - response.negTokenTarg.supportedMech = talloc_strdup(ctx, OID_KERBEROS5_OLD); - response.negTokenTarg.mechListMIC = data_blob_talloc(ctx, NULL, 0); - response.negTokenTarg.responseToken = data_blob_talloc(ctx, NULL, 0); - - status = ads_verify_ticket(mem_ctx, lp_realm(), 0, - &ticket, - &principal, &logon_info, &ap_rep, - &session_key, True); - - /* Now in "principal" we have the name we are - authenticated as. */ - - if (NT_STATUS_IS_OK(status)) { - - domain = strchr_m(principal, '@'); - - if (domain == NULL) { - DEBUG(1, ("Did not get a valid principal " - "from ads_verify_ticket\n")); - x_fprintf(x_stdout, "BH Did not get a " - "valid principal from " - "ads_verify_ticket\n"); - return; - } - - *domain++ = '\0'; - domain = SMB_STRDUP(domain); - user = SMB_STRDUP(principal); - - netsamlogon_cache_store( - user, &logon_info->info3); - - data_blob_free(&ap_rep); - data_blob_free(&session_key); + supportedMech = talloc_strdup(ctx, state->spnego_mech_oid); + if (supportedMech == NULL) { + x_fprintf(x_stdout, "BH Out of memory\n"); + return; } - TALLOC_FREE(mem_ctx); + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + } else { + status = NT_STATUS_NOT_SUPPORTED; + } + if (used_idx == 0) { + status = NT_STATUS_OK; + raw_in_token = request.negTokenInit.mechToken; } -#endif - } else { + if (state->spnego_mech == NULL) { + DEBUG(1,("Got netTokenTarg without negTokenInit\n")); + x_fprintf(x_stdout, "BH Got a negTokenTarg without " + "negTokenInit\n"); + return; + } - if ( (request.negTokenTarg.supportedMech == NULL) || - ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) { - /* Kerberos should never send a negTokenTarg, OID_NTLMSSP - is the only one we support that sends this stuff */ - DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n", - request.negTokenTarg.supportedMech)); - x_fprintf(x_stdout, "BH Got a negTokenTarg for " - "something non-NTLMSSP\n"); + if ((request.negTokenTarg.supportedMech != NULL) && + (strcmp(request.negTokenTarg.supportedMech, state->spnego_mech_oid) != 0 ) ) { + DEBUG(1, ("Got a negTokenTarg with mech[%s] while [%s] was already negotiated\n", + request.negTokenTarg.supportedMech, + state->spnego_mech_oid)); + x_fprintf(x_stdout, "BH Got a negTokenTarg with speficied mech\n"); return; } - if (request.negTokenTarg.responseToken.data == NULL) { - DEBUG(1, ("Got a negTokenTarg without a responseToken!\n")); - x_fprintf(x_stdout, "BH Got a negTokenTarg without a " - "responseToken!\n"); + status = NT_STATUS_OK; + raw_in_token = request.negTokenTarg.responseToken; + } + + if (!NT_STATUS_IS_OK(status)) { + /* error or more processing */ + } else if (strcmp(state->spnego_mech, "ntlmssp") == 0) { + + DEBUG(10, ("got NTLMSSP packet:\n")); + dump_data(10, raw_in_token.data, raw_in_token.length); + + status = ntlmssp_update(state->ntlmssp_state, + raw_in_token, + &raw_out_token); + if (NT_STATUS_IS_OK(status)) { + user = talloc_strdup(ctx, state->ntlmssp_state->user); + domain = talloc_strdup(ctx, state->ntlmssp_state->domain); + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + TALLOC_FREE(state->ntlmssp_state); + } +#ifdef HAVE_KRB5 + } else if (strcmp(state->spnego_mech, "krb5") == 0) { + char *principal; + DATA_BLOB ap_rep; + DATA_BLOB session_key; + struct PAC_LOGON_INFO *logon_info = NULL; + DATA_BLOB ticket; + uint8_t tok_id[2]; + + if (!spnego_parse_krb5_wrap(ctx, raw_in_token, + &ticket, tok_id)) { + DEBUG(1, ("spnego_parse_krb5_wrap failed\n")); + x_fprintf(x_stdout, "BH spnego_parse_krb5_wrap failed\n"); return; } - status = ntlmssp_update(ntlmssp_state, - request.negTokenTarg.responseToken, - &response.negTokenTarg.responseToken); + status = ads_verify_ticket(ctx, lp_realm(), 0, + &ticket, + &principal, &logon_info, &ap_rep, + &session_key, True); - response.type = SPNEGO_NEG_TOKEN_TARG; - response.negTokenTarg.supportedMech = talloc_strdup(ctx, OID_NTLMSSP); - response.negTokenTarg.mechListMIC = data_blob_talloc(ctx, NULL, 0); + /* Now in "principal" we have the name we are authenticated as. */ if (NT_STATUS_IS_OK(status)) { - user = SMB_STRDUP(ntlmssp_state->user); - domain = SMB_STRDUP(ntlmssp_state->domain); - TALLOC_FREE(ntlmssp_state); + + domain = strchr_m(principal, '@'); + + if (domain == NULL) { + DEBUG(1, ("Did not get a valid principal " + "from ads_verify_ticket\n")); + x_fprintf(x_stdout, "BH Did not get a " + "valid principal from " + "ads_verify_ticket\n"); + return; + } + + *domain++ = '\0'; + domain = talloc_strdup(ctx, domain); + user = talloc_strdup(ctx, principal); + + if (logon_info) { + netsamlogon_cache_store( + user, &logon_info->info3); + } + + data_blob_free(&ap_rep); + data_blob_free(&session_key); } + data_blob_free(&ticket); +#endif } spnego_free_data(&request); + ZERO_STRUCT(response); + response.type = SPNEGO_NEG_TOKEN_TARG; if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(state->spnego_mech); + TALLOC_FREE(state->spnego_mech_oid); response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED; + response.negTokenTarg.responseToken = raw_out_token; reply_code = "AF"; reply_argument = talloc_asprintf(ctx, "%s\\%s", domain, user); } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + response.negTokenTarg.supportedMech = supportedMech; + response.negTokenTarg.responseToken = raw_out_token; response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; reply_code = "TT"; reply_argument = talloc_strdup(ctx, "*"); } else { + TALLOC_FREE(state->spnego_mech); + TALLOC_FREE(state->spnego_mech_oid); + data_blob_free(&raw_out_token); response.negTokenTarg.negResult = SPNEGO_REJECT; reply_code = "NA"; reply_argument = talloc_strdup(ctx, nt_errstr(status)); @@ -1525,12 +1586,10 @@ static void manage_gss_spnego_request(struct ntlm_auth_state *state, if (!reply_argument) { DEBUG(1, ("Could not write SPNEGO data blob\n")); x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n"); + spnego_free_data(&response); return; } - SAFE_FREE(user); - SAFE_FREE(domain); - len = spnego_write_data(ctx, &token, &response); spnego_free_data(&response); -- cgit