s3-librpc Call GSSAPI via the auth_generic layer and gensec
[metze/samba/wip.git] / source3 / librpc / rpc / dcerpc_helpers.c
index 5c92a792e978f1548729e2bc5b070a2295d9521b..4cfe7933fe0c95002008aa6e9e6178ca52b306d3 100644 (file)
 #include "librpc/rpc/dcerpc.h"
 #include "librpc/gen_ndr/ndr_dcerpc.h"
 #include "librpc/gen_ndr/ndr_schannel.h"
+#include "../libcli/auth/schannel.h"
+#include "../libcli/auth/spnego.h"
+#include "librpc/crypto/gse.h"
+#include "librpc/crypto/spnego.h"
+#include "auth/gensec/gensec.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_RPC_PARSE
@@ -87,6 +92,7 @@ NTSTATUS dcerpc_push_ncacn_packet(TALLOC_CTX *mem_ctx,
 *                      elements
 * @param blob          The blob of data to decode
 * @param r             An empty ncacn_packet, must not be NULL
+* @param bigendian     Whether the packet is bignedian encoded
 *
 * @return a NTSTATUS error code
 */
@@ -234,3 +240,658 @@ NTSTATUS dcerpc_pull_dcerpc_auth(TALLOC_CTX *mem_ctx,
 
        return NT_STATUS_OK;
 }
+
+/**
+* @brief Calculate how much data we can in a packet, including calculating
+*       auth token and pad lengths.
+*
+* @param auth          The pipe_auth_data structure for this pipe.
+* @param header_len    The length of the packet header
+* @param data_left     The data left in the send buffer
+* @param max_xmit_frag The max fragment size.
+* @param pad_alignment The NDR padding size.
+* @param data_to_send  [out] The max data we will send in the pdu
+* @param frag_len      [out] The total length of the fragment
+* @param auth_len      [out] The length of the auth trailer
+* @param pad_len       [out] The padding to be applied
+*
+* @return A NT Error status code.
+*/
+NTSTATUS dcerpc_guess_sizes(struct pipe_auth_data *auth,
+                           size_t header_len, size_t data_left,
+                           size_t max_xmit_frag, size_t pad_alignment,
+                           size_t *data_to_send, size_t *frag_len,
+                           size_t *auth_len, size_t *pad_len)
+{
+       size_t max_len;
+       size_t mod_len;
+       struct gensec_security *gensec_security;
+       struct schannel_state *schannel_auth;
+       struct spnego_context *spnego_ctx;
+       struct gse_context *gse_ctx;
+       enum spnego_mech auth_type;
+       void *auth_ctx;
+       bool seal = false;
+       NTSTATUS status;
+
+       /* no auth token cases first */
+       switch (auth->auth_level) {
+       case DCERPC_AUTH_LEVEL_NONE:
+       case DCERPC_AUTH_LEVEL_CONNECT:
+       case DCERPC_AUTH_LEVEL_PACKET:
+               max_len = max_xmit_frag - header_len;
+               *data_to_send = MIN(max_len, data_left);
+               *pad_len = 0;
+               *auth_len = 0;
+               *frag_len = header_len + *data_to_send;
+               return NT_STATUS_OK;
+
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               seal = true;
+               break;
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               break;
+
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+
+       /* Sign/seal case, calculate auth and pad lengths */
+
+       max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH;
+
+       /* Treat the same for all authenticated rpc requests. */
+       switch (auth->auth_type) {
+       case DCERPC_AUTH_TYPE_SPNEGO:
+               spnego_ctx = talloc_get_type_abort(auth->auth_ctx,
+                                                  struct spnego_context);
+               status = spnego_get_negotiated_mech(spnego_ctx,
+                                                   &auth_type, &auth_ctx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               switch (auth_type) {
+               case SPNEGO_NTLMSSP:
+                       gensec_security = talloc_get_type_abort(auth_ctx,
+                                                               struct gensec_security);
+                       *auth_len = gensec_sig_size(gensec_security, max_len);
+                       break;
+
+               case SPNEGO_KRB5:
+                       gse_ctx = talloc_get_type_abort(auth_ctx,
+                                                       struct gse_context);
+                       if (!gse_ctx) {
+                               return NT_STATUS_INVALID_PARAMETER;
+                       }
+                       *auth_len = gse_get_signature_length(gse_ctx,
+                                                            seal, max_len);
+                       break;
+
+               default:
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               break;
+
+       case DCERPC_AUTH_TYPE_NTLMSSP:
+       case DCERPC_AUTH_TYPE_KRB5:
+               gensec_security = talloc_get_type_abort(auth->auth_ctx,
+                                                       struct gensec_security);
+               *auth_len = gensec_sig_size(gensec_security, max_len);
+               break;
+
+       case DCERPC_AUTH_TYPE_SCHANNEL:
+               schannel_auth = talloc_get_type_abort(auth->auth_ctx,
+                                                     struct schannel_state);
+               *auth_len = netsec_outgoing_sig_size(schannel_auth);
+               break;
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       max_len -= *auth_len;
+
+       *data_to_send = MIN(max_len, data_left);
+
+       mod_len = (header_len + *data_to_send) % pad_alignment;
+       if (mod_len) {
+               *pad_len = pad_alignment - mod_len;
+       } else {
+               *pad_len = 0;
+       }
+
+       if (*data_to_send + *pad_len > max_len) {
+               *data_to_send -= pad_alignment;
+       }
+
+       *frag_len = header_len + *data_to_send + *pad_len
+                       + DCERPC_AUTH_TRAILER_LENGTH + *auth_len;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Create and add the NTLMSSP sign/seal auth data.
+ ********************************************************************/
+
+static NTSTATUS add_generic_auth_footer(struct gensec_security *gensec_security,
+                                       enum dcerpc_AuthLevel auth_level,
+                                       DATA_BLOB *rpc_out)
+{
+       uint16_t data_and_pad_len = rpc_out->length
+                                       - DCERPC_RESPONSE_LENGTH
+                                       - DCERPC_AUTH_TRAILER_LENGTH;
+       DATA_BLOB auth_blob;
+       NTSTATUS status;
+
+       if (!gensec_security) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               /* Data portion is encrypted. */
+               status = gensec_seal_packet(gensec_security,
+                                           rpc_out->data,
+                                           rpc_out->data
+                                           + DCERPC_RESPONSE_LENGTH,
+                                           data_and_pad_len,
+                                           rpc_out->data,
+                                           rpc_out->length,
+                                           &auth_blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* Data is signed. */
+               status = gensec_sign_packet(gensec_security,
+                                           rpc_out->data,
+                                           rpc_out->data
+                                           + DCERPC_RESPONSE_LENGTH,
+                                           data_and_pad_len,
+                                           rpc_out->data,
+                                           rpc_out->length,
+                                           &auth_blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       default:
+               /* Can't happen. */
+               smb_panic("bad auth level");
+               /* Notreached. */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Finally attach the blob. */
+       if (!data_blob_append(NULL, rpc_out,
+                               auth_blob.data, auth_blob.length)) {
+               DEBUG(0, ("Failed to add %u bytes auth blob.\n",
+                         (unsigned int)auth_blob.length));
+               return NT_STATUS_NO_MEMORY;
+       }
+       data_blob_free(&auth_blob);
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Check/unseal the NTLMSSP auth data. (Unseal in place).
+ ********************************************************************/
+
+static NTSTATUS get_generic_auth_footer(struct gensec_security *gensec_security,
+                                       enum dcerpc_AuthLevel auth_level,
+                                       DATA_BLOB *data, DATA_BLOB *full_pkt,
+                                       DATA_BLOB *auth_token)
+{
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               /* Data portion is encrypted. */
+               return gensec_unseal_packet(gensec_security,
+                                           data->data,
+                                           data->length,
+                                           full_pkt->data,
+                                           full_pkt->length,
+                                           auth_token);
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* Data is signed. */
+               return gensec_check_packet(gensec_security,
+                                          data->data,
+                                          data->length,
+                                          full_pkt->data,
+                                          full_pkt->length,
+                                          auth_token);
+
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+}
+
+/*******************************************************************
+ Create and add the schannel sign/seal auth data.
+ ********************************************************************/
+
+static NTSTATUS add_schannel_auth_footer(struct schannel_state *sas,
+                                       enum dcerpc_AuthLevel auth_level,
+                                       DATA_BLOB *rpc_out)
+{
+       uint8_t *data_p = rpc_out->data + DCERPC_RESPONSE_LENGTH;
+       size_t data_and_pad_len = rpc_out->length
+                                       - DCERPC_RESPONSE_LENGTH
+                                       - DCERPC_AUTH_TRAILER_LENGTH;
+       DATA_BLOB auth_blob;
+       NTSTATUS status;
+
+       if (!sas) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       DEBUG(10,("add_schannel_auth_footer: SCHANNEL seq_num=%d\n",
+                       sas->seq_num));
+
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = netsec_outgoing_packet(sas,
+                                               rpc_out->data,
+                                               true,
+                                               data_p,
+                                               data_and_pad_len,
+                                               &auth_blob);
+               break;
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               status = netsec_outgoing_packet(sas,
+                                               rpc_out->data,
+                                               false,
+                                               data_p,
+                                               data_and_pad_len,
+                                               &auth_blob);
+               break;
+       default:
+               status = NT_STATUS_INTERNAL_ERROR;
+               break;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1,("add_schannel_auth_footer: failed to process packet: %s\n",
+                       nt_errstr(status)));
+               return status;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               dump_NL_AUTH_SIGNATURE(talloc_tos(), &auth_blob);
+       }
+
+       /* Finally attach the blob. */
+       if (!data_blob_append(NULL, rpc_out,
+                               auth_blob.data, auth_blob.length)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       data_blob_free(&auth_blob);
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Check/unseal the Schannel auth data. (Unseal in place).
+ ********************************************************************/
+
+static NTSTATUS get_schannel_auth_footer(TALLOC_CTX *mem_ctx,
+                                        struct schannel_state *auth_state,
+                                        enum dcerpc_AuthLevel auth_level,
+                                        DATA_BLOB *data, DATA_BLOB *full_pkt,
+                                        DATA_BLOB *auth_token)
+{
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               /* Data portion is encrypted. */
+               return netsec_incoming_packet(auth_state,
+                                               true,
+                                               data->data,
+                                               data->length,
+                                               auth_token);
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* Data is signed. */
+               return netsec_incoming_packet(auth_state,
+                                               false,
+                                               data->data,
+                                               data->length,
+                                               auth_token);
+
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+}
+
+/*******************************************************************
+ Create and add the spnego-negotiated sign/seal auth data.
+ ********************************************************************/
+
+static NTSTATUS add_spnego_auth_footer(struct spnego_context *spnego_ctx,
+                                       enum dcerpc_AuthLevel auth_level,
+                                       DATA_BLOB *rpc_out)
+{
+       DATA_BLOB auth_blob;
+       DATA_BLOB rpc_data;
+       NTSTATUS status;
+
+       if (!spnego_ctx) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       rpc_data = data_blob_const(rpc_out->data
+                                       + DCERPC_RESPONSE_LENGTH,
+                                  rpc_out->length
+                                       - DCERPC_RESPONSE_LENGTH
+                                       - DCERPC_AUTH_TRAILER_LENGTH);
+
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               /* Data portion is encrypted. */
+               status = spnego_seal(rpc_out->data, spnego_ctx,
+                                    &rpc_data, rpc_out, &auth_blob);
+               break;
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* Data is signed. */
+               status = spnego_sign(rpc_out->data, spnego_ctx,
+                                    &rpc_data, rpc_out, &auth_blob);
+               break;
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       default:
+               /* Can't happen. */
+               smb_panic("bad auth level");
+               /* Notreached. */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Finally attach the blob. */
+       if (!data_blob_append(NULL, rpc_out,
+                               auth_blob.data, auth_blob.length)) {
+               DEBUG(0, ("Failed to add %u bytes auth blob.\n",
+                         (unsigned int)auth_blob.length));
+               return NT_STATUS_NO_MEMORY;
+       }
+       data_blob_free(&auth_blob);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS get_spnego_auth_footer(TALLOC_CTX *mem_ctx,
+                                       struct spnego_context *sp_ctx,
+                                       enum dcerpc_AuthLevel auth_level,
+                                       DATA_BLOB *data, DATA_BLOB *full_pkt,
+                                       DATA_BLOB *auth_token)
+{
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               /* Data portion is encrypted. */
+               return spnego_unseal(mem_ctx, sp_ctx,
+                                    data, full_pkt, auth_token);
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* Data is signed. */
+               return spnego_sigcheck(mem_ctx, sp_ctx,
+                                      data, full_pkt, auth_token);
+
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+}
+
+/**
+* @brief   Append an auth footer according to what is the current mechanism
+*
+* @param auth          The pipe_auth_data associated with the connection
+* @param pad_len       The padding used in the packet
+* @param rpc_out       Packet blob up to and including the auth header
+*
+* @return A NTSTATUS error code.
+*/
+NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
+                               size_t pad_len, DATA_BLOB *rpc_out)
+{
+       struct schannel_state *schannel_auth;
+       struct gensec_security *gensec_security;
+       struct spnego_context *spnego_ctx;
+       char pad[CLIENT_NDR_PADDING_SIZE] = { 0, };
+       DATA_BLOB auth_info;
+       DATA_BLOB auth_blob;
+       NTSTATUS status;
+
+       if (auth->auth_type == DCERPC_AUTH_TYPE_NONE ||
+           auth->auth_type == DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM) {
+               return NT_STATUS_OK;
+       }
+
+       if (pad_len) {
+               /* Copy the sign/seal padding data. */
+               if (!data_blob_append(NULL, rpc_out, pad, pad_len)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       /* marshall the dcerpc_auth with an actually empty auth_blob.
+        * This is needed because the ntmlssp signature includes the
+        * auth header. We will append the actual blob later. */
+       auth_blob = data_blob_null;
+       status = dcerpc_push_dcerpc_auth(rpc_out->data,
+                                        auth->auth_type,
+                                        auth->auth_level,
+                                        pad_len,
+                                        1 /* context id. */,
+                                        &auth_blob,
+                                        &auth_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* append the header */
+       if (!data_blob_append(NULL, rpc_out,
+                               auth_info.data, auth_info.length)) {
+               DEBUG(0, ("Failed to add %u bytes auth blob.\n",
+                         (unsigned int)auth_info.length));
+               return NT_STATUS_NO_MEMORY;
+       }
+       data_blob_free(&auth_info);
+
+       /* Generate any auth sign/seal and add the auth footer. */
+       switch (auth->auth_type) {
+       case DCERPC_AUTH_TYPE_NONE:
+       case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM:
+               status = NT_STATUS_OK;
+               break;
+       case DCERPC_AUTH_TYPE_SPNEGO:
+               spnego_ctx = talloc_get_type_abort(auth->auth_ctx,
+                                                  struct spnego_context);
+               status = add_spnego_auth_footer(spnego_ctx,
+                                               auth->auth_level, rpc_out);
+               break;
+       case DCERPC_AUTH_TYPE_KRB5:
+       case DCERPC_AUTH_TYPE_NTLMSSP:
+               gensec_security = talloc_get_type_abort(auth->auth_ctx,
+                                               struct gensec_security);
+               status = add_generic_auth_footer(gensec_security,
+                                                auth->auth_level,
+                                                rpc_out);
+               break;
+       case DCERPC_AUTH_TYPE_SCHANNEL:
+               schannel_auth = talloc_get_type_abort(auth->auth_ctx,
+                                                     struct schannel_state);
+               status = add_schannel_auth_footer(schannel_auth,
+                                                 auth->auth_level,
+                                                 rpc_out);
+               break;
+       default:
+               status = NT_STATUS_INVALID_PARAMETER;
+               break;
+       }
+
+       return status;
+}
+
+/**
+* @brief Check authentication for request/response packets
+*
+* @param auth          The auth data for the connection
+* @param pkt           The actual ncacn_packet
+* @param pkt_trailer   The stub_and_verifier part of the packet
+* @param header_size   The header size
+* @param raw_pkt       The whole raw packet data blob
+* @param pad_len       [out] The padding length used in the packet
+*
+* @return A NTSTATUS error code
+*/
+NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth,
+                          struct ncacn_packet *pkt,
+                          DATA_BLOB *pkt_trailer,
+                          size_t header_size,
+                          DATA_BLOB *raw_pkt,
+                          size_t *pad_len)
+{
+       struct schannel_state *schannel_auth;
+       struct gensec_security *gensec_security;
+       struct spnego_context *spnego_ctx;
+       NTSTATUS status;
+       struct dcerpc_auth auth_info;
+       uint32_t auth_length;
+       DATA_BLOB full_pkt;
+       DATA_BLOB data;
+
+       switch (auth->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               DEBUG(10, ("Requested Privacy.\n"));
+               break;
+
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               DEBUG(10, ("Requested Integrity.\n"));
+               break;
+
+       case DCERPC_AUTH_LEVEL_CONNECT:
+               if (pkt->auth_length != 0) {
+                       break;
+               }
+               *pad_len = 0;
+               return NT_STATUS_OK;
+
+       case DCERPC_AUTH_LEVEL_NONE:
+               if (pkt->auth_length != 0) {
+                       DEBUG(3, ("Got non-zero auth len on non "
+                                 "authenticated connection!\n"));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               *pad_len = 0;
+               return NT_STATUS_OK;
+
+       default:
+               DEBUG(3, ("Unimplemented Auth Level %d",
+                         auth->auth_level));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Paranioa checks for auth_length. */
+       if (pkt->auth_length > pkt->frag_length) {
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
+       }
+       if (((unsigned int)pkt->auth_length
+            + DCERPC_AUTH_TRAILER_LENGTH < (unsigned int)pkt->auth_length) ||
+           ((unsigned int)pkt->auth_length
+            + DCERPC_AUTH_TRAILER_LENGTH < DCERPC_AUTH_TRAILER_LENGTH)) {
+               /* Integer wrap attempt. */
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
+       }
+
+       status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer,
+                                         &auth_info, &auth_length, false);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       data = data_blob_const(raw_pkt->data + header_size,
+                               pkt_trailer->length - auth_length);
+       full_pkt = data_blob_const(raw_pkt->data,
+                               raw_pkt->length - auth_info.credentials.length);
+
+       switch (auth->auth_type) {
+       case DCERPC_AUTH_TYPE_NONE:
+       case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM:
+               return NT_STATUS_OK;
+
+       case DCERPC_AUTH_TYPE_SPNEGO:
+               spnego_ctx = talloc_get_type_abort(auth->auth_ctx,
+                                                  struct spnego_context);
+               status = get_spnego_auth_footer(pkt, spnego_ctx,
+                                               auth->auth_level,
+                                               &data, &full_pkt,
+                                               &auth_info.credentials);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       case DCERPC_AUTH_TYPE_KRB5:
+       case DCERPC_AUTH_TYPE_NTLMSSP:
+
+               DEBUG(10, ("GENSEC auth\n"));
+
+               gensec_security = talloc_get_type_abort(auth->auth_ctx,
+                                               struct gensec_security);
+               status = get_generic_auth_footer(gensec_security,
+                                                auth->auth_level,
+                                                &data, &full_pkt,
+                                                &auth_info.credentials);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       case DCERPC_AUTH_TYPE_SCHANNEL:
+
+               DEBUG(10, ("SCHANNEL auth\n"));
+
+               schannel_auth = talloc_get_type_abort(auth->auth_ctx,
+                                                     struct schannel_state);
+               status = get_schannel_auth_footer(pkt, schannel_auth,
+                                                 auth->auth_level,
+                                                 &data, &full_pkt,
+                                                 &auth_info.credentials);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+
+       default:
+               DEBUG(0, ("process_request_pdu: "
+                         "unknown auth type %u set.\n",
+                         (unsigned int)auth->auth_type));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* TODO: remove later
+        * this is still needed because in the server code the
+        * pkt_trailer actually has a copy of the raw data, and they
+        * are still both used in later calls */
+       if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+               memcpy(pkt_trailer->data, data.data, data.length);
+       }
+
+       *pad_len = auth_info.auth_pad_length;
+       data_blob_free(&auth_info.credentials);
+       return NT_STATUS_OK;
+}
+