]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
Add the implementation of check_reduced_name_with_privilege(). Now to plumb into
authorJeremy Allison <jra@samba.org>
Thu, 1 Mar 2012 01:04:08 +0000 (17:04 -0800)
committerJeremy Allison <jra@samba.org>
Thu, 1 Mar 2012 01:04:08 +0000 (17:04 -0800)
SMB1 requests.

source3/include/smb.h
source3/smbd/filename.c
source3/smbd/process.c
source3/smbd/proto.h
source3/smbd/vfs.c

index 10e47984a806e3bdfbcb8de96c3b0d9e43cd6d63..a54d2069a8f0844ea63fa85d10e365c7a3ff3f8c 100644 (file)
@@ -442,6 +442,7 @@ struct current_user {
 };
 
 struct smbd_smb2_request;
+struct privilege_paths;
 
 struct smb_request {
        uint8_t cmd;
@@ -495,6 +496,12 @@ struct smb_request {
         * Back pointer to smb2 request.
         */
        struct smbd_smb2_request *smb2req;
+
+       /*
+        * Pathnames used if request done
+        * under privilege.
+        */
+       struct privilege_paths *priv_paths;
 };
 
 /* Defines for the sent_oplock_break field above. */
@@ -1349,6 +1356,15 @@ struct smb_filename {
        SMB_STRUCT_STAT st;
 };
 
+/*
+ * Pathnames used if request done
+ * under privilege.
+ */
+struct privilege_paths {
+       struct smb_filename parent_name;
+       struct smb_filename file_name;
+};
+
 /* Used to keep track of deferred opens. */
 struct deferred_open_record;
 
index b3e132b7ea6c093167feff3f5496ce50c74c74f8..95e8c1440987f6db0cba862290655400abc63ef3 100644 (file)
@@ -1021,7 +1021,7 @@ NTSTATUS check_name(connection_struct *conn, const char *name)
 }
 
 /****************************************************************************
- Must be called as root. Creates the struct priv_backup_restore_paths structure
+ Must be called as root. Creates the struct privilege_paths
  attached to the struct smb_request if this call is successful.
 ****************************************************************************/
 
@@ -1036,8 +1036,7 @@ static NTSTATUS check_name_with_privilege(connection_struct *conn,
        }
        return check_reduced_name_with_privilege(conn,
                        name,
-                       NULL,
-                       NULL);
+                       smbreq);
 }
 
 /****************************************************************************
index fc18f5e8693a5bec631d1c95445ed85e4857d0b8..6ffc06700f9ed82d82f13b15b5df812bf4ab8311 100644 (file)
@@ -540,6 +540,7 @@ static bool init_smb_request(struct smb_request *req,
        req->chain_outbuf = NULL;
        req->done = false;
        req->smb2req = NULL;
+       req->priv_paths = NULL;
        smb_init_perfcount_data(&req->pcd);
 
        /* Ensure we have at least wct words and 2 bytes of bcc. */
index 7101041195ef84afea2a0f16ac6188cc1b6b1be5..48ecfe52d59934a204da33d3f560776c7ab6002f 100644 (file)
@@ -1168,8 +1168,7 @@ char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn);
 NTSTATUS check_reduced_name(connection_struct *conn, const char *fname);
 NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
                        const char *fname,
-                       struct smb_filename **pp_parent_name,
-                       struct smb_filename **pp_file_name);
+                       struct smb_request *smbreq);
 int vfs_stat_smb_fname(struct connection_struct *conn, const char *fname,
                       SMB_STRUCT_STAT *psbuf);
 int vfs_lstat_smb_fname(struct connection_struct *conn, const char *fname,
index 65c25356290cdc8a4a75f2c3f6da4a4f0301ec20..7da38818283961cadd255b774759f176c9b0087f 100644 (file)
@@ -901,10 +901,164 @@ char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
 
 NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
                        const char *fname,
-                       struct smb_filename **pp_parent_name,
-                       struct smb_filename **pp_file_name)
+                       struct smb_request *smbreq)
 {
-       return NT_STATUS_NOT_IMPLEMENTED;
+       NTSTATUS status;
+       TALLOC_CTX *ctx = talloc_tos();
+       const char *conn_rootdir;
+       size_t rootdir_len;
+       char *dir_name = NULL;
+       const char *last_component = NULL;
+       char *resolved_name = NULL;
+       char *saved_dir = NULL;
+       struct smb_filename *smb_fname_cwd = NULL;
+       struct privilege_paths *priv_paths = NULL;
+       int ret;
+
+       DEBUG(3,("check_reduced_name_with_privilege [%s] [%s]\n",
+                       fname,
+                       conn->connectpath));
+
+
+       priv_paths = talloc_zero(smbreq, struct privilege_paths);
+       if (!priv_paths) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err;
+       }
+
+       if (!parent_dirname(ctx, fname, &dir_name, &last_component)) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err;
+       }
+
+       priv_paths->parent_name.base_name = talloc_strdup(priv_paths, dir_name);
+       priv_paths->file_name.base_name = talloc_strdup(priv_paths, last_component);
+
+       if (priv_paths->parent_name.base_name == NULL ||
+                       priv_paths->file_name.base_name == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto err;
+       }
+
+       if (SMB_VFS_STAT(conn, &priv_paths->parent_name) != 0) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+       /* Remember where we were. */
+       saved_dir = vfs_GetWd(ctx, conn);
+       if (!saved_dir) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       /* Go to the parent directory to lock in memory. */
+       if (vfs_ChDir(conn, priv_paths->parent_name.base_name) == -1) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       /* Get the absolute path of the parent directory. */
+       resolved_name = SMB_VFS_REALPATH(conn,".");
+       if (!resolved_name) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       if (*resolved_name != '/') {
+               DEBUG(0,("check_reduced_name_with_privilege: realpath "
+                       "doesn't return absolute paths !\n"));
+               status = NT_STATUS_OBJECT_NAME_INVALID;
+               goto err;
+       }
+
+       DEBUG(10,("check_reduced_name_with_privilege: realpath [%s] -> [%s]\n",
+               priv_paths->parent_name.base_name,
+               resolved_name));
+
+       /* Now check the stat value is the same. */
+       status = create_synthetic_smb_fname(talloc_tos(), ".",
+                                       NULL, NULL,
+                                       &smb_fname_cwd);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto err;
+       }
+
+       if (SMB_VFS_LSTAT(conn, smb_fname_cwd) != 0) {
+               status = map_nt_error_from_unix(errno);
+               goto err;
+       }
+
+       /* Ensure we're pointing at the same place. */
+       if (!check_same_stat(&smb_fname_cwd->st, &priv_paths->parent_name.st)) {
+               DEBUG(0,("check_reduced_name_with_privilege: "
+                       "device/inode/uid/gid on directory %s changed. "
+                       "Denying access !\n",
+                       priv_paths->parent_name.base_name));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto err;
+       }
+
+       /* Ensure we're below the connect path. */
+
+       conn_rootdir = SMB_VFS_CONNECTPATH(conn, fname);
+       if (conn_rootdir == NULL) {
+               DEBUG(2, ("check_reduced_name_with_privilege: Could not get "
+                       "conn_rootdir\n"));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto err;
+       }
+
+       rootdir_len = strlen(conn_rootdir);
+       if (strncmp(conn_rootdir, resolved_name, rootdir_len) != 0) {
+               DEBUG(2, ("check_reduced_name_with_privilege: Bad access "
+                       "attempt: %s is a symlink outside the "
+                       "share path\n",
+                       dir_name));
+               DEBUGADD(2, ("conn_rootdir =%s\n", conn_rootdir));
+               DEBUGADD(2, ("resolved_name=%s\n", resolved_name));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto err;
+       }
+
+       /* Now ensure that the last component either doesn't
+          exist, or is *NOT* a symlink. */
+
+       ret = SMB_VFS_LSTAT(conn, &priv_paths->file_name);
+       if (ret == -1) {
+               /* Errno must be ENOENT for this be ok. */
+               if (errno != ENOENT) {
+                       status = map_nt_error_from_unix(errno);
+                       DEBUG(2, ("check_reduced_name_with_privilege: "
+                               "LSTAT on %s failed with %s\n",
+                               priv_paths->file_name.base_name,
+                               nt_errstr(status)));
+                       goto err;
+               }
+       }
+
+       if (VALID_STAT(priv_paths->file_name.st) &&
+                       S_ISLNK(priv_paths->file_name.st.st_ex_mode)) {
+               DEBUG(2, ("check_reduced_name_with_privilege: "
+                       "Last component %s is a symlink. Denying"
+                       "access.\n",
+                       priv_paths->file_name.base_name));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto err;
+       }
+
+       smbreq->priv_paths = priv_paths;
+       status = NT_STATUS_OK;
+
+  err:
+
+       if (saved_dir) {
+               vfs_ChDir(conn, saved_dir);
+       }
+       SAFE_FREE(resolved_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(priv_paths);
+       }
+       return status;
 }
 
 /*******************************************************************