/* parse any auth information from a dcerpc alter request return false if we can't handle the auth request for some reason (in which case we send a bind_nak (is this true for here?)) */ bool dcesrv_auth_alter(struct dcesrv_call_state *call) { struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; uint32_t auth_length; /* on a pure interface change there is no auth blob */ if (pkt->u.alter.auth_info.length == 0) { return true; } /* We can't work without an existing gensec state */ if (!dce_conn->auth_state.gensec_security) { return false; } dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth); if (!dce_conn->auth_state.auth_info) { return false; } status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info, dce_conn->auth_state.auth_info, &auth_length, true); if (!NT_STATUS_IS_OK(status)) { return false; } return true; }
/* parse any auth information from a dcerpc bind request return false if we can't handle the auth request for some reason (in which case we send a bind_nak) */ bool dcesrv_auth_bind(struct dcesrv_call_state *call) { struct cli_credentials *server_credentials; struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; struct dcesrv_auth *auth = &dce_conn->auth_state; NTSTATUS status; uint32_t auth_length; if (pkt->u.bind.auth_info.length == 0) { dce_conn->auth_state.auth_info = NULL; return true; } dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth); if (!dce_conn->auth_state.auth_info) { return false; } status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info, dce_conn->auth_state.auth_info, &auth_length, false); server_credentials = cli_credentials_init(call); if (!server_credentials) { DEBUG(1, ("Failed to init server credentials\n")); return false; } cli_credentials_set_conf(server_credentials, call->conn->dce_ctx->lp_ctx); status = cli_credentials_set_machine_account(server_credentials, call->conn->dce_ctx->lp_ctx); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status))); talloc_free(server_credentials); server_credentials = NULL; } status = samba_server_gensec_start(dce_conn, call->event_ctx, call->msg_ctx, call->conn->dce_ctx->lp_ctx, server_credentials, NULL, &auth->gensec_security); status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, auth->auth_info->auth_level); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n", (int)auth->auth_info->auth_type, (int)auth->auth_info->auth_level, nt_errstr(status))); return false; } return true; }
/* process the final stage of a auth request */ bool dcesrv_auth_auth3(struct dcesrv_call_state *call) { struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; uint32_t auth_length; /* We can't work without an existing gensec state, and an new blob to feed it */ if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security || pkt->u.auth3.auth_info.length == 0) { return false; } status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info, dce_conn->auth_state.auth_info, &auth_length, true); if (!NT_STATUS_IS_OK(status)) { return false; } /* Pass the extra data we got from the client down to gensec for processing */ 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 false; } /* Now that we are authenticated, go back to the generic session key... */ dce_conn->auth_state.session_key = dcesrv_generic_session_key; return true; } else { DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_auth3: %s\n", nt_errstr(status))); return false; } }
/** * @brief Check authentication for request/response packets * * @param auth The auth data for the connection * @param pkt The actual ncacn_packet * @param pkt_trailer [in][out] The stub_and_verifier part of the packet, * the auth_trailer and padding will be removed. * @param header_size The header size * @param raw_pkt The whole raw packet data blob * * @return A NTSTATUS error code */ NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth, struct ncacn_packet *pkt, DATA_BLOB *pkt_trailer, uint8_t header_size, DATA_BLOB *raw_pkt) { struct gensec_security *gensec_security; NTSTATUS status; struct dcerpc_auth auth_info; uint32_t auth_length; DATA_BLOB full_pkt; DATA_BLOB data; /* * These check should be done in the caller. */ SMB_ASSERT(raw_pkt->length == pkt->frag_length); SMB_ASSERT(header_size <= pkt->frag_length); SMB_ASSERT(pkt_trailer->length < pkt->frag_length); SMB_ASSERT((pkt_trailer->length + header_size) <= pkt->frag_length); switch (auth->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: DEBUG(10, ("Requested Privacy.\n")); break; case DCERPC_AUTH_LEVEL_INTEGRITY: DEBUG(10, ("Requested Integrity.\n")); break; case DCERPC_AUTH_LEVEL_PACKET: DEBUG(10, ("Requested packet.\n")); break; case DCERPC_AUTH_LEVEL_CONNECT: if (pkt->auth_length != 0) { break; } return NT_STATUS_OK; case DCERPC_AUTH_LEVEL_NONE: if (pkt->auth_length != 0) { DEBUG(3, ("Got non-zero auth len on non " "authenticated connection!\n")); return NT_STATUS_INVALID_PARAMETER; } return NT_STATUS_OK; default: DEBUG(3, ("Unimplemented Auth Level %d", auth->auth_level)); return NT_STATUS_INVALID_PARAMETER; } if (pkt->auth_length == 0) { return NT_STATUS_INVALID_PARAMETER; } status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer, &auth_info, &auth_length, false); if (!NT_STATUS_IS_OK(status)) { return status; } if (auth_info.auth_type != auth->auth_type) { return NT_STATUS_INVALID_PARAMETER; } if (auth_info.auth_level != auth->auth_level) { return NT_STATUS_INVALID_PARAMETER; } if (auth_info.auth_context_id != auth->auth_context_id) { return NT_STATUS_INVALID_PARAMETER; } pkt_trailer->length -= auth_length; data = data_blob_const(raw_pkt->data + header_size, pkt_trailer->length); full_pkt = data_blob_const(raw_pkt->data, raw_pkt->length); full_pkt.length -= auth_info.credentials.length; switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: return NT_STATUS_OK; default: DEBUG(10, ("GENSEC auth\n")); gensec_security = auth->auth_ctx; status = get_generic_auth_footer(gensec_security, auth->auth_level, &data, &full_pkt, &auth_info.credentials); if (!NT_STATUS_IS_OK(status)) { return status; } break; } /* TODO: remove later * this is still needed because in the server code the * pkt_trailer actually has a copy of the raw data, and they * are still both used in later calls */ if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { if (pkt_trailer->length != data.length) { return NT_STATUS_INVALID_PARAMETER; } memcpy(pkt_trailer->data, data.data, data.length); } pkt_trailer->length -= auth_info.auth_pad_length; data_blob_free(&auth_info.credentials); return NT_STATUS_OK; }
/* check credentials on a request */ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet) { struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; struct dcerpc_auth auth; NTSTATUS status; uint32_t auth_length; size_t hdr_size = DCERPC_REQUEST_LENGTH; if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security) { return true; } if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) { hdr_size += 16; } switch (dce_conn->auth_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: case DCERPC_AUTH_LEVEL_INTEGRITY: break; case DCERPC_AUTH_LEVEL_CONNECT: if (pkt->auth_length != 0) { break; } return true; case DCERPC_AUTH_LEVEL_NONE: if (pkt->auth_length != 0) { return false; } return true; default: return false; } status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.request.stub_and_verifier, &auth, &auth_length, false); if (!NT_STATUS_IS_OK(status)) { return false; } pkt->u.request.stub_and_verifier.length -= auth_length; /* check signature or unseal the packet */ switch (dce_conn->auth_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_unseal_packet(dce_conn->auth_state.gensec_security, full_packet->data + hdr_size, pkt->u.request.stub_and_verifier.length, full_packet->data, full_packet->length-auth.credentials.length, &auth.credentials); memcpy(pkt->u.request.stub_and_verifier.data, full_packet->data + hdr_size, pkt->u.request.stub_and_verifier.length); break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = gensec_check_packet(dce_conn->auth_state.gensec_security, pkt->u.request.stub_and_verifier.data, pkt->u.request.stub_and_verifier.length, full_packet->data, full_packet->length-auth.credentials.length, &auth.credentials); break; case DCERPC_AUTH_LEVEL_CONNECT: /* for now we ignore possible signatures here */ status = NT_STATUS_OK; break; default: status = NT_STATUS_INVALID_LEVEL; break; } /* remove the indicated amount of padding */ if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) { return false; } pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length; return NT_STATUS_IS_OK(status); }
/** * @brief Check authentication for request/response packets * * @param auth The auth data for the connection * @param pkt The actual ncacn_packet * @param pkt_trailer The stub_and_verifier part of the packet * @param header_size The header size * @param raw_pkt The whole raw packet data blob * @param pad_len [out] The padding length used in the packet * * @return A NTSTATUS error code */ NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth, struct ncacn_packet *pkt, DATA_BLOB *pkt_trailer, size_t header_size, DATA_BLOB *raw_pkt, size_t *pad_len) { struct schannel_state *schannel_auth; struct auth_ntlmssp_state *ntlmssp_ctx; struct spnego_context *spnego_ctx; struct gse_context *gse_ctx; NTSTATUS status; struct dcerpc_auth auth_info; uint32_t auth_length; DATA_BLOB full_pkt; DATA_BLOB data; switch (auth->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: DEBUG(10, ("Requested Privacy.\n")); break; case DCERPC_AUTH_LEVEL_INTEGRITY: DEBUG(10, ("Requested Integrity.\n")); break; case DCERPC_AUTH_LEVEL_CONNECT: if (pkt->auth_length != 0) { break; } *pad_len = 0; return NT_STATUS_OK; case DCERPC_AUTH_LEVEL_NONE: if (pkt->auth_length != 0) { DEBUG(3, ("Got non-zero auth len on non " "authenticated connection!\n")); return NT_STATUS_INVALID_PARAMETER; } *pad_len = 0; return NT_STATUS_OK; default: DEBUG(3, ("Unimplemented Auth Level %d", auth->auth_level)); return NT_STATUS_INVALID_PARAMETER; } /* Paranioa checks for auth_length. */ if (pkt->auth_length > pkt->frag_length) { return NT_STATUS_INFO_LENGTH_MISMATCH; } if (((unsigned int)pkt->auth_length + DCERPC_AUTH_TRAILER_LENGTH < (unsigned int)pkt->auth_length) || ((unsigned int)pkt->auth_length + DCERPC_AUTH_TRAILER_LENGTH < DCERPC_AUTH_TRAILER_LENGTH)) { /* Integer wrap attempt. */ return NT_STATUS_INFO_LENGTH_MISMATCH; } status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer, &auth_info, &auth_length, false); if (!NT_STATUS_IS_OK(status)) { return status; } data = data_blob_const(raw_pkt->data + header_size, pkt_trailer->length - auth_length); full_pkt = data_blob_const(raw_pkt->data, raw_pkt->length - auth_info.credentials.length); switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: return NT_STATUS_OK; case DCERPC_AUTH_TYPE_SPNEGO: spnego_ctx = talloc_get_type_abort(auth->auth_ctx, struct spnego_context); status = get_spnego_auth_footer(pkt, spnego_ctx, auth->auth_level, &data, &full_pkt, &auth_info.credentials); if (!NT_STATUS_IS_OK(status)) { return status; } break; case DCERPC_AUTH_TYPE_NTLMSSP: DEBUG(10, ("NTLMSSP auth\n")); ntlmssp_ctx = talloc_get_type_abort(auth->auth_ctx, struct auth_ntlmssp_state); status = get_ntlmssp_auth_footer(ntlmssp_ctx, auth->auth_level, &data, &full_pkt, &auth_info.credentials); if (!NT_STATUS_IS_OK(status)) { return status; } break; case DCERPC_AUTH_TYPE_SCHANNEL: DEBUG(10, ("SCHANNEL auth\n")); schannel_auth = talloc_get_type_abort(auth->auth_ctx, struct schannel_state); status = get_schannel_auth_footer(pkt, schannel_auth, auth->auth_level, &data, &full_pkt, &auth_info.credentials); if (!NT_STATUS_IS_OK(status)) { return status; } break; case DCERPC_AUTH_TYPE_KRB5: DEBUG(10, ("KRB5 auth\n")); gse_ctx = talloc_get_type_abort(auth->auth_ctx, struct gse_context); status = get_gssapi_auth_footer(pkt, gse_ctx, auth->auth_level, &data, &full_pkt, &auth_info.credentials); if (!NT_STATUS_IS_OK(status)) { return status; } break; default: DEBUG(0, ("process_request_pdu: " "unknown auth type %u set.\n", (unsigned int)auth->auth_type)); return NT_STATUS_INVALID_PARAMETER; } /* TODO: remove later * this is still needed because in the server code the * pkt_trailer actually has a copy of the raw data, and they * are still both used in later calls */ if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { memcpy(pkt_trailer->data, data.data, data.length); } *pad_len = auth_info.auth_pad_length; data_blob_free(&auth_info.credentials); return NT_STATUS_OK; }
/** * @brief Check authentication for request/response packets * * @param auth The auth data for the connection * @param pkt The actual ncacn_packet * @param pkt_trailer The stub_and_verifier part of the packet * @param header_size The header size * @param raw_pkt The whole raw packet data blob * @param pad_len [out] The padding length used in the packet * * @return A NTSTATUS error code */ NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth, struct ncacn_packet *pkt, DATA_BLOB *pkt_trailer, size_t header_size, DATA_BLOB *raw_pkt, size_t *pad_len) { struct gensec_security *gensec_security; NTSTATUS status; struct dcerpc_auth auth_info; uint32_t auth_length; DATA_BLOB full_pkt; DATA_BLOB data; switch (auth->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: DEBUG(10, ("Requested Privacy.\n")); break; case DCERPC_AUTH_LEVEL_INTEGRITY: DEBUG(10, ("Requested Integrity.\n")); break; case DCERPC_AUTH_LEVEL_CONNECT: if (pkt->auth_length != 0) { break; } *pad_len = 0; return NT_STATUS_OK; case DCERPC_AUTH_LEVEL_NONE: if (pkt->auth_length != 0) { DEBUG(3, ("Got non-zero auth len on non " "authenticated connection!\n")); return NT_STATUS_INVALID_PARAMETER; } *pad_len = 0; return NT_STATUS_OK; default: DEBUG(3, ("Unimplemented Auth Level %d", auth->auth_level)); return NT_STATUS_INVALID_PARAMETER; } /* Paranioa checks for auth_length. */ if (pkt->auth_length > pkt->frag_length) { return NT_STATUS_INFO_LENGTH_MISMATCH; } if (((unsigned int)pkt->auth_length + DCERPC_AUTH_TRAILER_LENGTH < (unsigned int)pkt->auth_length) || ((unsigned int)pkt->auth_length + DCERPC_AUTH_TRAILER_LENGTH < DCERPC_AUTH_TRAILER_LENGTH)) { /* Integer wrap attempt. */ return NT_STATUS_INFO_LENGTH_MISMATCH; } status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer, &auth_info, &auth_length, false); if (!NT_STATUS_IS_OK(status)) { return status; } data = data_blob_const(raw_pkt->data + header_size, pkt_trailer->length - auth_length); full_pkt = data_blob_const(raw_pkt->data, raw_pkt->length - auth_info.credentials.length); switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: return NT_STATUS_OK; default: DEBUG(10, ("GENSEC auth\n")); gensec_security = auth->auth_ctx; status = get_generic_auth_footer(gensec_security, auth->auth_level, &data, &full_pkt, &auth_info.credentials); if (!NT_STATUS_IS_OK(status)) { return status; } break; } /* TODO: remove later * this is still needed because in the server code the * pkt_trailer actually has a copy of the raw data, and they * are still both used in later calls */ if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { memcpy(pkt_trailer->data, data.data, data.length); } *pad_len = auth_info.auth_pad_length; data_blob_free(&auth_info.credentials); return NT_STATUS_OK; }