static NTSTATUS get_generic_auth_footer(struct gensec_security *gensec_security, enum dcerpc_AuthLevel auth_level, DATA_BLOB *data, DATA_BLOB *full_pkt, DATA_BLOB *auth_token) { if (gensec_security == NULL) { return NT_STATUS_INVALID_PARAMETER; } switch (auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: /* Data portion is encrypted. */ return gensec_unseal_packet(gensec_security, data->data, data->length, full_pkt->data, full_pkt->length, auth_token); case DCERPC_AUTH_LEVEL_INTEGRITY: case DCERPC_AUTH_LEVEL_PACKET: /* Data is signed. */ return gensec_check_packet(gensec_security, data->data, data->length, full_pkt->data, full_pkt->length, auth_token); default: return NT_STATUS_INVALID_PARAMETER; } }
/* parse the authentication information on a dcerpc response packet */ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx, DATA_BLOB *raw_packet, struct ncacn_packet *pkt) { struct ndr_pull *ndr; NTSTATUS status; struct dcerpc_auth auth; DATA_BLOB auth_blob; enum ndr_err_code ndr_err; if (!c->security_state.auth_info || !c->security_state.generic_state) { return NT_STATUS_OK; } switch (c->security_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 NT_STATUS_OK; case DCERPC_AUTH_LEVEL_NONE: if (pkt->auth_length != 0) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } return NT_STATUS_OK; default: return NT_STATUS_INVALID_LEVEL; } auth_blob.length = 8 + pkt->auth_length; /* check for a valid length */ if (pkt->u.response.stub_and_verifier.length < auth_blob.length) { return NT_STATUS_INFO_LENGTH_MISMATCH; } auth_blob.data = pkt->u.response.stub_and_verifier.data + pkt->u.response.stub_and_verifier.length - auth_blob.length; pkt->u.response.stub_and_verifier.length -= auth_blob.length; /* pull the auth structure */ ndr = ndr_pull_init_flags(c, &auth_blob, mem_ctx); if (!ndr) { return NT_STATUS_NO_MEMORY; } if (!(pkt->drep[0] & DCERPC_DREP_LE)) { ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } status = NT_STATUS_OK; /* check signature or unseal the packet */ switch (c->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_unseal_packet(c->security_state.generic_state, mem_ctx, raw_packet->data + DCERPC_REQUEST_LENGTH, pkt->u.response.stub_and_verifier.length, raw_packet->data, raw_packet->length - auth.credentials.length, &auth.credentials); memcpy(pkt->u.response.stub_and_verifier.data, raw_packet->data + DCERPC_REQUEST_LENGTH, pkt->u.response.stub_and_verifier.length); break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = gensec_check_packet(c->security_state.generic_state, mem_ctx, pkt->u.response.stub_and_verifier.data, pkt->u.response.stub_and_verifier.length, raw_packet->data, raw_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 paddiing */ if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) { return NT_STATUS_INFO_LENGTH_MISMATCH; } pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length; return status; }
/* 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); }