libcli:smb: Use C99 initializer for derivation in smbXcli_base
[metze/samba/wip.git] / libcli / smb / smbXcli_base.c
index 70285e6317367937c3ffcb435241fd5acfacb272..3118365871aaf7ab1179828dc34f0664692035dc 100644 (file)
@@ -138,6 +138,8 @@ struct smbXcli_conn {
 
                uint8_t io_priority;
 
+               bool force_channel_sequence;
+
                uint8_t preauth_sha512[64];
        } smb2;
 
@@ -159,6 +161,7 @@ struct smb2cli_session {
        uint64_t nonce_low;
        uint16_t channel_sequence;
        bool replay_active;
+       bool require_signed_response;
 };
 
 struct smbXcli_session {
@@ -225,6 +228,8 @@ struct smbXcli_req_state {
 
        struct tevent_req *write_req;
 
+       struct timeval endtime;
+
        struct {
                /* Space for the header including the wct */
                uint8_t hdr[HDR_VWV];
@@ -285,6 +290,7 @@ struct smbXcli_req_state {
                uint64_t encryption_session_id;
 
                bool signing_skipped;
+               bool require_signed_response;
                bool notify_async;
                bool got_async;
                uint16_t cancel_flags;
@@ -468,6 +474,28 @@ bool smbXcli_conn_use_unicode(struct smbXcli_conn *conn)
        return false;
 }
 
+bool smbXcli_conn_signing_mandatory(struct smbXcli_conn *conn)
+{
+       return conn->mandatory_signing;
+}
+
+/*
+ * [MS-SMB] 2.2.2.3.5 - SMB1 support for passing through
+ * query/set commands to the file system
+ */
+bool smbXcli_conn_support_passthrough(struct smbXcli_conn *conn)
+{
+       if (conn->protocol >= PROTOCOL_SMB2_02) {
+               return true;
+       }
+
+       if (conn->smb1.capabilities & CAP_W2K_SMBS) {
+               return true;
+       }
+
+       return false;
+}
+
 void smbXcli_conn_set_sockopt(struct smbXcli_conn *conn, const char *options)
 {
        set_socket_options(conn->sock_fd, options);
@@ -527,6 +555,17 @@ const struct GUID *smbXcli_conn_server_guid(struct smbXcli_conn *conn)
        return &conn->smb1.server.guid;
 }
 
+bool smbXcli_conn_get_force_channel_sequence(struct smbXcli_conn *conn)
+{
+       return conn->smb2.force_channel_sequence;
+}
+
+void smbXcli_conn_set_force_channel_sequence(struct smbXcli_conn *conn,
+                                            bool v)
+{
+       conn->smb2.force_channel_sequence = v;
+}
+
 struct smbXcli_conn_samba_suicide_state {
        struct smbXcli_conn *conn;
        struct iovec iov;
@@ -856,7 +895,7 @@ static NTSTATUS smbXcli_req_cancel_write_req(struct tevent_req *req)
 
        /*
         * Check if it's possible to cancel the request.
-        * If the result is true it's not to late.
+        * If the result is true it's not too late.
         * See writev_cancel().
         */
        ok = tevent_req_cancel(state->write_req);
@@ -1326,28 +1365,6 @@ static size_t smbXcli_iov_len(const struct iovec *iov, int count)
        return ret;
 }
 
-static uint8_t *smbXcli_iov_concat(TALLOC_CTX *mem_ctx,
-                                  const struct iovec *iov,
-                                  int count)
-{
-       ssize_t buflen;
-       uint8_t *buf;
-
-       buflen = iov_buflen(iov, count);
-       if (buflen == -1) {
-               return NULL;
-       }
-
-       buf = talloc_array(mem_ctx, uint8_t, buflen);
-       if (buf == NULL) {
-               return NULL;
-       }
-
-       iov_buf(iov, count, buf, buflen);
-
-       return buf;
-}
-
 static void smb1cli_req_flags(enum protocol_types protocol,
                              uint32_t smb1_capabilities,
                              uint8_t smb_command,
@@ -1570,10 +1587,8 @@ struct tevent_req *smb1cli_req_create(TALLOC_CTX *mem_ctx,
        state->smb1.iov_count = iov_count + 4;
 
        if (timeout_msec > 0) {
-               struct timeval endtime;
-
-               endtime = timeval_current_ofs_msec(timeout_msec);
-               if (!tevent_req_set_endtime(req, ev, endtime)) {
+               state->endtime = timeval_current_ofs_msec(timeout_msec);
+               if (!tevent_req_set_endtime(req, ev, state->endtime)) {
                        return req;
                }
        }
@@ -1630,7 +1645,7 @@ static NTSTATUS smb1cli_conn_signv(struct smbXcli_conn *conn,
 
        frame = talloc_stackframe();
 
-       buf = smbXcli_iov_concat(frame, &iov[1], iov_count - 1);
+       buf = iov_concat(frame, &iov[1], iov_count - 1);
        if (buf == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -1666,6 +1681,9 @@ static NTSTATUS smb1cli_req_writev_submit(struct tevent_req *req,
        }
 
        if (state->conn->protocol > PROTOCOL_NT1) {
+               DBG_ERR("called for dialect[%s] server[%s]\n",
+                       smb_protocol_types_string(state->conn->protocol),
+                       smbXcli_conn_remote_name(state->conn));
                return NT_STATUS_REVISION_MISMATCH;
        }
 
@@ -1722,7 +1740,7 @@ static NTSTATUS smb1cli_req_writev_submit(struct tevent_req *req,
        if (common_encryption_on(state->conn->smb1.trans_enc)) {
                char *buf, *enc_buf;
 
-               buf = (char *)smbXcli_iov_concat(talloc_tos(), iov, iov_count);
+               buf = (char *)iov_concat(talloc_tos(), iov, iov_count);
                if (buf == NULL) {
                        return NT_STATUS_NO_MEMORY;
                }
@@ -1898,7 +1916,7 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx,
                                          struct iovec **piov, int *pnum_iov)
 {
        struct iovec *iov;
-       int num_iov;
+       size_t num_iov;
        size_t buflen;
        size_t taken;
        size_t remaining;
@@ -2089,10 +2107,10 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx,
                wct_ofs = SVAL(cur[0].iov_base, 2);
 
                if (wct_ofs < taken) {
-                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       goto inval;
                }
                if (wct_ofs > buflen) {
-                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       goto inval;
                }
 
                /*
@@ -2782,6 +2800,11 @@ void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
        conn->smb2.max_credits = max_credits;
 }
 
+uint16_t smb2cli_conn_get_cur_credits(struct smbXcli_conn *conn)
+{
+       return conn->smb2.cur_credits;
+}
+
 uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn)
 {
        if (conn->protocol < PROTOCOL_SMB3_11) {
@@ -2871,6 +2894,14 @@ static void smb2cli_req_cancel_done(struct tevent_req *subreq)
        TALLOC_FREE(subreq);
 }
 
+struct timeval smbXcli_req_endtime(struct tevent_req *req)
+{
+       struct smbXcli_req_state *state = tevent_req_data(
+               req, struct smbXcli_req_state);
+
+       return state->endtime;
+}
+
 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
                                      struct tevent_context *ev,
                                      struct smbXcli_conn *conn,
@@ -2891,7 +2922,7 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
        uint32_t flags = 0;
        uint32_t tid = 0;
        uint64_t uid = 0;
-       bool use_channel_sequence = false;
+       bool use_channel_sequence = conn->smb2.force_channel_sequence;
        uint16_t channel_sequence = 0;
        bool use_replay_flag = false;
 
@@ -2933,6 +2964,8 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
 
                state->smb2.should_sign = session->smb2->should_sign;
                state->smb2.should_encrypt = session->smb2->should_encrypt;
+               state->smb2.require_signed_response =
+                       session->smb2->require_signed_response;
 
                if (cmd == SMB2_OP_SESSSETUP &&
                    session->smb2_channel.signing_key.length == 0 &&
@@ -3020,10 +3053,8 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
        }
 
        if (timeout_msec > 0) {
-               struct timeval endtime;
-
-               endtime = timeval_current_ofs_msec(timeout_msec);
-               if (!tevent_req_set_endtime(req, ev, endtime)) {
+               state->endtime = timeval_current_ofs_msec(timeout_msec);
+               if (!tevent_req_set_endtime(req, ev, state->endtime)) {
                        return req;
                }
        }
@@ -3200,6 +3231,9 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
 
                avail = MIN(avail, state->conn->smb2.cur_credits);
                if (avail < charge) {
+                       DBG_ERR("Insufficient credits. "
+                               "%"PRIu64" available, %"PRIu16" needed\n",
+                               avail, charge);
                        return NT_STATUS_INTERNAL_ERROR;
                }
 
@@ -3449,7 +3483,8 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                                             uint8_t *buf,
                                             size_t buflen,
                                             TALLOC_CTX *mem_ctx,
-                                            struct iovec **piov, int *pnum_iov)
+                                            struct iovec **piov,
+                                            size_t *pnum_iov)
 {
        struct iovec *iov;
        int num_iov = 0;
@@ -3635,7 +3670,7 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
        struct tevent_req *req;
        struct smbXcli_req_state *state = NULL;
        struct iovec *iov = NULL;
-       int i, num_iov = 0;
+       size_t i, num_iov = 0;
        NTSTATUS status;
        bool defer = true;
        struct smbXcli_session *last_session = NULL;
@@ -3721,12 +3756,6 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                }
                last_session = session;
 
-               if (state->smb2.should_sign) {
-                       if (!(flags & SMB2_HDR_FLAG_SIGNED)) {
-                               return NT_STATUS_ACCESS_DENIED;
-                       }
-               }
-
                if (flags & SMB2_HDR_FLAG_SIGNED) {
                        uint64_t uid = BVAL(inhdr, SMB2_HDR_SESSION_ID);
 
@@ -3773,6 +3802,27 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                                 */
                                signing_key = NULL;
                        }
+
+                       if (!NT_STATUS_IS_OK(status)) {
+                               /*
+                                * Only check the signature of the last response
+                                * of a successfull session auth. This matches
+                                * Windows behaviour for NTLM auth and reauth.
+                                */
+                               state->smb2.require_signed_response = false;
+                       }
+               }
+
+               if (state->smb2.should_sign ||
+                   state->smb2.require_signed_response)
+               {
+                       if (!(flags & SMB2_HDR_FLAG_SIGNED)) {
+                               return NT_STATUS_ACCESS_DENIED;
+                       }
+               }
+
+               if (signing_key == NULL && state->smb2.require_signed_response) {
+                       signing_key = &session->smb2_channel.signing_key;
                }
 
                if (cur[0].iov_len == SMB2_TF_HDR_SIZE) {
@@ -3861,15 +3911,17 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                }
 
                if (signing_key) {
-                       status = smb2_signing_check_pdu(*signing_key,
-                                                       state->conn->protocol,
-                                                       &cur[1], 3);
-                       if (!NT_STATUS_IS_OK(status)) {
+                       NTSTATUS signing_status;
+
+                       signing_status = smb2_signing_check_pdu(*signing_key,
+                                                               state->conn->protocol,
+                                                               &cur[1], 3);
+                       if (!NT_STATUS_IS_OK(signing_status)) {
                                /*
                                 * If the signing check fails, we disconnect
                                 * the connection.
                                 */
-                               return status;
+                               return signing_status;
                        }
                }
 
@@ -4347,6 +4399,7 @@ static void smbXcli_negprot_smb1_done(struct tevent_req *subreq)
        }
 
        if (conn->protocol == PROTOCOL_NONE) {
+               DBG_ERR("No compatible protocol selected by server.\n");
                tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
                return;
        }
@@ -4913,10 +4966,19 @@ static void smbXcli_negprot_smb2_done(struct tevent_req *subreq)
                return;
        }
 
+       /*
+        * Here we are now at SMB3_11, so encryption should be
+        * negotiated via context, not capabilities.
+        */
+
        if (conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION) {
-               tevent_req_nterror(req,
-                               NT_STATUS_INVALID_NETWORK_RESPONSE);
-               return;
+               /*
+                * Server set SMB2_CAP_ENCRYPTION capability,
+                * but *SHOULD* not, not *MUST* not. Just mask it off.
+                * NetApp seems to do this:
+                * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13009
+                */
+               conn->smb2.server.capabilities &= ~SMB2_CAP_ENCRYPTION;
        }
 
        negotiate_context_offset = IVAL(body, 60);
@@ -5317,6 +5379,21 @@ static void smb2cli_validate_negotiate_info_done(struct tevent_req *subreq)
                tevent_req_done(req);
                return;
        }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+               /*
+                * The response was signed, but not supported
+                *
+                * This might be returned by older Windows versions or by
+                * NetApp SMB server implementations.
+                *
+                * See
+                *
+                * https://blogs.msdn.microsoft.com/openspecification/2012/06/28/smb3-secure-dialect-negotiation/
+                *
+                */
+               tevent_req_done(req);
+               return;
+       }
        if (tevent_req_nterror(req, status)) {
                return;
        }
@@ -5666,6 +5743,12 @@ void smb2cli_session_stop_replay(struct smbXcli_session *session)
        session->smb2->replay_active = false;
 }
 
+void smb2cli_session_require_signed_response(struct smbXcli_session *session,
+                                            bool require_signed_response)
+{
+       session->smb2->require_signed_response = require_signed_response;
+}
+
 NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session,
                                        const struct iovec *iov)
 {
@@ -5714,7 +5797,9 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                struct _derivation encryption;
                struct _derivation decryption;
                struct _derivation application;
-       } derivation = { };
+       } derivation = {
+               .signing.label.length = 0,
+       };
        size_t nonce_size = 0;
 
        if (conn == NULL) {
@@ -5998,7 +6083,9 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session,
        };
        struct {
                struct _derivation signing;
-       } derivation = { };
+       } derivation = {
+               .signing.label.length = 0,
+       };
 
        if (conn == NULL) {
                return NT_STATUS_INVALID_PARAMETER_MIX;
@@ -6099,6 +6186,38 @@ struct smbXcli_tcon *smbXcli_tcon_create(TALLOC_CTX *mem_ctx)
        return tcon;
 }
 
+/*
+ * Return a deep structure copy of a struct smbXcli_tcon *
+ */
+
+struct smbXcli_tcon *smbXcli_tcon_copy(TALLOC_CTX *mem_ctx,
+                               const struct smbXcli_tcon *tcon_in)
+{
+       struct smbXcli_tcon *tcon;
+
+       tcon = talloc_memdup(mem_ctx, tcon_in, sizeof(struct smbXcli_tcon));
+       if (tcon == NULL) {
+               return NULL;
+       }
+
+       /* Deal with the SMB1 strings. */
+       if (tcon_in->smb1.service != NULL) {
+               tcon->smb1.service = talloc_strdup(tcon, tcon_in->smb1.service);
+               if (tcon->smb1.service == NULL) {
+                       TALLOC_FREE(tcon);
+                       return NULL;
+               }
+       }
+       if (tcon->smb1.fs_type != NULL) {
+               tcon->smb1.fs_type = talloc_strdup(tcon, tcon_in->smb1.fs_type);
+               if (tcon->smb1.fs_type == NULL) {
+                       TALLOC_FREE(tcon);
+                       return NULL;
+               }
+       }
+       return tcon;
+}
+
 void smbXcli_tcon_set_fs_attributes(struct smbXcli_tcon *tcon,
                                    uint32_t fs_attributes)
 {
@@ -6177,6 +6296,11 @@ uint32_t smb2cli_tcon_current_id(struct smbXcli_tcon *tcon)
        return tcon->smb2.tcon_id;
 }
 
+void smb2cli_tcon_set_id(struct smbXcli_tcon *tcon, uint32_t tcon_id)
+{
+       tcon->smb2.tcon_id = tcon_id;
+}
+
 uint32_t smb2cli_tcon_capabilities(struct smbXcli_tcon *tcon)
 {
        return tcon->smb2.capabilities;
@@ -6243,3 +6367,13 @@ bool smb2cli_tcon_is_encryption_on(struct smbXcli_tcon *tcon)
 {
        return tcon->smb2.should_encrypt;
 }
+
+void smb2cli_conn_set_mid(struct smbXcli_conn *conn, uint64_t mid)
+{
+       conn->smb2.mid = mid;
+}
+
+uint64_t smb2cli_conn_get_mid(struct smbXcli_conn *conn)
+{
+       return conn->smb2.mid;
+}