_PUBLIC_ NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { NTSTATUS status; status = gensec_security->ops->update(gensec_security, out_mem_ctx, ev, in, out); if (!NT_STATUS_IS_OK(status)) { return status; } /* * Because callers using the * gensec_start_mech_by_auth_type() never call * gensec_want_feature(), it isn't sensible for them * to have to call gensec_have_feature() manually, and * these are not points of negotiation, but are * asserted by the client */ switch (gensec_security->dcerpc_auth_level) { case DCERPC_AUTH_LEVEL_INTEGRITY: if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SIGN for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } break; case DCERPC_AUTH_LEVEL_PRIVACY: if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SIGN for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SEAL for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } break; default: break; } return NT_STATUS_OK; }
static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out) { struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; krb5_auth_context auth_context = gensec_krb5_state->auth_context; krb5_error_code ret; krb5_data input, output; krb5_replay_data replay; input.length = in->length; input.data = in->data; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { ret = krb5_rd_priv(context, auth_context, &input, &output, &replay); if (ret) { DEBUG(1, ("krb5_rd_priv failed: %s\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, mem_ctx))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(mem_ctx, output.data, output.length); krb5_data_free(&output); } else { return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
_PUBLIC_ size_t gensec_sig_size(struct gensec_security *gensec_security, size_t data_size) { if (!gensec_security->ops->sig_size) { return 0; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { return 0; } if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE)) { return 0; } } return gensec_security->ops->sig_size(gensec_security, data_size); }
NTSTATUS auth_generic_server_check_flags(struct gensec_security *gensec_security, bool do_sign, bool do_seal) { if (do_sign && !gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(1, (__location__ "Integrity was requested but client " "failed to negotiate signing.\n")); return NT_STATUS_ACCESS_DENIED; } if (do_seal && !gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(1, (__location__ "Privacy was requested but client " "failed to negotiate sealing.\n")); return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); OM_uint32 maj_stat, min_stat; gss_buffer_desc input_token, output_token; int conf_state; input_token.length = in->length; input_token.value = in->data; maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_wrap: GSS Wrap failed: %s\n", gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); if (gensec_gssapi_state->sasl) { size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security); if (max_wrapped_size < out->length) { DEBUG(1, ("gensec_gssapi_wrap: when wrapped, INPUT data (%u) is grew to be larger than SASL negotiated maximum output size (%u > %u)\n", (unsigned)in->length, (unsigned)out->length, (unsigned int)max_wrapped_size)); return NT_STATUS_INVALID_PARAMETER; } } if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) && !conf_state) { return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
_PUBLIC_ NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, const uint8_t *whole_pdu, size_t pdu_length, DATA_BLOB *sig) { if (!gensec_security->ops->seal_packet) { return NT_STATUS_NOT_IMPLEMENTED; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { return NT_STATUS_INVALID_PARAMETER; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { return NT_STATUS_INVALID_PARAMETER; } return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); }
static NTSTATUS gensec_verify_dcerpc_auth_level(struct gensec_security *gensec_security) { if (gensec_security->dcerpc_auth_level == 0) { return NT_STATUS_OK; } /* * Because callers using the * gensec_start_mech_by_auth_type() never call * gensec_want_feature(), it isn't sensible for them * to have to call gensec_have_feature() manually, and * these are not points of negotiation, but are * asserted by the client */ switch (gensec_security->dcerpc_auth_level) { case DCERPC_AUTH_LEVEL_INTEGRITY: if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SIGN for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } break; case DCERPC_AUTH_LEVEL_PRIVACY: if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SIGN for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SEAL for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } break; default: break; } return NT_STATUS_OK; }
static bool pipe_auth_generic_bind(struct pipes_struct *p, struct ncacn_packet *pkt, struct dcerpc_auth *auth_info, DATA_BLOB *response) { TALLOC_CTX *mem_ctx = pkt; struct gensec_security *gensec_security = NULL; NTSTATUS status; status = auth_generic_server_authtype_start(p, auth_info->auth_type, auth_info->auth_level, &auth_info->credentials, response, p->remote_address, &gensec_security); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { DEBUG(0, (__location__ ": auth_generic_server_authtype_start[%u/%u] failed: %s\n", auth_info->auth_type, auth_info->auth_level, nt_errstr(status))); return false; } /* Make sure data is bound to the memctx, to be freed the caller */ talloc_steal(mem_ctx, response->data); p->auth.auth_ctx = gensec_security; p->auth.auth_type = auth_info->auth_type; if (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) { p->auth.client_hdr_signing = true; p->auth.hdr_signing = gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER); } if (p->auth.hdr_signing) { gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER); } if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { return true; } status = pipe_auth_verify_final(p); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("pipe_auth_verify_final failed: %s\n", nt_errstr(status))); return false; } return true; }
_PUBLIC_ NTSTATUS gensec_session_key(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, DATA_BLOB *session_key) { if (!gensec_security->ops->session_key) { return NT_STATUS_NOT_IMPLEMENTED; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SESSION_KEY)) { return NT_STATUS_NO_USER_SESSION_KEY; } return gensec_security->ops->session_key(gensec_security, mem_ctx, session_key); }
static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); OM_uint32 maj_stat, min_stat; gss_buffer_desc input_token, output_token; int conf_state; gss_qop_t qop_state; input_token.length = in->length; input_token.value = in->data; if (gensec_gssapi_state->sasl) { size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security); if (max_wrapped_size < in->length) { DEBUG(1, ("gensec_gssapi_unwrap: WRAPPED data is larger than SASL negotiated maximum size\n")); return NT_STATUS_INVALID_PARAMETER; } } maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_unwrap: GSS UnWrap failed: %s\n", gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) && !conf_state) { return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); OM_uint32 maj_stat, min_stat; OM_uint32 max_input_size; maj_stat = gss_wrap_size_limit(&min_stat, gensec_gssapi_state->gssapi_context, gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), GSS_C_QOP_DEFAULT, gensec_gssapi_state->max_wrap_buf_size, &max_input_size); if (GSS_ERROR(maj_stat)) { TALLOC_CTX *mem_ctx = talloc_new(NULL); DEBUG(1, ("gensec_gssapi_max_input_size: determining signature size with gss_wrap_size_limit failed: %s\n", gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); talloc_free(mem_ctx); return 0; } return max_input_size; }
/* perform a sasl bind using the given credentials */ _PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn, struct cli_credentials *creds, struct loadparm_context *lp_ctx) { NTSTATUS status; TALLOC_CTX *tmp_ctx = NULL; DATA_BLOB input = data_blob(NULL, 0); DATA_BLOB output = data_blob(NULL, 0); struct ldap_message **sasl_mechs_msgs; struct ldap_SearchResEntry *search; int count, i; bool first = true; int wrap_flags = 0; const char **sasl_names; uint32_t old_gensec_features; static const char *supported_sasl_mech_attrs[] = { "supportedSASLMechanisms", NULL }; unsigned int logon_retries = 0; size_t queue_length; if (conn->sockets.active == NULL) { status = NT_STATUS_CONNECTION_DISCONNECTED; goto failed; } queue_length = tevent_queue_length(conn->sockets.send_queue); if (queue_length != 0) { status = NT_STATUS_INVALID_PARAMETER_MIX; DEBUG(1, ("SASL bind triggered with non empty send_queue[%zu]: %s\n", queue_length, nt_errstr(status))); goto failed; } if (conn->pending != NULL) { status = NT_STATUS_INVALID_PARAMETER_MIX; DEBUG(1, ("SASL bind triggered with pending requests: %s\n", nt_errstr(status))); goto failed; } status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs, false, NULL, NULL, &sasl_mechs_msgs); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n", nt_errstr(status))); goto failed; } count = ildap_count_entries(conn, sasl_mechs_msgs); if (count != 1) { DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n", count)); goto failed; } tmp_ctx = talloc_new(conn); if (tmp_ctx == NULL) goto failed; search = &sasl_mechs_msgs[0]->r.SearchResultEntry; if (search->num_attributes != 1) { DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n", search->num_attributes)); goto failed; } sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1); if (!sasl_names) { DEBUG(1, ("talloc_arry(char *, %d) failed\n", count)); goto failed; } for (i=0; i<search->attributes[0].num_values; i++) { sasl_names[i] = (const char *)search->attributes[0].values[i].data; } sasl_names[i] = NULL; gensec_init(); if (conn->sockets.active == conn->sockets.tls) { /* * require Kerberos SIGN/SEAL only if we don't use SSL * Windows seem not to like double encryption */ wrap_flags = 0; } else if (cli_credentials_is_anonymous(creds)) { /* * anonymous isn't protected */ wrap_flags = 0; } else { wrap_flags = lpcfg_client_ldap_sasl_wrapping(lp_ctx); } try_logon_again: /* we loop back here on a logon failure, and re-create the gensec session. The logon_retries counter ensures we don't loop forever. */ data_blob_free(&input); TALLOC_FREE(conn->gensec); status = gensec_client_start(conn, &conn->gensec, lpcfg_gensec_settings(conn, lp_ctx)); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status))); goto failed; } old_gensec_features = cli_credentials_get_gensec_features(creds); if (wrap_flags == 0) { cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)); } /* this call also sets the gensec_want_features */ status = gensec_set_credentials(conn->gensec, creds); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to set GENSEC creds: %s\n", nt_errstr(status))); goto failed; } /* reset the original gensec_features (on the credentials * context, so we don't tatoo it ) */ cli_credentials_set_gensec_features(creds, old_gensec_features); if (wrap_flags & ADS_AUTH_SASL_SEAL) { gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN); gensec_want_feature(conn->gensec, GENSEC_FEATURE_SEAL); } if (wrap_flags & ADS_AUTH_SASL_SIGN) { gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN); } /* * This is an indication for the NTLMSSP backend to * also encrypt when only GENSEC_FEATURE_SIGN is requested * in gensec_[un]wrap(). */ gensec_want_feature(conn->gensec, GENSEC_FEATURE_LDAP_STYLE); if (conn->host) { status = gensec_set_target_hostname(conn->gensec, conn->host); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", nt_errstr(status))); goto failed; } } status = gensec_set_target_service(conn->gensec, "ldap"); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to set GENSEC target service: %s\n", nt_errstr(status))); goto failed; } status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n", count, nt_errstr(status))); goto failed; } while (1) { NTSTATUS gensec_status; struct ldap_message *response; struct ldap_message *msg; struct ldap_request *req; int result = LDAP_OTHER; status = gensec_update_ev(conn->gensec, tmp_ctx, conn->event.event_ctx, input, &output); /* The status value here, from GENSEC is vital to the security * of the system. Even if the other end accepts, if GENSEC * claims 'MORE_PROCESSING_REQUIRED' then you must keep * feeding it blobs, or else the remote host/attacker might * avoid mutal authentication requirements. * * Likewise, you must not feed GENSEC too much (after the OK), * it doesn't like that either. * * For SASL/EXTERNAL, there is no data to send, but we still * must send the actual Bind request the first time around. * Otherwise, a result of NT_STATUS_OK with 0 output means the * end of a multi-step authentication, and no message must be * sent. */ gensec_status = status; if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { break; } if (NT_STATUS_IS_OK(status) && output.length == 0) { if (!first) break; } first = false; /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */ msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL)); if (msg == NULL) { status = NT_STATUS_NO_MEMORY; goto failed; } req = ldap_request_send(conn, msg); if (req == NULL) { status = NT_STATUS_NO_MEMORY; goto failed; } talloc_reparent(conn, tmp_ctx, req); status = ldap_result_n(req, 0, &response); if (!NT_STATUS_IS_OK(status)) { goto failed; } if (response->type != LDAP_TAG_BindResponse) { status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; goto failed; } result = response->r.BindResponse.response.resultcode; if (result == LDAP_STRONG_AUTH_REQUIRED) { if (wrap_flags == 0) { wrap_flags = ADS_AUTH_SASL_SIGN; goto try_logon_again; } } if (result == LDAP_INVALID_CREDENTIALS) { /* try a second time on invalid credentials, to give the user a chance to re-enter the password and to handle the case where our kerberos ticket is invalid as the server password has changed */ const char *principal; principal = gensec_get_target_principal(conn->gensec); if (principal == NULL) { const char *hostname = gensec_get_target_hostname(conn->gensec); const char *service = gensec_get_target_service(conn->gensec); if (hostname != NULL && service != NULL) { principal = talloc_asprintf(tmp_ctx, "%s/%s", service, hostname); } } if (cli_credentials_failed_kerberos_login(creds, principal, &logon_retries) || cli_credentials_wrong_password(creds)) { /* destroy our gensec session and loop back up to the top to retry, offering the user a chance to enter new credentials, or get a new ticket if using kerberos */ goto try_logon_again; } } if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) { status = ldap_check_response(conn, &response->r.BindResponse.response); break; } /* This is where we check if GENSEC wanted to be fed more data */ if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { break; } if (response->r.BindResponse.SASL.secblob) { input = *response->r.BindResponse.SASL.secblob; } else { input = data_blob(NULL, 0); } } TALLOC_FREE(tmp_ctx); if (!NT_STATUS_IS_OK(status)) { goto failed; } conn->bind.type = LDAP_BIND_SASL; conn->bind.creds = creds; if (wrap_flags & ADS_AUTH_SASL_SEAL) { if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL)) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } } else if (wrap_flags & ADS_AUTH_SASL_SIGN) { if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } } if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) && !gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL)) { return NT_STATUS_OK; } status = gensec_create_tstream(conn->sockets.raw, conn->gensec, conn->sockets.raw, &conn->sockets.sasl); if (!NT_STATUS_IS_OK(status)) { goto failed; } conn->sockets.active = conn->sockets.sasl; return NT_STATUS_OK; failed: talloc_free(tmp_ctx); talloc_free(conn->gensec); conn->gensec = NULL; return status; }
/* perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads, const char *sasl, enum credentials_use_kerberos krb5_state, const char *target_service, const char *target_hostname, const DATA_BLOB server_blob) { DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; int rc; NTSTATUS nt_status; ADS_STATUS status; struct auth_generic_state *auth_generic_state; bool use_spnego_principal = lp_client_use_spnego_principal(); const char *sasl_list[] = { sasl, NULL }; NTTIME end_nt_time; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; nt_status = auth_generic_client_prepare(NULL, &auth_generic_state); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) { return ADS_ERROR_NT(nt_status); } if (server_blob.length == 0) { use_spnego_principal = false; } if (krb5_state == CRED_DONT_USE_KERBEROS) { use_spnego_principal = false; } cli_credentials_set_kerberos_state(auth_generic_state->credentials, krb5_state); if (target_service != NULL) { nt_status = gensec_set_target_service( auth_generic_state->gensec_security, target_service); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_hostname != NULL) { nt_status = gensec_set_target_hostname( auth_generic_state->gensec_security, target_hostname); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_service != NULL && target_hostname != NULL) { use_spnego_principal = false; } switch (wrap->wrap_type) { case ADS_SASLWRAP_TYPE_SEAL: gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL); break; case ADS_SASLWRAP_TYPE_SIGN: if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); } else { /* * windows servers are broken with sign only, * so we let the NTLMSSP backend to seal here, * via GENSEC_FEATURE_LDAP_STYLE. */ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE); } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } nt_status = auth_generic_client_start_by_sasl(auth_generic_state, sasl_list); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } rc = LDAP_SASL_BIND_IN_PROGRESS; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; if (use_spnego_principal) { blob_in = data_blob_dup_talloc(talloc_tos(), server_blob); if (blob_in.length == 0) { TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } } else { blob_in = data_blob_null; } blob_out = data_blob_null; while (true) { struct berval cred, *scred = NULL; nt_status = gensec_update(auth_generic_state->gensec_security, talloc_tos(), blob_in, &blob_out); data_blob_free(&blob_in); if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) { break; } cred.bv_val = (char *)blob_out.data; cred.bv_len = blob_out.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred); data_blob_free(&blob_out); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); } if (scred) { blob_in = data_blob_talloc(talloc_tos(), scred->bv_val, scred->bv_len); if (blob_in.length != scred->bv_len) { ber_bvfree(scred); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } ber_bvfree(scred); } else { blob_in = data_blob_null; } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) { break; } } data_blob_free(&blob_in); data_blob_free(&blob_out); if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) { bool ok; ok = gensec_have_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL); if (!ok) { DEBUG(0,("The gensec feature sealing request, but unavailable\n")); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } ok = gensec_have_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); if (!ok) { DEBUG(0,("The gensec feature signing request, but unavailable\n")); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) { bool ok; ok = gensec_have_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); if (!ok) { DEBUG(0,("The gensec feature signing request, but unavailable\n")); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } } ads->auth.tgs_expire = LONG_MAX; end_nt_time = gensec_expire_time(auth_generic_state->gensec_security); if (end_nt_time != GENSEC_EXPIRE_TIME_INFINITY) { struct timeval tv; nttime_to_timeval(&tv, end_nt_time); ads->auth.tgs_expire = tv.tv_sec; } if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { size_t max_wrapped = gensec_max_wrapped_size(auth_generic_state->gensec_security); wrap->out.max_unwrapped = gensec_max_input_size(auth_generic_state->gensec_security); wrap->out.sig_size = max_wrapped - wrap->out.max_unwrapped; /* * Note that we have to truncate this to 0x2C * (taken from a capture with LDAP unbind), as the * signature size is not constant for Kerberos with * arcfour-hmac-md5. */ wrap->in.min_wrapped = MIN(wrap->out.sig_size, 0x2C); wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(wrap, ads->ldap.ld, &ads_sasl_gensec_ops, auth_generic_state->gensec_security); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); TALLOC_FREE(auth_generic_state); return status; } /* Only keep the gensec_security element around long-term */ talloc_steal(NULL, auth_generic_state->gensec_security); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); }
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, struct socket_context *current_socket, struct tevent_context *ev, void (*recv_handler)(void *, uint16_t), void *recv_private, struct socket_context **new_socket) { struct gensec_socket *gensec_socket; struct socket_context *new_sock; NTSTATUS nt_status; nt_status = socket_create_with_ops(mem_ctx, &gensec_socket_ops, &new_sock, SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT); if (!NT_STATUS_IS_OK(nt_status)) { *new_socket = NULL; return nt_status; } new_sock->state = current_socket->state; gensec_socket = talloc(new_sock, struct gensec_socket); if (gensec_socket == NULL) { *new_socket = NULL; talloc_free(new_sock); return NT_STATUS_NO_MEMORY; } new_sock->private_data = gensec_socket; gensec_socket->socket = current_socket; /* Nothing to do here, if we are not actually wrapping on this socket */ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) && !gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { gensec_socket->wrap = false; talloc_steal(gensec_socket, current_socket); *new_socket = new_sock; return NT_STATUS_OK; } gensec_socket->gensec_security = gensec_security; gensec_socket->wrap = true; gensec_socket->eof = false; gensec_socket->error = NT_STATUS_OK; gensec_socket->interrupted = false; gensec_socket->in_extra_read = 0; gensec_socket->read_buffer = data_blob(NULL, 0); gensec_socket->recv_handler = recv_handler; gensec_socket->recv_private = recv_private; gensec_socket->ev = ev; gensec_socket->packet = packet_init(gensec_socket); if (gensec_socket->packet == NULL) { *new_socket = NULL; talloc_free(new_sock); return NT_STATUS_NO_MEMORY; } packet_set_private(gensec_socket->packet, gensec_socket); packet_set_socket(gensec_socket->packet, gensec_socket->socket); packet_set_callback(gensec_socket->packet, gensec_socket_unwrap); packet_set_full_request(gensec_socket->packet, gensec_socket_full_request); packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler); packet_set_serialise(gensec_socket->packet); /* TODO: full-request that knows about maximum packet size */ talloc_steal(gensec_socket, current_socket); *new_socket = new_sock; return NT_STATUS_OK; }
_PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { NTSTATUS status; const struct gensec_security_ops *ops = gensec_security->ops; TALLOC_CTX *frame = NULL; struct tevent_req *subreq = NULL; bool ok; if (ops->update_send == NULL) { if (ev == NULL) { frame = talloc_stackframe(); ev = samba_tevent_context_init(frame); if (ev == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } /* * TODO: remove this hack once the backends * are fixed. */ tevent_loop_allow_nesting(ev); } status = ops->update(gensec_security, out_mem_ctx, ev, in, out); TALLOC_FREE(frame); if (!NT_STATUS_IS_OK(status)) { return status; } /* * Because callers using the * gensec_start_mech_by_auth_type() never call * gensec_want_feature(), it isn't sensible for them * to have to call gensec_have_feature() manually, and * these are not points of negotiation, but are * asserted by the client */ switch (gensec_security->dcerpc_auth_level) { case DCERPC_AUTH_LEVEL_INTEGRITY: if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SIGN for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } break; case DCERPC_AUTH_LEVEL_PRIVACY: if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SIGN for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(0,("Did not manage to negotiate mandetory feature " "SEAL for dcerpc auth_level %u\n", gensec_security->dcerpc_auth_level)); return NT_STATUS_ACCESS_DENIED; } break; default: break; } return NT_STATUS_OK; } frame = talloc_stackframe(); if (ev == NULL) { ev = samba_tevent_context_init(frame); if (ev == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } /* * TODO: remove this hack once the backends * are fixed. */ tevent_loop_allow_nesting(ev); } subreq = ops->update_send(frame, ev, gensec_security, in); if (subreq == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } ok = tevent_req_poll_ntstatus(subreq, ev, &status); if (!ok) { goto fail; } status = ops->update_recv(subreq, out_mem_ctx, out); fail: TALLOC_FREE(frame); return status; }
/* add any auth information needed in a bind ack, and process the authentication information found in the bind. */ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt) { struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; bool want_header_signing = false; if (!call->conn->auth_state.gensec_security) { return NT_STATUS_OK; } if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) { dce_conn->auth_state.client_hdr_signing = true; want_header_signing = true; } if (!lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", true)) { want_header_signing = false; } status = gensec_update(dce_conn->auth_state.gensec_security, call, call->event_ctx, dce_conn->auth_state.auth_info->credentials, &dce_conn->auth_state.auth_info->credentials); if (NT_STATUS_IS_OK(status)) { status = gensec_session_info(dce_conn->auth_state.gensec_security, dce_conn, &dce_conn->auth_state.session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status))); return status; } if (!gensec_have_feature(dce_conn->auth_state.gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER)) { want_header_signing = false; } if (want_header_signing) { gensec_want_feature(dce_conn->auth_state.gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER); dce_conn->auth_state.hdr_signing = true; pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN; } /* Now that we are authenticated, go back to the generic session key... */ dce_conn->auth_state.session_key = dcesrv_generic_session_key; return NT_STATUS_OK; } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { dce_conn->auth_state.auth_info->auth_pad_length = 0; dce_conn->auth_state.auth_info->auth_reserved = 0; if (!gensec_have_feature(dce_conn->auth_state.gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER)) { want_header_signing = false; } if (want_header_signing) { gensec_want_feature(dce_conn->auth_state.gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER); dce_conn->auth_state.hdr_signing = true; pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN; } return NT_STATUS_OK; } else { DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_ack: %s\n", nt_errstr(status))); return status; } }
static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; OM_uint32 maj_stat, min_stat; OM_uint32 min_stat2; gss_buffer_desc input_token = { 0, NULL }; gss_buffer_desc output_token = { 0, NULL }; gss_OID gss_oid_p = NULL; OM_uint32 time_req = 0; OM_uint32 time_rec = 0; struct timeval tv; time_req = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "requested_life_time", time_req); input_token.length = in.length; input_token.value = in.data; switch (gensec_gssapi_state->sasl_state) { case STAGE_GSS_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { #ifdef SAMBA4_USES_HEIMDAL struct gsskrb5_send_to_kdc send_to_kdc; krb5_error_code ret; #endif nt_status = gensec_gssapi_client_creds(gensec_security, ev); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = ev; min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc); if (min_stat) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif maj_stat = gss_init_sec_context(&min_stat, gensec_gssapi_state->client_cred->creds, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_name, gensec_gssapi_state->gss_oid, gensec_gssapi_state->gss_want_flags, time_req, gensec_gssapi_state->input_chan_bindings, &input_token, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, /* ret flags */ &time_rec); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = NULL; ret = gsskrb5_set_send_to_kdc(&send_to_kdc); if (ret) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif break; } case GENSEC_SERVER: { maj_stat = gss_accept_sec_context(&min_stat, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_cred->creds, &input_token, gensec_gssapi_state->input_chan_bindings, &gensec_gssapi_state->client_name, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, &time_rec, &gensec_gssapi_state->delegated_cred_handle); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } break; } default: return NT_STATUS_INVALID_PARAMETER; } gensec_gssapi_state->gss_exchange_count++; if (maj_stat == GSS_S_COMPLETE) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG && gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { DEBUG(5, ("gensec_gssapi: credentials were delegated\n")); } else { DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n")); } tv = timeval_current_ofs(time_rec, 0); gensec_gssapi_state->expire_time = timeval_to_nttime(&tv); /* We may have been invoked as SASL, so there * is more work to do */ if (gensec_gssapi_state->sasl) { gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG; return NT_STATUS_MORE_PROCESSING_REQUIRED; } else { gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n")); } else { DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n")); } return NT_STATUS_OK; } } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); return NT_STATUS_MORE_PROCESSING_REQUIRED; } else if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gss_cred_id_t creds = NULL; gss_name_t name; gss_buffer_desc buffer; OM_uint32 lifetime = 0; gss_cred_usage_t usage; const char *role = NULL; DEBUG(0, ("GSS %s Update(krb5)(%d) Update failed, credentials expired during GSSAPI handshake!\n", role, gensec_gssapi_state->gss_exchange_count)); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: creds = gensec_gssapi_state->client_cred->creds; role = "client"; break; case GENSEC_SERVER: creds = gensec_gssapi_state->server_cred->creds; role = "server"; break; } maj_stat = gss_inquire_cred(&min_stat, creds, &name, &lifetime, &usage, NULL); if (maj_stat == GSS_S_COMPLETE) { const char *usage_string = NULL; switch (usage) { case GSS_C_BOTH: usage_string = "GSS_C_BOTH"; break; case GSS_C_ACCEPT: usage_string = "GSS_C_ACCEPT"; break; case GSS_C_INITIATE: usage_string = "GSS_C_INITIATE"; break; } maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); if (maj_stat) { buffer.value = NULL; buffer.length = 0; } if (lifetime > 0) { DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, lifetime, usage_string)); } else { DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, usage_string)); } gss_release_buffer(&min_stat, &buffer); gss_release_name(&min_stat, &name); } else if (maj_stat != GSS_S_COMPLETE) { DEBUG(0, ("inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); } return NT_STATUS_INVALID_PARAMETER; } else if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) { switch (min_stat) { case KRB5KRB_AP_ERR_TKT_NYV: DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_TKT_EXPIRED: DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticket to %s: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: DEBUG(3, ("Server %s is not registered with our KDC: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_MSG_TYPE: /* garbage input, possibly from the auto-mech detection */ return NT_STATUS_INVALID_PARAMETER; default: DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } } else { DEBUG(1, ("GSS %s Update(%d) failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } break; } /* These last two stages are only done if we were invoked as SASL */ case STAGE_SASL_SSF_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { uint8_t maxlength_proposed[4]; uint8_t maxlength_accepted[4]; uint8_t security_supported; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; /* As a client, we have just send a * zero-length blob to the server (after the * normal GSSAPI exchange), and it has replied * with it's SASL negotiation */ maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_proposed, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_supported = maxlength_proposed[0]; maxlength_proposed[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_supported & NEG_SEAL) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { gensec_gssapi_state->sasl_protection |= NEG_SEAL; } } if (security_supported & NEG_SIGN) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { gensec_gssapi_state->sasl_protection |= NEG_SIGN; } } if (security_supported & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } if (gensec_gssapi_state->sasl_protection == 0) { DEBUG(1, ("Remote server does not support unprotected connections\n")); return NT_STATUS_ACCESS_DENIED; } /* Send back the negotiated max length */ RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size); maxlength_accepted[0] = gensec_gssapi_state->sasl_protection; input_token.value = maxlength_accepted; input_token.length = sizeof(maxlength_accepted); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n")); } else { DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographically protection\n")); } return NT_STATUS_OK; } case GENSEC_SERVER: { uint8_t maxlength_proposed[4]; uint8_t security_supported = 0x0; int conf_state; /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */ if (in.length != 0) { DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n")); } /* Give the client some idea what we will support */ RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size); /* first byte is the proposed security */ maxlength_proposed[0] = '\0'; gensec_gssapi_state->sasl_protection = 0; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { security_supported |= NEG_SEAL; } if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { security_supported |= NEG_SIGN; } if (security_supported == 0) { /* If we don't support anything, this must be 0 */ RSIVAL(maxlength_proposed, 0, 0x0); } /* TODO: We may not wish to support this */ security_supported |= NEG_NONE; maxlength_proposed[0] = security_supported; input_token.value = maxlength_proposed; input_token.length = sizeof(maxlength_proposed); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT; return NT_STATUS_MORE_PROCESSING_REQUIRED; } default: return NT_STATUS_INVALID_PARAMETER; } } /* This is s server-only stage */ case STAGE_SASL_SSF_ACCEPT: { uint8_t maxlength_accepted[4]; uint8_t security_accepted; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_accepted, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_accepted = maxlength_accepted[0]; maxlength_accepted[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_accepted & NEG_SEAL) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(1, ("Remote client wanted seal, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SEAL; } if (security_accepted & NEG_SIGN) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(1, ("Remote client wanted sign, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SIGN; } if (security_accepted & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n")); } else { DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n")); } *out = data_blob(NULL, 0); return NT_STATUS_OK; } default: return NT_STATUS_INVALID_PARAMETER; } }