s4:librpc/rpc: allow a total reassembled response payload of 240 MBytes
[samba.git] / source4 / librpc / rpc / dcerpc.c
index 854a956f2574e12590cf8dcad8d98e26efca80ea..4225e1d1157df83df98f531bc80a65bb08f9621f 100644 (file)
@@ -144,7 +144,6 @@ static struct dcecli_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
        c->security_state.auth_type = DCERPC_AUTH_TYPE_NONE;
        c->security_state.auth_level = DCERPC_AUTH_LEVEL_NONE;
        c->security_state.auth_context_id = 0;
-       c->security_state.auth_info = NULL;
        c->security_state.session_key = dcerpc_generic_session_key;
        c->security_state.generic_state = NULL;
        c->flags = 0;
@@ -156,6 +155,7 @@ static struct dcecli_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
         */
        c->srv_max_xmit_frag = 5840;
        c->srv_max_recv_frag = 5840;
+       c->max_total_response_size = DCERPC_NCACN_RESPONSE_DEFAULT_MAX_SIZE;
        c->pending = NULL;
 
        c->io_trigger = tevent_create_immediate(c);
@@ -742,12 +742,16 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX
        struct dcerpc_auth auth;
        uint32_t auth_length;
 
-       if (!c->security_state.auth_info ||
-           !c->security_state.generic_state) {
-               return NT_STATUS_OK;
+       status = dcerpc_verify_ncacn_packet_header(pkt, DCERPC_PKT_RESPONSE,
+                                       pkt->u.response.stub_and_verifier.length,
+                                       0, /* required_flags */
+                                       DCERPC_PFC_FLAG_FIRST |
+                                       DCERPC_PFC_FLAG_LAST);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       switch (c->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
        case DCERPC_AUTH_LEVEL_INTEGRITY:
                break;
@@ -767,6 +771,14 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX
                return NT_STATUS_INVALID_LEVEL;
        }
 
+       if (pkt->auth_length == 0) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       if (c->security_state.generic_state == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
        status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
                                          &pkt->u.response.stub_and_verifier,
                                          &auth, &auth_length, false);
@@ -774,8 +786,20 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX
 
        pkt->u.response.stub_and_verifier.length -= auth_length;
 
+       if (auth.auth_type != c->security_state.auth_type) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
+       if (auth.auth_level != c->security_state.auth_level) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
+       if (auth.auth_context_id != c->security_state.auth_context_id) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
        /* check signature or unseal the packet */
-       switch (c->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_unseal_packet(c->security_state.generic_state, 
                                              raw_packet->data + DCERPC_REQUEST_LENGTH,
@@ -1221,7 +1245,7 @@ struct tevent_req *dcerpc_bind_send(TALLOC_CTX *mem_ctx,
 
        /* construct the NDR form of the packet */
        status = ncacn_push_auth(&blob, state, &pkt,
-                                p->conn->security_state.auth_info);
+                                p->conn->security_state.tmp_auth_info.out);
        if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
@@ -1294,6 +1318,7 @@ static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
                tevent_req_data(req,
                struct dcerpc_bind_state);
        struct dcecli_connection *conn = state->p->conn;
+       struct dcecli_security *sec = &conn->security_state;
        struct dcerpc_binding *b = NULL;
        NTSTATUS status;
        uint32_t flags;
@@ -1327,14 +1352,34 @@ static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
                return;
        }
 
-       if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
-           (pkt->u.bind_ack.num_results == 0) ||
-           (pkt->u.bind_ack.ctx_list[0].result != 0)) {
+       status = dcerpc_verify_ncacn_packet_header(pkt,
+                                       DCERPC_PKT_BIND_ACK,
+                                       pkt->u.bind_ack.auth_info.length,
+                                       DCERPC_PFC_FLAG_FIRST |
+                                       DCERPC_PFC_FLAG_LAST,
+                                       DCERPC_PFC_FLAG_CONC_MPX |
+                                       DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+       if (!NT_STATUS_IS_OK(status)) {
                state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
                tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
                return;
        }
 
+       if (pkt->u.bind_ack.num_results != 1) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+
+       if (pkt->u.bind_ack.ctx_list[0].result != 0) {
+               status = dcerpc_map_ack_reason(&pkt->u.bind_ack.ctx_list[0]);
+               DEBUG(2,("dcerpc: bind_ack failed - reason %d - %s\n",
+                        pkt->u.bind_ack.ctx_list[0].reason.value,
+                        nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
        /*
         * DCE-RPC 1.1 (c706) specifies
         * CONST_MUST_RCV_FRAG_SIZE as 1432
@@ -1367,11 +1412,13 @@ static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
        }
 
        /* the bind_ack might contain a reply set of credentials */
-       if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) {
+       if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) {
                uint32_t auth_length;
 
-               status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.bind_ack.auth_info,
-                                                 conn->security_state.auth_info, &auth_length, true);
+               status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem,
+                                                 &pkt->u.bind_ack.auth_info,
+                                                 sec->tmp_auth_info.in,
+                                                 &auth_length, true);
                if (tevent_req_nterror(req, status)) {
                        return;
                }
@@ -1421,9 +1468,8 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
        }
 
        /* construct the NDR form of the packet */
-       status = ncacn_push_auth(&blob, mem_ctx,
-                                &pkt,
-                                p->conn->security_state.auth_info);
+       status = ncacn_push_auth(&blob, mem_ctx, &pkt,
+                                p->conn->security_state.tmp_auth_info.out);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -1459,8 +1505,7 @@ static void dcerpc_request_recv_data(struct dcecli_connection *c,
          to run the auth routines so that we don't get the sign/seal
          info out of step with the server
        */
-       if (c->security_state.auth_info && c->security_state.generic_state &&
-           pkt->ptype == DCERPC_PKT_RESPONSE) {
+       if (pkt->ptype == DCERPC_PKT_RESPONSE) {
                status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
        }
 
@@ -1500,7 +1545,16 @@ static void dcerpc_request_recv_data(struct dcecli_connection *c,
        }
 
        if (pkt->ptype == DCERPC_PKT_FAULT) {
+               status = dcerpc_fault_to_nt_status(pkt->u.fault.status);
                DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+                       dcerpc_connection_dead(c, status);
+                       return;
+               }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+                       dcerpc_connection_dead(c, status);
+                       return;
+               }
                req->fault_code = pkt->u.fault.status;
                req->status = NT_STATUS_NET_WRITE_FAULT;
                goto req_done;
@@ -1509,20 +1563,27 @@ static void dcerpc_request_recv_data(struct dcecli_connection *c,
        if (pkt->ptype != DCERPC_PKT_RESPONSE) {
                DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
                         (int)pkt->ptype)); 
-               req->fault_code = DCERPC_FAULT_OTHER;
-               req->status = NT_STATUS_NET_WRITE_FAULT;
-               goto req_done;
+               dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return;
        }
 
        /* now check the status from the auth routines, and if it failed then fail
           this request accordingly */
        if (!NT_STATUS_IS_OK(status)) {
-               req->status = status;
-               goto req_done;
+               dcerpc_connection_dead(c, status);
+               return;
        }
 
        length = pkt->u.response.stub_and_verifier.length;
 
+       if (req->payload.length + length > c->max_total_response_size) {
+               DEBUG(2,("Unexpected total payload 0x%X > 0x%X dcerpc response\n",
+                        (unsigned)req->payload.length + length,
+                        (unsigned)c->max_total_response_size));
+               dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return;
+       }
+
        if (length > 0) {
                req->payload.data = talloc_realloc(req, 
                                                   req->payload.data, 
@@ -1637,11 +1698,7 @@ static NTSTATUS dcerpc_request_prepare_vt(struct rpc_request *req)
        struct ndr_push *ndr = NULL;
        enum ndr_err_code ndr_err;
 
-       if (sec->auth_info == NULL) {
-               return NT_STATUS_OK;
-       }
-
-       if (sec->auth_info->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
+       if (sec->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
                return NT_STATUS_OK;
        }
 
@@ -1776,25 +1833,9 @@ static void dcerpc_ship_next_request(struct dcecli_connection *c)
                need_async = true;
        }
 
-       if (c->security_state.auth_info &&
-           c->security_state.generic_state)
-       {
-               struct gensec_security *gensec = c->security_state.generic_state;
-
-               switch (c->security_state.auth_info->auth_level) {
-               case DCERPC_AUTH_LEVEL_PRIVACY:
-               case DCERPC_AUTH_LEVEL_INTEGRITY:
-                       can_async = gensec_have_feature(gensec,
+       if (c->security_state.auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+               can_async = gensec_have_feature(c->security_state.generic_state,
                                                GENSEC_FEATURE_ASYNC_REPLIES);
-                       break;
-               case DCERPC_AUTH_LEVEL_CONNECT:
-               case DCERPC_AUTH_LEVEL_NONE:
-                       can_async = true;
-                       break;
-               default:
-                       can_async = false;
-                       break;
-               }
        }
 
        if (need_async && !can_async) {
@@ -1814,8 +1855,7 @@ static void dcerpc_ship_next_request(struct dcecli_connection *c)
           request header size */
        chunk_size = p->conn->srv_max_recv_frag;
        chunk_size -= DCERPC_REQUEST_LENGTH;
-       if (c->security_state.auth_info &&
-           c->security_state.generic_state) {
+       if (c->security_state.auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
                size_t max_payload = chunk_size;
 
                max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
@@ -2251,7 +2291,7 @@ struct tevent_req *dcerpc_alter_context_send(TALLOC_CTX *mem_ctx,
 
        /* construct the NDR form of the packet */
        status = ncacn_push_auth(&blob, state, &pkt,
-                                p->conn->security_state.auth_info);
+                                p->conn->security_state.tmp_auth_info.out);
        if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
@@ -2324,6 +2364,7 @@ static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
                tevent_req_data(req,
                struct dcerpc_alter_context_state);
        struct dcecli_connection *conn = state->p->conn;
+       struct dcecli_security *sec = &conn->security_state;
        NTSTATUS status;
 
        /*
@@ -2345,17 +2386,6 @@ static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
         */
        tevent_req_defer_callback(req, state->ev);
 
-       if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
-           pkt->u.alter_resp.num_results == 1 &&
-           pkt->u.alter_resp.ctx_list[0].result != 0) {
-               status = dcerpc_map_ack_reason(&pkt->u.alter_resp.ctx_list[0]);
-               DEBUG(2,("dcerpc: alter_resp failed - reason %d - %s\n",
-                        pkt->u.alter_resp.ctx_list[0].reason.value,
-                        nt_errstr(status)));
-               tevent_req_nterror(req, status);
-               return;
-       }
-
        if (pkt->ptype == DCERPC_PKT_FAULT) {
                DEBUG(5,("dcerpc: alter_resp - rpc fault: %s\n",
                         dcerpc_errstr(state, pkt->u.fault.status)));
@@ -2373,21 +2403,42 @@ static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
                return;
        }
 
-       if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
-           pkt->u.alter_resp.num_results == 0 ||
-           pkt->u.alter_resp.ctx_list[0].result != 0) {
+       status = dcerpc_verify_ncacn_packet_header(pkt,
+                                       DCERPC_PKT_ALTER_RESP,
+                                       pkt->u.alter_resp.auth_info.length,
+                                       DCERPC_PFC_FLAG_FIRST |
+                                       DCERPC_PFC_FLAG_LAST,
+                                       DCERPC_PFC_FLAG_CONC_MPX |
+                                       DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+       if (!NT_STATUS_IS_OK(status)) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+
+       if (pkt->u.alter_resp.num_results != 1) {
                state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
                tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
                return;
        }
 
+       if (pkt->u.alter_resp.ctx_list[0].result != 0) {
+               status = dcerpc_map_ack_reason(&pkt->u.alter_resp.ctx_list[0]);
+               DEBUG(2,("dcerpc: alter_resp failed - reason %d - %s\n",
+                        pkt->u.alter_resp.ctx_list[0].reason.value,
+                        nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
        /* the alter_resp might contain a reply set of credentials */
-       if (conn->security_state.auth_info &&
-           pkt->u.alter_resp.auth_info.length) {
+       if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) {
                uint32_t auth_length;
 
-               status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.alter_resp.auth_info,
-                                                 conn->security_state.auth_info, &auth_length, true);
+               status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem,
+                                                 &pkt->u.alter_resp.auth_info,
+                                                 sec->tmp_auth_info.in,
+                                                 &auth_length, true);
                if (tevent_req_nterror(req, status)) {
                        return;
                }