CVE-2015-5370: s4:librpc/rpc: call dcerpc_connection_dead() on protocol errors
[samba.git] / source4 / librpc / rpc / dcerpc.c
index 01fc8e5fb203d5100a38fb3d0c084ac2394e19dc..827499155f8d8449fda59cc70d933091f6b06689 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;
@@ -742,6 +741,15 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX
        struct dcerpc_auth auth;
        uint32_t auth_length;
 
+       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_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
        case DCERPC_AUTH_LEVEL_INTEGRITY:
@@ -777,6 +785,18 @@ 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_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
@@ -1224,7 +1244,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);
        }
@@ -1297,6 +1317,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;
@@ -1330,14 +1351,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
@@ -1370,11 +1411,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;
                }
@@ -1424,9 +1467,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;
        }
@@ -1502,7 +1544,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;
@@ -1511,20 +1562,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 > DCERPC_NCACN_PAYLOAD_MAX_SIZE) {
+               DEBUG(2,("Unexpected total payload 0x%X > 0x%X dcerpc response\n",
+                        (unsigned)req->payload.length + length,
+                        DCERPC_NCACN_PAYLOAD_MAX_SIZE));
+               dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return;
+       }
+
        if (length > 0) {
                req->payload.data = talloc_realloc(req, 
                                                   req->payload.data, 
@@ -1639,11 +1697,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;
        }
 
@@ -1778,25 +1832,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) {
@@ -1816,8 +1854,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;
@@ -2253,7 +2290,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);
        }
@@ -2326,6 +2363,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;
 
        /*
@@ -2347,17 +2385,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)));
@@ -2375,21 +2402,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;
                }