ntlmssp over rpc over tcp now fully works
authorAndrew Tridgell <tridge@samba.org>
Sun, 14 Dec 2003 10:45:50 +0000 (10:45 +0000)
committerAndrew Tridgell <tridge@samba.org>
Sun, 14 Dec 2003 10:45:50 +0000 (10:45 +0000)
I needed to hack the ntlmssp code a little, as the auth code in samba4
is out of date relative to the samba3 auth code. I need to do a merge :)
(This used to be commit 6ee0935afe9444bf9bb24eed4e02e8377dc746b7)

source4/Makefile.in
source4/auth/auth_ntlmssp.c
source4/include/auth.h
source4/rpc_server/dcerpc_server.c
source4/rpc_server/dcerpc_server.h
source4/rpc_server/dcesrv_auth.c

index e8705da4cc493da2d233bcd87669f854d96d579d..2f35b8a47d0c990e5fae74f4e3e48f3aa9521aec 100644 (file)
@@ -258,7 +258,7 @@ PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o
 
 AUTH_OBJ = auth/auth.o auth/auth_sam.o \
           auth/auth_unix.o auth/auth_util.o    \
-          auth/auth_builtin.o auth/auth_compat.o \
+          auth/auth_builtin.o auth/auth_compat.o auth/auth_ntlmssp.o \
           $(PLAINTEXT_AUTH_OBJ) $(UNIGRP_OBJ)
 
 MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
index b3dff8dbe6ddc7f3f7b0ee86939bd842f999519b..940630b4c632cfa345d56ef5539b32a3c5b30827 100644 (file)
 
 #include "includes.h"
 
-static const uint8 *auth_ntlmssp_get_challenge(struct ntlmssp_state *ntlmssp_state)
+/**
+ * Return the challenge as determined by the authentication subsystem 
+ * @return an 8 byte random challenge
+ */
+
+static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state)
 {
        AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
        return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context);
 }
 
-static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) 
+/**
+ * Some authentication methods 'fix' the challenge, so we may not be able to set it
+ *
+ * @return If the effective challenge used by the auth subsystem may be modified
+ */
+static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+       AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+       struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+       return auth_context->challenge_may_be_modified;
+}
+
+/**
+ * NTLM2 authentication modifies the effective challange, 
+ * @param challenge The new challenge value
+ */
+static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+       AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+       struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+       SMB_ASSERT(challenge->length == 8);
+
+       auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, 
+                                                  challenge->data, challenge->length);
+
+       auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)";
+
+       DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by));
+       DEBUG(5, ("challenge is: \n"));
+       dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+       return NT_STATUS_OK;
+}
+
+/**
+ * Check the password on an NTLMSSP login.  
+ *
+ * Return the session keys used on the connection.
+ */
+
+static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) 
 {
        AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
        uint32 auth_flags = AUTH_FLAG_NONE;
@@ -45,18 +91,17 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state)
                auth_flags |= AUTH_FLAG_NTLM_RESP;
        } else  if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) {
                auth_flags |= AUTH_FLAG_NTLMv2_RESP;
-       };
+       }
 
+#if 0
        /* the client has given us its machine name (which we otherwise would not get on port 445).
           we need to possibly reload smb.conf if smb.conf includes depend on the machine name */
-
-       sub_set_remote_machine(auth_ntlmssp_state->ntlmssp_state->workstation);
-
+       set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True);
        /* setup the string used by %U */
        /* sub_set_smb_name checks for weird internally */
-       sub_set_user_name(auth_ntlmssp_state->ntlmssp_state->user);
-
+       sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user);
        reload_services(True);
+#endif
 
        nt_status = make_user_info_map(&user_info, 
                                       auth_ntlmssp_state->ntlmssp_state->user, 
@@ -71,10 +116,28 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state)
                return nt_status;
        }
 
-       nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, user_info, &auth_ntlmssp_state->server_info); 
-                       
+       nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, 
+                                                                         user_info, &auth_ntlmssp_state->server_info); 
+
        free_user_info(&user_info);
 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+       if (auth_ntlmssp_state->server_info->nt_session_key.length) {
+               DEBUG(5, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length));
+               *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, 
+                                                  auth_ntlmssp_state->server_info->nt_session_key.data,
+                                                  auth_ntlmssp_state->server_info->nt_session_key.length);
+       } else if (auth_ntlmssp_state->server_info->lm_session_key.length) {
+               DEBUG(5, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length));
+               *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, 
+                                                  auth_ntlmssp_state->server_info->lm_session_key.data,
+                                                  auth_ntlmssp_state->server_info->lm_session_key.length);
+       } else {
+               *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, 
+                                                  auth_ntlmssp_state->server_info->session_key, 16);
+       }
        return nt_status;
 }
 
@@ -106,18 +169,20 @@ NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
 
        (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state);
        (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
+       (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
+       (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
        (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password;
        (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role();
 
        return NT_STATUS_OK;
 }
 
-NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
 {
        TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx;
 
        if ((*auth_ntlmssp_state)->ntlmssp_state) {
-               ntlmssp_server_end(&(*auth_ntlmssp_state)->ntlmssp_state);
+               ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state);
        }
        if ((*auth_ntlmssp_state)->auth_context) {
                ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context);
@@ -127,12 +192,10 @@ NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
        }
        talloc_destroy(mem_ctx);
        *auth_ntlmssp_state = NULL;
-       return NT_STATUS_OK;
 }
 
 NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state, 
                             const DATA_BLOB request, DATA_BLOB *reply) 
 {
-       return ntlmssp_server_update(auth_ntlmssp_state->ntlmssp_state, request, reply);
+       return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply);
 }
-
index 89e46e3782dd0da3b29c2e49deda7107ba3fb018..84faba2b0d4ee700ac8da57ad40edb5066731829 100644 (file)
@@ -78,8 +78,9 @@ typedef struct auth_serversupplied_info
        NT_USER_TOKEN *ptok;
        
        uint8 session_key[16];
-       
        uint8 first_8_lm_hash[8];
+       DATA_BLOB nt_session_key;
+       DATA_BLOB lm_session_key;
 
        uint32 sam_fill_level;  /* How far is this structure filled? */
        
@@ -95,6 +96,8 @@ struct auth_context {
        /* Who set this up in the first place? */ 
        const char *challenge_set_by; 
 
+       BOOL challenge_may_be_modified;
+
        struct auth_methods *challenge_set_method; 
        /* What order are the various methods in?   Try to stop it changing under us */ 
        struct auth_methods *auth_method_list;  
index 7fa7a7aa8b7fc2d8a0cef37f928dae477dad4f22..16b573cfad0346f6cee53de8c1b14a974462e078 100644 (file)
@@ -369,7 +369,8 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL);
+       status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, 
+                                 call->dce->auth_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -377,7 +378,26 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
 
        DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
+       DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  handle a auth3 request
+*/
+static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
+{
+       /* handle the auth3 in the auth code */
+       if (!dcesrv_auth_auth3(call)) {
+               return dcesrv_fault(call, DCERPC_FAULT_OTHER);
+       }
+
+       talloc_destroy(call->mem_ctx);
 
+       /* we don't send a reply to a auth3 request, except by a
+          fault */
        return NT_STATUS_OK;
 }
 
@@ -473,10 +493,8 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
                pkt.u.response.stub_and_verifier.data = stub.data;
                pkt.u.response.stub_and_verifier.length = length;
 
-
-               status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, NULL);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
+               if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
+                       return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
                }
 
                SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
@@ -487,6 +505,8 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
                stub.length -= length;
        } while (stub.length != 0);
 
+       DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *);
+
        return NT_STATUS_OK;
 }
 
@@ -568,6 +588,13 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce)
 
        dce_partial_advance(dce, blob.length);
 
+       /* we have to check the signing here, before combining the
+          pdus */
+       if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+           !dcesrv_auth_request(call)) {
+               return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
+       }
+
        /* see if this is a continued packet */
        if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
                struct dcesrv_call_state *call2 = call;
@@ -623,6 +650,9 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce)
        case DCERPC_PKT_BIND:
                status = dcesrv_bind(call);
                break;
+       case DCERPC_PKT_AUTH3:
+               status = dcesrv_auth3(call);
+               break;
        case DCERPC_PKT_REQUEST:
                status = dcesrv_request(call);
                break;
@@ -634,9 +664,7 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce)
        /* if we are going to be sending a reply then add
           it to the list of pending calls. We add it to the end to keep the call
           list in the order we will answer */
-       if (NT_STATUS_IS_OK(status)) {
-               DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
-       } else {
+       if (!NT_STATUS_IS_OK(status)) {
                talloc_destroy(mem_ctx);
        }
 
index a2be1d8e06627ee13aa98debc6640be72005391c..e731a5719c1315f5394a99b489fe39630e156de2 100644 (file)
@@ -72,7 +72,7 @@ struct dcesrv_handle {
 
 /* hold the authentication state information */
 struct dcesrv_auth {
-       struct ntlmssp_state *ntlmssp_state;
+       struct auth_ntlmssp_state *ntlmssp_state;
        struct dcerpc_auth *auth_info;
 };
 
index aea79f2927e5b26394c0d22801ea8a0fe8dcfe6a..f290c741cb6650d23f2c439c2bef10bc11e37f0d 100644 (file)
 
 /*
   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)
 {
        struct dcerpc_packet *pkt = &call->pkt;
+       struct dcesrv_state *dce = call->dce;
+       NTSTATUS status;
+
+       if (pkt->u.bind.auth_info.length == 0) {
+               dce->auth_state.auth_info = NULL;
+               return True;
+       }
+
+       dce->auth_state.auth_info = talloc_p(dce->mem_ctx, struct dcerpc_auth);
+       if (!dce->auth_state.auth_info) {
+               return False;
+       }
+
+       status = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
+                                     call->mem_ctx,
+                                     dce->auth_state.auth_info,
+                                     (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       if (dce->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_NTLMSSP) {
+               /* only do NTLMSSP for now */
+               DEBUG(2,("auth_type %d not supported\n", dce->auth_state.auth_info->auth_type));
+               return False;
+       }
+
+       if (dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY &&
+           dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
+               DEBUG(2,("auth_level %d not supported\n", dce->auth_state.auth_info->auth_level));
+               return False;
+       }
+
+       status = auth_ntlmssp_start(&dce->auth_state.ntlmssp_state);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
 
        return True;
 }
@@ -38,5 +77,219 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
 */
 BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *pkt)
 {
+       struct dcesrv_state *dce = call->dce;
+       NTSTATUS status;
+
+       if (!call->dce->auth_state.ntlmssp_state) {
+               return True;
+       }
+
+       status = auth_ntlmssp_update(dce->auth_state.ntlmssp_state,
+                                    dce->auth_state.auth_info->credentials, 
+                                    &dce->auth_state.auth_info->credentials);
+       if (!NT_STATUS_IS_OK(status) && 
+           !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               return False;
+       }
+
+       dce->auth_state.auth_info->auth_pad_length = 0;
+       dce->auth_state.auth_info->auth_reserved = 0;
+                                    
+       return True;
+}
+
+
+/*
+  process the final stage of a NTLMSSP auth request
+*/
+BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
+{
+       struct dcerpc_packet *pkt = &call->pkt;
+       struct dcesrv_state *dce = call->dce;
+       NTSTATUS status;
+
+       if (!dce->auth_state.auth_info ||
+           !dce->auth_state.ntlmssp_state ||
+           pkt->u.auth.auth_info.length == 0) {
+               return False;
+       }
+
+       status = ndr_pull_struct_blob(&pkt->u.auth.auth_info,
+                                     call->mem_ctx,
+                                     dce->auth_state.auth_info,
+                                     (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       if (dce->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_NTLMSSP) {
+               return False;
+       }
+       if (dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY &&
+           dce->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
+               return False;
+       }
+
+       status = auth_ntlmssp_update(dce->auth_state.ntlmssp_state,
+                                    dce->auth_state.auth_info->credentials, 
+                                    &dce->auth_state.auth_info->credentials);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       switch (dce->auth_state.auth_info->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* setup for signing */
+               status = ntlmssp_sign_init(dce->auth_state.ntlmssp_state->ntlmssp_state);
+               break;
+       }
+
+       return True;
+}
+
+
+/*
+  check credentials on a request
+*/
+BOOL dcesrv_auth_request(struct dcesrv_call_state *call)
+{
+       struct dcerpc_packet *pkt = &call->pkt;
+       struct dcesrv_state *dce = call->dce;
+       DATA_BLOB auth_blob;
+       struct dcerpc_auth auth;
+       struct ndr_pull *ndr;
+       NTSTATUS status;
+
+       if (!dce->auth_state.auth_info ||
+           !dce->auth_state.ntlmssp_state) {
+               return True;
+       }
+
+       auth_blob.length = 8 + pkt->auth_length;
+
+       /* check for a valid length */
+       if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
+               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;
+
+       /* pull the auth structure */
+       ndr = ndr_pull_init_blob(&auth_blob, call->mem_ctx);
+       if (!ndr) {
+               return False;
+       }
+
+       status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       /* check signature or unseal the packet */
+       switch (dce->auth_state.auth_info->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = ntlmssp_unseal_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, 
+                                              pkt->u.request.stub_and_verifier.data, 
+                                              pkt->u.request.stub_and_verifier.length, 
+                                              &auth.credentials);
+               break;
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               status = ntlmssp_check_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, 
+                                             pkt->u.request.stub_and_verifier.data, 
+                                             pkt->u.request.stub_and_verifier.length, 
+                                             &auth.credentials);
+               break;
+
+       default:
+               status = NT_STATUS_INVALID_LEVEL;
+               break;
+       }
+
+       /* remove the indicated amount of paddiing */
+       if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
+               return False;
+       }
+       pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/* 
+   push a signed or sealed dcerpc request packet into a blob
+*/
+BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
+                         DATA_BLOB *blob, struct dcerpc_packet *pkt)
+{
+       struct dcesrv_state *dce = call->dce;
+       NTSTATUS status;
+       struct ndr_push *ndr;
+
+       /* non-signed packets are simple */
+       if (!dce->auth_state.auth_info || !dce->auth_state.ntlmssp_state) {
+               status = dcerpc_push_auth(blob, call->mem_ctx, pkt, NULL);
+               return NT_STATUS_IS_OK(status);
+       }
+
+       ndr = ndr_push_init_ctx(call->mem_ctx);
+       if (!ndr) {
+               return False;
+       }
+
+       status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       /* pad to 8 byte multiple */
+       dce->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
+       ndr_push_zero(ndr, dce->auth_state.auth_info->auth_pad_length);
+
+       /* sign or seal the packet */
+       switch (dce->auth_state.auth_info->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = ntlmssp_seal_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, 
+                                            ndr->data + DCERPC_REQUEST_LENGTH, 
+                                            ndr->offset - DCERPC_REQUEST_LENGTH,
+                                            &dce->auth_state.auth_info->credentials);
+               break;
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               status = ntlmssp_sign_packet(dce->auth_state.ntlmssp_state->ntlmssp_state, 
+                                            ndr->data + DCERPC_REQUEST_LENGTH, 
+                                            ndr->offset - DCERPC_REQUEST_LENGTH,
+                                            &dce->auth_state.auth_info->credentials);
+               break;
+       default:
+               status = NT_STATUS_INVALID_LEVEL;
+               break;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }       
+
+       /* add the auth verifier */
+       status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce->auth_state.auth_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               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) */
+       SSVAL(blob->data,  DCERPC_FRAG_LEN_OFFSET, blob->length);
+       SSVAL(blob->data,  DCERPC_AUTH_LEN_OFFSET, dce->auth_state.auth_info->credentials.length);
+
+       data_blob_free(&dce->auth_state.auth_info->credentials);
+
        return True;
 }