s4:rpc_server: add dcesrv_iface_state_{store,find}_{assoc,conn}() helpers
[samba.git] / source4 / rpc_server / dcesrv_auth.c
index ee7a86ab853e931191f091d86812a22db08877f5..98827ffd2141cb41e57cc902d7c0fae3fbbacf6c 100644 (file)
 #include "param/param.h"
 #include "librpc/rpc/rpc_common.h"
 
-/*
-  parse any auth information from a dcerpc bind request
-  return false if we can't handle the auth request for some 
-  reason (in which case we send a bind_nak)
-*/
-bool dcesrv_auth_bind(struct dcesrv_call_state *call)
+static bool dcesrv_auth_prepare_gensec(struct dcesrv_call_state *call)
 {
-       struct cli_credentials *server_credentials;
-       struct ncacn_packet *pkt = &call->pkt;
+       struct cli_credentials *server_credentials = NULL;
        struct dcesrv_connection *dce_conn = call->conn;
-       struct dcesrv_auth *auth = &dce_conn->auth_state;
+       struct dcesrv_auth *auth = call->auth_state;
+       bool want_header_signing = false;
        NTSTATUS status;
-       uint32_t auth_length;
 
-       if (pkt->u.bind.auth_info.length == 0) {
-               dce_conn->auth_state.auth_info = NULL;
-               return true;
+       if (auth->auth_started) {
+               return false;
        }
 
-       dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
-       if (!dce_conn->auth_state.auth_info) {
+       auth->auth_started = true;
+
+       if (auth->auth_invalid) {
                return false;
        }
 
-       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info,
-                                         dce_conn->auth_state.auth_info,
-                                         &auth_length, false);
+       if (auth->auth_finished) {
+               return false;
+       }
+
+       if (auth->gensec_security != NULL) {
+               return false;
+       }
+
+       switch (call->in_auth_info.auth_level) {
+       case DCERPC_AUTH_LEVEL_CONNECT:
+       case DCERPC_AUTH_LEVEL_CALL:
+       case DCERPC_AUTH_LEVEL_PACKET:
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               /*
+                * We evaluate auth_type only if auth_level was valid
+                */
+               break;
+       default:
+               /*
+                * Setting DCERPC_AUTH_LEVEL_NONE,
+                * gives the caller the reject_reason
+                * as auth_context_id.
+                *
+                * Note: DCERPC_AUTH_LEVEL_NONE == 1
+                */
+               auth->auth_type = DCERPC_AUTH_TYPE_NONE;
+               auth->auth_level = DCERPC_AUTH_LEVEL_NONE;
+               auth->auth_context_id = DCERPC_BIND_NAK_REASON_NOT_SPECIFIED;
+               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);
+               = cli_credentials_init(auth);
        if (!server_credentials) {
                DEBUG(1, ("Failed to init server credentials\n"));
                return false;
@@ -69,445 +96,500 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call)
        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;
        }
 
-       status = samba_server_gensec_start(dce_conn, call->event_ctx, 
+       status = samba_server_gensec_start(auth,
+                                          call->event_ctx,
                                           call->msg_ctx,
                                           call->conn->dce_ctx->lp_ctx,
                                           server_credentials,
                                           NULL,
                                           &auth->gensec_security);
-
-       status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, 
-                                              auth->auth_info->auth_level);
-
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(3, ("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(1, ("Failed to call samba_server_gensec_start %s\n",
                          nt_errstr(status)));
                return false;
        }
 
-       if (call->conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
-               gensec_want_feature(auth->gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER);
+       /*
+        * We have to call this because we set the target_service for
+        * Kerberos to NULL above, and in any case we wish to log a
+        * more specific service target.
+        *
+        */
+       status = gensec_set_target_service_description(auth->gensec_security,
+                                                      "DCE/RPC");
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Failed to call gensec_set_target_service_description %s\n",
+                         nt_errstr(status)));
+               return false;
        }
 
-       return true;
-}
-
-/*
-  add any auth information needed in a bind ack, and process the authentication
-  information found in the bind.
-*/
-NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
-{
-       struct dcesrv_connection *dce_conn = call->conn;
-       NTSTATUS status;
-
-       if (!call->conn->auth_state.gensec_security) {
-               return NT_STATUS_OK;
+       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_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,
-                                            &dce_conn->auth_state.session_info);
+       if (call->conn->local_address != NULL) {
+               status = gensec_set_local_address(auth->gensec_security,
+                                                 call->conn->local_address);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
-                       return status;
+                       DEBUG(1, ("Failed to call gensec_set_local_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)) {
+               const char *backend_name =
+                       gensec_get_name_by_authtype(auth->gensec_security,
+                                                   auth->auth_type);
+
+               DEBUG(3, ("Failed to start GENSEC mechanism for DCERPC server: "
+                         "auth_type=%d (%s), auth_level=%d: %s\n",
+                         (int)auth->auth_type, backend_name,
+                         (int)auth->auth_level,
+                         nt_errstr(status)));
 
-               if (dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
-                       gensec_want_feature(dce_conn->auth_state.gensec_security,
-                                           GENSEC_FEATURE_SIGN_PKT_HEADER);
+               /*
+                * Setting DCERPC_AUTH_LEVEL_NONE,
+                * gives the caller the reject_reason
+                * as auth_context_id.
+                *
+                * Note: DCERPC_AUTH_LEVEL_NONE == 1
+                */
+               auth->auth_type = DCERPC_AUTH_TYPE_NONE;
+               auth->auth_level = DCERPC_AUTH_LEVEL_NONE;
+               if (backend_name != NULL) {
+                       auth->auth_context_id =
+                               DCERPC_BIND_NAK_REASON_INVALID_CHECKSUM;
+               } else {
+                       auth->auth_context_id =
+                               DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE;
                }
+               return false;
+       }
 
-               /* 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;
-               return NT_STATUS_OK;
-       } else {
-               DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_ack: %s\n",
-                         nt_errstr(status)));
-               return status;
+       if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
+               auth->client_hdr_signing = true;
+               want_header_signing = true;
        }
+
+       if (want_header_signing) {
+               want_header_signing = gensec_have_feature(auth->gensec_security,
+                                               GENSEC_FEATURE_SIGN_PKT_HEADER);
+       }
+
+       if (want_header_signing) {
+               want_header_signing = lpcfg_parm_bool(dce_conn->dce_ctx->lp_ctx,
+                                                     NULL,
+                                                     "dcesrv",
+                                                     "header signing",
+                                                     true);
+       }
+
+       if (want_header_signing) {
+               gensec_want_feature(auth->gensec_security,
+                                   GENSEC_FEATURE_SIGN_PKT_HEADER);
+               auth->hdr_signing = true;
+       }
+
+       return true;
 }
 
+static void log_successful_dcesrv_authz_event(struct dcesrv_call_state *call)
+{
+       struct dcesrv_auth *auth = call->auth_state;
+       enum dcerpc_transport_t transport =
+               dcerpc_binding_get_transport(call->conn->endpoint->ep_description);
+       const char *auth_type = derpc_transport_string_by_transport(transport);
+       const char *transport_protection = AUTHZ_TRANSPORT_PROTECTION_NONE;
+
+       if (transport == NCACN_NP) {
+               transport_protection = AUTHZ_TRANSPORT_PROTECTION_SMB;
+       }
+
+       /*
+        * Log the authorization to this RPC interface.  This
+        * covered ncacn_np pass-through auth, and anonymous
+        * DCE/RPC (eg epmapper, netlogon etc)
+        */
+       log_successful_authz_event(call->conn->msg_ctx,
+                                  call->conn->dce_ctx->lp_ctx,
+                                  call->conn->remote_address,
+                                  call->conn->local_address,
+                                  "DCE/RPC",
+                                  auth_type,
+                                  transport_protection,
+                                  auth->session_info);
+}
 
 /*
-  process the final stage of a auth request
+  parse any auth information from a dcerpc bind request
+  return false if we can't handle the auth request for some
+  reason (in which case we send a bind_nak)
 */
-bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
+bool dcesrv_auth_bind(struct dcesrv_call_state *call)
 {
        struct ncacn_packet *pkt = &call->pkt;
-       struct dcesrv_connection *dce_conn = call->conn;
+       struct dcesrv_auth *auth = call->auth_state;
        NTSTATUS status;
-       uint32_t auth_length;
 
-       /* 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) {
-               return false;
+       if (pkt->auth_length == 0) {
+               auth->auth_type = DCERPC_AUTH_TYPE_NONE;
+               auth->auth_level = DCERPC_AUTH_LEVEL_NONE;
+               auth->auth_context_id = 0;
+               auth->auth_started = true;
+
+               log_successful_dcesrv_authz_event(call);
+
+               return true;
        }
 
-       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info,
-                                         dce_conn->auth_state.auth_info, &auth_length, true);
+       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info,
+                                         &call->in_auth_info,
+                                         NULL, true);
        if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * Setting DCERPC_AUTH_LEVEL_NONE,
+                * gives the caller the reject_reason
+                * as auth_context_id.
+                *
+                * Note: DCERPC_AUTH_LEVEL_NONE == 1
+                */
+               auth->auth_type = DCERPC_AUTH_TYPE_NONE;
+               auth->auth_level = DCERPC_AUTH_LEVEL_NONE;
+               auth->auth_context_id =
+                       DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED;
                return false;
        }
 
-       /* 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);
-       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;
-               }
-               /* Now that we are authenticated, go back to the generic session key... */
-               dce_conn->auth_state.session_key = dcesrv_generic_session_key;
-               return true;
-       } else {
-               DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_auth3: %s\n",
-                         nt_errstr(status)));
-               return false;
-       }
+       return dcesrv_auth_prepare_gensec(call);
 }
 
-/*
-  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)
+NTSTATUS dcesrv_auth_complete(struct dcesrv_call_state *call, NTSTATUS status)
 {
-       struct ncacn_packet *pkt = &call->pkt;
-       struct dcesrv_connection *dce_conn = call->conn;
-       NTSTATUS status;
-       uint32_t auth_length;
+       struct dcesrv_auth *auth = call->auth_state;
+       const char *pdu = "<unknown>";
 
-       /* on a pure interface change there is no auth blob */
-       if (pkt->u.alter.auth_info.length == 0) {
-               return true;
+       switch (call->pkt.ptype) {
+       case DCERPC_PKT_BIND:
+               pdu = "BIND";
+               break;
+       case DCERPC_PKT_ALTER:
+               pdu = "ALTER";
+               break;
+       case DCERPC_PKT_AUTH3:
+               pdu = "AUTH3";
+               if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       DEBUG(4, ("GENSEC not finished at at %s\n", pdu));
+                       return NT_STATUS_RPC_SEC_PKG_ERROR;
+               }
+               break;
+       default:
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       /* We can't work without an existing gensec state */
-       if (!dce_conn->auth_state.gensec_security) {
-               return false;
+       if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               return NT_STATUS_OK;
        }
 
-       dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
-       if (!dce_conn->auth_state.auth_info) {
-               return false;
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(4, ("GENSEC mech rejected the incoming authentication "
+                         "at %s: %s\n", pdu, nt_errstr(status)));
+               return status;
        }
 
-       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info,
-                                         dce_conn->auth_state.auth_info,
-                                         &auth_length, true);
+       status = gensec_session_info(auth->gensec_security,
+                                    auth,
+                                    &auth->session_info);
        if (!NT_STATUS_IS_OK(status)) {
-               return false;
+               DEBUG(1, ("Failed to establish session_info: %s\n",
+                         nt_errstr(status)));
+               return status;
        }
+       auth->auth_finished = true;
 
-       return true;
+       if (call->pkt.ptype != DCERPC_PKT_AUTH3) {
+               return NT_STATUS_OK;
+       }
+
+       if (call->out_auth_info->credentials.length != 0) {
+               DEBUG(4, ("GENSEC produced output token (len=%zu) at %s\n",
+                         call->out_auth_info->credentials.length, pdu));
+               return NT_STATUS_RPC_SEC_PKG_ERROR;
+       }
+
+       return NT_STATUS_OK;
 }
 
 /*
-  add any auth information needed in a alter ack, and process the authentication
-  information found in the alter.
+  add any auth information needed in a bind ack, and process the authentication
+  information found in the bind.
 */
-NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
+NTSTATUS dcesrv_auth_prepare_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
 {
        struct dcesrv_connection *dce_conn = call->conn;
-       NTSTATUS status;
+       struct dcesrv_auth *auth = call->auth_state;
 
-       /* 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) {
+       dce_conn->allow_alter = true;
+
+       if (call->pkt.auth_length == 0) {
+               auth->auth_finished = true;
                return NT_STATUS_OK;
        }
 
-       if (!call->conn->auth_state.gensec_security) {
-               return NT_STATUS_INVALID_PARAMETER;
+       /* We can't work without an existing gensec state */
+       if (auth->gensec_security == NULL) {
+               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);
-
-       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;
-               }
-
-               /* 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;
+       if (auth->hdr_signing) {
+               pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
        }
 
-       DEBUG(4, ("GENSEC mech rejected the incoming authentication at auth alter_ack: %s\n",
-                 nt_errstr(status)));
-       return status;
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = auth->auth_type,
+               .auth_level = auth->auth_level,
+               .auth_context_id = auth->auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
+
+       return NT_STATUS_OK;
 }
 
 /*
-  check credentials on a request
+  process the final stage of a auth request
 */
-bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
+bool dcesrv_auth_prepare_auth3(struct dcesrv_call_state *call)
 {
        struct ncacn_packet *pkt = &call->pkt;
-       struct dcesrv_connection *dce_conn = call->conn;
-       struct dcerpc_auth auth;
+       struct dcesrv_auth *auth = call->auth_state;
        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 (pkt->auth_length == 0) {
+               return false;
        }
 
-       if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
-               hdr_size += 16;
+       if (auth->auth_finished) {
+               return false;
        }
 
-       switch (dce_conn->auth_state.auth_info->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:
+       /* We can't work without an existing gensec state */
+       if (auth->gensec_security == NULL) {
                return false;
        }
 
-       status = dcerpc_pull_auth_trailer(pkt, call,
-                                         &pkt->u.request.stub_and_verifier,
-                                         &auth, &auth_length, false);
+       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info,
+                                         &call->in_auth_info, NULL, true);
        if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * Windows returns DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY
+                * instead of DCERPC_NCA_S_PROTO_ERROR.
+                */
+               call->fault_code = DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY;
                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) {
-       case DCERPC_AUTH_LEVEL_PRIVACY:
-               status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
-                                             full_packet->data + hdr_size,
-                                             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 + 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,
-                                            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);
-               break;
-
-       case DCERPC_AUTH_LEVEL_CONNECT:
-               /* for now we ignore possible signatures here */
-               status = NT_STATUS_OK;
-               break;
+       if (call->in_auth_info.auth_type != auth->auth_type) {
+               return false;
+       }
 
-       default:
-               status = NT_STATUS_INVALID_LEVEL;
-               break;
+       if (call->in_auth_info.auth_level != auth->auth_level) {
+               return false;
        }
 
-       /* remove the indicated amount of padding */
-       if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
+       if (call->in_auth_info.auth_context_id != auth->auth_context_id) {
                return false;
        }
-       pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
 
-       return NT_STATUS_IS_OK(status);
-}
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = auth->auth_type,
+               .auth_level = auth->auth_level,
+               .auth_context_id = auth->auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
 
+       return true;
+}
 
-/* 
-   push a signed or sealed dcerpc request packet into a blob
+/*
+  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_response(struct dcesrv_call_state *call,
-                         DATA_BLOB *blob, size_t sig_size,
-                         struct ncacn_packet *pkt)
+bool dcesrv_auth_alter(struct dcesrv_call_state *call)
 {
-       struct dcesrv_connection *dce_conn = call->conn;
+       struct ncacn_packet *pkt = &call->pkt;
+       struct dcesrv_auth *auth = call->auth_state;
        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 (sig_size == 0) {
-               status = ncacn_push_auth(blob, call, pkt, NULL);
-               return NT_STATUS_IS_OK(status);
+       /* on a pure interface change there is no auth blob */
+       if (pkt->auth_length == 0) {
+               if (!auth->auth_finished) {
+                       return false;
+               }
+               return true;
        }
 
-       switch (dce_conn->auth_state.auth_info->auth_level) {
-       case DCERPC_AUTH_LEVEL_PRIVACY:
-       case DCERPC_AUTH_LEVEL_INTEGRITY:
-               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);
+       if (auth->auth_finished) {
+               call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
+               return false;
+       }
 
-       case DCERPC_AUTH_LEVEL_NONE:
-               status = ncacn_push_auth(blob, call, pkt, NULL);
-               return NT_STATUS_IS_OK(status);
+       /* We can't work without an existing gensec state */
+       if (auth->gensec_security == NULL) {
+               return false;
+       }
 
-       default:
+       status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info,
+                                         &call->in_auth_info, NULL, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               call->fault_code = DCERPC_NCA_S_PROTO_ERROR;
                return false;
        }
 
-       ndr = ndr_push_init_ctx(call);
-       if (!ndr) {
+       if (call->in_auth_info.auth_type == DCERPC_AUTH_TYPE_NONE) {
+               call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
                return false;
        }
 
-       if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
-               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+       if (call->in_auth_info.auth_type != auth->auth_type) {
+               return false;
        }
 
-       ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+       if (call->in_auth_info.auth_level != auth->auth_level) {
                return false;
        }
 
-       /* 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 */
-       dce_conn->auth_state.auth_info->auth_pad_length =
-               (16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15;
-       ndr_err = ndr_push_zero(ndr,
-                               dce_conn->auth_state.auth_info->auth_pad_length);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+       if (call->in_auth_info.auth_context_id != auth->auth_context_id) {
                return false;
        }
 
-       payload_length = pkt->u.response.stub_and_verifier.length +
-               dce_conn->auth_state.auth_info->auth_pad_length;
+       return true;
+}
 
-       /* we start without signature, it will appended later */
-       dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0);
+/*
+  add any auth information needed in a alter ack, and process the authentication
+  information found in the alter.
+*/
+NTSTATUS dcesrv_auth_prepare_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
+{
+       struct dcesrv_auth *auth = call->auth_state;
 
-       /* add the auth verifier */
-       ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
-                                      dce_conn->auth_state.auth_info);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               return false;
+       /* on a pure interface change there is no auth_info structure
+          setup */
+       if (call->pkt.auth_length == 0) {
+               return NT_STATUS_OK;
        }
 
-       /* extract the whole packet as a blob */
-       *blob = ndr_push_blob(ndr);
+       if (auth->gensec_security == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
 
-       /*
-        * 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);
+       call->_out_auth_info = (struct dcerpc_auth) {
+               .auth_type = auth->auth_type,
+               .auth_level = auth->auth_level,
+               .auth_context_id = auth->auth_context_id,
+       };
+       call->out_auth_info = &call->_out_auth_info;
 
-       /* sign or seal the packet */
-       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,
-                                           ndr->data + DCERPC_REQUEST_LENGTH, 
-                                           payload_length,
-                                           blob->data,
-                                           blob->length,
-                                           &creds2);
-               break;
+       return NT_STATUS_OK;
+}
 
-       case DCERPC_AUTH_LEVEL_INTEGRITY:
-               status = gensec_sign_packet(dce_conn->auth_state.gensec_security, 
-                                           call,
-                                           ndr->data + DCERPC_REQUEST_LENGTH, 
-                                           payload_length,
-                                           blob->data,
-                                           blob->length,
-                                           &creds2);
-               break;
+/*
+  check credentials on a packet
+*/
+bool dcesrv_auth_pkt_pull(struct dcesrv_call_state *call,
+                         DATA_BLOB *full_packet,
+                         uint8_t required_flags,
+                         uint8_t optional_flags,
+                         uint8_t payload_offset,
+                         DATA_BLOB *payload_and_verifier)
+{
+       struct ncacn_packet *pkt = &call->pkt;
+       struct dcesrv_auth *auth = call->auth_state;
+       const struct dcerpc_auth tmp_auth = {
+               .auth_type = auth->auth_type,
+               .auth_level = auth->auth_level,
+               .auth_context_id = auth->auth_context_id,
+       };
+       NTSTATUS status;
 
-       default:
-               status = NT_STATUS_INVALID_LEVEL;
-               break;
+       if (!auth->auth_finished) {
+               call->fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               return false;
        }
 
-       if (!NT_STATUS_IS_OK(status)) {
+       if (auth->auth_invalid) {
                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)dce_conn->auth_state.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;
+       status = dcerpc_ncacn_pull_pkt_auth(&tmp_auth,
+                                           auth->gensec_security,
+                                           call,
+                                           pkt->ptype,
+                                           required_flags,
+                                           optional_flags,
+                                           payload_offset,
+                                           payload_and_verifier,
+                                           full_packet,
+                                           pkt);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+               call->fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               return false;
+       }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL)) {
+               call->fault_code = DCERPC_NCA_S_UNSUPPORTED_AUTHN_LEVEL;
+               return false;
+       }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+               call->fault_code = DCERPC_FAULT_SEC_PKG_ERROR;
+               return false;
+       }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+               call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
+               return false;
+       }
+       if (!NT_STATUS_IS_OK(status)) {
                return false;
        }
-       data_blob_free(&creds2);
 
        return true;
 }
+
+/* 
+   push a signed or sealed dcerpc request packet into a blob
+*/
+bool dcesrv_auth_pkt_push(struct dcesrv_call_state *call,
+                         DATA_BLOB *blob, size_t sig_size,
+                         uint8_t payload_offset,
+                         const DATA_BLOB *payload,
+                         const struct ncacn_packet *pkt)
+{
+       struct dcesrv_auth *auth = call->auth_state;
+       const struct dcerpc_auth tmp_auth = {
+               .auth_type = auth->auth_type,
+               .auth_level = auth->auth_level,
+               .auth_context_id = auth->auth_context_id,
+       };
+       NTSTATUS status;
+
+       status = dcerpc_ncacn_push_pkt_auth(&tmp_auth,
+                                           auth->gensec_security,
+                                           call, blob, sig_size,
+                                           payload_offset,
+                                           payload,
+                                           pkt);
+       return NT_STATUS_IS_OK(status);
+}