/** * @brief Decodes a dcerpc_auth blob * * @param mem_ctx The memory context on which to allocate the packet * elements * @param blob The blob of data to decode * @param r An empty dcerpc_auth structure, must not be NULL * * @return a NTSTATUS error code */ NTSTATUS dcerpc_pull_dcerpc_auth(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, struct dcerpc_auth *r, bool bigendian) { enum ndr_err_code ndr_err; struct ndr_pull *ndr; ndr = ndr_pull_init_blob(blob, mem_ctx); if (!ndr) { return NT_STATUS_NO_MEMORY; } if (bigendian) { ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, r); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(ndr); return ndr_map_error2ntstatus(ndr_err); } talloc_free(ndr); if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(dcerpc_auth, r); } return NT_STATUS_OK; }
/* 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; }
/** * @brief Pull a dcerpc_auth structure, taking account of any auth * padding in the blob. For request/response packets we pass * the whole data blob, so auth_data_only must be set to false * as the blob contains data+pad+auth and no just pad+auth. * * @param pkt - The ncacn_packet strcuture * @param mem_ctx - The mem_ctx used to allocate dcerpc_auth elements * @param pkt_trailer - The packet trailer data, usually the trailing * auth_info blob, but in the request/response case * this is the stub_and_verifier blob. * @param auth - A preallocated dcerpc_auth *empty* structure * @param auth_length - The length of the auth trail, sum of auth header * lenght and pkt->auth_length * @param auth_data_only - Whether the pkt_trailer includes only the auth_blob * (+ padding) or also other data. * * @return - A NTSTATUS error code. */ NTSTATUS dcerpc_pull_auth_trailer(struct ncacn_packet *pkt, TALLOC_CTX *mem_ctx, DATA_BLOB *pkt_trailer, struct dcerpc_auth *auth, uint32_t *auth_length, bool auth_data_only) { struct ndr_pull *ndr; enum ndr_err_code ndr_err; uint32_t data_and_pad; data_and_pad = pkt_trailer->length - (DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length); /* paranoia check for pad size. This would be caught anyway by the ndr_pull_advance() a few lines down, but it scared Jeremy enough for him to call me, so we might as well check it now, just to prevent someone posting a bogus YouTube video in the future. */ if (data_and_pad > pkt_trailer->length) { return NT_STATUS_INFO_LENGTH_MISMATCH; } *auth_length = pkt_trailer->length - data_and_pad; ndr = ndr_pull_init_blob(pkt_trailer, 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_advance(ndr, data_and_pad); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(ndr); return ndr_map_error2ntstatus(ndr_err); } ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(ndr); return ndr_map_error2ntstatus(ndr_err); } if (auth_data_only && data_and_pad != auth->auth_pad_length) { DEBUG(1, (__location__ ": WARNING: pad length mismatch. " "Calculated %u got %u\n", (unsigned)data_and_pad, (unsigned)auth->auth_pad_length)); } DEBUG(6,(__location__ ": auth_pad_length %u\n", (unsigned)auth->auth_pad_length)); talloc_steal(mem_ctx, auth->credentials.data); talloc_free(ndr); return NT_STATUS_OK; }