Move tcons.num_open from smb1 to sconn->num_tcons_open as this is needed for SMB2...
[abartlet/samba.git/.git] / source3 / smbd / smb2_tcon.c
index 090f9b79086e044a4664ecb489dea7776c2a5f54..203325e71a4cb96d6be438c16f898fcc4ab46227 100644 (file)
@@ -20,7 +20,8 @@
 
 #include "includes.h"
 #include "smbd/globals.h"
-#include "../source4/libcli/smb2/smb2_constants.h"
+#include "../libcli/smb/smb_common.h"
+#include "../libcli/security/security.h"
 
 static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
                                       const char *in_path,
@@ -43,11 +44,11 @@ NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
        DATA_BLOB in_path_buffer;
        char *in_path_string;
        size_t in_path_string_size;
-       uint8_t out_share_type;
-       uint32_t out_share_flags;
-       uint32_t out_capabilities;
-       uint32_t out_maximal_access;
-       uint32_t out_tree_id;
+       uint8_t out_share_type = 0;
+       uint32_t out_share_flags = 0;
+       uint32_t out_capabilities = 0;
+       uint32_t out_maximal_access = 0;
+       uint32_t out_tree_id = 0;
        NTSTATUS status;
        bool ok;
 
@@ -126,8 +127,13 @@ static int smbd_smb2_tcon_destructor(struct smbd_smb2_tcon *tcon)
 
        idr_remove(tcon->session->tcons.idtree, tcon->tid);
        DLIST_REMOVE(tcon->session->tcons.list, tcon);
+       SMB_ASSERT(tcon->session->sconn->num_tcons_open > 0);
+       tcon->session->sconn->num_tcons_open--;
 
-       conn_free(tcon->compat_conn);
+       if (tcon->compat_conn) {
+               set_current_service(tcon->compat_conn, 0, true);
+               close_cnum(tcon->compat_conn, tcon->session->vuid);
+       }
 
        tcon->compat_conn = NULL;
        tcon->tid = 0;
@@ -148,6 +154,8 @@ static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
        fstring service;
        int snum = -1;
        struct smbd_smb2_tcon *tcon;
+       connection_struct *compat_conn = NULL;
+       user_struct *compat_vuser = req->session->compat_vuser;
        int id;
        NTSTATUS status;
 
@@ -165,15 +173,31 @@ static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
 
        strlower_m(service);
 
-       snum = find_service(service);
+       /* TODO: do more things... */
+       if (strequal(service,HOMES_NAME)) {
+               if (compat_vuser->homes_snum == -1) {
+                       DEBUG(2, ("[homes] share not available for "
+                               "user %s because it was not found "
+                               "or created at session setup "
+                               "time\n",
+                               compat_vuser->server_info->unix_name));
+                       return NT_STATUS_BAD_NETWORK_NAME;
+               }
+               snum = compat_vuser->homes_snum;
+       } else if ((compat_vuser->homes_snum != -1)
+                   && strequal(service,
+                       lp_servicename(compat_vuser->homes_snum))) {
+               snum = compat_vuser->homes_snum;
+       } else {
+               snum = find_service(service);
+       }
+
        if (snum < 0) {
-               DEBUG(1,("smbd_smb2_tree_connect: couldn't find service %s\n",
+               DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
                         service));
                return NT_STATUS_BAD_NETWORK_NAME;
        }
 
-       /* TODO: do more things... */
-
        /* create a new tcon as child of the session */
        tcon = talloc_zero(req->session, struct smbd_smb2_tcon);
        if (tcon == NULL) {
@@ -192,21 +216,53 @@ static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
        DLIST_ADD_END(req->session->tcons.list, tcon,
                      struct smbd_smb2_tcon *);
        tcon->session = req->session;
+       tcon->session->sconn->num_tcons_open++;
        talloc_set_destructor(tcon, smbd_smb2_tcon_destructor);
 
-       tcon->compat_conn = make_connection_snum(req->conn,
+       compat_conn = make_connection_snum(req->sconn,
                                        snum, req->session->compat_vuser,
                                        data_blob_null, "???",
                                        &status);
-       if (tcon->compat_conn == NULL) {
+       if (compat_conn == NULL) {
                TALLOC_FREE(tcon);
                return status;
        }
+       tcon->compat_conn = talloc_move(tcon, &compat_conn);
        tcon->compat_conn->cnum = tcon->tid;
 
-       *out_share_type = 0x01;
-       *out_share_flags = SMB2_SHAREFLAG_ALL;
-       *out_capabilities = 0;
+       if (IS_PRINT(tcon->compat_conn)) {
+               *out_share_type = SMB2_SHARE_TYPE_PRINT;
+       } else if (IS_IPC(tcon->compat_conn)) {
+               *out_share_type = SMB2_SHARE_TYPE_PIPE;
+       } else {
+               *out_share_type = SMB2_SHARE_TYPE_DISK;
+       }
+
+       *out_share_flags = SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING;
+
+       if (lp_msdfs_root(SNUM(tcon->compat_conn)) && lp_host_msdfs()) {
+               *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT);
+               *out_capabilities = SMB2_SHARE_CAP_DFS;
+       } else {
+               *out_capabilities = 0;
+       }
+
+       switch(lp_csc_policy(SNUM(tcon->compat_conn))) {
+       case CSC_POLICY_MANUAL:
+               break;
+       case CSC_POLICY_DOCUMENTS:
+               *out_share_flags |= SMB2_SHAREFLAG_AUTO_CACHING;
+               break;
+       case CSC_POLICY_PROGRAMS:
+               *out_share_flags |= SMB2_SHAREFLAG_VDO_CACHING;
+               break;
+       case CSC_POLICY_DISABLE:
+               *out_share_flags |= SMB2_SHAREFLAG_NO_CACHING;
+               break;
+       default:
+               break;
+       }
+
        *out_maximal_access = FILE_GENERIC_ALL;
 
        *out_tree_id = tcon->tid;
@@ -216,15 +272,36 @@ static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
 NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req)
 {
        const uint8_t *inhdr;
+       const uint8_t *outhdr;
        int i = req->current_idx;
        uint32_t in_tid;
        void *p;
        struct smbd_smb2_tcon *tcon;
+       bool chained_fixup = false;
 
        inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
 
        in_tid = IVAL(inhdr, SMB2_HDR_TID);
 
+       if (in_tid == (0xFFFFFFFF)) {
+               if (req->async) {
+                       /*
+                        * async request - fill in tid from
+                        * already setup out.vector[].iov_base.
+                        */
+                       outhdr = (const uint8_t *)req->out.vector[i].iov_base;
+                       in_tid = IVAL(outhdr, SMB2_HDR_TID);
+               } else if (i > 2) {
+                       /*
+                        * Chained request - fill in tid from
+                        * the previous request out.vector[].iov_base.
+                        */
+                       outhdr = (const uint8_t *)req->out.vector[i-3].iov_base;
+                       in_tid = IVAL(outhdr, SMB2_HDR_TID);
+                       chained_fixup = true;
+               }
+       }
+
        /* lookup an existing session */
        p = idr_find(req->session->tcons.idtree, in_tid);
        if (p == NULL) {
@@ -242,6 +319,13 @@ NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req)
        }
 
        req->tcon = tcon;
+
+       if (chained_fixup) {
+               /* Fix up our own outhdr. */
+               outhdr = (const uint8_t *)req->out.vector[i].iov_base;
+               SIVAL(outhdr, SMB2_HDR_TID, in_tid);
+       }
+
        return NT_STATUS_OK;
 }