r4663: fixed SPNEGO auth in the rpc server
[samba.git] / source4 / rpc_server / dcesrv_auth.c
index ea029d4d7da1cd403284755ee7d1466e48c56b40..62c879408f423441d6b59b53b32c4ef42d6291d5 100644 (file)
@@ -22,6 +22,7 @@
 */
 
 #include "includes.h"
+#include "rpc_server/dcerpc_server.h"
 
 /*
   startup the cryptographic side of an authenticated dcerpc server
@@ -31,7 +32,8 @@ NTSTATUS dcesrv_crypto_select_type(struct dcesrv_connection *dce_conn,
 {
        NTSTATUS status;
        if (auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY &&
-           auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
+           auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY &&
+           auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_CONNECT) {
                DEBUG(2,("auth_level %d not supported in dcesrv auth\n", 
                         auth->auth_info->auth_level));
                return NT_STATUS_INVALID_PARAMETER;
@@ -47,7 +49,7 @@ NTSTATUS dcesrv_crypto_select_type(struct dcesrv_connection *dce_conn,
                 */
        }
 
-       status = gensec_server_start(&auth->gensec_security);
+       status = gensec_server_start(dce_conn, &auth->gensec_security);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
                return status;
@@ -82,13 +84,13 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
                return True;
        }
 
-       dce_conn->auth_state.auth_info = talloc_p(dce_conn->mem_ctx, struct dcerpc_auth);
+       dce_conn->auth_state.auth_info = talloc_p(dce_conn, struct dcerpc_auth);
        if (!dce_conn->auth_state.auth_info) {
                return False;
        }
 
        status = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
-                                     call->mem_ctx,
+                                     call,
                                      dce_conn->auth_state.auth_info,
                                      (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
        if (!NT_STATUS_IS_OK(status)) {
@@ -117,7 +119,7 @@ BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *
        }
 
        status = gensec_update(dce_conn->auth_state.gensec_security,
-                              call->mem_ctx,
+                              call,
                               dce_conn->auth_state.auth_info->credentials, 
                               &dce_conn->auth_state.auth_info->credentials);
        
@@ -128,6 +130,9 @@ BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *
                        DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
                        return False;
                }
+
+               /* Now that we are authenticated, got back to the generic session key... */
+               dce_conn->auth_state.session_key = dcesrv_generic_session_key;
                return True;
        } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                dce_conn->auth_state.auth_info->auth_pad_length = 0;
@@ -152,12 +157,12 @@ BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
        /* We can't work without an existing gensec state, and an new blob to feed it */
        if (!dce_conn->auth_state.auth_info ||
            !dce_conn->auth_state.gensec_security ||
-           pkt->u.auth.auth_info.length == 0) {
+           pkt->u.auth3.auth_info.length == 0) {
                return False;
        }
 
-       status = ndr_pull_struct_blob(&pkt->u.auth.auth_info,
-                                     call->mem_ctx,
+       status = ndr_pull_struct_blob(&pkt->u.auth3.auth_info,
+                                     call,
                                      dce_conn->auth_state.auth_info,
                                      (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
        if (!NT_STATUS_IS_OK(status)) {
@@ -166,7 +171,7 @@ BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
 
        /* Pass the extra data we got from the client down to gensec for processing */
        status = gensec_update(dce_conn->auth_state.gensec_security,
-                              call->mem_ctx,
+                              call,
                               dce_conn->auth_state.auth_info->credentials, 
                               &dce_conn->auth_state.auth_info->credentials);
        if (NT_STATUS_IS_OK(status)) {
@@ -176,6 +181,8 @@ BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
                        DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
                        return False;
                }
+               /* Now that we are authenticated, got back to the generic session key... */
+               dce_conn->auth_state.session_key = dcesrv_generic_session_key;
                return True;
        } else {
                DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n", 
@@ -186,6 +193,115 @@ BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
        return True;
 }
 
+/*
+  parse any auth information from a dcerpc alter request
+  return False if we can't handle the auth request for some 
+  reason (in which case we send a bind_nak (is this true for here?))
+*/
+BOOL dcesrv_auth_alter(struct dcesrv_call_state *call)
+{
+       struct dcerpc_packet *pkt = &call->pkt;
+       struct dcesrv_connection *dce_conn = call->conn;
+       NTSTATUS status;
+
+       /* on a pure interface change there is no auth blob */
+       if (pkt->u.alter.auth_info.length == 0) {
+               return True;
+       }
+
+       /* We can't work without an existing gensec state */
+       if (!dce_conn->auth_state.gensec_security) {
+               return False;
+       }
+
+       dce_conn->auth_state.auth_info = talloc_p(dce_conn, struct dcerpc_auth);
+       if (!dce_conn->auth_state.auth_info) {
+               return False;
+       }
+
+       status = ndr_pull_struct_blob(&pkt->u.alter.auth_info,
+                                     call,
+                                     dce_conn->auth_state.auth_info,
+                                     (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       return True;
+}
+
+/*
+  add any auth information needed in a alter ack, and process the authentication
+  information found in the alter.
+*/
+BOOL dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct dcerpc_packet *pkt)
+{
+       struct dcesrv_connection *dce_conn = call->conn;
+       NTSTATUS status;
+
+       /* on a pure interface change there is no auth_info structure
+          setup */
+       if (!call->conn->auth_state.auth_info) {
+               return True;
+       }
+
+       if (!call->conn->auth_state.gensec_security) {
+               return False;
+       }
+
+       status = gensec_update(dce_conn->auth_state.gensec_security,
+                              call,
+                              dce_conn->auth_state.auth_info->credentials, 
+                              &dce_conn->auth_state.auth_info->credentials);
+
+       if (NT_STATUS_IS_OK(status)) {
+               status = gensec_session_info(dce_conn->auth_state.gensec_security,
+                                            &dce_conn->auth_state.session_info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+                       return False;
+               }
+
+               /* Now that we are authenticated, got back to the generic session key... */
+               dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+               return True;
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               dce_conn->auth_state.auth_info->auth_pad_length = 0;
+               dce_conn->auth_state.auth_info->auth_reserved = 0;
+               return True;
+       } else {
+               DEBUG(2, ("Failed to finish dcesrv auth alter_ack: %s\n", nt_errstr(status)));
+               return True;
+       }
+}
+
+/*
+  generate a CONNECT level verifier
+*/
+static NTSTATUS dcesrv_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;
+}
+
+/*
+  generate a CONNECT level verifier
+*/
+static NTSTATUS dcesrv_check_connect_verifier(DATA_BLOB *blob)
+{
+       if (blob->length != 16 ||
+           IVAL(blob->data, 0) != 1) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       return NT_STATUS_OK;
+}
+
+
 /*
   check credentials on a request
 */
@@ -216,7 +332,7 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
        pkt->u.request.stub_and_verifier.length -= auth_blob.length;
 
        /* pull the auth structure */
-       ndr = ndr_pull_init_blob(&auth_blob, call->mem_ctx);
+       ndr = ndr_pull_init_blob(&auth_blob, call);
        if (!ndr) {
                return False;
        }
@@ -227,6 +343,7 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
 
        status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(ndr);
                return False;
        }
 
@@ -234,17 +351,20 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
        switch (dce_conn->auth_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
-                                             call->mem_ctx,
-                                             pkt->u.request.stub_and_verifier.data, 
+                                             call,
+                                             full_packet->data + DCERPC_REQUEST_LENGTH,
                                              pkt->u.request.stub_and_verifier.length, 
                                              full_packet->data,
                                              full_packet->length-auth.credentials.length,
                                              &auth.credentials);
+               memcpy(pkt->u.request.stub_and_verifier.data, 
+                      full_packet->data + DCERPC_REQUEST_LENGTH,
+                      pkt->u.request.stub_and_verifier.length);
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
                status = gensec_check_packet(dce_conn->auth_state.gensec_security,
-                                            call->mem_ctx,
+                                            call,
                                             pkt->u.request.stub_and_verifier.data, 
                                             pkt->u.request.stub_and_verifier.length,
                                             full_packet->data,
@@ -252,6 +372,10 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
                                             &auth.credentials);
                break;
 
+       case DCERPC_AUTH_LEVEL_CONNECT:
+               status = dcesrv_check_connect_verifier(&auth.credentials);
+               break;
+
        default:
                status = NT_STATUS_INVALID_LEVEL;
                break;
@@ -259,9 +383,11 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
 
        /* remove the indicated amount of padding */
        if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
+               talloc_free(ndr);
                return False;
        }
        pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
+       talloc_free(ndr);
 
        return NT_STATUS_IS_OK(status);
 }
@@ -276,14 +402,15 @@ BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
        struct dcesrv_connection *dce_conn = call->conn;
        NTSTATUS status;
        struct ndr_push *ndr;
+       uint32_t payload_length;
 
        /* non-signed packets are simple */
        if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security) {
-               status = dcerpc_push_auth(blob, call->mem_ctx, pkt, NULL);
+               status = dcerpc_push_auth(blob, call, pkt, NULL);
                return NT_STATUS_IS_OK(status);
        }
 
-       ndr = ndr_push_init_ctx(call->mem_ctx);
+       ndr = ndr_push_init_ctx(call);
        if (!ndr) {
                return False;
        }
@@ -301,10 +428,19 @@ BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
        dce_conn->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
        ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
 
-       
-       dce_conn->auth_state.auth_info->credentials
-               = data_blob_talloc(call->mem_ctx, NULL, 
-                                  gensec_sig_size(dce_conn->auth_state.gensec_security));
+       payload_length = ndr->offset - DCERPC_REQUEST_LENGTH;
+
+       if (dce_conn->auth_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
+               status = dcesrv_connect_verifier(call,
+                                                &dce_conn->auth_state.auth_info->credentials);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return False;
+               }
+       } else {
+               dce_conn->auth_state.auth_info->credentials
+                       = data_blob_talloc(call, NULL, 
+                                          gensec_sig_size(dce_conn->auth_state.gensec_security));
+       }
 
        /* add the auth verifier */
        status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce_conn->auth_state.auth_info);
@@ -325,24 +461,28 @@ BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
        switch (dce_conn->auth_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_seal_packet(dce_conn->auth_state.gensec_security, 
-                                           call->mem_ctx,
+                                           call,
                                            ndr->data + DCERPC_REQUEST_LENGTH, 
-                                           ndr->offset - DCERPC_REQUEST_LENGTH,
+                                           payload_length,
                                            blob->data,
                                            blob->length - dce_conn->auth_state.auth_info->credentials.length,
-                                             &dce_conn->auth_state.auth_info->credentials);
+                                           &dce_conn->auth_state.auth_info->credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
                status = gensec_sign_packet(dce_conn->auth_state.gensec_security, 
-                                           call->mem_ctx,
+                                           call,
                                            ndr->data + DCERPC_REQUEST_LENGTH, 
-                                           ndr->offset - DCERPC_REQUEST_LENGTH,
+                                           payload_length,
                                            blob->data,
                                            blob->length - dce_conn->auth_state.auth_info->credentials.length,
                                            &dce_conn->auth_state.auth_info->credentials);
 
                break;
+
+       case DCERPC_AUTH_LEVEL_CONNECT:
+               break;
+
        default:
                status = NT_STATUS_INVALID_LEVEL;
                break;