libcli/smb: Add smbXcli_conn_nt_smbs_supported
[obnox/samba/samba-obnox.git] / libcli / smb / smbXcli_base.c
index 45da5fd90f1e029fddbe77bd5ca880698ae9885e..b62e81d98b3615dbf3d399d231d6da083ea762e4 100644 (file)
@@ -157,6 +157,13 @@ struct smbXcli_session {
        struct {
                DATA_BLOB signing_key;
        } smb2_channel;
+
+       /*
+        * this should be a short term hack
+        * until the upper layers have implemented
+        * re-authentication.
+        */
+       bool disconnect_expired;
 };
 
 struct smbXcli_tcon {
@@ -404,6 +411,45 @@ enum protocol_types smbXcli_conn_protocol(struct smbXcli_conn *conn)
        return conn->protocol;
 }
 
+bool smbXcli_conn_nt_smbs_supported(struct smbXcli_conn *conn)
+{
+       if (conn->protocol >= PROTOCOL_SMB2_02) {
+               return true;
+       }
+
+       if (smb1cli_conn_capabilities(conn) & CAP_NT_SMBS) {
+               return true;
+       }
+
+       return false;
+}
+
+bool smbXcli_conn_use_status32(struct smbXcli_conn *conn)
+{
+       if (conn->protocol >= PROTOCOL_SMB2_02) {
+               return true;
+       }
+
+       if (smb1cli_conn_capabilities(conn) & CAP_STATUS32) {
+               return true;
+       }
+
+       return false;
+}
+
+bool smbXcli_conn_use_large_files(struct smbXcli_conn *conn)
+{
+       if (conn->protocol >= PROTOCOL_SMB2_02) {
+               return true;
+       }
+
+       if (smb1cli_conn_capabilities(conn) & CAP_LARGE_FILES) {
+               return true;
+       }
+
+       return false;
+}
+
 bool smbXcli_conn_use_unicode(struct smbXcli_conn *conn)
 {
        if (conn->protocol >= PROTOCOL_SMB2_02) {
@@ -554,7 +600,7 @@ NTSTATUS smbXcli_conn_samba_suicide(struct smbXcli_conn *conn,
                status = NT_STATUS_INVALID_PARAMETER_MIX;
                goto fail;
        }
-       ev = tevent_context_init(frame);
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
@@ -1608,13 +1654,27 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx,
        uint8_t *hdr;
        uint8_t cmd;
        uint32_t wct_ofs;
+       NTSTATUS status;
+       size_t min_size = MIN_SMB_SIZE;
 
-       buflen = smb_len_nbt(buf);
+       buflen = smb_len_tcp(buf);
        taken = 0;
 
        hdr = buf + NBT_HDR_SIZE;
 
-       if (buflen < MIN_SMB_SIZE) {
+       status = smb1cli_pull_raw_error(hdr);
+       if (NT_STATUS_IS_ERR(status)) {
+               /*
+                * This is an ugly hack to support OS/2
+                * which skips the byte_count in the DATA block
+                * on some error responses.
+                *
+                * See bug #9096
+                */
+               min_size -= sizeof(uint16_t);
+       }
+
+       if (buflen < min_size) {
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
@@ -1655,9 +1715,9 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx,
                size_t needed;
 
                /*
-                * we need at least WCT and BCC
+                * we need at least WCT
                 */
-               needed = sizeof(uint8_t) + sizeof(uint16_t);
+               needed = sizeof(uint8_t);
                if (len < needed) {
                        DEBUG(10, ("%s: %d bytes left, expected at least %d\n",
                                   __location__, (int)len, (int)needed));
@@ -1675,6 +1735,46 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx,
                        goto inval;
                }
 
+               if ((num_iov == 1) &&
+                   (len == needed) &&
+                   NT_STATUS_IS_ERR(status))
+               {
+                       /*
+                        * This is an ugly hack to support OS/2
+                        * which skips the byte_count in the DATA block
+                        * on some error responses.
+                        *
+                        * See bug #9096
+                        */
+                       iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
+                                                num_iov + 2);
+                       if (iov_tmp == NULL) {
+                               TALLOC_FREE(iov);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+                       iov = iov_tmp;
+                       cur = &iov[num_iov];
+                       num_iov += 2;
+
+                       cur[0].iov_len = 0;
+                       cur[0].iov_base = hdr + (wct_ofs + sizeof(uint8_t));
+                       cur[1].iov_len = 0;
+                       cur[1].iov_base = cur[0].iov_base;
+
+                       taken += needed;
+                       break;
+               }
+
+               /*
+                * we need at least BCC
+                */
+               needed += sizeof(uint16_t);
+               if (len < needed) {
+                       DEBUG(10, ("%s: %d bytes left, expected at least %d\n",
+                                  __location__, (int)len, (int)needed));
+                       goto inval;
+               }
+
                /*
                 * Now we check if the specified bytes are there
                 */
@@ -1784,7 +1884,7 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
        uint16_t mid;
        bool oplock_break;
        uint8_t *inhdr = inbuf + NBT_HDR_SIZE;
-       size_t len = smb_len_nbt(inbuf);
+       size_t len = smb_len_tcp(inbuf);
        struct iovec *iov = NULL;
        int num_iov = 0;
        struct tevent_req **chain = NULL;
@@ -1916,6 +2016,17 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
        cmd = CVAL(inhdr, HDR_COM);
        status = smb1cli_pull_raw_error(inhdr);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED) &&
+           (state->session != NULL) && state->session->disconnect_expired)
+       {
+               /*
+                * this should be a short term hack
+                * until the upper layers have implemented
+                * re-authentication.
+                */
+               return status;
+       }
+
        if (state->smb1.chained_requests == NULL) {
                if (num_iov != 3) {
                        return NT_STATUS_INVALID_NETWORK_RESPONSE;
@@ -2358,6 +2469,15 @@ bool smbXcli_conn_has_async_calls(struct smbXcli_conn *conn)
                || (talloc_array_length(conn->pending) != 0));
 }
 
+bool smbXcli_conn_dfs_supported(struct smbXcli_conn *conn)
+{
+       if(conn->protocol >= PROTOCOL_SMB2_02) {
+               return (smb2cli_conn_server_capabilities(conn) & SMB2_CAP_DFS);
+       }
+
+       return (smb1cli_conn_capabilities(conn) & CAP_DFS);
+}
+
 uint32_t smb2cli_conn_server_capabilities(struct smbXcli_conn *conn)
 {
        return conn->smb2.server.capabilities;
@@ -2987,6 +3107,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                        struct smbXcli_session *s;
                        uint64_t uid;
                        struct iovec tf_iov[2];
+                       size_t enc_len;
                        NTSTATUS status;
 
                        if (len < SMB2_TF_HDR_SIZE) {
@@ -2999,9 +3120,16 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                        taken += tf_len;
 
                        hdr = first_hdr + taken;
-                       len = IVAL(tf, SMB2_TF_MSG_SIZE);
+                       enc_len = IVAL(tf, SMB2_TF_MSG_SIZE);
                        uid = BVAL(tf, SMB2_TF_SESSION_ID);
 
+                       if (len < SMB2_TF_HDR_SIZE + enc_len) {
+                               DEBUG(10, ("%d bytes left, expected at least %d\n",
+                                          (int)len,
+                                          (int)(SMB2_TF_HDR_SIZE + enc_len)));
+                               goto inval;
+                       }
+
                        s = conn->sessions;
                        for (; s; s = s->next) {
                                if (s->smb2->session_id != uid) {
@@ -3019,7 +3147,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                        tf_iov[0].iov_base = (void *)tf;
                        tf_iov[0].iov_len = tf_len;
                        tf_iov[1].iov_base = (void *)hdr;
-                       tf_iov[1].iov_len = len;
+                       tf_iov[1].iov_len = enc_len;
 
                        status = smb2_signing_decrypt_pdu(s->smb2->decryption_key,
                                                          conn->protocol,
@@ -3029,7 +3157,8 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
                                return status;
                        }
 
-                       verified_buflen = taken + len;
+                       verified_buflen = taken + enc_len;
+                       len = enc_len;
                }
 
                /*
@@ -3379,6 +3508,17 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn,
                        }
                }
 
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED) &&
+                   (session != NULL) && session->disconnect_expired)
+               {
+                       /*
+                        * this should be a short term hack
+                        * until the upper layers have implemented
+                        * re-authentication.
+                        */
+                       return status;
+               }
+
                smbXcli_req_unset_pending(req);
 
                /*
@@ -3954,6 +4094,15 @@ static void smbXcli_negprot_smb1_done(struct tevent_req *subreq)
                if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
                        server_signing = "supported";
                        server_allowed = true;
+               } else if (conn->mandatory_signing) {
+                       /*
+                        * We have mandatory signing as client
+                        * lets assume the server will look at our
+                        * FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED
+                        * flag in the session setup
+                        */
+                       server_signing = "not announced";
+                       server_allowed = true;
                }
                if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
                        server_signing = "required";
@@ -4338,7 +4487,7 @@ NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn,
                status = NT_STATUS_INVALID_PARAMETER_MIX;
                goto fail;
        }
-       ev = tevent_context_init(frame);
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
@@ -4390,6 +4539,33 @@ struct smbXcli_session *smbXcli_session_create(TALLOC_CTX *mem_ctx,
        return session;
 }
 
+struct smbXcli_session *smbXcli_session_copy(TALLOC_CTX *mem_ctx,
+                                               struct smbXcli_session *src)
+{
+       struct smbXcli_session *session;
+
+       session = talloc_zero(mem_ctx, struct smbXcli_session);
+       if (session == NULL) {
+               return NULL;
+       }
+       session->smb2 = talloc_zero(session, struct smb2cli_session);
+       if (session->smb2 == NULL) {
+               talloc_free(session);
+               return NULL;
+       }
+
+       session->conn = src->conn;
+       *session->smb2 = *src->smb2;
+       session->smb2_channel = src->smb2_channel;
+       session->disconnect_expired = src->disconnect_expired;
+
+       DLIST_ADD_END(src->conn->sessions, session, struct smbXcli_session *);
+       talloc_set_destructor(session, smbXcli_session_destructor);
+
+       return session;
+}
+
+
 NTSTATUS smbXcli_session_application_key(struct smbXcli_session *session,
                                         TALLOC_CTX *mem_ctx,
                                         DATA_BLOB *key)
@@ -4420,6 +4596,11 @@ NTSTATUS smbXcli_session_application_key(struct smbXcli_session *session,
        return NT_STATUS_OK;
 }
 
+void smbXcli_session_set_disconnect_expired(struct smbXcli_session *session)
+{
+       session->disconnect_expired = true;
+}
+
 uint16_t smb1cli_session_current_id(struct smbXcli_session *session)
 {
        return session->smb1.session_id;