r1030: added server side schannel support
authorAndrew Tridgell <tridge@samba.org>
Sat, 5 Jun 2004 05:01:38 +0000 (05:01 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:56:30 +0000 (12:56 -0500)
(This used to be commit 2ac79dfba0e64056a680f21d7dd0c007f79d4a70)

12 files changed:
source4/librpc/idl/dcerpc.idl
source4/librpc/idl/idl_types.h
source4/librpc/ndr/ndr_basic.c
source4/librpc/rpc/dcerpc_schannel.c
source4/librpc/rpc/dcerpc_util.c
source4/rpc_server/config.mk
source4/rpc_server/dcerpc_server.h
source4/rpc_server/dcesrv_auth.c
source4/rpc_server/dcesrv_crypto.c
source4/rpc_server/dcesrv_crypto_ntlmssp.c
source4/rpc_server/dcesrv_crypto_schannel.c [new file with mode: 0644]
source4/rpc_server/netlogon/schannel_state.c

index 0ee3d7b69b4f98b01a2df8e01c06e19aeac5bd99..8805b61da61adff0d126a417c9426b4499a0112c 100644 (file)
@@ -23,6 +23,17 @@ interface dcerpc
                dcerpc_syntax_id transfer_syntaxes[num_transfer_syntaxes];
        } dcerpc_ctx_list;
 
+       /*
+         a schannel bind blob - used in auth_info
+         on a schannel bind
+       */
+       typedef [public] struct {
+               uint32 unknown1;
+               uint32 unknown2;
+               astring domain;
+               astring hostname;
+       } dcerpc_bind_schannel;
+
        typedef struct {
                uint16 max_xmit_frag;
                uint16 max_recv_frag;
index 7f1ba48bc8d6bbdaf1c5a0b4c0706f69fd9f02c1..d36e51f8be6cdad2fab5e37e40cafce3dc8a5abf 100644 (file)
 */
 #define ascstr_noterm        [flag(STR_NOTERM|STR_ASCII|STR_SIZE4|STR_LEN4)]  string
 
+/*
+  a null terminated ascii string
+*/
+#define astring       [flag(STR_ASCII|STR_NULLTERM)]                  string
+
 
 #define NDR_NOALIGN       LIBNDR_FLAG_NOALIGN
 #define NDR_REMAINING     LIBNDR_FLAG_REMAINING
index f8315b3af070daf1b4fa65a395a787bfa2c43f87..ea58a77c470abe56e96a437c7fcec526835ae5ff 100644 (file)
@@ -497,6 +497,17 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
                (*s) = as;
                break;
 
+       case LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM:
+               len1 = strnlen(ndr->data+ndr->offset, (ndr->data_size - ndr->offset));
+               if (len1+1 <= ndr->data_size - ndr->offset) {
+                       len1++;
+               }
+               NDR_ALLOC_N(ndr, as, (len1+1));
+               NDR_CHECK(ndr_pull_bytes(ndr, as, len1));
+               as[len1] = 0;
+               (*s) = as;
+               break;
+
        default:
                return ndr_pull_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
                                      ndr->flags & LIBNDR_STRING_FLAGS);
@@ -639,6 +650,18 @@ NTSTATUS ndr_push_string(struct ndr_push *ndr, int ndr_flags, const char *s)
                ndr->offset += c_len + 1;
                break;
 
+       case LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM:
+               NDR_PUSH_NEED_BYTES(ndr, c_len + 1);
+               ret = convert_string(CH_UNIX, CH_DOS, 
+                                    s, s_len+1,
+                                    ndr->data+ndr->offset, c_len + 1);
+               if (ret == -1) {
+                       return ndr_push_error(ndr, NDR_ERR_CHARCNV, 
+                                             "Bad character conversion");
+               }
+               ndr->offset += c_len + 1;
+               break;
+
        default:
                return ndr_push_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
                                      ndr->flags & LIBNDR_STRING_FLAGS);
index ebfcdf7ff34ac03dc4f6d4d2816aa075b3bdf4b9..c271a94bf0ae2baade538bbdbb34202032df8211 100644 (file)
@@ -173,6 +173,7 @@ NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
        uint8_t full_session_key[16];
        struct schannel_state *schannel_state;
        const char *workgroup, *workstation;
+       struct dcerpc_bind_schannel bind_schannel;
 
        memcpy(full_session_key, session_key, 8);
        memset(full_session_key+8, 0, 8);
@@ -203,21 +204,17 @@ NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
        p->auth_info->auth_context_id = random();
        p->security_state = NULL;
 
-       p->auth_info->credentials = data_blob_talloc(p->mem_ctx, 
-                                                    NULL,
-                                                    8 +
-                                                    strlen(workgroup)+1 +
-                                                    strlen(workstation)+1);
-       if (!p->auth_info->credentials.data) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       /* TODO: what are these?? */
+       bind_schannel.unknown1 = 0;
+       bind_schannel.unknown2 = 3;
+       bind_schannel.domain = workgroup;
+       bind_schannel.hostname = workstation;
 
-       /* oh, this is ugly! */
-       SIVAL(p->auth_info->credentials.data, 0, 0);
-       SIVAL(p->auth_info->credentials.data, 4, 3);
-       memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
-       memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1, 
-              workstation, strlen(workstation)+1);
+       status = ndr_push_struct_blob(&p->auth_info->credentials, p->mem_ctx, &bind_schannel,
+                                     (ndr_push_flags_fn_t)ndr_push_dcerpc_bind_schannel);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
 
        /* send the authenticated bind request */
        status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
index d8da1327d500688f646654fa074afa8f8f0912fc..623d5ec24a723aa4837b79eddbfa51ddbd1eaddf 100644 (file)
@@ -483,7 +483,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
        (*p)->flags = binding->flags;
 
        if (binding->flags & DCERPC_SCHANNEL) {
-               const char *trust_password = secrets_fetch_machine_password();
+               const char *trust_password = NULL; // samdb_fetch_member_password();
                if (!trust_password) {
                        DEBUG(0,("Unable to fetch machine password\n"));
                        goto done;
@@ -635,9 +635,9 @@ NTSTATUS dcerpc_pipe_connect(struct dcerpc_pipe **p,
 
 
 /*
-  create a secondary dcerpc connection on SMB
-  the secondary connection will be on the same SMB connection, but
-  use a new fnum
+  create a secondary dcerpc connection from a primary SMB connection
+
+  the secondary connection will be on the same SMB connection, but use a new fnum
 */
 NTSTATUS dcerpc_secondary_smb(struct dcerpc_pipe *p, struct dcerpc_pipe **p2,
                              const char *pipe_name,
index 4a7b17cffb64dffd646db31da177bee93e64c663..83bc53f1dfcccdad0139e766ca8e21816d379ca2 100644 (file)
@@ -126,6 +126,7 @@ ADD_OBJ_FILES = \
                rpc_server/dcesrv_auth.o \
                rpc_server/dcesrv_crypto.o \
                rpc_server/dcesrv_crypto_ntlmssp.o \
+               rpc_server/dcesrv_crypto_schannel.o \
                rpc_server/handles.o
 #
 # End SUBSYSTEM DCERPC
index bc5376b6cedcf29e213cc548078164bad4f080f1..f73dbaf4cbf7be1de73c2b5b96d07832989a1f52 100644 (file)
@@ -97,7 +97,7 @@ struct dcesrv_handle {
 struct dcesrv_crypto_ops {
        const char *name;
        uint8 auth_type;
-       NTSTATUS (*start)(struct dcesrv_auth *auth);
+       NTSTATUS (*start)(struct dcesrv_auth *auth, DATA_BLOB *auth_blob);
        NTSTATUS (*update)(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx,
                                const DATA_BLOB in, DATA_BLOB *out);
        NTSTATUS (*session_info)(struct dcesrv_auth *auth, struct auth_session_info **session_info);
index df1a8200397407a6030110db2f4517a1e584da1e..6d08cca5fc1503914eaa1a5662b71463b759b4a3 100644 (file)
@@ -56,7 +56,7 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
                return False;
        }
 
-       status = dcesrv_crypto_start(&dce_conn->auth_state);
+       status = dcesrv_crypto_start(&dce_conn->auth_state, &dce_conn->auth_state.auth_info->credentials);
        if (!NT_STATUS_IS_OK(status)) {
                return False;
        }
index b8dfe46835d0eef4175a68f6a0e921d90933f15c..31039510abbe5157f5de304588aa6880ab113f04 100644 (file)
@@ -68,9 +68,9 @@ NTSTATUS dcesrv_crypto_select_type(struct dcesrv_connection *dce_conn,
 /*
   start crypto state
 */
-NTSTATUS dcesrv_crypto_start(struct dcesrv_auth *auth) 
+NTSTATUS dcesrv_crypto_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob
 {
-       return auth->crypto_ctx.ops->start(auth);
+       return auth->crypto_ctx.ops->start(auth, auth_blob);
 }
 
 /*
@@ -138,10 +138,8 @@ void dcesrv_crypto_end(struct dcesrv_auth *auth)
 const struct dcesrv_crypto_ops *dcesrv_crypto_backend_bytype(uint8_t auth_type)
 {
        switch (auth_type) {
-#if 0
                case DCERPC_AUTH_TYPE_SCHANNEL:
                        return dcesrv_crypto_schannel_get_ops();
-#endif
                case DCERPC_AUTH_TYPE_NTLMSSP:
                        return dcesrv_crypto_ntlmssp_get_ops();
        }
index 5fb6345599679734bdbcfd731873500bb1ebd193..a0bb153ef2c79eb6ef1b43254671f75c131aa12d 100644 (file)
@@ -22,8 +22,7 @@
 */
 
 /*
-  this provides a crypto interface to the various backends (such as
-  NTLMSSP and SCHANNEL) for the rpc server code
+  this provides the NTLMSSP backend for server side rpc
 */
 
 #include "includes.h"
 /*
   start crypto state
 */
-static NTSTATUS dcesrv_crypto_ntlmssp_start(struct dcesrv_auth *auth)
+static NTSTATUS dcesrv_crypto_ntlmssp_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob)
 {
        struct auth_ntlmssp_state *ntlmssp = NULL;
        NTSTATUS status;
 
+       /* TODO: we should parse the auth_blob and remember the client
+          hostname and target domain, then check against the auth3
+          bind packet */
+       
        status = auth_ntlmssp_start(&ntlmssp);
 
        auth->crypto_ctx.private_data = ntlmssp;
diff --git a/source4/rpc_server/dcesrv_crypto_schannel.c b/source4/rpc_server/dcesrv_crypto_schannel.c
new file mode 100644 (file)
index 0000000..a9256fb
--- /dev/null
@@ -0,0 +1,170 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   server side dcerpc authentication code - schannel auth/crypto code
+
+   Copyright (C) Andrew Tridgell 2004
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct srv_schannel_state {
+       TALLOC_CTX *mem_ctx;
+       struct dcerpc_bind_schannel bind_info;
+       struct schannel_state *state;
+};
+
+/*
+  start crypto state
+*/
+static NTSTATUS dcesrv_crypto_schannel_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob)
+{
+       struct srv_schannel_state *schannel = NULL;
+       NTSTATUS status;
+       TALLOC_CTX *mem_ctx;
+       uint8_t session_key[16];
+
+       mem_ctx = talloc_init("schannel_start");
+       if (!mem_ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       schannel = talloc_p(mem_ctx, struct srv_schannel_state);
+       if (!schannel) {
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       schannel->mem_ctx = mem_ctx;
+
+       /* parse the schannel startup blob */
+       status = ndr_pull_struct_blob(auth_blob, mem_ctx, &schannel->bind_info, 
+                                     (ndr_pull_flags_fn_t)ndr_pull_dcerpc_bind_schannel);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* pull the session key for this client */
+       status = schannel_fetch_session_key(mem_ctx, schannel->bind_info.hostname, session_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_INVALID_HANDLE;
+       }
+       
+       /* start up the schannel server code */
+       status = schannel_start(&schannel->state, session_key, False);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       auth->crypto_ctx.private_data = schannel;
+
+       return status;
+}
+
+/*
+  update crypto state
+*/
+static NTSTATUS dcesrv_crypto_schannel_update(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx, 
+                                               const DATA_BLOB in, DATA_BLOB *out) 
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  seal a packet
+*/
+static NTSTATUS dcesrv_crypto_schannel_seal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
+                                               uint8_t *data, size_t length, DATA_BLOB *sig)
+{
+       struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
+
+       return schannel_seal_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig);
+}
+
+/*
+  sign a packet
+*/
+static NTSTATUS dcesrv_crypto_schannel_sign(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
+                                               const uint8_t *data, size_t length, DATA_BLOB *sig) 
+{
+       struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
+
+       return schannel_sign_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig);
+}
+
+/*
+  check a packet signature
+*/
+static NTSTATUS dcesrv_crypto_schannel_check_sig(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
+                                               const uint8_t *data, size_t length, const DATA_BLOB *sig)
+{
+       struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
+
+       return schannel_check_packet(srv_schannel_state->state, data, length, sig);
+}
+
+/*
+  unseal a packet
+*/
+static NTSTATUS dcesrv_crypto_schannel_unseal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
+                                               uint8_t *data, size_t length, DATA_BLOB *sig)
+{
+       struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
+
+       return schannel_unseal_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig);
+}
+
+/*
+  end crypto state
+*/
+static void dcesrv_crypto_schannel_end(struct dcesrv_auth *auth)
+{
+       struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
+
+       if (srv_schannel_state == NULL) {
+               return;
+       }
+
+       schannel_end(&srv_schannel_state->state);
+
+       talloc_destroy(srv_schannel_state->mem_ctx);
+
+       auth->crypto_ctx.private_data = NULL;
+}
+
+static const struct dcesrv_crypto_ops dcesrv_crypto_schannel_ops = {
+       .name           = "schannel",
+       .auth_type      = DCERPC_AUTH_TYPE_SCHANNEL,
+       .start          = dcesrv_crypto_schannel_start,
+       .update         = dcesrv_crypto_schannel_update,
+       .seal           = dcesrv_crypto_schannel_seal,
+       .sign           = dcesrv_crypto_schannel_sign,
+       .check_sig      = dcesrv_crypto_schannel_check_sig,
+       .unseal         = dcesrv_crypto_schannel_unseal,
+       .end            = dcesrv_crypto_schannel_end
+};
+
+/*
+  startup the cryptographic side of an authenticated dcerpc server
+*/
+const struct dcesrv_crypto_ops *dcesrv_crypto_schannel_get_ops(void)
+{
+       return &dcesrv_crypto_schannel_ops;
+}
index f4190f21b46e72c87425b74f577588dbd286d796..eaa5013572cb85c8d503bb3872f66cd093112097 100644 (file)
@@ -88,13 +88,17 @@ NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
        ldb_msg_add_value(ldb, &msg, "sessionKey", &val);
        ldb_msg_add_string(ldb, &msg, "expiry", s);
 
+       ldb_delete(ldb, msg.dn);
+
        ret = ldb_add(ldb, &msg);
-       ldb_close(ldb);
 
        if (ret != 0) {
+               DEBUG(1,("Unable to add %s to session key db - %s\n", msg.dn, ldb_errstring(ldb)));
+               ldb_close(ldb);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
+       ldb_close(ldb);
        return NT_STATUS_OK;
 }
 
@@ -110,13 +114,20 @@ NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
        struct ldb_message **res;
        int ret;
        const struct ldb_val *val;
+       char *expr=NULL;
 
        ldb = schannel_db_connect(mem_ctx);
        if (ldb == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, "(dn=%s)", NULL, &res);
+       expr = talloc_asprintf(mem_ctx, "(dn=%s)", computer_name);
+       if (expr == NULL) {
+               ldb_close(ldb);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, expr, NULL, &res);
        if (ret != 1) {
                ldb_close(ldb);
                return NT_STATUS_INVALID_HANDLE;