diff options
-rw-r--r-- | source4/Makefile.in | 19 | ||||
-rw-r--r-- | source4/include/includes.h | 1 | ||||
-rw-r--r-- | source4/lib/crypto/hmacmd5.c | 2 | ||||
-rw-r--r-- | source4/lib/hmacmd5.c | 2 | ||||
-rw-r--r-- | source4/libcli/auth/schannel.c | 309 | ||||
-rw-r--r-- | source4/libcli/auth/schannel.h | 35 | ||||
-rw-r--r-- | source4/librpc/idl/dcerpc.idl | 7 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.c | 42 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.h | 27 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_auth.c | 121 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_ntlm.c | 197 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_schannel.c | 219 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_smb.c | 1 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_util.c | 15 |
14 files changed, 833 insertions, 164 deletions
diff --git a/source4/Makefile.in b/source4/Makefile.in index f5f0d203b4..aa099a9945 100644 --- a/source4/Makefile.in +++ b/source4/Makefile.in @@ -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 diff --git a/source4/include/includes.h b/source4/include/includes.h index 607392b158..dc83f55430 100644 --- a/source4/include/includes.h +++ b/source4/include/includes.h @@ -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" diff --git a/source4/lib/crypto/hmacmd5.c b/source4/lib/crypto/hmacmd5.c index f436fd30c0..8ca7dba841 100644 --- a/source4/lib/crypto/hmacmd5.c +++ b/source4/lib/crypto/hmacmd5.c @@ -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/lib/hmacmd5.c b/source4/lib/hmacmd5.c index f436fd30c0..8ca7dba841 100644 --- a/source4/lib/hmacmd5.c +++ b/source4/lib/hmacmd5.c @@ -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 index 0000000000..e5a786ff24 --- /dev/null +++ b/source4/libcli/auth/schannel.c @@ -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 index 0000000000..7b710d7cb9 --- /dev/null +++ b/source4/libcli/auth/schannel.h @@ -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 } + diff --git a/source4/librpc/idl/dcerpc.idl b/source4/librpc/idl/dcerpc.idl index 4d09a9ec4f..334ae8ce5d 100644 --- a/source4/librpc/idl/dcerpc.idl +++ b/source4/librpc/idl/dcerpc.idl @@ -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; diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index d00f2c2986..8987cead92 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -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: diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h index c5cf07ddba..55c81c374e 100644 --- a/source4/librpc/rpc/dcerpc.h +++ b/source4/librpc/rpc/dcerpc.h @@ -20,16 +20,23 @@ 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 */ diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c index 99ea03c216..2b01ad2d4e 100644 --- a/source4/librpc/rpc/dcerpc_auth.c +++ b/source4/librpc/rpc/dcerpc_auth.c @@ -23,127 +23,6 @@ #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 */ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p, diff --git a/source4/librpc/rpc/dcerpc_ntlm.c b/source4/librpc/rpc/dcerpc_ntlm.c new file mode 100644 index 0000000000..8bfe0cb5ee --- /dev/null +++ b/source4/librpc/rpc/dcerpc_ntlm.c @@ -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 index 0000000000..9ef16a0d5c --- /dev/null +++ b/source4/librpc/rpc/dcerpc_schannel.c @@ -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; +} + diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c index a79e5adb10..7822231b82 100644 --- a/source4/librpc/rpc/dcerpc_smb.c +++ b/source4/librpc/rpc/dcerpc_smb.c @@ -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 */ diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c index 96f0b959e7..ba61f28c95 100644 --- a/source4/librpc/rpc/dcerpc_util.c +++ b/source4/librpc/rpc/dcerpc_util.c @@ -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); |