libcli: Use "all_zero" where appropriate
[bbaumbach/samba-autobuild/.git] / libcli / smb / smbXcli_base.c
index c8ae5b06a9006434262b5ded36bda54e491a3251..a7b24f014977021fd9711ea8a3301624dc2f5264 100644 (file)
@@ -167,6 +167,7 @@ struct smbXcli_session {
 
        struct {
                uint16_t session_id;
+               uint16_t action;
                DATA_BLOB application_key;
                bool protected_key;
        } smb1;
@@ -376,6 +377,13 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx,
                conn->desire_signing = false;
                conn->mandatory_signing = false;
                break;
+       case SMB_SIGNING_DESIRED:
+               /* if the server desires it */
+               conn->allow_signing = true;
+               conn->desire_signing = true;
+               conn->mandatory_signing = false;
+               break;
+       case SMB_SIGNING_IPC_DEFAULT:
        case SMB_SIGNING_REQUIRED:
                /* always */
                conn->allow_signing = true;
@@ -831,6 +839,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 to 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 =
@@ -839,14 +911,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;
        }
 
@@ -858,8 +939,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;
        }
 
@@ -874,6 +965,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;
        }
 
@@ -890,6 +990,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;
 }
 
@@ -899,19 +1008,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;
        }
 }
@@ -1076,6 +1197,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.
@@ -1089,6 +1212,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.
@@ -1102,6 +1233,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);
 
@@ -1118,6 +1251,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.
@@ -3289,6 +3430,21 @@ static void smb2cli_req_writev_done(struct tevent_req *subreq)
        }
 }
 
+static struct smbXcli_session* smbXcli_session_by_uid(struct smbXcli_conn *conn,
+                                                    uint64_t uid)
+{
+       struct smbXcli_session *s = conn->sessions;
+
+       for (; s; s = s->next) {
+               if (s->smb2->session_id != uid) {
+                       continue;
+               }
+               break;
+       }
+
+       return s;
+}
+
 static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                                             uint8_t *buf,
                                             size_t buflen,
@@ -3356,14 +3512,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                                goto inval;
                        }
 
-                       s = conn->sessions;
-                       for (; s; s = s->next) {
-                               if (s->smb2->session_id != uid) {
-                                       continue;
-                               }
-                               break;
-                       }
-
+                       s = smbXcli_session_by_uid(conn, uid);
                        if (s == NULL) {
                                DEBUG(10, ("unknown session_id %llu\n",
                                           (unsigned long long)uid));
@@ -3485,8 +3634,8 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
 {
        struct tevent_req *req;
        struct smbXcli_req_state *state = NULL;
-       struct iovec *iov;
-       int i, num_iov;
+       struct iovec *iov = NULL;
+       int i, num_iov = 0;
        NTSTATUS status;
        bool defer = true;
        struct smbXcli_session *last_session = NULL;
@@ -3582,17 +3731,8 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                        uint64_t uid = BVAL(inhdr, SMB2_HDR_SESSION_ID);
 
                        if (session == NULL) {
-                               struct smbXcli_session *s;
-
-                               s = state->conn->sessions;
-                               for (; s; s = s->next) {
-                                       if (s->smb2->session_id != uid) {
-                                               continue;
-                                       }
-
-                                       session = s;
-                                       break;
-                               }
+                               session = smbXcli_session_by_uid(state->conn,
+                                                                uid);
                        }
 
                        if (session == NULL) {
@@ -3711,13 +3851,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;
                                }
@@ -4592,8 +4728,12 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta
                }
 
                SSVAL(p, 0, 2); /* ChiperCount */
-               SSVAL(p, 2, SMB2_ENCRYPTION_AES128_GCM);
-               SSVAL(p, 4, SMB2_ENCRYPTION_AES128_CCM);
+               /*
+                * For now we preferr CCM because our implementation
+                * is faster than GCM, see bug #11451.
+                */
+               SSVAL(p, 2, SMB2_ENCRYPTION_AES128_CCM);
+               SSVAL(p, 4, SMB2_ENCRYPTION_AES128_GCM);
 
                b = data_blob_const(p, 6);
                status = smb2_negotiate_context_add(state, &c,
@@ -5255,7 +5395,7 @@ struct smbXcli_session *smbXcli_session_create(TALLOC_CTX *mem_ctx,
        }
        talloc_set_destructor(session, smbXcli_session_destructor);
 
-       DLIST_ADD_END(conn->sessions, session, struct smbXcli_session *);
+       DLIST_ADD_END(conn->sessions, session);
        session->conn = conn;
 
        memcpy(session->smb2_channel.preauth_sha512,
@@ -5285,16 +5425,48 @@ struct smbXcli_session *smbXcli_session_copy(TALLOC_CTX *mem_ctx,
        session->smb2_channel = src->smb2_channel;
        session->disconnect_expired = src->disconnect_expired;
 
-       DLIST_ADD_END(src->conn->sessions, session, struct smbXcli_session *);
+       DLIST_ADD_END(src->conn->sessions, session);
        talloc_set_destructor(session, smbXcli_session_destructor);
 
        return session;
 }
 
+bool smbXcli_session_is_guest(struct smbXcli_session *session)
+{
+       if (session == NULL) {
+               return false;
+       }
+
+       if (session->conn == NULL) {
+               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;
+               }
+               return false;
+       }
+
+       if (session->smb1.action & SMB_SETUP_GUEST) {
+               return true;
+       }
+
+       return false;
+}
+
 bool smbXcli_session_is_authenticated(struct smbXcli_session *session)
 {
        const DATA_BLOB *application_key;
 
+       if (session == NULL) {
+               return false;
+       }
+
        if (session->conn == NULL) {
                return false;
        }
@@ -5362,6 +5534,12 @@ void smb1cli_session_set_id(struct smbXcli_session *session,
        session->smb1.session_id = session_id;
 }
 
+void smb1cli_session_set_action(struct smbXcli_session *session,
+                               uint16_t action)
+{
+       session->smb1.action = action;
+}
+
 NTSTATUS smb1cli_session_set_session_key(struct smbXcli_session *session,
                                         const DATA_BLOB _session_key)
 {
@@ -5436,6 +5614,9 @@ uint8_t smb2cli_session_security_mode(struct smbXcli_session *session)
        if (conn->mandatory_signing) {
                security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
        }
+       if (session->smb2->should_sign) {
+               security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
+       }
 
        return security_mode;
 }
@@ -5474,6 +5655,11 @@ uint16_t smb2cli_session_reset_channel_sequence(struct smbXcli_session *session,
        return prev_cs;
 }
 
+uint16_t smb2cli_session_current_channel_sequence(struct smbXcli_session *session)
+{
+       return session->smb2->channel_sequence;
+}
+
 void smb2cli_session_start_replay(struct smbXcli_session *session)
 {
        session->smb2->replay_active = true;
@@ -5518,7 +5704,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;
@@ -5543,7 +5729,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;
@@ -5781,7 +5978,7 @@ NTSTATUS smb2cli_session_create_channel(TALLOC_CTX *mem_ctx,
        }
 
        talloc_set_destructor(session2, smbXcli_session_destructor);
-       DLIST_ADD_END(conn->sessions, session2, struct smbXcli_session *);
+       DLIST_ADD_END(conn->sessions, session2);
        session2->conn = conn;
 
        memcpy(session2->smb2_channel.preauth_sha512,
@@ -5867,6 +6064,14 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session,
 
 NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session)
 {
+       if (!session->smb2->should_sign) {
+               /*
+                * We need required signing on the session
+                * in order to prevent man in the middle attacks.
+                */
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
        if (session->smb2->should_encrypt) {
                return NT_STATUS_OK;
        }
@@ -5981,6 +6186,11 @@ uint32_t smb2cli_tcon_capabilities(struct smbXcli_tcon *tcon)
        return tcon->smb2.capabilities;
 }
 
+uint32_t smb2cli_tcon_flags(struct smbXcli_tcon *tcon)
+{
+       return tcon->smb2.flags;
+}
+
 void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon,
                             struct smbXcli_session *session,
                             uint32_t tcon_id,