X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Frpc_server%2Fdcerpc_server.c;h=b8df7924a510b02f2fe9b23251c1f3e8b623f542;hb=818e09fff2ffdac0518fdbc0dcf7a3c3e37d2b48;hp=dbdc67ffd15cdec54131353ee559dd8d9428326c;hpb=fd90d270c7e97a639f42a96b674a674d1b51aa0d;p=samba.git diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index dbdc67ffd15..b8df7924a51 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -269,7 +269,7 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx, /* check if this endpoint exists */ if ((ep=find_endpoint(dce_ctx, binding))==NULL) { - ep = talloc(dce_ctx, struct dcesrv_endpoint); + ep = talloc_zero(dce_ctx, struct dcesrv_endpoint); if (!ep) { return NT_STATUS_NO_MEMORY; } @@ -278,7 +278,7 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx, add_ep = true; /* add mgmt interface */ - ifl = talloc(ep, struct dcesrv_if_list); + ifl = talloc_zero(ep, struct dcesrv_if_list); if (!ifl) { return NT_STATUS_NO_MEMORY; } @@ -297,7 +297,7 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx, } /* talloc a new interface list element */ - ifl = talloc(ep, struct dcesrv_if_list); + ifl = talloc_zero(ep, struct dcesrv_if_list); if (!ifl) { return NT_STATUS_NO_MEMORY; } @@ -408,6 +408,9 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, p->msg_ctx = msg_ctx; p->server_id = server_id; p->state_flags = state_flags; + p->allow_bind = true; + p->max_recv_frag = 5840; + p->max_xmit_frag = 5840; *_p = p; return NT_STATUS_OK; @@ -439,17 +442,34 @@ static void dcesrv_call_set_list(struct dcesrv_call_state *call, case DCESRV_LIST_NONE: break; case DCESRV_LIST_CALL_LIST: - DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *); + DLIST_ADD_END(call->conn->call_list, call); break; case DCESRV_LIST_FRAGMENTED_CALL_LIST: - DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *); + DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call); break; case DCESRV_LIST_PENDING_CALL_LIST: - DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *); + DLIST_ADD_END(call->conn->pending_call_list, call); break; } } +static void dcesrv_call_disconnect_after(struct dcesrv_call_state *call, + const char *reason) +{ + if (call->conn->terminate != NULL) { + return; + } + + call->conn->allow_bind = false; + call->conn->allow_alter = false; + call->conn->allow_auth3 = false; + call->conn->allow_request = false; + + call->terminate_reason = talloc_strdup(call, reason); + if (call->terminate_reason == NULL) { + call->terminate_reason = __location__; + } +} /* return a dcerpc bind_nak @@ -460,6 +480,13 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason) struct dcerpc_bind_nak_version version; struct data_blob_list_item *rep; NTSTATUS status; + static const uint8_t _pad[3] = { 0, }; + + /* + * We add the call to the pending_call_list + * in order to defer the termination. + */ + dcesrv_call_disconnect_after(call, "dcesrv_bind_nak"); /* setup a bind_nak */ dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)); @@ -472,9 +499,9 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason) version.rpc_vers_minor = 0; pkt.u.bind_nak.num_versions = 1; pkt.u.bind_nak.versions = &version; - pkt.u.bind_nak._pad = data_blob_null; + pkt.u.bind_nak._pad = data_blob_const(_pad, sizeof(_pad)); - rep = talloc(call, struct data_blob_list_item); + rep = talloc_zero(call, struct data_blob_list_item); if (!rep) { return NT_STATUS_NO_MEMORY; } @@ -486,7 +513,7 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason) dcerpc_set_frag_length(&rep->blob, rep->blob.length); - DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); + DLIST_ADD_END(call->replies, rep); dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST); if (call->conn->call_list && call->conn->call_list->replies) { @@ -498,6 +525,19 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason) return NT_STATUS_OK; } +static NTSTATUS dcesrv_fault_disconnect(struct dcesrv_call_state *call, + uint32_t fault_code) +{ + /* + * We add the call to the pending_call_list + * in order to defer the termination. + */ + dcesrv_call_disconnect_after(call, "dcesrv_fault_disconnect"); + + return dcesrv_fault_with_flags(call, fault_code, + DCERPC_PFC_FLAG_DID_NOT_EXECUTE); +} + static int dcesrv_connection_context_destructor(struct dcesrv_connection_context *c) { DLIST_REMOVE(c->conn->contexts, c); @@ -510,6 +550,115 @@ static int dcesrv_connection_context_destructor(struct dcesrv_connection_context return 0; } +static void dcesrv_prepare_context_auth(struct dcesrv_call_state *dce_call) +{ + struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; + const struct dcesrv_endpoint *endpoint = dce_call->conn->endpoint; + enum dcerpc_transport_t transport = + dcerpc_binding_get_transport(endpoint->ep_description); + struct dcesrv_connection_context *context = dce_call->context; + const struct dcesrv_interface *iface = context->iface; + + context->min_auth_level = DCERPC_AUTH_LEVEL_NONE; + + if (transport == NCALRPC) { + context->allow_connect = true; + return; + } + + /* + * allow overwrite per interface + * allow dcerpc auth level connect: + */ + context->allow_connect = lpcfg_allow_dcerpc_auth_level_connect(lp_ctx); + context->allow_connect = lpcfg_parm_bool(lp_ctx, NULL, + "allow dcerpc auth level connect", + iface->name, + context->allow_connect); +} + +NTSTATUS dcesrv_interface_bind_require_integrity(struct dcesrv_call_state *dce_call, + const struct dcesrv_interface *iface) +{ + if (dce_call->context == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + dce_call->context->min_auth_level = DCERPC_AUTH_LEVEL_INTEGRITY; + return NT_STATUS_OK; +} + +NTSTATUS dcesrv_interface_bind_require_privacy(struct dcesrv_call_state *dce_call, + const struct dcesrv_interface *iface) +{ + if (dce_call->context == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + dce_call->context->min_auth_level = DCERPC_AUTH_LEVEL_PRIVACY; + return NT_STATUS_OK; +} + +_PUBLIC_ NTSTATUS dcesrv_interface_bind_reject_connect(struct dcesrv_call_state *dce_call, + const struct dcesrv_interface *iface) +{ + struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; + const struct dcesrv_endpoint *endpoint = dce_call->conn->endpoint; + enum dcerpc_transport_t transport = + dcerpc_binding_get_transport(endpoint->ep_description); + struct dcesrv_connection_context *context = dce_call->context; + + if (context == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + if (transport == NCALRPC) { + context->allow_connect = true; + return NT_STATUS_OK; + } + + /* + * allow overwrite per interface + * allow dcerpc auth level connect: + */ + context->allow_connect = false; + context->allow_connect = lpcfg_parm_bool(lp_ctx, NULL, + "allow dcerpc auth level connect", + iface->name, + context->allow_connect); + return NT_STATUS_OK; +} + +_PUBLIC_ NTSTATUS dcesrv_interface_bind_allow_connect(struct dcesrv_call_state *dce_call, + const struct dcesrv_interface *iface) +{ + struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; + const struct dcesrv_endpoint *endpoint = dce_call->conn->endpoint; + enum dcerpc_transport_t transport = + dcerpc_binding_get_transport(endpoint->ep_description); + struct dcesrv_connection_context *context = dce_call->context; + + if (context == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + if (transport == NCALRPC) { + context->allow_connect = true; + return NT_STATUS_OK; + } + + /* + * allow overwrite per interface + * allow dcerpc auth level connect: + */ + context->allow_connect = true; + context->allow_connect = lpcfg_parm_bool(lp_ctx, NULL, + "allow dcerpc auth level connect", + iface->name, + context->allow_connect); + return NT_STATUS_OK; +} + /* handle a bind request */ @@ -524,6 +673,43 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) uint32_t context_id; const struct dcesrv_interface *iface; uint32_t extra_flags = 0; + uint16_t max_req = 0; + uint16_t max_rep = 0; + const char *ep_prefix = ""; + const char *endpoint = NULL; + + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_BIND, + call->pkt.u.bind.auth_info.length, + 0, /* required flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_bind_nak(call, + DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED); + } + + /* max_recv_frag and max_xmit_frag result always in the same value! */ + max_req = MIN(call->pkt.u.bind.max_xmit_frag, + call->pkt.u.bind.max_recv_frag); + /* + * The values are between 2048 and 5840 tested against Windows 2012R2 + * via ncacn_ip_tcp on port 135. + */ + max_req = MAX(2048, max_req); + max_rep = MIN(max_req, call->conn->max_recv_frag); + /* They are truncated to an 8 byte boundary. */ + max_rep &= 0xFFF8; + + /* max_recv_frag and max_xmit_frag result always in the same value! */ + call->conn->max_recv_frag = max_rep; + call->conn->max_xmit_frag = max_rep; /* if provided, check the assoc_group is valid @@ -540,12 +726,6 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) } context_id = call->pkt.u.bind.ctx_list[0].context_id; - - /* you can't bind twice on one context */ - if (dcesrv_find_context(call->conn, context_id) != NULL) { - return dcesrv_bind_nak(call, 0); - } - if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version; uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid; @@ -573,7 +753,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) if (iface) { /* add this context to the list of available context_ids */ - struct dcesrv_connection_context *context = talloc(call->conn, + struct dcesrv_connection_context *context = talloc_zero(call->conn, struct dcesrv_connection_context); if (context == NULL) { return dcesrv_bind_nak(call, 0); @@ -597,6 +777,8 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) call->context = context; talloc_set_destructor(context, dcesrv_connection_context_destructor); + dcesrv_prepare_context_auth(call); + status = iface->bind(call, iface, if_version); if (!NT_STATUS_IS_OK(status)) { char *uuid_str = GUID_string(call, &uuid); @@ -611,10 +793,6 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) } } - if (call->conn->cli_max_recv_frag == 0) { - call->conn->cli_max_recv_frag = MIN(0x2000, call->pkt.u.bind.max_recv_frag); - } - if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_CONC_MPX) && (call->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED)) { call->context->conn->state_flags |= DCESRV_CALL_STATE_FLAG_MULTIPLEXED; @@ -638,8 +816,8 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) pkt.call_id = call->pkt.call_id; pkt.ptype = DCERPC_PKT_BIND_ACK; pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags; - pkt.u.bind_ack.max_xmit_frag = call->conn->cli_max_recv_frag; - pkt.u.bind_ack.max_recv_frag = 0x2000; + pkt.u.bind_ack.max_xmit_frag = call->conn->max_xmit_frag; + pkt.u.bind_ack.max_recv_frag = call->conn->max_recv_frag; /* make it possible for iface->bind() to specify the assoc_group_id @@ -654,13 +832,34 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) } if (iface) { - /* FIXME: Use pipe name as specified by endpoint instead of interface name */ - pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name); - } else { - pkt.u.bind_ack.secondary_address = ""; + endpoint = dcerpc_binding_get_string_option( + call->conn->endpoint->ep_description, + "endpoint"); + } + + if (endpoint == NULL) { + endpoint = ""; + } + + if (strncasecmp(endpoint, "\\pipe\\", 6) == 0) { + /* + * TODO: check if this is really needed + * + * Or if we should fix this in our idl files. + */ + ep_prefix = "\\PIPE\\"; + endpoint += 6; + } + + pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "%s%s", + ep_prefix, + endpoint); + if (pkt.u.bind_ack.secondary_address == NULL) { + TALLOC_FREE(call->context); + return NT_STATUS_NO_MEMORY; } pkt.u.bind_ack.num_results = 1; - pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx); + pkt.u.bind_ack.ctx_list = talloc_zero(call, struct dcerpc_ack_ctx); if (!pkt.u.bind_ack.ctx_list) { talloc_free(call->context); call->context = NULL; @@ -678,7 +877,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return dcesrv_bind_nak(call, 0); } - rep = talloc(call, struct data_blob_list_item); + rep = talloc_zero(call, struct data_blob_list_item); if (!rep) { talloc_free(call->context); call->context = NULL; @@ -686,7 +885,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) } status = ncacn_push_auth(&rep->blob, call, &pkt, - call->conn->auth_state.auth_info); + call->out_auth_info); if (!NT_STATUS_IS_OK(status)) { talloc_free(call->context); call->context = NULL; @@ -695,7 +894,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) dcerpc_set_frag_length(&rep->blob, rep->blob.length); - DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); + DLIST_ADD_END(call->replies, rep); dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST); if (call->conn->call_list && call->conn->call_list->replies) { @@ -713,9 +912,35 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) */ static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call) { + NTSTATUS status; + + if (!call->conn->allow_auth3) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + + if (call->conn->auth_state.auth_finished) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_AUTH3, + call->pkt.u.auth3.auth_info.length, + 0, /* required flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + /* handle the auth3 in the auth code */ if (!dcesrv_auth_auth3(call)) { - return dcesrv_fault(call, DCERPC_FAULT_OTHER); + call->conn->auth_state.auth_invalid = true; } talloc_free(call); @@ -757,7 +982,7 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_ } /* add this context to the list of available context_ids */ - context = talloc(call->conn, struct dcesrv_connection_context); + context = talloc_zero(call->conn, struct dcesrv_connection_context); if (context == NULL) { return NT_STATUS_NO_MEMORY; } @@ -781,6 +1006,8 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_ call->context = context; talloc_set_destructor(context, dcesrv_connection_context_destructor); + dcesrv_prepare_context_auth(call); + status = iface->bind(call, iface, if_version); if (!NT_STATUS_IS_OK(status)) { /* we don't want to trigger the iface->unbind() hook */ @@ -793,76 +1020,41 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_ return NT_STATUS_OK; } - -/* - handle a alter context request -*/ -static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) +/* setup and send an alter_resp */ +static NTSTATUS dcesrv_alter_resp(struct dcesrv_call_state *call, + uint32_t result, + uint32_t reason) { struct ncacn_packet pkt; - struct data_blob_list_item *rep; - NTSTATUS status; - uint32_t result=0, reason=0; - uint32_t context_id; uint32_t extra_flags = 0; + struct data_blob_list_item *rep = NULL; + NTSTATUS status; - /* handle any authentication that is being requested */ - if (!dcesrv_auth_alter(call)) { - /* TODO: work out the right reject code */ - result = DCERPC_BIND_PROVIDER_REJECT; - reason = DCERPC_BIND_REASON_ASYNTAX; - } - - context_id = call->pkt.u.alter.ctx_list[0].context_id; - - /* see if they are asking for a new interface */ - if (result == 0) { - call->context = dcesrv_find_context(call->conn, context_id); - if (!call->context) { - status = dcesrv_alter_new_context(call, context_id); - if (!NT_STATUS_IS_OK(status)) { - result = DCERPC_BIND_PROVIDER_REJECT; - reason = DCERPC_BIND_REASON_ASYNTAX; - } - } - } - - if (result == 0 && - call->pkt.u.alter.assoc_group_id != 0 && - lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) && - call->pkt.u.alter.assoc_group_id != call->context->assoc_group->id) { - DEBUG(0,(__location__ ": Failed attempt to use new assoc_group in alter context (0x%08x 0x%08x)\n", - call->context->assoc_group->id, call->pkt.u.alter.assoc_group_id)); - /* TODO: can they ask for a new association group? */ - result = DCERPC_BIND_PROVIDER_REJECT; - reason = DCERPC_BIND_REASON_ASYNTAX; - } - - if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) { - if (call->context->conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED) { - extra_flags |= DCERPC_PFC_FLAG_CONC_MPX; - } - } - - if (call->state_flags & DCESRV_CALL_STATE_FLAG_PROCESS_PENDING_CALL) { - call->context->conn->state_flags |= DCESRV_CALL_STATE_FLAG_PROCESS_PENDING_CALL; - } - - /* setup a alter_resp */ dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)); pkt.auth_length = 0; pkt.call_id = call->pkt.call_id; pkt.ptype = DCERPC_PKT_ALTER_RESP; + if (result == 0) { + if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_CONC_MPX) && + call->context->conn->state_flags & + DCESRV_CALL_STATE_FLAG_MULTIPLEXED) { + extra_flags |= DCERPC_PFC_FLAG_CONC_MPX; + } + if (call->state_flags & DCESRV_CALL_STATE_FLAG_PROCESS_PENDING_CALL) { + call->context->conn->state_flags |= + DCESRV_CALL_STATE_FLAG_PROCESS_PENDING_CALL; + } + } pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags; - pkt.u.alter_resp.max_xmit_frag = 0x2000; - pkt.u.alter_resp.max_recv_frag = 0x2000; + pkt.u.alter_resp.max_xmit_frag = call->conn->max_xmit_frag; + pkt.u.alter_resp.max_recv_frag = call->conn->max_recv_frag; if (result == 0) { pkt.u.alter_resp.assoc_group_id = call->context->assoc_group->id; } else { pkt.u.alter_resp.assoc_group_id = 0; } pkt.u.alter_resp.num_results = 1; - pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1); + pkt.u.alter_resp.ctx_list = talloc_zero(call, struct dcerpc_ack_ctx); if (!pkt.u.alter_resp.ctx_list) { return NT_STATUS_NO_MEMORY; } @@ -874,28 +1066,22 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) status = dcesrv_auth_alter_ack(call, &pkt); if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) - || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE) - || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER) - || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { - return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); - } - return dcesrv_fault(call, 0); + return dcesrv_fault_disconnect(call, DCERPC_FAULT_SEC_PKG_ERROR); } - rep = talloc(call, struct data_blob_list_item); + rep = talloc_zero(call, struct data_blob_list_item); if (!rep) { return NT_STATUS_NO_MEMORY; } - status = ncacn_push_auth(&rep->blob, call, &pkt, call->conn->auth_state.auth_info); + status = ncacn_push_auth(&rep->blob, call, &pkt, call->out_auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } dcerpc_set_frag_length(&rep->blob, rep->blob.length); - DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); + DLIST_ADD_END(call->replies, rep); dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST); if (call->conn->call_list && call->conn->call_list->replies) { @@ -907,6 +1093,110 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) return NT_STATUS_OK; } +/* + handle a alter context request +*/ +static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) +{ + NTSTATUS status; + const struct dcerpc_ctx_list *ctx = NULL; + bool auth_ok = false; + + if (!call->conn->allow_alter) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_ALTER, + call->pkt.u.alter.auth_info.length, + 0, /* required flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + + auth_ok = dcesrv_auth_alter(call); + if (!auth_ok) { + if (call->in_auth_info.auth_type == DCERPC_AUTH_TYPE_NONE) { + return dcesrv_fault_disconnect(call, + DCERPC_FAULT_ACCESS_DENIED); + } + } + + if (call->pkt.u.alter.num_contexts < 1) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + ctx = &call->pkt.u.alter.ctx_list[0]; + if (ctx->num_transfer_syntaxes < 1) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + + /* see if they are asking for a new interface */ + call->context = dcesrv_find_context(call->conn, ctx->context_id); + if (!call->context) { + status = dcesrv_alter_new_context(call, ctx->context_id); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_alter_resp(call, + DCERPC_BIND_PROVIDER_REJECT, + DCERPC_BIND_REASON_ASYNTAX); + } + } else { + bool ok; + + ok = ndr_syntax_id_equal(&ctx->abstract_syntax, + &call->context->iface->syntax_id); + if (!ok) { + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); + } + + if (ctx->num_transfer_syntaxes != 1) { + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); + } + + ok = ndr_syntax_id_equal(&ctx->transfer_syntaxes[0], + &ndr_transfer_syntax_ndr); + if (!ok) { + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); + } + } + + if (call->pkt.u.alter.assoc_group_id != 0 && + lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) && + call->pkt.u.alter.assoc_group_id != call->context->assoc_group->id) { + DEBUG(0,(__location__ ": Failed attempt to use new assoc_group in alter context (0x%08x 0x%08x)\n", + call->context->assoc_group->id, call->pkt.u.alter.assoc_group_id)); + /* TODO: can they ask for a new association group? */ + return dcesrv_alter_resp(call, + DCERPC_BIND_PROVIDER_REJECT, + DCERPC_BIND_REASON_ASYNTAX); + } + + /* handle any authentication that is being requested */ + if (!auth_ok) { + if (call->in_auth_info.auth_type != + call->conn->auth_state.auth_type) + { + return dcesrv_fault_disconnect(call, + DCERPC_FAULT_SEC_PKG_ERROR); + } + return dcesrv_fault_disconnect(call, DCERPC_FAULT_ACCESS_DENIED); + } + + return dcesrv_alter_resp(call, + DCERPC_BIND_ACK_RESULT_ACCEPTANCE, + DCERPC_BIND_ACK_REASON_NOT_SPECIFIED); +} + /* possibly save the call for inspection with ndrdump */ @@ -972,10 +1262,17 @@ done: */ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) { + const struct dcesrv_endpoint *endpoint = call->conn->endpoint; + enum dcerpc_transport_t transport = + dcerpc_binding_get_transport(endpoint->ep_description); struct ndr_pull *pull; NTSTATUS status; struct dcesrv_connection_context *context; + if (!call->conn->allow_request) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + /* if authenticated, and the mech we use can't do async replies, don't use them... */ if (call->conn->auth_state.gensec_security && !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) { @@ -987,6 +1284,49 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) return dcesrv_fault(call, DCERPC_FAULT_UNK_IF); } + switch (call->conn->auth_state.auth_level) { + case DCERPC_AUTH_LEVEL_NONE: + case DCERPC_AUTH_LEVEL_INTEGRITY: + case DCERPC_AUTH_LEVEL_PRIVACY: + break; + default: + if (!context->allow_connect) { + char *addr; + + addr = tsocket_address_string(call->conn->remote_address, + call); + + DEBUG(2, ("%s: restrict auth_level_connect access " + "to [%s] with auth[type=0x%x,level=0x%x] " + "on [%s] from [%s]\n", + __func__, context->iface->name, + call->conn->auth_state.auth_type, + call->conn->auth_state.auth_level, + derpc_transport_string_by_transport(transport), + addr)); + return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); + } + break; + } + + if (call->conn->auth_state.auth_level < context->min_auth_level) { + char *addr; + + addr = tsocket_address_string(call->conn->remote_address, call); + + DEBUG(2, ("%s: restrict access by min_auth_level[0x%x] " + "to [%s] with auth[type=0x%x,level=0x%x] " + "on [%s] from [%s]\n", + __func__, + context->min_auth_level, + context->iface->name, + call->conn->auth_state.auth_type, + call->conn->auth_state.auth_level, + derpc_transport_string_by_transport(transport), + addr)); + return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); + } + pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call); NT_STATUS_HAVE_NO_MEMORY(pull); @@ -1073,9 +1413,9 @@ _PUBLIC_ const struct tsocket_address *dcesrv_connection_get_remote_address(stru /* process some input to a dcerpc endpoint server. */ -NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, - struct ncacn_packet *pkt, - DATA_BLOB blob) +static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, + struct ncacn_packet *pkt, + DATA_BLOB blob) { NTSTATUS status; struct dcesrv_call_state *call; @@ -1099,11 +1439,49 @@ NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, talloc_set_destructor(call, dcesrv_call_dequeue); + if (call->conn->allow_bind) { + /* + * Only one bind is possible per connection + */ + call->conn->allow_bind = false; + return dcesrv_bind(call); + } + /* we have to check the signing here, before combining the pdus */ - if (call->pkt.ptype == DCERPC_PKT_REQUEST && - !dcesrv_auth_request(call, &blob)) { - return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); + if (call->pkt.ptype == DCERPC_PKT_REQUEST) { + if (!call->conn->allow_request) { + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); + } + + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_REQUEST, + call->pkt.u.request.stub_and_verifier.length, + 0, /* required_flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_PENDING_CANCEL | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); + } + + if (!dcesrv_auth_request(call, &blob)) { + /* + * We don't use dcesrv_fault_disconnect() + * here, because we don't want to set + * DCERPC_PFC_FLAG_DID_NOT_EXECUTE + */ + dcesrv_call_disconnect_after(call, + "dcesrv_auth_request - failed"); + return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); + } } /* see if this is a continued packet */ @@ -1112,33 +1490,34 @@ NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, struct dcesrv_call_state *call2 = call; uint32_t alloc_size; - /* we only allow fragmented requests, no other packet types */ - if (call->pkt.ptype != DCERPC_PKT_REQUEST) { - return dcesrv_fault(call2, DCERPC_FAULT_OTHER); - } - /* this is a continuation of an existing call - find the call then tack it on the end */ call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id); if (!call) { - return dcesrv_fault(call2, DCERPC_FAULT_OTHER); + return dcesrv_fault_disconnect(call2, + DCERPC_NCA_S_PROTO_ERROR); } if (call->pkt.ptype != call2->pkt.ptype) { /* trying to play silly buggers are we? */ - return dcesrv_fault(call2, DCERPC_NCA_S_PROTO_ERROR); + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); } if (memcmp(call->pkt.drep, call2->pkt.drep, sizeof(pkt->drep)) != 0) { - return dcesrv_fault(call2, DCERPC_NCA_S_PROTO_ERROR); + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); } if (call->pkt.call_id != call2->pkt.call_id) { - return dcesrv_fault(call2, DCERPC_NCA_S_PROTO_ERROR); + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); } if (call->pkt.u.request.context_id != call2->pkt.u.request.context_id) { - return dcesrv_fault(call2, DCERPC_NCA_S_PROTO_ERROR); + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); } if (call->pkt.u.request.opnum != call2->pkt.u.request.opnum) { - return dcesrv_fault(call2, DCERPC_NCA_S_PROTO_ERROR); + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); } alloc_size = call->pkt.u.request.stub_and_verifier.length + @@ -1179,7 +1558,8 @@ NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, switch (call->pkt.ptype) { case DCERPC_PKT_BIND: - status = dcesrv_bind(call); + status = dcesrv_bind_nak(call, + DCERPC_BIND_NAK_REASON_NOT_SPECIFIED); break; case DCERPC_PKT_AUTH3: status = dcesrv_auth3(call); @@ -1191,7 +1571,7 @@ NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, status = dcesrv_request(call); break; default: - status = NT_STATUS_INVALID_PARAMETER; + status = dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); break; } @@ -1218,9 +1598,17 @@ _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, return NT_STATUS_INTERNAL_ERROR; } - dce_ctx = talloc(mem_ctx, struct dcesrv_context); + dce_ctx = talloc_zero(mem_ctx, struct dcesrv_context); NT_STATUS_HAVE_NO_MEMORY(dce_ctx); + + if (uid_wrapper_enabled()) { + setenv("UID_WRAPPER_MYUID", "1", 1); + } dce_ctx->initial_euid = geteuid(); + if (uid_wrapper_enabled()) { + unsetenv("UID_WRAPPER_MYUID"); + } + dce_ctx->endpoint_list = NULL; dce_ctx->lp_ctx = lp_ctx; dce_ctx->assoc_groups_idr = idr_init(dce_ctx); @@ -1357,6 +1745,11 @@ static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, cons srv_conn = talloc_get_type(dce_conn->transport.private_data, struct stream_connection); + dce_conn->allow_bind = false; + dce_conn->allow_auth3 = false; + dce_conn->allow_alter = false; + dce_conn->allow_request = false; + if (dce_conn->pending_call_list == NULL) { char *full_reason = talloc_asprintf(dce_conn, "dcesrv: %s", reason); @@ -1375,7 +1768,7 @@ static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, cons if (dce_conn->terminate == NULL) { dce_conn->terminate = "dcesrv: defered terminating connection - no memory"; } - DLIST_ADD_END(dce_ctx->broken_connections, dce_conn, NULL); + DLIST_ADD_END(dce_ctx->broken_connections, dce_conn); } static void dcesrv_cleanup_broken_connections(struct dcesrv_context *dce_ctx) @@ -1420,6 +1813,7 @@ struct dcesrv_sock_reply_state { }; static void dcesrv_sock_reply_done(struct tevent_req *subreq); +static void dcesrv_call_terminate_step1(struct tevent_req *subreq); static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn) { @@ -1435,7 +1829,7 @@ static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn) struct dcesrv_sock_reply_state *substate; struct tevent_req *subreq; - substate = talloc(call, struct dcesrv_sock_reply_state); + substate = talloc_zero(call, struct dcesrv_sock_reply_state); if (!substate) { dcesrv_terminate_connection(dce_conn, "no memory"); return; @@ -1446,7 +1840,7 @@ static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn) DLIST_REMOVE(call->replies, rep); - if (call->replies == NULL) { + if (call->replies == NULL && call->terminate_reason == NULL) { substate->call = call; } @@ -1466,6 +1860,20 @@ static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn) substate); } + if (call->terminate_reason != NULL) { + struct tevent_req *subreq; + + subreq = tevent_queue_wait_send(call, + dce_conn->event_ctx, + dce_conn->send_queue); + if (!subreq) { + dcesrv_terminate_connection(dce_conn, __location__); + return; + } + tevent_req_set_callback(subreq, dcesrv_call_terminate_step1, + call); + } + DLIST_REMOVE(call->conn->call_list, call); call->list = DCESRV_LIST_NONE; } @@ -1493,8 +1901,51 @@ static void dcesrv_sock_reply_done(struct tevent_req *subreq) } } +static void dcesrv_call_terminate_step2(struct tevent_req *subreq); + +static void dcesrv_call_terminate_step1(struct tevent_req *subreq) +{ + struct dcesrv_call_state *call = tevent_req_callback_data(subreq, + struct dcesrv_call_state); + bool ok; + struct timeval tv; + + /* make sure we stop send queue before removing subreq */ + tevent_queue_stop(call->conn->send_queue); + + ok = tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + if (!ok) { + dcesrv_terminate_connection(call->conn, __location__); + return; + } + + /* disconnect after 200 usecs */ + tv = timeval_current_ofs_usec(200); + subreq = tevent_wakeup_send(call, call->conn->event_ctx, tv); + if (subreq == NULL) { + dcesrv_terminate_connection(call->conn, __location__); + return; + } + tevent_req_set_callback(subreq, dcesrv_call_terminate_step2, + call); +} + +static void dcesrv_call_terminate_step2(struct tevent_req *subreq) +{ + struct dcesrv_call_state *call = tevent_req_callback_data(subreq, + struct dcesrv_call_state); + bool ok; + ok = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ok) { + dcesrv_terminate_connection(call->conn, __location__); + return; + } + dcesrv_terminate_connection(call->conn, call->terminate_reason); +} struct dcesrv_socket_context { const struct dcesrv_endpoint *endpoint; @@ -1710,7 +2161,7 @@ static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx, NTSTATUS status; const char *endpoint; - dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context); + dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context); NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock); /* remember the endpoint of this socket */ @@ -1766,7 +2217,7 @@ static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx, full_path = talloc_asprintf(dce_ctx, "%s/%s", lpcfg_ncalrpc_dir(lp_ctx), endpoint); - dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context); + dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context); NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock); /* remember the endpoint of this socket */ @@ -1800,7 +2251,7 @@ static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx, return NT_STATUS_INVALID_PARAMETER; } - dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context); + dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context); NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock); /* remember the endpoint of this socket */ @@ -1838,7 +2289,7 @@ static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct port = atoi(endpoint); } - dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context); + dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context); NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock); /* remember the endpoint of this socket */