CVE-2016-2115: s4:libcli/raw: pass the minprotocol to smb_raw_negotiate*()
[samba.git] / source4 / libcli / raw / rawnegotiate.c
index 4f7d7b4058d3bb9a4e123cd1625ae0c0da97d2f9..4b42c2662a0ffd2453b65c7f25ae714022446c98 100644 (file)
@@ -1,12 +1,14 @@
 /* 
    Unix SMB/CIFS implementation.
+
    SMB client negotiate context management functions
-   Copyright (C) Andrew Tridgell 1994-1998
+
+   Copyright (C) Andrew Tridgell 1994-2005
    Copyright (C) James Myers 2003 <myersjj@samba.org>
    
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
-#include "libcli/raw/libcliraw.h"
+#include <tevent.h>
 #include "system/time.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "../lib/util/tevent_ntstatus.h"
 
-static const struct {
-       enum protocol_types prot;
-       const char *name;
-} prots[] = {
-       {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
-       {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
-       {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
-       {PROTOCOL_LANMAN1,"LANMAN1.0"},
-       {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"},
+struct smb_raw_negotiate_state {
+       struct smbcli_transport *transport;
 };
 
-/****************************************************************************
- Send a negprot command.
-****************************************************************************/
-struct smbcli_request *smb_negprot_send(struct smbcli_transport *transport, int maxprotocol)
-{
-       struct smbcli_request *req;
-       int i;
-       uint16_t flags2 = 0;
+static void smb_raw_negotiate_done(struct tevent_req *subreq);
 
-       req = smbcli_request_setup_transport(transport, SMBnegprot, 0, 0);
-       if (!req) {
+struct tevent_req *smb_raw_negotiate_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         struct smbcli_transport *transport,
+                                         int minprotocol,
+                                         int maxprotocol)
+{
+       struct tevent_req *req;
+       struct smb_raw_negotiate_state *state;
+       struct tevent_req *subreq;
+       uint32_t timeout_msec = transport->options.request_timeout * 1000;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_raw_negotiate_state);;
+       if (req == NULL) {
                return NULL;
        }
+       state->transport = transport;
 
-       flags2 |= FLAGS2_32_BIT_ERROR_CODES;
-       if (lp_unicode()) {
-               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;
+       if (maxprotocol > PROTOCOL_NT1) {
+               maxprotocol = PROTOCOL_NT1;
        }
 
-       SSVAL(req->out.hdr,HDR_FLG2, flags2);
-
-       /* setup the protocol strings */
-       for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) {
-               smbcli_req_append_bytes(req, "\2", 1);
-               smbcli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII);
-       }
-
-       if (!smbcli_request_send(req)) {
-               smbcli_request_destroy(req);
-               return NULL;
+       subreq = smbXcli_negprot_send(state, ev,
+                                     transport->conn,
+                                     timeout_msec,
+                                     minprotocol,
+                                     maxprotocol);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
        }
+       tevent_req_set_callback(subreq, smb_raw_negotiate_done, req);
 
        return req;
 }
 
-/****************************************************************************
- Send a negprot command.
-****************************************************************************/
-NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport) 
+static void smb_raw_negotiate_done(struct tevent_req *subreq)
 {
-       struct smbcli_request *req;
-       int protocol;
-
-       req = smb_negprot_send(transport, lp_maxprotocol());
-       if (!req) {
-               return NT_STATUS_UNSUCCESSFUL;
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct smb_raw_negotiate_state *state =
+               tevent_req_data(req,
+               struct smb_raw_negotiate_state);
+       struct smbcli_negotiate *n = &state->transport->negotiate;
+       struct smbXcli_conn *c = state->transport->conn;
+       NTSTATUS status;
+       NTTIME ntt;
+
+       status = smbXcli_negprot_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       if (!smbcli_request_receive(req) ||
-           smbcli_request_is_error(req)) {
-               return smbcli_request_destroy(req);
-       }
+       n->protocol = smbXcli_conn_protocol(c);
 
-       SMBCLI_CHECK_MIN_WCT(req, 1);
+       n->sec_mode = smb1cli_conn_server_security_mode(c);
+       n->max_mux  = smbXcli_conn_max_requests(c);
+       n->max_xmit = smb1cli_conn_max_xmit(c);
+       n->sesskey  = smb1cli_conn_server_session_key(c);
+       n->capabilities = smb1cli_conn_capabilities(c);;
 
-       protocol = SVALS(req->in.vwv, VWV(0));
+       /* this time arrives in real GMT */
+       ntt = smbXcli_conn_server_system_time(c);
+       n->server_time = nt_time_to_unix(ntt);
+       n->server_zone = smb1cli_conn_server_time_zone(c);
 
-       if (protocol >= ARRAY_SIZE(prots) || protocol < 0) {
-               req->status = NT_STATUS_UNSUCCESSFUL;
-               return smbcli_request_destroy(req);
+       if (n->capabilities & CAP_EXTENDED_SECURITY) {
+               const DATA_BLOB *b = smbXcli_conn_server_gss_blob(c);
+               if (b) {
+                       n->secblob = *b;
+               }
+       } else {
+               const uint8_t *p = smb1cli_conn_server_challenge(c);
+               if (p) {
+                       n->secblob = data_blob_const(p, 8);
+               }
        }
 
-       transport->negotiate.protocol = prots[protocol].prot;
-
-       if (transport->negotiate.protocol >= PROTOCOL_NT1) {
-               NTTIME ntt;
-
-               /* NT protocol */
-               SMBCLI_CHECK_WCT(req, 17);
-               transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1));
-               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.capabilities = IVAL(req->in.vwv,VWV(9)+1);
-
-               /* this time arrives in real GMT */
-               ntt = smbcli_pull_nttime(req->in.vwv, VWV(11)+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 = smbcli_req_pull_blob(req, transport, req->in.data, 16);
-                       transport->negotiate.secblob = smbcli_req_pull_blob(req, transport, req->in.data + 16, req->in.data_size - 16);
-               } else {
-                       if (req->in.data_size < (transport->negotiate.key_len)) {
-                               goto failed;
-                       }
-                       transport->negotiate.secblob = smbcli_req_pull_blob(req, transport, req->in.data, transport->negotiate.key_len);
-                       smbcli_req_pull_string(req, transport, &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 */
-               }
+       n->readbraw_supported = smb1cli_conn_server_readbraw(c);
+       n->readbraw_supported = smb1cli_conn_server_writebraw(c);
+       n->lockread_supported = smb1cli_conn_server_lockread(c);
 
-               if (transport->negotiate.capabilities & CAP_RAW_MODE) {
-                       transport->negotiate.readbraw_supported = True;
-                       transport->negotiate.writebraw_supported = True;
-               }
-       } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) {
-               SMBCLI_CHECK_WCT(req, 13);
-               transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1));
-               transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2));
-               transport->negotiate.sesskey =  IVAL(req->in.vwv,VWV(6));
-               transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60;
-               
-               /* this time is converted to GMT by raw_pull_dos_date */
-               transport->negotiate.server_time = raw_pull_dos_date(transport,
-                                                                    req->in.vwv+VWV(8));
-               if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) {
-                       transport->negotiate.readbraw_supported = 1;
-               }
-               if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) {
-                       transport->negotiate.writebraw_supported = 1;
-               }
-               transport->negotiate.secblob = smbcli_req_pull_blob(req, transport, 
-                                                                req->in.data, req->in.data_size);
-       } else {
-               /* the old core protocol */
-               transport->negotiate.sec_mode = 0;
-               transport->negotiate.server_time = time(NULL);
-               transport->negotiate.max_xmit = transport->options.max_xmit;
-               transport->negotiate.server_zone = get_time_zone(transport->negotiate.server_time);
+       tevent_req_done(req);
+}
+
+/*
+ Send a negprot command.
+*/
+NTSTATUS smb_raw_negotiate_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+
+/*
+ Send a negprot command (sync interface)
+*/
+NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport, bool unicode,
+                          int minprotocol, int maxprotocol)
+{
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       struct tevent_req *subreq = NULL;
+       bool ok;
+
+       subreq = smb_raw_negotiate_send(transport,
+                                       transport->ev,
+                                       transport,
+                                       minprotocol,
+                                       maxprotocol);
+       if (subreq == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
-       /* a way to force ascii SMB */
-       if (!lp_unicode() || getenv("SMBCLI_FORCE_ASCII")) {
-               transport->negotiate.capabilities &= ~CAP_UNICODE;
+       ok = tevent_req_poll(subreq, transport->ev);
+       if (!ok) {
+               status = map_nt_error_from_unix_common(errno);
+               goto failed;
        }
 
+       status = smb_raw_negotiate_recv(subreq);
+
 failed:
-       return smbcli_request_destroy(req);
+       TALLOC_FREE(subreq);
+       return status;
 }