Fix bug #7126 - [SMBD] With access denied error smbd return wrong NT_STATUS_OBJECT_PA...
[samba.git] / source3 / smbd / service.c
index 0124b2b047cbdabf72bd816b5ad8acd5315ad1e6..6e57e1f0d5faa41901edc134f62bd88b638c564f 100644 (file)
@@ -56,7 +56,12 @@ bool set_conn_connectpath(connection_struct *conn, const char *connectpath)
        const char *s = connectpath;
         bool start_of_name_component = true;
 
-       destname = SMB_STRDUP(connectpath);
+       if (connectpath == NULL || connectpath[0] == '\0') {
+               return false;
+       }
+
+       /* Allocate for strlen + '\0' + possible leading '/' */
+       destname = SMB_MALLOC(strlen(connectpath) + 2);
        if (!destname) {
                return false;
        }
@@ -182,8 +187,8 @@ bool set_current_service(connection_struct *conn, uint16 flags, bool do_chdir)
        if (do_chdir &&
            vfs_ChDir(conn,conn->connectpath) != 0 &&
            vfs_ChDir(conn,conn->origpath) != 0) {
-               DEBUG(0,("chdir (%s) failed\n",
-                        conn->connectpath));
+                DEBUG(((errno!=EACCES)?0:3),("chdir (%s) failed, reason: %s\n",
+                         conn->connectpath, strerror(errno)));
                return(False);
        }
 
@@ -259,7 +264,7 @@ int add_home_service(const char *service, const char *username, const char *home
 {
        int iHomeService;
 
-       if (!service || !homedir)
+       if (!service || !homedir || homedir[0] == '\0')
                return -1;
 
        if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) {
@@ -673,7 +678,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                DEBUG(1, ("create_connection_server_info failed: %s\n",
                          nt_errstr(status)));
                *pstatus = status;
-               conn_free(sconn, conn);
+               conn_free(conn);
                return NULL;
        }
 
@@ -692,7 +697,6 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
        conn->printer = (strncmp(dev,"LPT",3) == 0);
        conn->ipc = ( (strncmp(dev,"IPC",3) == 0) ||
                      ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) );
-       conn->dirptr = NULL;
 
        /* Case options for the share. */
        if (lp_casesensitive(snum) == Auto) {
@@ -712,7 +716,6 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
        conn->hide_list = NULL;
        conn->veto_oplock_list = NULL;
        conn->aio_write_behind_list = NULL;
-       string_set(&conn->dirpath,"");
 
        conn->read_only = lp_readonly(SNUM(conn));
        conn->admin_user = False;
@@ -730,7 +733,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                fuser = talloc_string_sub(conn, lp_force_user(snum), "%S",
                                          lp_servicename(snum));
                if (fuser == NULL) {
-                       conn_free(sconn, conn);
+                       conn_free(conn);
                        *pstatus = NT_STATUS_NO_MEMORY;
                        return NULL;
                }
@@ -739,7 +742,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                        conn, fuser, conn->server_info->guest,
                        &forced_serverinfo);
                if (!NT_STATUS_IS_OK(status)) {
-                       conn_free(sconn, conn);
+                       conn_free(conn);
                        *pstatus = status;
                        return NULL;
                }
@@ -764,7 +767,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                        &conn->server_info->utok.gid);
 
                if (!NT_STATUS_IS_OK(status)) {
-                       conn_free(sconn, conn);
+                       conn_free(conn);
                        *pstatus = status;
                        return NULL;
                }
@@ -790,14 +793,14 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                                        pdb_get_domain(conn->server_info->sam_account),
                                        lp_pathname(snum));
                if (!s) {
-                       conn_free(sconn, conn);
+                       conn_free(conn);
                        *pstatus = NT_STATUS_NO_MEMORY;
                        return NULL;
                }
 
                if (!set_conn_connectpath(conn,s)) {
                        TALLOC_FREE(s);
-                       conn_free(sconn, conn);
+                       conn_free(conn);
                        *pstatus = NT_STATUS_NO_MEMORY;
                        return NULL;
                }
@@ -829,7 +832,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                                         "denied due to security "
                                         "descriptor.\n",
                                          lp_servicename(snum)));
-                               conn_free(sconn, conn);
+                               conn_free(conn);
                                *pstatus = NT_STATUS_ACCESS_DENIED;
                                return NULL;
                        } else {
@@ -842,30 +845,11 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
        if (!smbd_vfs_init(conn)) {
                DEBUG(0, ("vfs_init failed for service %s\n",
                          lp_servicename(snum)));
-               conn_free(sconn, conn);
+               conn_free(conn);
                *pstatus = NT_STATUS_BAD_NETWORK_NAME;
                return NULL;
        }
 
-       /*
-        * If widelinks are disallowed we need to canonicalise the connect
-        * path here to ensure we don't have any symlinks in the
-        * connectpath. We will be checking all paths on this connection are
-        * below this directory. We must do this after the VFS init as we
-        * depend on the realpath() pointer in the vfs table. JRA.
-        */
-       if (!lp_widelinks(snum)) {
-               if (!canonicalize_connect_path(conn)) {
-                       DEBUG(0, ("canonicalize_connect_path failed "
-                       "for service %s, path %s\n",
-                               lp_servicename(snum),
-                               conn->connectpath));
-                       conn_free(sconn, conn);
-                       *pstatus = NT_STATUS_BAD_NETWORK_NAME;
-                       return NULL;
-               }
-       }
-
        if ((!conn->printer) && (!conn->ipc)) {
                conn->notify_ctx = notify_init(conn, server_id_self(),
                                               smbd_messaging_context(),
@@ -873,7 +857,11 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                                               conn);
        }
 
-/* ROOT Activities: */ 
+/* ROOT Activities: */
+       /* explicitly check widelinks here so that we can correctly warn
+        * in the logs. */
+       widelinks_warning(snum);
+
        /*
         * Enforce the max connections parameter.
         */
@@ -884,7 +872,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
 
                DEBUG(1, ("Max connections (%d) exceeded for %s\n",
                          lp_max_connections(snum), lp_servicename(snum)));
-               conn_free(sconn, conn);
+               conn_free(conn);
                *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES;
                return NULL;
        }  
@@ -894,11 +882,23 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
         */
        if (!claim_connection(conn, lp_servicename(snum), 0)) {
                DEBUG(1, ("Could not store connections entry\n"));
-               conn_free(sconn, conn);
+               conn_free(conn);
                *pstatus = NT_STATUS_INTERNAL_DB_ERROR;
                return NULL;
        }  
 
+       /*
+        * Fix compatibility issue pointed out by Volker.
+        * We pass the conn->connectpath to the preexec
+        * scripts as a parameter, so attempt to canonicalize
+        * it here before calling the preexec scripts.
+        * We ignore errors here, as it is possible that
+        * the conn->connectpath doesn't exist yet and
+        * the preexec scripts will create them.
+        */
+
+       (void)canonicalize_connect_path(conn);
+
        /* Preexecs are done here as they might make the dir we are to ChDir
         * to below */
        /* execute any "root preexec = " line */
@@ -918,7 +918,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                        DEBUG(1,("root preexec gave %d - failing "
                                 "connection\n", ret));
                        yield_connection(conn, lp_servicename(snum));
-                       conn_free(sconn, conn);
+                       conn_free(conn);
                        *pstatus = NT_STATUS_ACCESS_DENIED;
                        return NULL;
                }
@@ -929,7 +929,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                /* No point continuing if they fail the basic checks */
                DEBUG(0,("Can't become connected user!\n"));
                yield_connection(conn, lp_servicename(snum));
-               conn_free(sconn, conn);
+               conn_free(conn);
                *pstatus = NT_STATUS_LOGON_FAILURE;
                return NULL;
        }
@@ -960,6 +960,24 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                }
        }
 
+       /*
+        * If widelinks are disallowed we need to canonicalise the connect
+        * path here to ensure we don't have any symlinks in the
+        * connectpath. We will be checking all paths on this connection are
+        * below this directory. We must do this after the VFS init as we
+        * depend on the realpath() pointer in the vfs table. JRA.
+        */
+       if (!lp_widelinks(snum)) {
+               if (!canonicalize_connect_path(conn)) {
+                       DEBUG(0, ("canonicalize_connect_path failed "
+                       "for service %s, path %s\n",
+                               lp_servicename(snum),
+                               conn->connectpath));
+                       *pstatus = NT_STATUS_BAD_NETWORK_NAME;
+                       goto err_root_exit;
+               }
+       }
+
 #ifdef WITH_FAKE_KASERVER
        if (lp_afs_share(snum)) {
                afs_login(conn);
@@ -1001,20 +1019,27 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
           check during individual operations. To match this behaviour
           I have disabled this chdir check (tridge) */
        /* the alternative is just to check the directory exists */
-       if ((ret = SMB_VFS_STAT(conn, smb_fname_cpath)) != 0 ||
-           !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) {
-               if (ret == 0 && !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) {
+       if (SMB_VFS_STAT(conn, smb_fname_cpath) == 0) {
+               if (!S_ISDIR(smb_fname_cpath->st.st_ex_mode)) {
                        DEBUG(0,("'%s' is not a directory, when connecting to "
                                 "[%s]\n", conn->connectpath,
                                 lp_servicename(snum)));
-               } else {
-                       DEBUG(0,("'%s' does not exist or permission denied "
-                                "when connecting to [%s] Error was %s\n",
-                                conn->connectpath, lp_servicename(snum),
+                       *pstatus = NT_STATUS_BAD_NETWORK_NAME;
+                       goto err_root_exit;
+               }
+       } else {
+               /* Stat failed. Bail on any error except permission denied. */
+               if (errno != EACCES) {
+                       DEBUG(0,("Connecting to share [%s], path '%s' "
+                               "gives error %s\n",
+                               lp_servicename(snum),
+                                conn->connectpath,
                                 strerror(errno) ));
+                       *pstatus = NT_STATUS_BAD_NETWORK_NAME;
+                       goto err_root_exit;
                }
-               *pstatus = NT_STATUS_BAD_NETWORK_NAME;
-               goto err_root_exit;
+               /* As Windows does, on permsission denied we continue.
+                * Pathname calls fail, not TconX calls. */
        }
 
        string_set(&conn->origpath,conn->connectpath);
@@ -1041,7 +1066,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
         * the same characteristics, which is likely but not guaranteed.
         */
 
-       conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
+       conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
 
        /*
         * Print out the 'connected as' stuff here as we need
@@ -1072,7 +1097,7 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
                SMB_VFS_DISCONNECT(conn);
        }
        yield_connection(conn, lp_servicename(snum));
-       conn_free(sconn, conn);
+       conn_free(conn);
        return NULL;
 }
 
@@ -1191,7 +1216,7 @@ connection_struct *make_connection(struct smbd_server_connection *sconn,
                        return NULL;
                }
 
-               DEBUG(0,("%s (%s) couldn't find service %s\n",
+               DEBUG(3,("%s (%s) couldn't find service %s\n",
                        get_remote_machine_name(),
                        client_addr(get_client_fd(),addr,sizeof(addr)),
                        service));
@@ -1219,8 +1244,7 @@ connection_struct *make_connection(struct smbd_server_connection *sconn,
  Close a cnum.
 ****************************************************************************/
 
-void close_cnum(struct smbd_server_connection *sconn,
-               connection_struct *conn, uint16 vuid)
+void close_cnum(connection_struct *conn, uint16 vuid)
 {
        file_close_conn(conn);
 
@@ -1274,5 +1298,5 @@ void close_cnum(struct smbd_server_connection *sconn,
                TALLOC_FREE(cmd);
        }
 
-       conn_free(sconn, conn);
+       conn_free(conn);
 }