smbXcli: Add "force_channel_sequence"
[metze/samba/wip.git] / libcli / smb / smbXcli_base.c
index 135538b0746cb380105b4d5fe86785c8bc505637..e9fdc1dc32a83e2efae12b3df393b28a8079817d 100644 (file)
@@ -138,6 +138,8 @@ struct smbXcli_conn {
 
                uint8_t io_priority;
 
+               bool force_channel_sequence;
+
                uint8_t preauth_sha512[64];
        } smb2;
 
@@ -468,6 +470,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 +551,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;
@@ -839,6 +874,70 @@ static uint16_t smb1cli_alloc_mid(struct smbXcli_conn *conn)
        }
 }
 
+static NTSTATUS smbXcli_req_cancel_write_req(struct tevent_req *req)
+{
+       struct smbXcli_req_state *state =
+               tevent_req_data(req,
+               struct smbXcli_req_state);
+       struct smbXcli_conn *conn = state->conn;
+       size_t num_pending = talloc_array_length(conn->pending);
+       ssize_t ret;
+       int err;
+       bool ok;
+
+       if (state->write_req == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       /*
+        * Check if it's possible to cancel the request.
+        * If the result is true it's not too late.
+        * See writev_cancel().
+        */
+       ok = tevent_req_cancel(state->write_req);
+       if (ok) {
+               TALLOC_FREE(state->write_req);
+
+               if (conn->protocol >= PROTOCOL_SMB2_02) {
+                       /*
+                        * SMB2 has a sane signing state.
+                        */
+                       return NT_STATUS_OK;
+               }
+
+               if (num_pending > 1) {
+                       /*
+                        * We have more pending requests following us.  This
+                        * means the signing state will be broken for them.
+                        *
+                        * As a solution we could add the requests directly to
+                        * our outgoing queue and do the signing in the trigger
+                        * function and then use writev_send() without passing a
+                        * queue.  That way we'll only sign packets we're most
+                        * likely send to the wire.
+                        */
+                       return NT_STATUS_REQUEST_OUT_OF_SEQUENCE;
+               }
+
+               /*
+                * If we're the only request that's
+                * pending, we're able to recover the signing
+                * state.
+                */
+               smb_signing_cancel_reply(conn->smb1.signing,
+                                        state->smb1.one_way_seqnum);
+               return NT_STATUS_OK;
+       }
+
+       ret = writev_recv(state->write_req, &err);
+       TALLOC_FREE(state->write_req);
+       if (ret == -1) {
+               return map_nt_error_from_unix_common(err);
+       }
+
+       return NT_STATUS_OK;
+}
+
 void smbXcli_req_unset_pending(struct tevent_req *req)
 {
        struct smbXcli_req_state *state =
@@ -847,14 +946,23 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
        struct smbXcli_conn *conn = state->conn;
        size_t num_pending = talloc_array_length(conn->pending);
        size_t i;
+       NTSTATUS cancel_status;
 
-       TALLOC_FREE(state->write_req);
+       cancel_status = smbXcli_req_cancel_write_req(req);
 
        if (state->smb1.mid != 0) {
                /*
                 * This is a [nt]trans[2] request which waits
                 * for more than one reply.
                 */
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 
@@ -866,8 +974,18 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
                 * conn->pending. So if nothing is pending anymore, we need to
                 * delete the socket read fde.
                 */
+               /* TODO: smbXcli_conn_cancel_read_req */
                TALLOC_FREE(conn->pending);
                conn->read_smb_req = NULL;
+
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 
@@ -882,6 +1000,15 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
                 * right thing nevertheless, the point of this routine is to
                 * remove ourselves from conn->pending.
                 */
+
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 
@@ -898,6 +1025,15 @@ void smbXcli_req_unset_pending(struct tevent_req *req)
         */
        conn->pending = talloc_realloc(NULL, conn->pending, struct tevent_req *,
                                       num_pending - 1);
+
+       if (!NT_STATUS_IS_OK(cancel_status)) {
+               /*
+                * If the write_req cancel didn't work
+                * we can't use the connection anymore.
+                */
+               smbXcli_conn_disconnect(conn, cancel_status);
+               return;
+       }
        return;
 }
 
@@ -907,19 +1043,31 @@ static void smbXcli_req_cleanup(struct tevent_req *req,
        struct smbXcli_req_state *state =
                tevent_req_data(req,
                struct smbXcli_req_state);
-
-       TALLOC_FREE(state->write_req);
+       struct smbXcli_conn *conn = state->conn;
+       NTSTATUS cancel_status;
 
        switch (req_state) {
        case TEVENT_REQ_RECEIVED:
                /*
                 * Make sure we really remove it from
                 * the pending array on destruction.
+                *
+                * smbXcli_req_unset_pending() calls
+                * smbXcli_req_cancel_write_req() internal
                 */
                state->smb1.mid = 0;
                smbXcli_req_unset_pending(req);
                return;
        default:
+               cancel_status = smbXcli_req_cancel_write_req(req);
+               if (!NT_STATUS_IS_OK(cancel_status)) {
+                       /*
+                        * If the write_req cancel didn't work
+                        * we can't use the connection anymore.
+                        */
+                       smbXcli_conn_disconnect(conn, cancel_status);
+                       return;
+               }
                return;
        }
 }
@@ -1084,6 +1232,8 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                state = tevent_req_data(req, struct smbXcli_req_state);
 
                if (state->smb1.chained_requests == NULL) {
+                       bool in_progress;
+
                        /*
                         * We're dead. No point waiting for trans2
                         * replies.
@@ -1097,6 +1247,14 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                                continue;
                        }
 
+                       in_progress = tevent_req_is_in_progress(req);
+                       if (!in_progress) {
+                               /*
+                                * already finished
+                                */
+                               continue;
+                       }
+
                        /*
                         * we need to defer the callback, because we may notify
                         * more then one caller.
@@ -1110,6 +1268,8 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                num_chained = talloc_array_length(chain);
 
                for (i=0; i<num_chained; i++) {
+                       bool in_progress;
+
                        req = chain[i];
                        state = tevent_req_data(req, struct smbXcli_req_state);
 
@@ -1126,6 +1286,14 @@ void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status)
                                continue;
                        }
 
+                       in_progress = tevent_req_is_in_progress(req);
+                       if (!in_progress) {
+                               /*
+                                * already finished
+                                */
+                               continue;
+                       }
+
                        /*
                         * we need to defer the callback, because we may notify
                         * more than one caller.
@@ -1193,28 +1361,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,
@@ -1497,7 +1643,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;
        }
@@ -1533,6 +1679,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;
        }
 
@@ -1589,7 +1738,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;
                }
@@ -2649,6 +2798,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) {
@@ -2758,7 +2912,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;
 
@@ -3718,13 +3872,9 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                                }
                        }
                        if (signing_key) {
-                               int cmp;
-                               static const uint8_t zeros[16];
-
-                               cmp = memcmp(inhdr+SMB2_HDR_SIGNATURE,
-                                            zeros,
-                                            16);
-                               if (cmp == 0) {
+                               bool zero;
+                               zero = all_zero(inhdr+SMB2_HDR_SIGNATURE, 16);
+                               if (zero) {
                                        state->smb2.signing_skipped = true;
                                        signing_key = NULL;
                                }
@@ -3963,7 +4113,8 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
                                        struct smbXcli_conn *conn,
                                        uint32_t timeout_msec,
                                        enum protocol_types min_protocol,
-                                       enum protocol_types max_protocol)
+                                       enum protocol_types max_protocol,
+                                       uint16_t max_credits)
 {
        struct tevent_req *req, *subreq;
        struct smbXcli_negprot_state *state;
@@ -3996,6 +4147,10 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
        conn->max_protocol = max_protocol;
        conn->protocol = PROTOCOL_NONE;
 
+       if (max_protocol >= PROTOCOL_SMB2_02) {
+               conn->smb2.max_credits = max_credits;
+       }
+
        if ((min_protocol < PROTOCOL_SMB2_02) &&
            (max_protocol < PROTOCOL_SMB2_02)) {
                /*
@@ -4018,16 +4173,6 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
                 */
                conn->dispatch_incoming = smb2cli_conn_dispatch_incoming;
 
-               /*
-                * As we're starting with an SMB2 negprot, emulate Windows
-                * and ask for 31 credits in the initial SMB2 negprot.
-                * If we don't and leave requested credits at
-                * zero, MacOSX servers return zero credits on
-                * the negprot reply and we fail to connect.
-                */
-               smb2cli_conn_set_max_credits(conn,
-                       WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
-
                subreq = smbXcli_negprot_smb2_subreq(state);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
@@ -4789,10 +4934,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);
@@ -5008,7 +5162,8 @@ NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn,
                goto fail;
        }
        req = smbXcli_negprot_send(frame, ev, conn, timeout_msec,
-                                  min_protocol, max_protocol);
+                                  min_protocol, max_protocol,
+                                  WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
        if (req == NULL) {
                goto fail;
        }
@@ -5192,6 +5347,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;
        }
@@ -5312,6 +5482,10 @@ bool smbXcli_session_is_guest(struct smbXcli_session *session)
                return false;
        }
 
+       if (session->conn->mandatory_signing) {
+               return false;
+       }
+
        if (session->conn->protocol >= PROTOCOL_SMB2_02) {
                if (session->smb2->session_flags & SMB2_SESSION_FLAG_IS_GUEST) {
                        return true;
@@ -5571,7 +5745,7 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                                         const struct iovec *recv_iov)
 {
        struct smbXcli_conn *conn = session->conn;
-       uint16_t no_sign_flags;
+       uint16_t no_sign_flags = 0;
        uint8_t session_key[16];
        bool check_signature = true;
        uint32_t hdr_flags;
@@ -5596,7 +5770,18 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session,
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
 
-       no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST | SMB2_SESSION_FLAG_IS_NULL;
+       if (!conn->mandatory_signing) {
+               /*
+                * only allow guest sessions without
+                * mandatory signing.
+                *
+                * If we try an authentication with username != ""
+                * and the server let us in without verifying the
+                * password we don't have a negotiated session key
+                * for signing.
+                */
+               no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST;
+       }
 
        if (session->smb2->session_flags & no_sign_flags) {
                session->smb2->should_sign = false;
@@ -5959,6 +6144,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)
 {
@@ -6037,6 +6254,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;
@@ -6103,3 +6325,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;
+}