From 4f0e5e069064c11a8efc407cd42412d38534d0d2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 5 Jul 2004 23:28:49 +0000 Subject: r1345: add extended security spnego support to the smb client code set lp_use_spnego = False, because I can't get it working yet but I commit it so others can help me metze (This used to be commit 2445cceba9ab9bd928c8bc50927a39509e4526b0) --- source4/include/cli_context.h | 28 +++++----- source4/libcli/raw/clisession.c | 111 ++++++++++++++++++++++++++++++++++++-- source4/libcli/raw/clitransport.c | 1 + source4/libcli/raw/clitree.c | 4 +- source4/libcli/raw/rawnegotiate.c | 47 +++++++++++----- source4/libcli/raw/smb_signing.c | 50 +++++++++++++++++ source4/param/loadparm.c | 2 +- 7 files changed, 210 insertions(+), 33 deletions(-) diff --git a/source4/include/cli_context.h b/source4/include/cli_context.h index 24dcfe7235..930017bb26 100644 --- a/source4/include/cli_context.h +++ b/source4/include/cli_context.h @@ -29,21 +29,12 @@ struct cli_request; /* forward declare */ struct cli_session; /* forward declare */ struct cli_transport; /* forward declare */ -typedef struct smb_sign_info { - void (*sign_outgoing_message)(struct cli_request *req); - BOOL (*check_incoming_message)(struct cli_request *req); - void (*free_signing_context)(struct cli_transport *transport); - void *signing_context; - - BOOL doing_signing; -} smb_sign_info; - /* context that will be and has been negotiated between the client and server */ struct cli_negotiate { /* * negotiated maximum transmit size - this is given to us by the server */ - uint_t max_xmit; + uint32_t max_xmit; /* maximum number of requests that can be multiplexed */ uint16_t max_mux; @@ -51,16 +42,24 @@ struct cli_negotiate { /* the negotiatiated protocol */ enum protocol_types protocol; - int sec_mode; /* security mode returned by negprot */ + uint8_t sec_mode; /* security mode returned by negprot */ + uint8_t key_len; + DATA_BLOB server_guid; /* server_guid */ DATA_BLOB secblob; /* cryptkey or negTokenInit blob */ uint32_t sesskey; - smb_sign_info sign_info; + struct { + void (*sign_outgoing_message)(struct cli_request *req); + BOOL (*check_incoming_message)(struct cli_request *req); + void (*free_signing_context)(struct cli_transport *transport); + void *signing_context; + BOOL doing_signing; + } sign_info; /* capabilities that the server reported */ uint32_t capabilities; - int server_zone; + int16_t server_zone; time_t server_time; uint_t readbraw_supported:1; uint_t writebraw_supported:1; @@ -187,6 +186,9 @@ struct cli_session { uint32_t pid; DATA_BLOB user_session_key; + + /* the spnego context if we use extented security */ + struct gensec_security *gensec; }; /* diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c index 780ff5837b..7ff5db59c3 100644 --- a/source4/libcli/raw/clisession.c +++ b/source4/libcli/raw/clisession.c @@ -121,10 +121,12 @@ struct cli_request *smb_raw_session_setup_send(struct cli_session *session, unio SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num); SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey); SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length); + SIVAL(req->out.vwv, VWV(8), 0); /* reserved */ SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities); cli_req_append_blob(req, &parms->spnego.in.secblob); cli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE); cli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE); + cli_req_append_string(req, parms->spnego.in.domain, STR_TERMINATE); break; } @@ -369,6 +371,109 @@ static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session, return NT_STATUS_OK; } +/**************************************************************************** + Perform a session setup (sync interface) using generic interface and the SPNEGO + style sesssetup call +****************************************************************************/ +static NTSTATUS smb_raw_session_setup_generic_spnego(struct cli_session *session, + TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + NTSTATUS status; + union smb_sesssetup s2; + + s2.generic.level = RAW_SESSSETUP_SPNEGO; + s2.spnego.in.bufsize = ~0; + s2.spnego.in.mpx_max = 50; + s2.spnego.in.vc_num = 1; + s2.spnego.in.sesskey = parms->generic.in.sesskey; + s2.spnego.in.capabilities = parms->generic.in.capabilities; + s2.spnego.in.domain = parms->generic.in.domain; + s2.spnego.in.os = "Unix"; + s2.spnego.in.lanman = "Samba"; + + cli_temp_set_signing(session->transport); + + status = gensec_client_start(&session->gensec); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status))); + goto done; + } + + status = gensec_set_domain(session->gensec, parms->generic.in.domain); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", + parms->generic.in.domain, nt_errstr(status))); + goto done; + } + + status = gensec_set_username(session->gensec, parms->generic.in.user); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", + parms->generic.in.user, nt_errstr(status))); + goto done; + } + + status = gensec_set_password(session->gensec, parms->generic.in.password); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client password: %s\n", + nt_errstr(status))); + goto done; + } + + status = gensec_start_mech_by_name(session->gensec, "spnego"); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism: %s\n", + nt_errstr(status))); + goto done; + } + + status = gensec_update(session->gensec, mem_ctx, + session->transport->negotiate.secblob, + &s2.spnego.in.secblob); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + + while(1) { + status = smb_raw_session_setup(session, mem_ctx, &s2); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + + status = gensec_update(session->gensec, mem_ctx, + s2.spnego.out.secblob, + &s2.spnego.in.secblob); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + } + +done: + if (NT_STATUS_IS_OK(status)) { + DATA_BLOB null_data_blob = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob(NULL, 0); + + status = gensec_session_key(session->gensec, &session_key); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + cli_transport_simple_set_signing(session->transport, session_key, null_data_blob); + + cli_session_set_user_session_key(session, &session_key); + + parms->generic.out.vuid = s2.spnego.out.vuid; + parms->generic.out.os = s2.spnego.out.os; + parms->generic.out.lanman = s2.spnego.out.lanman; + parms->generic.out.domain = s2.spnego.out.domain; + } + + return status; +} /**************************************************************************** Perform a session setup (sync interface) using generic interface @@ -389,14 +494,12 @@ static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session, } /* see if we should use the NT1 interface */ - if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) || - !session->transport->options.use_spnego) { + if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY)) { return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms); } /* default to using SPNEGO/NTLMSSP */ - DEBUG(0,("Need to add client SPNEGO code back in\n")); - return NT_STATUS_UNSUCCESSFUL; + return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms); } diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c index 1e9032459f..a378ac8aad 100644 --- a/source4/libcli/raw/clitransport.c +++ b/source4/libcli/raw/clitransport.c @@ -38,6 +38,7 @@ struct cli_transport *cli_transport_init(struct cli_socket *sock) transport->mem_ctx = mem_ctx; transport->socket = sock; transport->negotiate.protocol = PROTOCOL_NT1; + transport->options.use_spnego = lp_use_spnego(); transport->negotiate.max_xmit = ~0; cli_null_set_signing(transport); transport->socket->reference_count++; diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c index b35bf67c94..3b16c4c336 100644 --- a/source4/libcli/raw/clitree.c +++ b/source4/libcli/raw/clitree.c @@ -235,9 +235,7 @@ NTSTATUS cli_tree_full_connection(struct cli_tree **ret_tree, /* prepare a session setup to establish a security context */ setup.generic.level = RAW_SESSSETUP_GENERIC; setup.generic.in.sesskey = transport->negotiate.sesskey; - setup.generic.in.capabilities = CAP_UNICODE | CAP_STATUS32 | - CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | - CAP_W2K_SMBS | CAP_LARGE_READX | CAP_LARGE_WRITEX; + setup.generic.in.capabilities = transport->negotiate.capabilities; if (!user || !user[0]) { setup.generic.in.password = NULL; setup.generic.in.user = ""; diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c index 5b94ef63d8..6bf35fb26d 100644 --- a/source4/libcli/raw/rawnegotiate.c +++ b/source4/libcli/raw/rawnegotiate.c @@ -32,6 +32,7 @@ static const struct { {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"}, {PROTOCOL_LANMAN2,"LM1.2X002"}, {PROTOCOL_LANMAN2,"DOS LANMAN2.1"}, + {PROTOCOL_LANMAN2,"LANMAN2.1"}, {PROTOCOL_LANMAN2,"Samba"}, {PROTOCOL_NT1,"NT LANMAN 1.0"}, {PROTOCOL_NT1,"NT LM 0.12"}, @@ -44,12 +45,25 @@ struct cli_request *smb_negprot_send(struct cli_transport *transport, int maxpro { struct cli_request *req; int i; + uint16_t flags2 = 0; req = cli_request_setup_transport(transport, SMBnegprot, 0, 0); if (!req) { return NULL; } + flags2 |= FLAGS2_32_BIT_ERROR_CODES; + flags2 |= FLAGS2_UNICODE_STRINGS; + flags2 |= FLAGS2_EXTENDED_ATTRIBUTES; + flags2 |= FLAGS2_LONG_PATH_COMPONENTS; + flags2 |= FLAGS2_IS_LONG_NAME; + + if (transport->options.use_spnego) { + flags2 |= FLAGS2_EXTENDED_SECURITY; + } + + SSVAL(req->out.hdr,HDR_FLG2, flags2); + /* setup the protocol strings */ for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) { cli_req_append_bytes(req, "\2", 1); @@ -102,26 +116,35 @@ NTSTATUS smb_raw_negotiate(struct cli_transport *transport) transport->negotiate.max_mux = SVAL(req->in.vwv,VWV(1)+1); transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1); transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(7)+1); - transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60; + transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1); /* this time arrives in real GMT */ ntt = cli_pull_nttime(req->in.vwv, VWV(11)+1); - transport->negotiate.server_time = nt_time_to_unix(ntt); - transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1); + transport->negotiate.server_time = nt_time_to_unix(ntt); + transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60; + transport->negotiate.key_len = CVAL(req->in.vwv,VWV(16)+1); + + if (transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) { + if (req->in.data_size < 16) { + goto failed; + } + transport->negotiate.server_guid = cli_req_pull_blob(req, transport->mem_ctx, req->in.data, 16); + transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, req->in.data + 16, req->in.data_size - 16); + } else { + if (req->in.data_size < (transport->negotiate.key_len)) { + goto failed; + } + transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, req->in.data, transport->negotiate.key_len); + cli_req_pull_string(req, transport->mem_ctx, &transport->negotiate.server_domain, + req->in.data+transport->negotiate.key_len, + req->in.data_size-transport->negotiate.key_len, STR_UNICODE|STR_NOALIGN); + /* here comes the server name */ + } - transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, req->in.data, req->in.data_size); if (transport->negotiate.capabilities & CAP_RAW_MODE) { transport->negotiate.readbraw_supported = True; transport->negotiate.writebraw_supported = True; } - - /* work out if they sent us a workgroup */ - if ((transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) && - req->in.data_size > 16) { - cli_req_pull_string(req, transport->mem_ctx, &transport->negotiate.server_domain, - req->in.data+16, - req->in.data_size-16, STR_UNICODE|STR_NOALIGN); - } } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) { CLI_CHECK_WCT(req, 13); transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1)); diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c index a39f33c290..20b44a5348 100644 --- a/source4/libcli/raw/smb_signing.c +++ b/source4/libcli/raw/smb_signing.c @@ -299,6 +299,56 @@ BOOL cli_null_set_signing(struct cli_transport *transport) return True; } +/*********************************************************** + SMB signing - TEMP implementation - calculate a MAC to send. +************************************************************/ +static void cli_request_temp_sign_outgoing_message(struct cli_request *req) +{ + /* mark the packet as signed - BEFORE we sign it...*/ + mark_packet_signed(req); + + /* I wonder what BSRSPYL stands for - but this is what MS + actually sends! */ + memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8); + return; +} + +/*********************************************************** + SMB signing - TEMP implementation - check a MAC sent by server. +************************************************************/ +static BOOL cli_request_temp_check_incoming_message(struct cli_request *req) +{ + return True; +} + +/*********************************************************** + SMB signing - NULL implementation - free signing context +************************************************************/ +static void cli_temp_free_signing_context(struct cli_transport *transport) +{ + return; +} + +/** + SMB signing - TEMP implementation - setup the MAC key. + + @note Used as an initialisation only - it will not correctly + shut down a real signing mechanism +*/ +BOOL cli_temp_set_signing(struct cli_transport *transport) +{ + if (!set_smb_signing_common(transport)) { + return False; + } + + transport->negotiate.sign_info.signing_context = NULL; + + transport->negotiate.sign_info.sign_outgoing_message = cli_request_temp_sign_outgoing_message; + transport->negotiate.sign_info.check_incoming_message = cli_request_temp_check_incoming_message; + transport->negotiate.sign_info.free_signing_context = cli_temp_free_signing_context; + + return True; +} /** * Free the signing context diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c index 68a16501d2..33019c1bf1 100644 --- a/source4/param/loadparm.c +++ b/source4/param/loadparm.c @@ -1101,7 +1101,7 @@ static void init_globals(void) Globals.name_cache_timeout = 660; /* In seconds */ - Globals.bUseSpnego = True; + Globals.bUseSpnego = False; Globals.server_signing = False; -- cgit