📄 dcerpc.c
字号:
/* Unix SMB/CIFS implementation. raw dcerpc operations Copyright (C) Tim Potter 2003 Copyright (C) Andrew Tridgell 2003-2005 Copyright (C) Jelmer Vernooij 2004-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.*/#include "includes.h"#include "lib/util/dlinklist.h"#include "lib/events/events.h"#include "librpc/rpc/dcerpc.h"#include "librpc/rpc/dcerpc_proto.h"#include "librpc/gen_ndr/ndr_misc.h"#include "librpc/gen_ndr/ndr_dcerpc.h"#include "libcli/composite/composite.h"#include "auth/gensec/gensec.h"#include "param/param.h"_PUBLIC_ NTSTATUS dcerpc_init(void){ gensec_init(global_loadparm); return NT_STATUS_OK;}static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);static void dcerpc_ship_next_request(struct dcerpc_connection *c);/* destroy a dcerpc connection */static int dcerpc_connection_destructor(struct dcerpc_connection *conn){ if (conn->dead) { conn->free_skipped = true; return -1; } dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT); return 0;}/* initialise a dcerpc connection. the event context is optional*/static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, struct event_context *ev, struct smb_iconv_convenience *ic){ struct dcerpc_connection *c; c = talloc_zero(mem_ctx, struct dcerpc_connection); if (!c) { return NULL; } c->iconv_convenience = talloc_reference(c, ic); c->event_ctx = talloc_reference(c, ev); if (c->event_ctx == NULL) { talloc_free(c); return NULL; } c->call_id = 1; c->security_state.auth_info = NULL; c->security_state.session_key = dcerpc_generic_session_key; c->security_state.generic_state = NULL; c->binding_string = NULL; c->flags = 0; c->srv_max_xmit_frag = 0; c->srv_max_recv_frag = 0; c->pending = NULL; talloc_set_destructor(c, dcerpc_connection_destructor); return c;}/* initialise a dcerpc pipe. */_PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *ev, struct smb_iconv_convenience *ic){ struct dcerpc_pipe *p; p = talloc(mem_ctx, struct dcerpc_pipe); if (!p) { return NULL; } p->conn = dcerpc_connection_init(p, ev, ic); if (p->conn == NULL) { talloc_free(p); return NULL; } p->last_fault_code = 0; p->context_id = 0; p->request_timeout = DCERPC_REQUEST_TIMEOUT; p->binding = NULL; ZERO_STRUCT(p->syntax); ZERO_STRUCT(p->transfer_syntax); return p;}/* choose the next call id to use*/static uint32_t next_call_id(struct dcerpc_connection *c){ c->call_id++; if (c->call_id == 0) { c->call_id++; } return c->call_id;}/* we need to be able to get/set the fragment length without doing a full decode */void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v){ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) { SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v); } else { RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v); }}uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob){ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) { return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET); } else { return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET); }}void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v){ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) { SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v); } else { RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v); }}/** setup for a ndr pull, also setting up any flags from the binding string*/static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx){ struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx, c->iconv_convenience); if (ndr == NULL) return ndr; if (c->flags & DCERPC_DEBUG_PAD_CHECK) { ndr->flags |= LIBNDR_FLAG_PAD_CHECK; } if (c->flags & DCERPC_NDR_REF_ALLOC) { ndr->flags |= LIBNDR_FLAG_REF_ALLOC; } return ndr;}/* parse a data blob into a ncacn_packet structure. This handles both input and output packets*/static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct ncacn_packet *pkt){ struct ndr_pull *ndr; enum ndr_err_code ndr_err; ndr = ndr_pull_init_flags(c, blob, mem_ctx); if (!ndr) { return NT_STATUS_NO_MEMORY; } if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) { ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } return NT_STATUS_OK;}/* generate a CONNECT level verifier*/static NTSTATUS dcerpc_connect_verifier(TALLOC_CTX *mem_ctx, DATA_BLOB *blob){ *blob = data_blob_talloc(mem_ctx, NULL, 16); if (blob->data == NULL) { return NT_STATUS_NO_MEMORY; } SIVAL(blob->data, 0, 1); memset(blob->data+4, 0, 12); return NT_STATUS_OK;}/* check a CONNECT level verifier*/static NTSTATUS dcerpc_check_connect_verifier(DATA_BLOB *blob){ if (blob->length != 16 || IVAL(blob->data, 0) != 1) { return NT_STATUS_ACCESS_DENIED; } 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 (pkt->auth_length == 0 && c->security_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) { return NT_STATUS_OK; } 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: status = dcerpc_check_connect_verifier(&auth.credentials); break; case DCERPC_AUTH_LEVEL_NONE: 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;}/* push a dcerpc request packet into a blob, possibly signing it.*/static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct ncacn_packet *pkt){ NTSTATUS status; struct ndr_push *ndr; DATA_BLOB creds2; size_t payload_length; enum ndr_err_code ndr_err; /* non-signed packets are simpler */ if (!c->security_state.auth_info || !c->security_state.generic_state) { return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, c->security_state.auth_info); } ndr = ndr_push_init_ctx(mem_ctx, c->iconv_convenience); if (!ndr) { return NT_STATUS_NO_MEMORY; } if (c->flags & DCERPC_PUSH_BIGENDIAN) { ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) { ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT; } ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } status = NT_STATUS_OK; /* pad to 16 byte multiple in the payload portion of the packet. This matches what w2k3 does */ c->security_state.auth_info->auth_pad_length = (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15; ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length); payload_length = pkt->u.request.stub_and_verifier.length + c->security_state.auth_info->auth_pad_length; /* sign or seal the packet */ switch (c->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: case DCERPC_AUTH_LEVEL_INTEGRITY: /* We hope this length is accruate. If must be if the * GENSEC mech does AEAD signing of the packet * headers */ c->security_state.auth_info->credentials = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state, payload_length)); data_blob_clear(&c->security_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_CONNECT: status = dcerpc_connect_verifier(mem_ctx, &c->security_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_NONE: c->security_state.auth_info->credentials = data_blob(NULL, 0); break; default: status = NT_STATUS_INVALID_LEVEL; break; } if (!NT_STATUS_IS_OK(status)) { return status; } /* add the auth verifier */ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } status = NT_STATUS_OK; /* extract the whole packet as a blob */ *blob = ndr_push_blob(ndr); /* fill in the fragment length and auth_length, we can't fill in these earlier as we don't know the signature length (it could be variable length) */ dcerpc_set_frag_length(blob, blob->length); /* We hope this value is accruate. If must be if the GENSEC * mech does AEAD signing of the packet headers */ dcerpc_set_auth_length(blob, c->security_state.auth_info->credentials.length); /* sign or seal the packet */ switch (c->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_seal_packet(c->security_state.generic_state, mem_ctx, blob->data + DCERPC_REQUEST_LENGTH, payload_length, blob->data, blob->length - c->security_state.auth_info->credentials.length, &creds2); if (!NT_STATUS_IS_OK(status)) { return status; } blob->length -= c->security_state.auth_info->credentials.length; if (!data_blob_append(mem_ctx, blob, creds2.data, creds2.length)) { return NT_STATUS_NO_MEMORY; } dcerpc_set_auth_length(blob, creds2.length); if (c->security_state.auth_info->credentials.length == 0) { /* this is needed for krb5 only, to correct the total packet length */ dcerpc_set_frag_length(blob, dcerpc_get_frag_length(blob) +creds2.length); } break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = gensec_sign_packet(c->security_state.generic_state, mem_ctx, blob->data + DCERPC_REQUEST_LENGTH, payload_length, blob->data, blob->length - c->security_state.auth_info->credentials.length, &creds2); if (!NT_STATUS_IS_OK(status)) { return status; } blob->length -= c->security_state.auth_info->credentials.length; if (!data_blob_append(mem_ctx, blob, creds2.data, creds2.length)) { return NT_STATUS_NO_MEMORY; } dcerpc_set_auth_length(blob, creds2.length); if (c->security_state.auth_info->credentials.length == 0) { /* this is needed for krb5 only, to correct the total packet length */ dcerpc_set_frag_length(blob, dcerpc_get_frag_length(blob) +creds2.length); } break; case DCERPC_AUTH_LEVEL_CONNECT: break; case DCERPC_AUTH_LEVEL_NONE: c->security_state.auth_info->credentials = data_blob(NULL, 0); break; default: status = NT_STATUS_INVALID_LEVEL; break; } data_blob_free(&c->security_state.auth_info->credentials); return NT_STATUS_OK;}/* fill in the fixed values in a dcerpc header */static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt){ pkt->rpc_vers = 5; pkt->rpc_vers_minor = 0; if (c->flags & DCERPC_PUSH_BIGENDIAN) { pkt->drep[0] = 0; } else { pkt->drep[0] = DCERPC_DREP_LE; } pkt->drep[1] = 0; pkt->drep[2] = 0; pkt->drep[3] = 0;}/* map a bind nak reason to a NTSTATUS*/static NTSTATUS dcerpc_map_reason(uint16_t reason){ switch (reason) { case DCERPC_BIND_REASON_ASYNTAX: return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX; case DCERPC_BIND_REASON_INVALID_AUTH_TYPE: return NT_STATUS_INVALID_PARAMETER; } return NT_STATUS_UNSUCCESSFUL;}/* a bind or alter context has failed*/static void dcerpc_composite_fail(struct rpc_request *req){ struct composite_context *c = talloc_get_type(req->async.private_data, struct composite_context); composite_error(c, req->status);}/* remove requests from the pending or queued queues */static int dcerpc_req_dequeue(struct rpc_request *req){ switch (req->state) { case RPC_REQUEST_QUEUED: DLIST_REMOVE(req->p->conn->request_queue, req); break; case RPC_REQUEST_PENDING: DLIST_REMOVE(req->p->conn->pending, req); break; case RPC_REQUEST_DONE: break; } return 0;}/* mark the dcerpc connection dead. All outstanding requests get an error*/static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -