r1345: add extended security spnego support to the smb client
authorStefan Metzmacher <metze@samba.org>
Mon, 5 Jul 2004 23:28:49 +0000 (23:28 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:56:51 +0000 (12:56 -0500)
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
source4/libcli/raw/clisession.c
source4/libcli/raw/clitransport.c
source4/libcli/raw/clitree.c
source4/libcli/raw/rawnegotiate.c
source4/libcli/raw/smb_signing.c
source4/param/loadparm.c

index 24dcfe72355b3bc32351f623dac3f711f9bfb6df..930017bb26c281dd3279fffde010911d4c0d6286 100644 (file)
@@ -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;
 };
 
 /* 
index 780ff5837b35af3f891ca9a86bfc4c79d005d3c1..7ff5db59c33e5058af679ce4c456981876732998 100644 (file)
@@ -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);
 }
 
 
index 1e9032459fe739924b7e788bba5aa06fdfa502e9..a378ac8aad79a604710e7a06484fb7a09e81222e 100644 (file)
@@ -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++;
index b35bf67c94a3ce76c8dee005b7b3d11a640e601c..3b16c4c336b6aa70e0533cdc1fcf947712133d42 100644 (file)
@@ -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 = "";
index 5b94ef63d836cd15829855f0be7eef19317604ea..6bf35fb26db6d7c6f6363a0d1b9cfaa6cfce2e70 100644 (file)
@@ -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));
index a39f33c290ba899493f4a6980055af3e6f3b4de9..20b44a53484fdf4f8dea9679d2aaf725b263802c 100644 (file)
@@ -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
index 68a16501d2202cf62bb2c177f256ab0f1e51558c..33019c1bf1994323542218fe68602a2240ad6b0e 100644 (file)
@@ -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;