CVE-2015-5370: s4:rpc_server: let a failing auth3 mark the authentication as invalid
[samba.git] / source4 / rpc_server / dcesrv_auth.c
index 911cfe47999a057461d6af528b0f772bea4707b5..f3de2c33f964457e2e3d700f436cdfc48f940a19 100644 (file)
 
 #include "includes.h"
 #include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "rpc_server/common/proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
 #include "librpc/gen_ndr/ndr_dcerpc.h"
 #include "auth/credentials/credentials.h"
 #include "auth/gensec/gensec.h"
+#include "auth/auth.h"
 #include "param/param.h"
+#include "librpc/rpc/rpc_common.h"
 
 /*
   parse any auth information from a dcerpc bind request
 */
 bool dcesrv_auth_bind(struct dcesrv_call_state *call)
 {
-       struct cli_credentials *server_credentials;
+       struct cli_credentials *server_credentials = NULL;
        struct ncacn_packet *pkt = &call->pkt;
        struct dcesrv_connection *dce_conn = call->conn;
        struct dcesrv_auth *auth = &dce_conn->auth_state;
        NTSTATUS status;
+       uint32_t auth_length;
 
-       if (pkt->u.bind.auth_info.length == 0) {
-               dce_conn->auth_state.auth_info = NULL;
+       if (pkt->auth_length == 0) {
+               auth->auth_type = DCERPC_AUTH_TYPE_NONE;
+               auth->auth_level = DCERPC_AUTH_LEVEL_NONE;
+               auth->auth_context_id = 0;
                return true;
        }
 
-       dce_conn->auth_state.auth_info = talloc(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,
-                                     dce_conn->auth_state.auth_info,
-                                     (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info,
+                                         &call->in_auth_info,
+                                         &auth_length, false);
        if (!NT_STATUS_IS_OK(status)) {
                return false;
        }
 
-       status = gensec_server_start(dce_conn, call->event_ctx, call->msg_ctx, &auth->gensec_security);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start GENSEC for DCERPC server: %s\n", nt_errstr(status)));
-               return false;
-       }
+       auth->auth_type = call->in_auth_info.auth_type;
+       auth->auth_level = call->in_auth_info.auth_level;
+       auth->auth_context_id = call->in_auth_info.auth_context_id;
 
        server_credentials 
                = cli_credentials_init(call);
@@ -71,23 +71,42 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call)
                return false;
        }
        
-       cli_credentials_set_conf(server_credentials, global_loadparm);
-       status = cli_credentials_set_machine_account(server_credentials);
+       cli_credentials_set_conf(server_credentials, call->conn->dce_ctx->lp_ctx);
+       status = cli_credentials_set_machine_account(server_credentials, call->conn->dce_ctx->lp_ctx);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
-               talloc_free(server_credentials);
-               server_credentials = NULL;
+               DEBUG(1, ("Failed to obtain server credentials: %s\n",
+                         nt_errstr(status)));
+               return false;
        }
 
-       gensec_set_credentials(auth->gensec_security, server_credentials);
+       status = samba_server_gensec_start(dce_conn, call->event_ctx, 
+                                          call->msg_ctx,
+                                          call->conn->dce_ctx->lp_ctx,
+                                          server_credentials,
+                                          NULL,
+                                          &auth->gensec_security);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Failed to call samba_server_gensec_start %s\n",
+                         nt_errstr(status)));
+               return false;
+       }
 
-       status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, 
-                                              auth->auth_info->auth_level);
+       if (call->conn->remote_address != NULL) {
+               status = gensec_set_remote_address(auth->gensec_security,
+                                               call->conn->remote_address);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("Failed to call gensec_set_remote_address() %s\n",
+                                 nt_errstr(status)));
+                       return false;
+               }
+       }
 
+       status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_type,
+                                              auth->auth_level);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n", 
-                         (int)auth->auth_info->auth_type,
-                         (int)auth->auth_info->auth_level,
+               DEBUG(3, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n",
+                         (int)auth->auth_type,
+                         (int)auth->auth_level,
                          nt_errstr(status)));
                return false;
        }
@@ -103,33 +122,88 @@ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packe
 {
        struct dcesrv_connection *dce_conn = call->conn;
        NTSTATUS status;
+       bool want_header_signing = false;
 
-       if (!call->conn->auth_state.gensec_security) {
+       dce_conn->allow_alter = true;
+       dce_conn->allow_auth3 = true;
+
+       if (call->pkt.auth_length == 0) {
+               dce_conn->auth_state.auth_finished = true;
+               dce_conn->allow_request = true;
                return NT_STATUS_OK;
        }
 
-       status = gensec_update(dce_conn->auth_state.gensec_security,
-                              call,
-                              dce_conn->auth_state.auth_info->credentials, 
-                              &dce_conn->auth_state.auth_info->credentials);
+       /* We can't work without an existing gensec state */
+       if (!call->conn->auth_state.gensec_security) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
+               dce_conn->auth_state.client_hdr_signing = true;
+               want_header_signing = true;
+       }
+
+       if (!lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", true)) {
+               want_header_signing = false;
+       }
+
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = dce_conn->auth_state.auth_type,
+               .auth_level = dce_conn->auth_state.auth_level,
+               .auth_context_id = dce_conn->auth_state.auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
+
+       status = gensec_update_ev(dce_conn->auth_state.gensec_security,
+                              call, call->event_ctx,
+                              call->in_auth_info.credentials,
+                              &call->out_auth_info->credentials);
        
        if (NT_STATUS_IS_OK(status)) {
                status = gensec_session_info(dce_conn->auth_state.gensec_security,
+                                            dce_conn,
                                             &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 status;
                }
+               dce_conn->auth_state.auth_finished = true;
+               dce_conn->allow_request = true;
+
+               if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
+                                        GENSEC_FEATURE_SIGN_PKT_HEADER))
+               {
+                       want_header_signing = false;
+               }
+
+               if (want_header_signing) {
+                       gensec_want_feature(dce_conn->auth_state.gensec_security,
+                                           GENSEC_FEATURE_SIGN_PKT_HEADER);
+                       dce_conn->auth_state.hdr_signing = true;
+                       pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+               }
 
                /* Now that we are authenticated, go back to the generic session key... */
                dce_conn->auth_state.session_key = dcesrv_generic_session_key;
                return NT_STATUS_OK;
        } 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;
+               if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
+                                        GENSEC_FEATURE_SIGN_PKT_HEADER))
+               {
+                       want_header_signing = false;
+               }
+
+               if (want_header_signing) {
+                       gensec_want_feature(dce_conn->auth_state.gensec_security,
+                                           GENSEC_FEATURE_SIGN_PKT_HEADER);
+                       dce_conn->auth_state.hdr_signing = true;
+                       pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+               }
+
                return NT_STATUS_OK;
        } else {
-               DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status)));
+               DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_ack: %s\n",
+                         nt_errstr(status)));
                return status;
        }
 }
@@ -143,44 +217,77 @@ bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
        struct ncacn_packet *pkt = &call->pkt;
        struct dcesrv_connection *dce_conn = call->conn;
        NTSTATUS status;
+       uint32_t auth_length;
+
+       if (pkt->auth_length == 0) {
+               return false;
+       }
 
-       /* 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.auth3.auth_info.length == 0) {
+       if (dce_conn->auth_state.auth_finished) {
                return false;
        }
 
-       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);
+       /* We can't work without an existing gensec state */
+       if (!dce_conn->auth_state.gensec_security) {
+               return false;
+       }
+
+       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info,
+                                         &call->in_auth_info, &auth_length, true);
        if (!NT_STATUS_IS_OK(status)) {
                return false;
        }
 
+       if (call->in_auth_info.auth_type != dce_conn->auth_state.auth_type) {
+               return false;
+       }
+
+       if (call->in_auth_info.auth_level != dce_conn->auth_state.auth_level) {
+               return false;
+       }
+
+       if (call->in_auth_info.auth_context_id != dce_conn->auth_state.auth_context_id) {
+               return false;
+       }
+
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = dce_conn->auth_state.auth_type,
+               .auth_level = dce_conn->auth_state.auth_level,
+               .auth_context_id = dce_conn->auth_state.auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
+
        /* Pass the extra data we got from the client down to gensec for processing */
-       status = gensec_update(dce_conn->auth_state.gensec_security,
-                              call,
-                              dce_conn->auth_state.auth_info->credentials, 
-                              &dce_conn->auth_state.auth_info->credentials);
+       status = gensec_update_ev(dce_conn->auth_state.gensec_security,
+                              call, call->event_ctx,
+                              call->in_auth_info.credentials,
+                              &call->out_auth_info->credentials);
        if (NT_STATUS_IS_OK(status)) {
                status = gensec_session_info(dce_conn->auth_state.gensec_security,
+                                            dce_conn,
                                             &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;
                }
+               dce_conn->auth_state.auth_finished = true;
+               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;
+
+               if (call->out_auth_info->credentials.length != 0) {
+
+                       DEBUG(4, ("GENSEC produced output token (len=%u) at bind_auth3\n",
+                                 (unsigned)call->out_auth_info->credentials.length));
+                       return false;
+               }
                return true;
        } else {
-               DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n", 
+               DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_auth3: %s\n",
                          nt_errstr(status)));
                return false;
        }
-
-       return true;
 }
 
 /*
@@ -193,27 +300,40 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call)
        struct ncacn_packet *pkt = &call->pkt;
        struct dcesrv_connection *dce_conn = call->conn;
        NTSTATUS status;
+       uint32_t auth_length;
 
        /* on a pure interface change there is no auth blob */
-       if (pkt->u.alter.auth_info.length == 0) {
+       if (pkt->auth_length == 0) {
+               if (!dce_conn->auth_state.auth_finished) {
+                       return false;
+               }
                return true;
        }
 
+       if (dce_conn->auth_state.auth_finished) {
+               return false;
+       }
+
        /* 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(dce_conn, struct dcerpc_auth);
-       if (!dce_conn->auth_state.auth_info) {
+       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info,
+                                         &call->in_auth_info, &auth_length, true);
+       if (!NT_STATUS_IS_OK(status)) {
                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)) {
+       if (call->in_auth_info.auth_type != dce_conn->auth_state.auth_type) {
+               return false;
+       }
+
+       if (call->in_auth_info.auth_level != dce_conn->auth_state.auth_level) {
+               return false;
+       }
+
+       if (call->in_auth_info.auth_context_id != dce_conn->auth_state.auth_context_id) {
                return false;
        }
 
@@ -231,68 +351,49 @@ NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_pack
 
        /* on a pure interface change there is no auth_info structure
           setup */
-       if (!call->conn->auth_state.auth_info ||
-           dce_conn->auth_state.auth_info->credentials.length == 0) {
+       if (call->pkt.auth_length == 0) {
                return NT_STATUS_OK;
        }
 
        if (!call->conn->auth_state.gensec_security) {
-               return NT_STATUS_INVALID_PARAMETER;
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       status = gensec_update(dce_conn->auth_state.gensec_security,
-                              call,
-                              dce_conn->auth_state.auth_info->credentials, 
-                              &dce_conn->auth_state.auth_info->credentials);
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = dce_conn->auth_state.auth_type,
+               .auth_level = dce_conn->auth_state.auth_level,
+               .auth_context_id = dce_conn->auth_state.auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
+
+       status = gensec_update_ev(dce_conn->auth_state.gensec_security,
+                              call, call->event_ctx,
+                              call->in_auth_info.credentials,
+                              &call->out_auth_info->credentials);
 
        if (NT_STATUS_IS_OK(status)) {
                status = gensec_session_info(dce_conn->auth_state.gensec_security,
+                                            dce_conn,
                                             &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 status;
                }
+               dce_conn->auth_state.auth_finished = true;
+               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;
                return NT_STATUS_OK;
        } 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 NT_STATUS_OK;
        }
 
-       DEBUG(2, ("Failed to finish dcesrv auth alter_ack: %s\n", nt_errstr(status)));
+       DEBUG(4, ("GENSEC mech rejected the incoming authentication at auth alter_ack: %s\n",
+                 nt_errstr(status)));
        return status;
 }
 
-/*
-  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
 */
@@ -300,71 +401,95 @@ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
 {
        struct ncacn_packet *pkt = &call->pkt;
        struct dcesrv_connection *dce_conn = call->conn;
-       DATA_BLOB auth_blob;
-       struct dcerpc_auth auth;
-       struct ndr_pull *ndr;
        NTSTATUS status;
+       uint32_t auth_length;
+       size_t hdr_size = DCERPC_REQUEST_LENGTH;
 
-       if (!dce_conn->auth_state.auth_info ||
-           !dce_conn->auth_state.gensec_security) {
-               return true;
+       if (!dce_conn->allow_request) {
+               return false;
        }
 
-       auth_blob.length = 8 + pkt->auth_length;
-
-       /* check for a valid length */
-       if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
+       if (dce_conn->auth_state.auth_invalid) {
                return false;
        }
 
-       auth_blob.data = 
-               pkt->u.request.stub_and_verifier.data + 
-               pkt->u.request.stub_and_verifier.length - auth_blob.length;
-       pkt->u.request.stub_and_verifier.length -= auth_blob.length;
+       if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
+               hdr_size += 16;
+       }
 
-       /* pull the auth structure */
-       ndr = ndr_pull_init_blob(&auth_blob, call);
-       if (!ndr) {
+       switch (dce_conn->auth_state.auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               break;
+
+       case DCERPC_AUTH_LEVEL_CONNECT:
+               if (pkt->auth_length != 0) {
+                       break;
+               }
+               return true;
+       case DCERPC_AUTH_LEVEL_NONE:
+               if (pkt->auth_length != 0) {
+                       return false;
+               }
+               return true;
+
+       default:
                return false;
        }
 
-       if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
-               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+       if (!dce_conn->auth_state.gensec_security) {
+               return false;
        }
 
-       status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
+       status = dcerpc_pull_auth_trailer(pkt, call,
+                                         &pkt->u.request.stub_and_verifier,
+                                         &call->in_auth_info, &auth_length, false);
        if (!NT_STATUS_IS_OK(status)) {
-               talloc_free(ndr);
                return false;
        }
 
+       if (call->in_auth_info.auth_type != dce_conn->auth_state.auth_type) {
+               return false;
+       }
+
+       if (call->in_auth_info.auth_level != dce_conn->auth_state.auth_level) {
+               return false;
+       }
+
+       if (call->in_auth_info.auth_context_id != dce_conn->auth_state.auth_context_id) {
+               return false;
+       }
+
+       pkt->u.request.stub_and_verifier.length -= auth_length;
+
        /* check signature or unseal the packet */
-       switch (dce_conn->auth_state.auth_info->auth_level) {
+       switch (dce_conn->auth_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
-                                             call,
-                                             full_packet->data + DCERPC_REQUEST_LENGTH,
+                                             full_packet->data + hdr_size,
                                              pkt->u.request.stub_and_verifier.length, 
                                              full_packet->data,
-                                             full_packet->length-auth.credentials.length,
-                                             &auth.credentials);
+                                             full_packet->length-
+                                             call->in_auth_info.credentials.length,
+                                             &call->in_auth_info.credentials);
                memcpy(pkt->u.request.stub_and_verifier.data, 
-                      full_packet->data + DCERPC_REQUEST_LENGTH,
+                      full_packet->data + hdr_size,
                       pkt->u.request.stub_and_verifier.length);
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
                status = gensec_check_packet(dce_conn->auth_state.gensec_security,
-                                            call,
                                             pkt->u.request.stub_and_verifier.data, 
                                             pkt->u.request.stub_and_verifier.length,
                                             full_packet->data,
-                                            full_packet->length-auth.credentials.length,
-                                            &auth.credentials);
+                                            full_packet->length-
+                                            call->in_auth_info.credentials.length,
+                                            &call->in_auth_info.credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_CONNECT:
-               status = dcesrv_check_connect_verifier(&auth.credentials);
+               /* for now we ignore possible signatures here */
+               status = NT_STATUS_OK;
                break;
 
        default:
@@ -373,12 +498,10 @@ 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);
+       if (pkt->u.request.stub_and_verifier.length < call->in_auth_info.auth_pad_length) {
                return false;
        }
-       pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
-       talloc_free(ndr);
+       pkt->u.request.stub_and_verifier.length -= call->in_auth_info.auth_pad_length;
 
        return NT_STATUS_IS_OK(status);
 }
@@ -388,18 +511,43 @@ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
    push a signed or sealed dcerpc request packet into a blob
 */
 bool dcesrv_auth_response(struct dcesrv_call_state *call,
-                         DATA_BLOB *blob, struct ncacn_packet *pkt)
+                         DATA_BLOB *blob, size_t sig_size,
+                         struct ncacn_packet *pkt)
 {
        struct dcesrv_connection *dce_conn = call->conn;
        NTSTATUS status;
+       enum ndr_err_code ndr_err;
        struct ndr_push *ndr;
        uint32_t payload_length;
        DATA_BLOB creds2;
 
-       /* non-signed packets are simple */
-       if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security) {
+       switch (dce_conn->auth_state.auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               if (sig_size == 0) {
+                       return false;
+               }
+
+               break;
+
+       case DCERPC_AUTH_LEVEL_CONNECT:
+               /*
+                * TODO: let the gensec mech decide if it wants to generate a
+                *       signature that might be needed for schannel...
+                */
+               status = ncacn_push_auth(blob, call, pkt, NULL);
+               return NT_STATUS_IS_OK(status);
+
+       case DCERPC_AUTH_LEVEL_NONE:
                status = ncacn_push_auth(blob, call, pkt, NULL);
                return NT_STATUS_IS_OK(status);
+
+       default:
+               return false;
+       }
+
+       if (!dce_conn->auth_state.gensec_security) {
+               return false;
        }
 
        ndr = ndr_push_init_ctx(call);
@@ -411,78 +559,62 @@ bool dcesrv_auth_response(struct dcesrv_call_state *call,
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
-       status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
-       if (!NT_STATUS_IS_OK(status)) {
+       ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return false;
        }
 
-       /* pad to 16 byte multiple, match win2k3 */
-       dce_conn->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 16);
-       ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
-
-       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 {
-
-               /* We hope this length is accruate.  If must be if the
-                * GENSEC mech does AEAD signing of the packet
-                * headers */
-               dce_conn->auth_state.auth_info->credentials
-                       = data_blob_talloc(call, NULL, 
-                                          gensec_sig_size(dce_conn->auth_state.gensec_security, 
-                                                          payload_length));
-               data_blob_clear(&dce_conn->auth_state.auth_info->credentials);
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = dce_conn->auth_state.auth_type,
+               .auth_level = dce_conn->auth_state.auth_level,
+               .auth_context_id = dce_conn->auth_state.auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
+
+       /* pad to 16 byte multiple in the payload portion of the
+          packet. This matches what w2k3 does. Note that we can't use
+          ndr_push_align() as that is relative to the start of the
+          whole packet, whereas w2k8 wants it relative to the start
+          of the stub */
+       call->out_auth_info->auth_pad_length =
+               DCERPC_AUTH_PAD_LENGTH(pkt->u.response.stub_and_verifier.length);
+       ndr_err = ndr_push_zero(ndr, call->out_auth_info->auth_pad_length);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return false;
        }
 
+       payload_length = pkt->u.response.stub_and_verifier.length +
+               call->out_auth_info->auth_pad_length;
+
        /* add the auth verifier */
-       status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, 
-                                     dce_conn->auth_state.auth_info);
-       if (!NT_STATUS_IS_OK(status)) {
+       ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
+                                      call->out_auth_info);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return false;
        }
 
        /* extract the whole packet as a blob */
        *blob = ndr_push_blob(ndr);
 
-       /* fill in the fragment length and auth_length, we can't fill
-          in these earlier as we don't know the signature length (it
-          could be variable length) */
-       dcerpc_set_frag_length(blob, blob->length);
-
-       /* We hope this value is accruate.  If must be if the GENSEC
-        * mech does AEAD signing of the packet headers */
-       dcerpc_set_auth_length(blob, dce_conn->auth_state.auth_info->credentials.length);
+       /*
+        * Setup the frag and auth length in the packet buffer.
+        * This is needed if the GENSEC mech does AEAD signing
+        * of the packet headers. The signature itself will be
+        * appended later.
+        */
+       dcerpc_set_frag_length(blob, blob->length + sig_size);
+       dcerpc_set_auth_length(blob, sig_size);
 
        /* sign or seal the packet */
-       switch (dce_conn->auth_state.auth_info->auth_level) {
+       switch (dce_conn->auth_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_seal_packet(dce_conn->auth_state.gensec_security, 
                                            call,
                                            ndr->data + DCERPC_REQUEST_LENGTH, 
                                            payload_length,
                                            blob->data,
-                                           blob->length - dce_conn->auth_state.auth_info->credentials.length,
+                                           blob->length,
                                            &creds2);
-
-               if (NT_STATUS_IS_OK(status)) {
-                       blob->length -= dce_conn->auth_state.auth_info->credentials.length;
-                       if (!data_blob_append(call, blob, creds2.data, creds2.length))
-                               status = NT_STATUS_NO_MEMORY;
-                       else
-                               status = NT_STATUS_OK;
-               }
-
-               /* If we did AEAD signing of the packet headers, then we hope
-                * this value didn't change... */
-               dcerpc_set_auth_length(blob, creds2.length);
-               dcerpc_set_frag_length(blob, dcerpc_get_frag_length(blob)+creds2.length);
-               data_blob_free(&creds2);
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
@@ -491,24 +623,8 @@ bool dcesrv_auth_response(struct dcesrv_call_state *call,
                                            ndr->data + DCERPC_REQUEST_LENGTH, 
                                            payload_length,
                                            blob->data,
-                                           blob->length - dce_conn->auth_state.auth_info->credentials.length,
+                                           blob->length,
                                            &creds2);
-               if (NT_STATUS_IS_OK(status)) {
-                       blob->length -= dce_conn->auth_state.auth_info->credentials.length;
-                       if (!data_blob_append(call, blob, creds2.data, creds2.length))
-                               status = NT_STATUS_NO_MEMORY;
-                       else
-                               status = NT_STATUS_OK;
-               }
-
-               /* If we did AEAD signing of the packet headers, then we hope
-                * this value didn't change... */
-               dcerpc_set_auth_length(blob, creds2.length);
-               dcerpc_set_frag_length(blob, dcerpc_get_frag_length(blob)+creds2.length);
-               data_blob_free(&creds2);
-               break;
-
-       case DCERPC_AUTH_LEVEL_CONNECT:
                break;
 
        default:
@@ -516,11 +632,24 @@ bool dcesrv_auth_response(struct dcesrv_call_state *call,
                break;
        }
 
-       data_blob_free(&dce_conn->auth_state.auth_info->credentials);
-
        if (!NT_STATUS_IS_OK(status)) {
                return false;
        }       
 
+       if (creds2.length != sig_size) {
+               DEBUG(3,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
+                        (unsigned)creds2.length, (uint32_t)sig_size,
+                        (unsigned)call->out_auth_info->auth_pad_length,
+                        (unsigned)pkt->u.response.stub_and_verifier.length));
+               dcerpc_set_frag_length(blob, blob->length + creds2.length);
+               dcerpc_set_auth_length(blob, creds2.length);
+       }
+
+       if (!data_blob_append(call, blob, creds2.data, creds2.length)) {
+               status = NT_STATUS_NO_MEMORY;
+               return false;
+       }
+       data_blob_free(&creds2);
+
        return true;
 }