- modified the dcerpc client security code to be generic, so ntlmssp
authorAndrew Tridgell <tridge@samba.org>
Tue, 10 Feb 2004 10:22:12 +0000 (10:22 +0000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 10 Feb 2004 10:22:12 +0000 (10:22 +0000)
  and schannel are both instances of possible security modules

- added schannel sign and sign/seal support to the dcerpc client
  code. You select it with binding options of "schannel,sign" or
  "schannel,seal".
(This used to be commit 05db0b9d942cad8f1dd574dc35b759e5e79d4195)

14 files changed:
source4/Makefile.in
source4/include/includes.h
source4/lib/crypto/hmacmd5.c
source4/lib/hmacmd5.c
source4/libcli/auth/schannel.c [new file with mode: 0644]
source4/libcli/auth/schannel.h [new file with mode: 0644]
source4/librpc/idl/dcerpc.idl
source4/librpc/rpc/dcerpc.c
source4/librpc/rpc/dcerpc.h
source4/librpc/rpc/dcerpc_auth.c
source4/librpc/rpc/dcerpc_ntlm.c [new file with mode: 0644]
source4/librpc/rpc/dcerpc_schannel.c [new file with mode: 0644]
source4/librpc/rpc/dcerpc_smb.c
source4/librpc/rpc/dcerpc_util.c

index f5f0d203b400df6c802e068dc427287567c1179c..aa099a9945c80bf42e109dd31ed14983bd44a7ef 100644 (file)
@@ -156,10 +156,11 @@ LIBRAW_NDR_OBJ = librpc/ndr/ndr.o librpc/ndr/ndr_basic.o librpc/ndr/ndr_sec.o \
 
 LIBRAW_RPC_OBJ = librpc/rpc/dcerpc.o librpc/rpc/dcerpc_auth.o \
                librpc/rpc/dcerpc_util.o \
+               librpc/rpc/dcerpc_schannel.o librpc/rpc/dcerpc_ntlm.o \
                librpc/rpc/dcerpc_smb.o librpc/rpc/dcerpc_tcp.o
 
 LIBNTLMSSP_OBJ = libcli/auth/ntlmssp.o libcli/auth/ntlmssp_parse.o \
-               libcli/auth/ntlmssp_sign.o
+               libcli/auth/ntlmssp_sign.o libcli/auth/schannel.o
 
 LIBCLIAUTH_OBJ = $(LIBNTLMSSP_OBJ) libcli/auth/credentials.o
 
@@ -217,7 +218,7 @@ SMBD_LIBS = $(LIBS) $(SMB_LIBS) $(PROCESS_MODEL_LIBS) $(DCERPC_LIBS) $(AUTH_LIBS
 
 CLIENT_OBJ1 = client/client.o client/clitar.o libcli/raw/clirewrite.o
 
-CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) \
+CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(SECRETS_OBJ) \
             $(LIB_OBJ) \
              $(READLINE_OBJ) $(POPT_LIB_OBJ)
 
@@ -231,16 +232,16 @@ SMBTORTURE_OBJS = $(TORTURE_OBJS) $(SECRETS_OBJ) $(LIBSMB_OBJ) $(PARAM_OBJ) $(LI
 SMBTORTURE_LIBS = $(LIBS)
 
 GENTEST_OBJ = torture/gentest.o torture/torture_util.o $(LIBSMB_OBJ) $(PARAM_OBJ) \
-               $(LIB_OBJ) libcli/raw/clirewrite.o
+               $(LIB_OBJ) $(SECRETS_OBJ) libcli/raw/clirewrite.o
 
 MASKTEST_OBJ = torture/masktest.o $(LIBSMB_OBJ) $(PARAM_OBJ) \
-               $(LIB_OBJ) libcli/raw/clirewrite.o
+               $(LIB_OBJ) $(SECRETS_OBJ) libcli/raw/clirewrite.o
 
 LOCKTEST_OBJ = torture/locktest.o $(LIBSMB_OBJ) $(PARAM_OBJ) \
-               $(LIB_OBJ) libcli/raw/clirewrite.o
+               $(LIB_OBJ) $(SECRETS_OBJ) libcli/raw/clirewrite.o
 
 NDRDUMP_OBJ = utils/ndrdump.o utils/rewrite.o \
-               $(LIBSMB_OBJ) $(PARAM_OBJ) $(LIB_OBJ)
+               $(LIBSMB_OBJ) $(PARAM_OBJ) $(LIB_OBJ) $(SECRETS_OBJ)
 
 PROTO_OBJ = $(SMBD_OBJ_SRV) \
            $(SMBD_OBJ_MAIN) $(PROCESS_MODEL_OBJS) \
@@ -493,6 +494,12 @@ proto_test:
 
 .PHONY: headers proto
 
+etags:
+       etags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+ctags:
+       ctags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
 realclean: clean delheaders
        -rm -f config.log bin/.dummy
 
index 607392b1585c7930e20dc06959e0373b9761ec9c..dc83f554309823f17d442e26e75fe56e773688aa 100644 (file)
@@ -741,6 +741,7 @@ extern int errno;
 #include "hmacmd5.h"
 
 #include "libcli/auth/ntlmssp.h"
+#include "libcli/auth/schannel.h"
 
 #include "auth/auth.h"
 #include "passdb/passdb.h"
index f436fd30c0e8e67c20ce550e96ae8dd0535e2c34..8ca7dba841132b43be54dad2b94b765c2e9ecdac 100644 (file)
@@ -121,7 +121,7 @@ void hmac_md5_final(uchar *digest, HMACMD5Context *ctx)
  single function to calculate an HMAC MD5 digest from data.
  use the microsoft hmacmd5 init method because the key is 16 bytes.
 ************************************************************/
-void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest)
+void hmac_md5(const uchar key[16], const uchar *data, int data_len, uchar* digest)
 {
        HMACMD5Context ctx;
        hmac_md5_init_limK_to_64(key, 16, &ctx);
index f436fd30c0e8e67c20ce550e96ae8dd0535e2c34..8ca7dba841132b43be54dad2b94b765c2e9ecdac 100644 (file)
@@ -121,7 +121,7 @@ void hmac_md5_final(uchar *digest, HMACMD5Context *ctx)
  single function to calculate an HMAC MD5 digest from data.
  use the microsoft hmacmd5 init method because the key is 16 bytes.
 ************************************************************/
-void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest)
+void hmac_md5(const uchar key[16], const uchar *data, int data_len, uchar* digest)
 {
        HMACMD5Context ctx;
        hmac_md5_init_limK_to_64(key, 16, &ctx);
diff --git a/source4/libcli/auth/schannel.c b/source4/libcli/auth/schannel.c
new file mode 100644 (file)
index 0000000..e5a786f
--- /dev/null
@@ -0,0 +1,309 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   schannel library 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"
+
+/*******************************************************************
+ Encode or Decode the sequence number (which is symmetric)
+ ********************************************************************/
+static void netsec_deal_with_seq_num(struct schannel_state *state,
+                                    const uchar packet_digest[8],
+                                    uchar seq_num[8])
+{
+       static const uchar zeros[4];
+       uchar sequence_key[16];
+       uchar digest1[16];
+
+       hmac_md5(state->session_key, zeros, sizeof(zeros), digest1);
+       hmac_md5(digest1, packet_digest, 8, sequence_key);
+       SamOEMhash(seq_num, sequence_key, 8);
+
+       state->seq_num++;
+}
+
+
+/*******************************************************************
+ Calculate the key with which to encode the data payload 
+ ********************************************************************/
+static void netsec_get_sealing_key(const uchar session_key[16],
+                                  const uchar seq_num[8],
+                                  uchar sealing_key[16]) 
+{
+       static const uchar zeros[4];
+       uchar digest2[16];
+       uchar sess_kf0[16];
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               sess_kf0[i] = session_key[i] ^ 0xf0;
+       }
+       
+       hmac_md5(sess_kf0, zeros, 4, digest2);
+       hmac_md5(digest2, seq_num, 8, sealing_key);
+}
+
+
+/*******************************************************************
+ Create a digest over the entire packet (including the data), and 
+ MD5 it with the session key.
+ ********************************************************************/
+static void schannel_digest(const uchar sess_key[16],
+                           const uchar netsec_sig[8],
+                           const uchar *confounder,
+                           const uchar *data, size_t data_len,
+                           uchar digest_final[16]) 
+{
+       uchar packet_digest[16];
+       static const uchar zeros[4];
+       struct MD5Context ctx;
+       
+       MD5Init(&ctx);
+       MD5Update(&ctx, zeros, 4);
+       MD5Update(&ctx, netsec_sig, 8);
+       if (confounder) {
+               MD5Update(&ctx, confounder, 8);
+       }
+       MD5Update(&ctx, data, data_len);
+       MD5Final(packet_digest, &ctx);
+       
+       hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final);
+}
+
+
+/*
+  unseal a packet
+*/
+NTSTATUS schannel_unseal_packet(struct schannel_state *state,
+                               uchar *data, size_t length, 
+                               DATA_BLOB *sig)
+{
+       uchar digest_final[16];
+       uchar confounder[8];
+       uchar seq_num[8];
+       uchar sealing_key[16];
+       static const uchar netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
+
+       if (sig->length != 32) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       memcpy(confounder, sig->data+24, 8);
+
+       RSIVAL(seq_num, 0, state->seq_num);
+       SIVAL(seq_num, 4, state->initiator?0:0x80);
+
+       netsec_get_sealing_key(state->session_key, seq_num, sealing_key);
+       SamOEMhash(confounder, sealing_key, 8);
+       SamOEMhash(data, sealing_key, length);
+
+       schannel_digest(state->session_key, 
+                       netsec_sig, confounder, 
+                       data, length, digest_final);
+
+       if (memcmp(digest_final, sig->data+16, 8) != 0) {
+               dump_data_pw("calc digest:", digest_final, 8);
+               dump_data_pw("wire digest:", sig->data+16, 8);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+       if (memcmp(seq_num, sig->data+8, 8) != 0) {
+               dump_data_pw("calc seq num:", seq_num, 8);
+               dump_data_pw("wire seq num:", sig->data+8, 8);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  check the signature on a packet
+*/
+NTSTATUS schannel_check_packet(struct schannel_state *state, 
+                              const uchar *data, size_t length, 
+                              const DATA_BLOB *sig)
+{
+       uchar digest_final[16];
+       uchar seq_num[8];
+       static const uchar netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
+
+       if (sig->length != 32) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       RSIVAL(seq_num, 0, state->seq_num);
+       SIVAL(seq_num, 4, state->initiator?0:0x80);
+
+       dump_data_pw("seq_num:\n", seq_num, 8);
+       dump_data_pw("sess_key:\n", state->session_key, 16);
+
+       schannel_digest(state->session_key, 
+                       netsec_sig, NULL, 
+                       data, length, digest_final);
+
+       netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+       if (memcmp(seq_num, sig->data+8, 8) != 0) {
+               dump_data_pw("calc seq num:", seq_num, 8);
+               dump_data_pw("wire seq num:", sig->data+8, 8);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (memcmp(digest_final, sig->data+16, 8) != 0) {
+               dump_data_pw("calc digest:", digest_final, 8);
+               dump_data_pw("wire digest:", sig->data+16, 8);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  seal a packet
+*/
+NTSTATUS schannel_seal_packet(struct schannel_state *state, 
+                             uchar *data, size_t length, 
+                             DATA_BLOB *sig)
+{
+       uchar digest_final[16];
+       uchar confounder[8];
+       uchar seq_num[8];
+       uchar sealing_key[16];
+       static const uchar netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
+
+       generate_random_buffer(confounder, 8, False);
+
+       RSIVAL(seq_num, 0, state->seq_num);
+       SIVAL(seq_num, 4, state->initiator?0x80:0);
+
+       schannel_digest(state->session_key, 
+                       netsec_sig, confounder, 
+                       data, length, digest_final);
+
+       netsec_get_sealing_key(state->session_key, seq_num, sealing_key);
+       SamOEMhash(confounder, sealing_key, 8);
+       SamOEMhash(data, sealing_key, length);
+
+       netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+       if (!state->signature.data) {
+               state->signature = data_blob_talloc(state->mem_ctx, NULL, 32);
+               if (!state->signature.data) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+       (*sig) = state->signature;
+
+       memcpy(sig->data, netsec_sig, 8);
+       memcpy(sig->data+8, seq_num, 8);
+       memcpy(sig->data+16, digest_final, 8);
+       memcpy(sig->data+24, confounder, 8);
+
+       dump_data_pw("signature:", sig->data+ 0, 8);
+       dump_data_pw("seq_num  :", sig->data+ 8, 8);
+       dump_data_pw("digest   :", sig->data+16, 8);
+       dump_data_pw("confound :", sig->data+24, 8);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  sign a packet
+*/
+NTSTATUS schannel_sign_packet(struct schannel_state *state, 
+                             const uchar *data, size_t length, 
+                             DATA_BLOB *sig)
+{
+       uchar digest_final[16];
+       uchar seq_num[8];
+       static const uchar netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
+
+       RSIVAL(seq_num, 0, state->seq_num);
+       SIVAL(seq_num, 4, state->initiator?0x80:0);
+
+       schannel_digest(state->session_key, 
+                       netsec_sig, NULL, 
+                       data, length, digest_final);
+
+       netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+       if (!state->signature.data) {
+               state->signature = data_blob_talloc(state->mem_ctx, NULL, 32);
+               if (!state->signature.data) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+       (*sig) = state->signature;
+
+       memcpy(sig->data, netsec_sig, 8);
+       memcpy(sig->data+8, seq_num, 8);
+       memcpy(sig->data+16, digest_final, 8);
+       memset(sig->data+24, 0, 8);
+
+       dump_data_pw("signature:", sig->data+ 0, 8);
+       dump_data_pw("seq_num  :", sig->data+ 8, 8);
+       dump_data_pw("digest   :", sig->data+16, 8);
+       dump_data_pw("confound :", sig->data+24, 8);
+
+       return NT_STATUS_OK;
+}
+
+/*
+  destroy an schannel context
+ */
+void schannel_end(struct schannel_state **state)
+{
+       talloc_destroy((*state)->mem_ctx);
+       (*state) = NULL;
+}
+
+/*
+  create an schannel context state
+*/
+NTSTATUS schannel_start(struct schannel_state **state,
+                       uint8 session_key[16],
+                       BOOL initiator)
+{
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("schannel_state");
+       if (!mem_ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*state) = talloc_p(mem_ctx, struct schannel_state);
+       if (!(*state)) {
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*state)->mem_ctx = mem_ctx;
+       memcpy((*state)->session_key, session_key, 16);
+       (*state)->initiator = initiator;
+       (*state)->signature = data_blob(NULL, 0);
+       (*state)->seq_num = 0;
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/libcli/auth/schannel.h b/source4/libcli/auth/schannel.h
new file mode 100644 (file)
index 0000000..7b710d7
--- /dev/null
@@ -0,0 +1,35 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   schannel library 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 schannel_state {
+       TALLOC_CTX *mem_ctx;
+       uint8 session_key[16];
+       uint32 seq_num;
+       BOOL initiator;
+       DATA_BLOB signature;
+};
+
+#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
+#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
+
index 4d09a9ec4f12682303e95e588852ef99d97bce27..334ae8ce5dbf2c967b9bcf0f81640d54eed29489 100644 (file)
@@ -99,9 +99,10 @@ interface dcerpc
        } dcerpc_fault;
 
 
-       const uint8 DCERPC_AUTH_TYPE_NONE    = 0;
-       const uint8 DCERPC_AUTH_TYPE_KRB5    = 1;
-       const uint8 DCERPC_AUTH_TYPE_NTLMSSP = 10;
+       const uint8 DCERPC_AUTH_TYPE_NONE     = 0;
+       const uint8 DCERPC_AUTH_TYPE_KRB5     = 1;
+       const uint8 DCERPC_AUTH_TYPE_NTLMSSP  = 10;
+       const uint8 DCERPC_AUTH_TYPE_SCHANNEL = 68;
        
        const uint8 DCERPC_AUTH_LEVEL_NONE      = 1;
        const uint8 DCERPC_AUTH_LEVEL_CONNECT   = 2;
index d00f2c2986945e893b90bcc2971c352105fd9c31..8987cead92fde8e36b1bcca868ab67ef8677c4da 100644 (file)
@@ -42,7 +42,7 @@ struct dcerpc_pipe *dcerpc_pipe_init(void)
        p->mem_ctx = mem_ctx;
        p->call_id = 1;
        p->auth_info = NULL;
-       p->ntlmssp_state = NULL;
+       p->security_state = NULL;
        p->flags = 0;
        p->srv_max_xmit_frag = 0;
        p->srv_max_recv_frag = 0;
@@ -56,8 +56,8 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p)
        if (!p) return;
        p->reference_count--;
        if (p->reference_count <= 0) {
-               if (p->ntlmssp_state) {
-                       ntlmssp_end(&p->ntlmssp_state);
+               if (p->security_state) {
+                       p->security_state->security_end(p->security_state);
                }
                p->transport.shutdown_pipe(p);
                talloc_destroy(p->mem_ctx);
@@ -128,7 +128,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
        DATA_BLOB auth_blob;
 
        /* non-signed packets are simpler */
-       if (!p->auth_info || !p->ntlmssp_state) {
+       if (!p->auth_info || !p->security_state) {
                return dcerpc_pull(blob, mem_ctx, pkt);
        }
 
@@ -182,17 +182,17 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
        /* check signature or unseal the packet */
        switch (p->auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
-               status = ntlmssp_unseal_packet(p->ntlmssp_state, 
-                                              pkt->u.response.stub_and_verifier.data, 
-                                              pkt->u.response.stub_and_verifier.length, 
-                                              &auth.credentials);
+               status = p->security_state->unseal_packet(p->security_state, 
+                                                         pkt->u.response.stub_and_verifier.data, 
+                                                         pkt->u.response.stub_and_verifier.length, 
+                                                         &auth.credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
-               status = ntlmssp_check_packet(p->ntlmssp_state, 
-                                             pkt->u.response.stub_and_verifier.data, 
-                                             pkt->u.response.stub_and_verifier.length, 
-                                             &auth.credentials);
+               status = p->security_state->check_packet(p->security_state, 
+                                                        pkt->u.response.stub_and_verifier.data, 
+                                                        pkt->u.response.stub_and_verifier.length, 
+                                                        &auth.credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_NONE:
@@ -224,7 +224,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
        struct ndr_push *ndr;
 
        /* non-signed packets are simpler */
-       if (!p->auth_info || !p->ntlmssp_state) {
+       if (!p->auth_info || !p->security_state) {
                return dcerpc_push_auth(blob, mem_ctx, pkt, p->auth_info);
        }
 
@@ -249,17 +249,17 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
        /* sign or seal the packet */
        switch (p->auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
-               status = ntlmssp_seal_packet(p->ntlmssp_state, 
-                                            ndr->data + DCERPC_REQUEST_LENGTH, 
-                                            ndr->offset - DCERPC_REQUEST_LENGTH,
-                                            &p->auth_info->credentials);
+               status = p->security_state->seal_packet(p->security_state, 
+                                                       ndr->data + DCERPC_REQUEST_LENGTH, 
+                                                       ndr->offset - DCERPC_REQUEST_LENGTH,
+                                                       &p->auth_info->credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
-               status = ntlmssp_sign_packet(p->ntlmssp_state, 
-                                            ndr->data + DCERPC_REQUEST_LENGTH, 
-                                            ndr->offset - DCERPC_REQUEST_LENGTH,
-                                            &p->auth_info->credentials);
+               status = p->security_state->sign_packet(p->security_state, 
+                                                       ndr->data + DCERPC_REQUEST_LENGTH, 
+                                                       ndr->offset - DCERPC_REQUEST_LENGTH,
+                                                       &p->auth_info->credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_NONE:
index c5cf07ddba4ccbb38853b2d664dfad54ef1cc74b..55c81c374e505570cad5833e811137544f2fe8fa 100644 (file)
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-/*
-  see http://www.opengroup.org/onlinepubs/9629399/chap12.htm for details
-  of these structures
+enum dcerpc_transport_t {NCACN_NP, NCACN_IP_TCP};
 
-  note that the structure definitions here don't include some of the
-  fields that are wire-artifacts. Those are put on the wire by the
-  marshalling/unmarshalling routines in decrpc.c
+/*
+  this defines a generic security context for signed/sealed dcerpc pipes.
 */
-
-enum dcerpc_transport_t {NCACN_NP, NCACN_IP_TCP};
+struct dcerpc_security {
+       void *private;
+       NTSTATUS (*unseal_packet)(struct dcerpc_security *, 
+                                 uchar *data, size_t length, DATA_BLOB *sig);
+       NTSTATUS (*check_packet)(struct dcerpc_security *, 
+                                const uchar *data, size_t length, const DATA_BLOB *sig);
+       NTSTATUS (*seal_packet)(struct dcerpc_security *, 
+                                uchar *data, size_t length, DATA_BLOB *sig);
+       NTSTATUS (*sign_packet)(struct dcerpc_security *, 
+                               const uchar *data, size_t length, DATA_BLOB *sig);
+       void (*security_end)(struct dcerpc_security *);
+};
 
 
 struct dcerpc_pipe {
@@ -39,7 +46,7 @@ struct dcerpc_pipe {
        uint32 srv_max_xmit_frag;
        uint32 srv_max_recv_frag;
        unsigned flags;
-       struct ntlmssp_state *ntlmssp_state;
+       struct dcerpc_security *security_state;
        struct dcerpc_auth *auth_info;
        const char *binding_string;
        
@@ -73,6 +80,8 @@ struct dcerpc_pipe {
 #define DCERPC_PUSH_BIGENDIAN   64
 #define DCERPC_PULL_BIGENDIAN  128
 
+#define DCERPC_SCHANNEL        256
+
 /*
   this is used to find pointers to calls
 */
index 99ea03c216fd5231aa6287c0e4c57d8ee1e2234d..2b01ad2d4e9e452864d9b9cad3dae0785c07fe30 100644 (file)
 
 #include "includes.h"
 
-/*
-  do a simple ntlm style authentication on a dcerpc pipe
-*/
-NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p,
-                              const char *uuid, unsigned version,
-                              const char *domain,
-                              const char *username,
-                              const char *password)
-{
-       NTSTATUS status;
-       struct ntlmssp_state *state;
-       TALLOC_CTX *mem_ctx;
-       DATA_BLOB credentials;
-
-       mem_ctx = talloc_init("dcerpc_bind_auth_ntlm");
-       if (!mem_ctx) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       status = ntlmssp_client_start(&state);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       status = ntlmssp_set_domain(state, domain);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
-       }
-       
-       status = ntlmssp_set_username(state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
-       }
-
-       status = ntlmssp_set_password(state, password);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
-       }
-
-       p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
-       if (!p->auth_info) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
-
-       p->auth_info->auth_type = DCERPC_AUTH_TYPE_NTLMSSP;
-       
-       if (p->flags & DCERPC_SEAL) {
-               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
-               state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL;
-       } else if (p->flags & DCERPC_SIGN) {
-               state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
-               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
-       } else {
-               state->neg_flags &= ~(NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL);
-               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_NONE;
-       }
-       p->auth_info->auth_pad_length = 0;
-       p->auth_info->auth_reserved = 0;
-       p->auth_info->auth_context_id = random();
-       p->auth_info->credentials = data_blob(NULL, 0);
-       p->ntlmssp_state = NULL;
-
-       status = ntlmssp_update(state, 
-                               p->auth_info->credentials,
-                               &credentials);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               goto done;
-       }
-
-       p->auth_info->credentials = data_blob_talloc(mem_ctx, 
-                                                    credentials.data, 
-                                                    credentials.length);
-       data_blob_free(&credentials);
-
-       status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
-       }
-
-
-       status = ntlmssp_update(state, 
-                               p->auth_info->credentials, 
-                               &credentials);
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               goto done;
-       }
-
-       p->auth_info->credentials = data_blob_talloc(mem_ctx, 
-                                                    credentials.data, 
-                                                    credentials.length);
-       data_blob_free(&credentials);
-
-       status = dcerpc_auth3(p, mem_ctx);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
-       }
-
-       p->ntlmssp_state = state;
-
-       switch (p->auth_info->auth_level) {
-       case DCERPC_AUTH_LEVEL_PRIVACY:
-       case DCERPC_AUTH_LEVEL_INTEGRITY:
-               /* setup for signing */
-               status = ntlmssp_sign_init(state);
-               break;
-       }
-
-done:
-       talloc_destroy(mem_ctx);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               p->ntlmssp_state = NULL;
-               p->auth_info = NULL;
-       }
-
-       return status;
-}
-
-
 /*
   do a non-athenticated dcerpc bind
 */
diff --git a/source4/librpc/rpc/dcerpc_ntlm.c b/source4/librpc/rpc/dcerpc_ntlm.c
new file mode 100644 (file)
index 0000000..8bfe0cb
--- /dev/null
@@ -0,0 +1,197 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   dcerpc authentication operations
+
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/*
+  wrappers for the ntlmssp_*() functions
+*/
+static NTSTATUS ntlm_unseal_packet(struct dcerpc_security *dcerpc_security, 
+                                 uchar *data, size_t length, DATA_BLOB *sig)
+{
+       struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+       return ntlmssp_unseal_packet(ntlmssp_state, data, length, sig);
+}
+
+static NTSTATUS ntlm_check_packet(struct dcerpc_security *dcerpc_security, 
+                                 const uchar *data, size_t length, 
+                                 const DATA_BLOB *sig)
+{
+       struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+       return ntlmssp_check_packet(ntlmssp_state, data, length, sig);
+}
+
+static NTSTATUS ntlm_seal_packet(struct dcerpc_security *dcerpc_security, 
+                                uchar *data, size_t length, 
+                                DATA_BLOB *sig)
+{
+       struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+       return ntlmssp_seal_packet(ntlmssp_state, data, length, sig);
+}
+
+static NTSTATUS ntlm_sign_packet(struct dcerpc_security *dcerpc_security, 
+                                const uchar *data, size_t length, 
+                                DATA_BLOB *sig)
+{
+       struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+       return ntlmssp_sign_packet(ntlmssp_state, data, length, sig);
+}
+
+static void ntlm_security_end(struct dcerpc_security *dcerpc_security)
+{
+       struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+       return ntlmssp_end(&ntlmssp_state);
+}
+
+
+
+/*
+  do ntlm style authentication on a dcerpc pipe
+*/
+NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p,
+                              const char *uuid, unsigned version,
+                              const char *domain,
+                              const char *username,
+                              const char *password)
+{
+       NTSTATUS status;
+       struct ntlmssp_state *state;
+       TALLOC_CTX *mem_ctx;
+       DATA_BLOB credentials;
+
+       mem_ctx = talloc_init("dcerpc_bind_auth_ntlm");
+       if (!mem_ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = ntlmssp_client_start(&state);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = ntlmssp_set_domain(state, domain);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+       
+       status = ntlmssp_set_username(state, username);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = ntlmssp_set_password(state, password);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
+       if (!p->auth_info) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       p->auth_info->auth_type = DCERPC_AUTH_TYPE_NTLMSSP;
+       
+       if (p->flags & DCERPC_SEAL) {
+               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+               state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL;
+       } else {
+               /* ntlmssp does not work on dcerpc with
+                  AUTH_LEVEL_NONE */
+               state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+       }
+       p->auth_info->auth_pad_length = 0;
+       p->auth_info->auth_reserved = 0;
+       p->auth_info->auth_context_id = random();
+       p->auth_info->credentials = data_blob(NULL, 0);
+       p->security_state = NULL;
+
+       status = ntlmssp_update(state, 
+                               p->auth_info->credentials,
+                               &credentials);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               goto done;
+       }
+
+       p->auth_info->credentials = data_blob_talloc(mem_ctx, 
+                                                    credentials.data, 
+                                                    credentials.length);
+       data_blob_free(&credentials);
+
+       status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+
+       status = ntlmssp_update(state, 
+                               p->auth_info->credentials, 
+                               &credentials);
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               goto done;
+       }
+
+       p->auth_info->credentials = data_blob_talloc(mem_ctx, 
+                                                    credentials.data, 
+                                                    credentials.length);
+       data_blob_free(&credentials);
+
+       status = dcerpc_auth3(p, mem_ctx);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
+       if (!p->security_state) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       p->security_state->private = state;
+       p->security_state->unseal_packet = ntlm_unseal_packet;
+       p->security_state->check_packet = ntlm_check_packet;
+       p->security_state->seal_packet = ntlm_seal_packet;
+       p->security_state->sign_packet = ntlm_sign_packet;
+       p->security_state->security_end = ntlm_security_end;
+
+       switch (p->auth_info->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* setup for signing */
+               status = ntlmssp_sign_init(state);
+               break;
+       }
+
+done:
+       talloc_destroy(mem_ctx);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               p->security_state = NULL;
+               p->auth_info = NULL;
+       }
+
+       return status;
+}
+
+
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
new file mode 100644 (file)
index 0000000..9ef16a0
--- /dev/null
@@ -0,0 +1,219 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   dcerpc schannel operations
+
+   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"
+
+/*
+  wrappers for the schannel_*() functions
+*/
+static NTSTATUS schan_unseal_packet(struct dcerpc_security *dcerpc_security, 
+                                 uchar *data, size_t length, DATA_BLOB *sig)
+{
+       struct schannel_state *schannel_state = dcerpc_security->private;
+       return schannel_unseal_packet(schannel_state, data, length, sig);
+}
+
+static NTSTATUS schan_check_packet(struct dcerpc_security *dcerpc_security, 
+                                 const uchar *data, size_t length, 
+                                 const DATA_BLOB *sig)
+{
+       struct schannel_state *schannel_state = dcerpc_security->private;
+       return schannel_check_packet(schannel_state, data, length, sig);
+}
+
+static NTSTATUS schan_seal_packet(struct dcerpc_security *dcerpc_security, 
+                                uchar *data, size_t length, 
+                                DATA_BLOB *sig)
+{
+       struct schannel_state *schannel_state = dcerpc_security->private;
+       return schannel_seal_packet(schannel_state, data, length, sig);
+}
+
+static NTSTATUS schan_sign_packet(struct dcerpc_security *dcerpc_security, 
+                                const uchar *data, size_t length, 
+                                DATA_BLOB *sig)
+{
+       struct schannel_state *schannel_state = dcerpc_security->private;
+       return schannel_sign_packet(schannel_state, data, length, sig);
+}
+
+static void schan_security_end(struct dcerpc_security *dcerpc_security)
+{
+       struct schannel_state *schannel_state = dcerpc_security->private;
+       return schannel_end(&schannel_state);
+}
+
+
+/*
+  do a schannel style bind on a dcerpc pipe. The username is usually
+  of the form HOSTNAME$ and the password is the domain trust password
+*/
+NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
+                                  const char *uuid, unsigned version,
+                                  const char *domain,
+                                  const char *username,
+                                  const char *password)
+{
+       NTSTATUS status;
+       struct dcerpc_pipe *p2;
+       struct netr_ServerReqChallenge r;
+       struct netr_ServerAuthenticate2 a;
+       uint8 mach_pwd[16];
+       uint8 session_key[16];
+       struct netr_CredentialState creds;
+       struct schannel_state *schannel_state;
+       const char *workgroup, *workstation;
+       uint32 negotiate_flags = 0;
+
+       workstation = username;
+       workgroup = domain;
+
+       /*
+         step 1 - establish a netlogon connection, with no authentication
+       */
+       status = dcerpc_secondary_smb(p, &p2, 
+                                     DCERPC_NETLOGON_NAME, 
+                                     DCERPC_NETLOGON_UUID, 
+                                     DCERPC_NETLOGON_VERSION);
+
+
+       /*
+         step 2 - request a netlogon challenge
+       */
+       r.in.server_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dcerpc_server_name(p));
+       r.in.computer_name = workstation;
+       generate_random_buffer(r.in.credentials.data, sizeof(r.in.credentials.data), False);
+
+       status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /*
+         step 3 - authenticate on the netlogon pipe
+       */
+       E_md4hash(password, mach_pwd);
+       creds_client_init(&creds, &r.in.credentials, &r.out.credentials, mach_pwd,
+                         &a.in.credentials);
+
+       a.in.server_name = r.in.server_name;
+       a.in.username = talloc_asprintf(p->mem_ctx, "%s$", workstation);
+       if (lp_server_role() == ROLE_DOMAIN_BDC) {
+               a.in.secure_channel_type = SEC_CHAN_BDC;
+       } else {
+               a.in.secure_channel_type = SEC_CHAN_WKSTA;
+       }
+       a.in.computer_name = workstation;
+       a.in.negotiate_flags = &negotiate_flags;
+       a.out.negotiate_flags = &negotiate_flags;
+
+       status = dcerpc_netr_ServerAuthenticate2(p2, p->mem_ctx, &a);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       if (!creds_client_check(&creds, &a.out.credentials)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /*
+         the schannel session key is now in creds.session_key
+       */
+
+
+       /*
+         step 4 - perform a bind with security type schannel
+       */
+       p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
+       if (!p->auth_info) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
+       
+       if (p->flags & DCERPC_SEAL) {
+               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+       } else {
+               /* note that DCERPC_AUTH_LEVEL_NONE does not make any 
+                  sense, and would be rejected by the server */
+               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+       }
+       p->auth_info->auth_pad_length = 0;
+       p->auth_info->auth_reserved = 0;
+       p->auth_info->auth_context_id = 1;
+       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;
+       }
+
+       /* 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);
+
+       /* send the authenticated bind request */
+       status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
+       if (!p->security_state) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       schannel_state = talloc_p(p->mem_ctx, struct schannel_state);
+       if (!schannel_state) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       memcpy(session_key, creds.session_key, 8);
+       memset(session_key+8, 0, 8);
+
+       status = schannel_start(&schannel_state, session_key, True);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       dump_data_pw("session key:\n", schannel_state->session_key, 16);
+
+       p->security_state->private = schannel_state;
+       p->security_state->unseal_packet = schan_unseal_packet;
+       p->security_state->check_packet = schan_check_packet;
+       p->security_state->seal_packet = schan_seal_packet;
+       p->security_state->sign_packet = schan_sign_packet;
+       p->security_state->security_end = schan_security_end;
+
+done:
+       return status;
+}
+
index a79e5adb1028686b961390fd3e1bfbbe04af1e00..7822231b82649961a2db6dc832eaf97ead270e19 100644 (file)
@@ -295,7 +295,6 @@ static const char *smb_peer_name(struct dcerpc_pipe *p)
        return smb->tree->session->transport->called.name;
 }
 
-
 /* 
    open a rpc connection to a named pipe 
 */
index 96f0b959e74fb253874652ef51cc3773a3affb97..ba61f28c95d2e2394139097427baee75a2224870 100644 (file)
@@ -275,6 +275,7 @@ static const struct {
 } ncacn_options[] = {
        {"sign", DCERPC_SIGN},
        {"seal", DCERPC_SEAL},
+       {"schannel", DCERPC_SCHANNEL},
        {"validate", DCERPC_DEBUG_VALIDATE_BOTH},
        {"print", DCERPC_DEBUG_PRINT_BOTH},
        {"bigendian", DCERPC_PUSH_BIGENDIAN}
@@ -481,11 +482,23 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
        
        (*p)->flags = binding->flags;
 
-       if (binding->flags & (DCERPC_SIGN | DCERPC_SEAL)) {
+       if (binding->flags & DCERPC_SCHANNEL) {
+               const char *trust_password = secrets_fetch_machine_password();
+               if (!trust_password) {
+                       DEBUG(0,("Unable to fetch machine password\n"));
+                       goto done;
+               }
+               status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, 
+                                                  lp_workgroup(), 
+                                                  lp_netbios_name(), 
+                                                  trust_password);
+       } else if (binding->flags & (DCERPC_SIGN | DCERPC_SEAL)) {
                status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
        } else {    
                status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
        }
+
+done:
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status)));
                dcerpc_pipe_close(*p);