CVE-2015-5370: s4:rpc_server: ensure that the message ordering doesn't violate the...
authorStefan Metzmacher <metze@samba.org>
Fri, 26 Jun 2015 06:10:46 +0000 (08:10 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 30 Mar 2016 02:39:43 +0000 (04:39 +0200)
The first pdu is always a BIND.

REQUEST pdus are only allowed once the authentication
is finished.

A simple anonymous authentication is finished after the BIND.
Real authentication may need additional ALTER or AUTH3 exchanges.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=11344

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Günther Deschner <gd@samba.org>
source4/rpc_server/dcerpc_server.c
source4/rpc_server/dcerpc_server.h
source4/rpc_server/dcesrv_auth.c

index 435bb72ec641b71b34a9d91f58c5fcc30666b6c5..76adb18bd41d5a8afc8931e89bf66de847d81c35 100644 (file)
@@ -408,6 +408,7 @@ _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;
 
@@ -459,6 +460,11 @@ static void dcesrv_call_disconnect_after(struct dcesrv_call_state *call,
                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__;
@@ -914,6 +920,10 @@ 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);
+       }
+
        status = dcerpc_verify_ncacn_packet_header(&call->pkt,
                        DCERPC_PKT_AUTH3,
                        call->pkt.u.auth3.auth_info.length,
@@ -1099,6 +1109,10 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        NTSTATUS status;
        uint32_t context_id;
 
+       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,
@@ -1224,6 +1238,10 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        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)) {
@@ -1390,9 +1408,22 @@ static 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) {
+               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,
@@ -1488,7 +1519,8 @@ static 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);
@@ -1500,7 +1532,7 @@ static 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;
        }
 
@@ -1674,6 +1706,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);
 
index 6c52cb06b951b82c884a5eda91d4f70570da7ccd..2627f751ee3ff44a9c5d1df3ac177e42c5c7208f 100644 (file)
@@ -255,6 +255,14 @@ struct dcesrv_connection {
 
        /* the current authentication state */
        struct dcesrv_auth auth_state;
+
+       /*
+        * remember which pdu types are allowed
+        */
+       bool allow_bind;
+       bool allow_auth3;
+       bool allow_alter;
+       bool allow_request;
 };
 
 
index 03231a5cfde6e6fc1dcc767bfca5497b7c68bf5e..f5086ac4d0f4ce92d3ad2e841900593388d6fa6f 100644 (file)
@@ -130,7 +130,11 @@ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packe
        NTSTATUS status;
        bool want_header_signing = false;
 
+       dce_conn->allow_alter = true;
+       dce_conn->allow_auth3 = true;
+
        if (call->pkt.auth_length == 0) {
+               dce_conn->allow_request = true;
                return NT_STATUS_OK;
        }
 
@@ -161,6 +165,7 @@ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packe
                        DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
                        return status;
                }
+               dce_conn->allow_request = true;
 
                if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
                                         GENSEC_FEATURE_SIGN_PKT_HEADER))
@@ -246,6 +251,8 @@ bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
                        DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
                        return false;
                }
+               dce_conn->allow_request = true;
+
                /* Now that we are authenticated, go back to the generic session key... */
                dce_conn->auth_state.session_key = dcesrv_generic_session_key;
                return true;
@@ -325,6 +332,7 @@ NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_pack
                        DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
                        return status;
                }
+               dce_conn->allow_request = true;
 
                /* Now that we are authenticated, got back to the generic session key... */
                dce_conn->auth_state.session_key = dcesrv_generic_session_key;
@@ -352,6 +360,10 @@ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
        uint32_t auth_length;
        size_t hdr_size = DCERPC_REQUEST_LENGTH;
 
+       if (!dce_conn->allow_request) {
+               return false;
+       }
+
        if (!dce_conn->auth_state.auth_info ||
            !dce_conn->auth_state.gensec_security) {
                if (pkt->auth_length != 0) {