s4:kdc: Implement KDC plugin hardware authentication policy
[samba.git] / source3 / modules / vfs_streams_depot.c
index bfaab2bf9d4b1e4bb431a5544d05d37c911c40a7..1221b2c2be220d2c28c92d5b9aa9c8dd9dee7104 100644 (file)
@@ -20,6 +20,7 @@
 #include "includes.h"
 #include "smbd/smbd.h"
 #include "system/filesys.h"
+#include "source3/smbd/dir.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -71,14 +72,36 @@ static bool file_is_valid(vfs_handle_struct *handle,
                        const struct smb_filename *smb_fname)
 {
        char buf;
+       NTSTATUS status;
+       struct smb_filename *pathref = NULL;
+       int ret;
 
        DEBUG(10, ("file_is_valid (%s) called\n", smb_fname->base_name));
 
-       if (SMB_VFS_GETXATTR(handle->conn, smb_fname, SAMBA_XATTR_MARKER,
-                                 &buf, sizeof(buf)) != sizeof(buf)) {
-               DEBUG(10, ("GETXATTR failed: %s\n", strerror(errno)));
+       status = synthetic_pathref(talloc_tos(),
+                               handle->conn->cwd_fsp,
+                                smb_fname->base_name,
+                                NULL,
+                                NULL,
+                                smb_fname->twrp,
+                                smb_fname->flags,
+                                &pathref);
+       if (!NT_STATUS_IS_OK(status)) {
                return false;
        }
+       ret = SMB_VFS_FGETXATTR(pathref->fsp,
+                               SAMBA_XATTR_MARKER,
+                               &buf,
+                               sizeof(buf));
+       if (ret != sizeof(buf)) {
+               int saved_errno = errno;
+               DBG_DEBUG("FGETXATTR failed: %s\n", strerror(saved_errno));
+               TALLOC_FREE(pathref);
+               errno = saved_errno;
+               return false;
+       }
+
+       TALLOC_FREE(pathref);
 
        if (buf != '1') {
                DEBUG(10, ("got wrong buffer content: '%c'\n", buf));
@@ -88,23 +111,37 @@ static bool file_is_valid(vfs_handle_struct *handle,
        return true;
 }
 
-static bool mark_file_valid(vfs_handle_struct *handle,
-                               const struct smb_filename *smb_fname)
-{
-       char buf = '1';
-       int ret;
-
-       DEBUG(10, ("marking file %s as valid\n", smb_fname->base_name));
+/*
+ * Return the root of the stream directory. Can be
+ * external to the share definition but by default
+ * is "handle->conn->connectpath/.streams".
+ *
+ * Note that this is an *absolute* path, starting
+ * with '/', so the dirfsp being used in the
+ * calls below isn't looked at.
+ */
 
-       ret = SMB_VFS_SETXATTR(handle->conn, smb_fname, SAMBA_XATTR_MARKER,
-                                   &buf, sizeof(buf), 0);
+static char *stream_rootdir(vfs_handle_struct *handle,
+                           TALLOC_CTX *ctx)
+{
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
+       char *tmp;
 
-       if (ret == -1) {
-               DEBUG(10, ("SETXATTR failed: %s\n", strerror(errno)));
-               return false;
+       tmp = talloc_asprintf(ctx,
+                             "%s/.streams",
+                             handle->conn->connectpath);
+       if (tmp == NULL) {
+               errno = ENOMEM;
+               return NULL;
        }
 
-       return true;
+       return lp_parm_substituted_string(ctx,
+                                         lp_sub,
+                                         SNUM(handle->conn),
+                                         "streams_depot",
+                                         "directory",
+                                         tmp);
 }
 
 /**
@@ -115,14 +152,12 @@ static char *stream_dir(vfs_handle_struct *handle,
                        const struct smb_filename *smb_fname,
                        const SMB_STRUCT_STAT *base_sbuf, bool create_it)
 {
-       const struct loadparm_substitution *lp_sub =
-               loadparm_s3_global_substitution();
        uint32_t hash;
        struct smb_filename *smb_fname_hash = NULL;
        char *result = NULL;
        SMB_STRUCT_STAT base_sbuf_tmp;
+       char *tmp = NULL;
        uint8_t first, second;
-       char *tmp;
        char *id_hex;
        struct file_id id;
        uint8_t id_buf[16];
@@ -135,17 +170,8 @@ static char *stream_dir(vfs_handle_struct *handle,
        check_valid = lp_parm_bool(SNUM(handle->conn),
                      "streams_depot", "check_valid", true);
 
-       tmp = talloc_asprintf(talloc_tos(), "%s/.streams",
-               handle->conn->connectpath);
-
-       if (tmp == NULL) {
-               errno = ENOMEM;
-               goto fail;
-       }
-
-       rootdir = lp_parm_substituted_string(talloc_tos(), lp_sub,
-               SNUM(handle->conn), "streams_depot", "directory",
-               tmp);
+       rootdir = stream_rootdir(handle,
+                                talloc_tos());
        if (rootdir == NULL) {
                errno = ENOMEM;
                goto fail;
@@ -189,7 +215,7 @@ static char *stream_dir(vfs_handle_struct *handle,
 
        id = SMB_VFS_FILE_ID_CREATE(handle->conn, &base_sbuf_tmp);
 
-       push_file_id_16((char *)id_buf, &id);
+       push_file_id_16(id_buf, &id);
 
        hash = hash_fn(data_blob_const(id_buf, sizeof(id_buf)));
 
@@ -379,10 +405,6 @@ static char *stream_dir(vfs_handle_struct *handle,
                goto fail;
        }
 
-       if (check_valid && !mark_file_valid(handle, smb_fname)) {
-               goto fail;
-       }
-
        TALLOC_FREE(rootdir_fname);
        TALLOC_FREE(rootdir);
        TALLOC_FREE(tmp_fname);
@@ -402,6 +424,7 @@ static char *stream_dir(vfs_handle_struct *handle,
  * stream.
  */
 static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
+                                const struct stat_ex *base_sbuf,
                                 const struct smb_filename *smb_fname,
                                 struct smb_filename **smb_fname_out,
                                 bool create_dir)
@@ -420,7 +443,7 @@ static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
                }
        }
 
-       dirname = stream_dir(handle, smb_fname, NULL, create_dir);
+       dirname = stream_dir(handle, smb_fname, base_sbuf, create_dir);
 
        if (dirname == NULL) {
                status = map_nt_error_from_unix(errno);
@@ -444,7 +467,7 @@ static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
                        goto fail;
                }
        } else {
-               /* Normalize the stream type to upercase. */
+               /* Normalize the stream type to uppercase. */
                if (!strupper_m(strrchr_m(stream_fname, ':') + 1)) {
                        status = NT_STATUS_INVALID_PARAMETER;
                        goto fail;
@@ -481,11 +504,13 @@ static NTSTATUS walk_streams(vfs_handle_struct *handle,
                             void *private_data)
 {
        char *dirname;
+       char *rootdir = NULL;
+       char *orig_connectpath = NULL;
        struct smb_filename *dir_smb_fname = NULL;
        struct smb_Dir *dir_hnd = NULL;
        const char *dname = NULL;
-       long offset = 0;
        char *talloced = NULL;
+       NTSTATUS status;
 
        dirname = stream_dir(handle, smb_fname_base, &smb_fname_base->st,
                             false);
@@ -513,16 +538,33 @@ static NTSTATUS walk_streams(vfs_handle_struct *handle,
                return NT_STATUS_NO_MEMORY;
        }
 
-       dir_hnd = OpenDir(talloc_tos(), handle->conn, dir_smb_fname, NULL, 0);
-       if (dir_hnd == NULL) {
+       /*
+        * For OpenDir to succeed if the stream rootdir is outside
+        * the share path, we must temporarily swap out the connect
+        * path for this share. We're dealing with absolute paths
+        * here so we don't care about chdir calls.
+        */
+       rootdir = stream_rootdir(handle, talloc_tos());
+       if (rootdir == NULL) {
                TALLOC_FREE(dir_smb_fname);
                TALLOC_FREE(dirname);
-               return map_nt_error_from_unix(errno);
+               return NT_STATUS_NO_MEMORY;
        }
 
-        while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
-              != NULL)
-       {
+       orig_connectpath = handle->conn->connectpath;
+       handle->conn->connectpath = rootdir;
+
+       status = OpenDir(
+               talloc_tos(), handle->conn, dir_smb_fname, NULL, 0, &dir_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               handle->conn->connectpath = orig_connectpath;
+               TALLOC_FREE(rootdir);
+               TALLOC_FREE(dir_smb_fname);
+               TALLOC_FREE(dirname);
+               return status;
+       }
+
+       while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
                if (ISDOT(dname) || ISDOTDOT(dname)) {
                        TALLOC_FREE(talloced);
                        continue;
@@ -537,6 +579,9 @@ static NTSTATUS walk_streams(vfs_handle_struct *handle,
                TALLOC_FREE(talloced);
        }
 
+       /* Restore the original connectpath. */
+       handle->conn->connectpath = orig_connectpath;
+       TALLOC_FREE(rootdir);
        TALLOC_FREE(dir_smb_fname);
        TALLOC_FREE(dir_hnd);
 
@@ -565,8 +610,8 @@ static int streams_depot_stat(vfs_handle_struct *handle,
        }
 
        /* Stat the actual stream now. */
-       status = stream_smb_fname(handle, smb_fname, &smb_fname_stream,
-                                 false);
+       status = stream_smb_fname(
+               handle, NULL, smb_fname, &smb_fname_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
@@ -599,8 +644,8 @@ static int streams_depot_lstat(vfs_handle_struct *handle,
        }
 
        /* Stat the actual stream now. */
-       status = stream_smb_fname(handle, smb_fname, &smb_fname_stream,
-                                 false);
+       status = stream_smb_fname(
+               handle, NULL, smb_fname, &smb_fname_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
@@ -618,13 +663,12 @@ static int streams_depot_openat(struct vfs_handle_struct *handle,
                                const struct files_struct *dirfsp,
                                const struct smb_filename *smb_fname,
                                struct files_struct *fsp,
-                               int flags,
-                               mode_t mode)
+                               const struct vfs_open_how *how)
 {
        struct smb_filename *smb_fname_stream = NULL;
-       struct smb_filename *smb_fname_base = NULL;
        struct files_struct *fspcwd = NULL;
        NTSTATUS status;
+       bool create_it;
        int ret = -1;
 
        if (!is_named_stream(smb_fname)) {
@@ -632,41 +676,61 @@ static int streams_depot_openat(struct vfs_handle_struct *handle,
                                           dirfsp,
                                           smb_fname,
                                           fsp,
-                                          flags,
-                                          mode);
+                                          how);
        }
 
-       /*
-        * For now assert this so the below SMB_VFS_STAT() is ok.
-        */
-       SMB_ASSERT(fsp_get_pathref_fd(dirfsp) == AT_FDCWD);
-
-       /* Ensure the base file still exists. */
-       smb_fname_base = synthetic_smb_fname(talloc_tos(),
-                                       smb_fname->base_name,
-                                       NULL,
-                                       NULL,
-                                       smb_fname->twrp,
-                                       smb_fname->flags);
-       if (smb_fname_base == NULL) {
-               ret = -1;
-               errno = ENOMEM;
-               goto done;
+       if (how->resolve != 0) {
+               errno = ENOSYS;
+               return -1;
        }
 
-       ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
-       if (ret == -1) {
-               goto done;
-       }
+       SMB_ASSERT(fsp_is_alternate_stream(fsp));
+       SMB_ASSERT(dirfsp == NULL);
+       SMB_ASSERT(VALID_STAT(fsp->base_fsp->fsp_name->st));
+
+       create_it = (how->flags & O_CREAT);
 
        /* Determine the stream name, and then open it. */
-       status = stream_smb_fname(handle, smb_fname, &smb_fname_stream, true);
+       status = stream_smb_fname(
+               handle,
+               &fsp->base_fsp->fsp_name->st,
+               fsp->fsp_name,
+               &smb_fname_stream,
+               create_it);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
+       if (create_it) {
+               bool check_valid = lp_parm_bool(
+                       SNUM(handle->conn),
+                       "streams_depot",
+                       "check_valid",
+                       true);
+
+               if (check_valid) {
+                       char buf = '1';
+
+                       DBG_DEBUG("marking file %s as valid\n",
+                                 fsp->base_fsp->fsp_name->base_name);
+
+                       ret = SMB_VFS_FSETXATTR(
+                               fsp->base_fsp,
+                               SAMBA_XATTR_MARKER,
+                               &buf,
+                               sizeof(buf),
+                               0);
+
+                       if (ret == -1) {
+                               DBG_DEBUG("FSETXATTR failed: %s\n",
+                                         strerror(errno));
+                               goto done;
+                       }
+               }
+       }
+
        status = vfs_at_fspcwd(talloc_tos(), handle->conn, &fspcwd);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
@@ -678,12 +742,10 @@ static int streams_depot_openat(struct vfs_handle_struct *handle,
                                  fspcwd,
                                  smb_fname_stream,
                                  fsp,
-                                 flags,
-                                 mode);
+                                 how);
 
  done:
        TALLOC_FREE(smb_fname_stream);
-       TALLOC_FREE(smb_fname_base);
        TALLOC_FREE(fspcwd);
        return ret;
 }
@@ -712,8 +774,8 @@ static int streams_depot_unlink_internal(vfs_handle_struct *handle,
                struct smb_filename *smb_fname_stream = NULL;
                NTSTATUS status;
 
-               status = stream_smb_fname(handle, full_fname, &smb_fname_stream,
-                                         false);
+               status = stream_smb_fname(
+                       handle, NULL, full_fname, &smb_fname_stream, false);
                TALLOC_FREE(full_fname);
                if (!NT_STATUS_IS_OK(status)) {
                        errno = map_errno_from_nt_status(status);
@@ -737,6 +799,16 @@ static int streams_depot_unlink_internal(vfs_handle_struct *handle,
                ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
        } else {
                ret = SMB_VFS_NEXT_STAT(handle, full_fname);
+               if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
+                       if (VALID_STAT(smb_fname->st) &&
+                                       S_ISLNK(smb_fname->st.st_ex_mode)) {
+                               /*
+                                * Original name was a link - Could be
+                                * trying to remove a dangling symlink.
+                                */
+                               ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+                       }
+               }
        }
        if (ret == -1) {
                TALLOC_FREE(full_fname);
@@ -894,6 +966,8 @@ static int streams_depot_renameat(vfs_handle_struct *handle,
 {
        struct smb_filename *smb_fname_src_stream = NULL;
        struct smb_filename *smb_fname_dst_stream = NULL;
+       struct smb_filename *full_src = NULL;
+       struct smb_filename *full_dst = NULL;
        bool src_is_stream, dst_is_stream;
        NTSTATUS status;
        int ret = -1;
@@ -920,24 +994,47 @@ static int streams_depot_renameat(vfs_handle_struct *handle,
                goto done;
        }
 
-       status = stream_smb_fname(handle, smb_fname_src, &smb_fname_src_stream,
-                                 false);
+       full_src = full_path_from_dirfsp_atname(talloc_tos(),
+                                               srcfsp,
+                                               smb_fname_src);
+       if (full_src == NULL) {
+               errno = ENOMEM;
+               goto done;
+       }
+
+       full_dst = full_path_from_dirfsp_atname(talloc_tos(),
+                                               dstfsp,
+                                               smb_fname_dst);
+       if (full_dst == NULL) {
+               errno = ENOMEM;
+               goto done;
+       }
+
+       status = stream_smb_fname(
+               handle, NULL, full_src, &smb_fname_src_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
-       status = stream_smb_fname(handle, smb_fname_dst,
-                                 &smb_fname_dst_stream, false);
+       status = stream_smb_fname(
+               handle, NULL, full_dst, &smb_fname_dst_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
+       /*
+        * We must use handle->conn->cwd_fsp as
+        * srcfsp and dstfsp directory handles here
+        * as we used the full pathname from the cwd dir
+        * to calculate the streams directory and filename
+        * within.
+        */
        ret = SMB_VFS_NEXT_RENAMEAT(handle,
-                               srcfsp,
+                               handle->conn->cwd_fsp,
                                smb_fname_src_stream,
-                               dstfsp,
+                               handle->conn->cwd_fsp,
                                smb_fname_dst_stream);
 
 done:
@@ -1034,9 +1131,8 @@ static bool collect_one_stream(const struct smb_filename *dirfname,
        return ret;
 }
 
-static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
+static NTSTATUS streams_depot_fstreaminfo(vfs_handle_struct *handle,
                                         struct files_struct *fsp,
-                                        const struct smb_filename *smb_fname,
                                         TALLOC_CTX *mem_ctx,
                                         unsigned int *pnum_streams,
                                         struct stream_struct **pstreams)
@@ -1047,26 +1143,16 @@ static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
        struct streaminfo_state state;
 
        smb_fname_base = synthetic_smb_fname(talloc_tos(),
-                                       smb_fname->base_name,
+                                       fsp->fsp_name->base_name,
                                        NULL,
                                        NULL,
-                                       smb_fname->twrp,
-                                       smb_fname->flags);
+                                       fsp->fsp_name->twrp,
+                                       fsp->fsp_name->flags);
        if (smb_fname_base == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       if ((fsp != NULL) && (fsp_get_pathref_fd(fsp) != -1)) {
-               ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
-       }
-       else {
-               if (smb_fname_base->flags & SMB_FILENAME_POSIX_PATH) {
-                       ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_base);
-               } else {
-                       ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
-               }
-       }
-
+       ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
        if (ret == -1) {
                status = map_nt_error_from_unix(errno);
                goto out;
@@ -1078,19 +1164,11 @@ static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
        state.handle = handle;
        state.status = NT_STATUS_OK;
 
-       if (S_ISLNK(smb_fname_base->st.st_ex_mode)) {
-               /*
-                * Currently we do't have SMB_VFS_LLISTXATTR
-                * inside the VFS which means there's no way
-                * to cope with a symlink when lp_posix_pathnames().
-                * returns true. For now ignore links.
-                * FIXME - by adding SMB_VFS_LLISTXATTR. JRA.
-                */
-               status = NT_STATUS_OK;
-       } else {
-               status = walk_streams(handle, smb_fname_base, NULL, collect_one_stream,
-                             &state);
-       }
+       status = walk_streams(handle,
+                               smb_fname_base,
+                               NULL,
+                               collect_one_stream,
+                               &state);
 
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(state.streams);
@@ -1105,9 +1183,8 @@ static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
 
        *pnum_streams = state.num_streams;
        *pstreams = state.streams;
-       status = SMB_VFS_NEXT_STREAMINFO(handle,
-                               fsp,
-                               smb_fname_base,
+       status = SMB_VFS_NEXT_FSTREAMINFO(handle,
+                               fsp->base_fsp ? fsp->base_fsp : fsp,
                                mem_ctx,
                                pnum_streams,
                                pstreams);
@@ -1130,7 +1207,7 @@ static struct vfs_fn_pointers vfs_streams_depot_fns = {
        .lstat_fn = streams_depot_lstat,
        .unlinkat_fn = streams_depot_unlinkat,
        .renameat_fn = streams_depot_renameat,
-       .streaminfo_fn = streams_depot_streaminfo,
+       .fstreaminfo_fn = streams_depot_fstreaminfo,
 };
 
 static_decl_vfs;