s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source3 / modules / vfs_virusfilter.c
index 0244c04289c326fa28eaf85bf2fe10796bd7c5bf..ea1886d85c839eb49107926a34b2cf0b4219a547 100644 (file)
 
 enum virusfilter_scanner_enum {
        VIRUSFILTER_SCANNER_CLAMAV,
+       VIRUSFILTER_SCANNER_DUMMY,
        VIRUSFILTER_SCANNER_FSAV,
        VIRUSFILTER_SCANNER_SOPHOS
 };
 
 static const struct enum_list scanner_list[] = {
        { VIRUSFILTER_SCANNER_CLAMAV,   "clamav" },
+       { VIRUSFILTER_SCANNER_DUMMY,    "dummy" },
        { VIRUSFILTER_SCANNER_FSAV,     "fsav" },
        { VIRUSFILTER_SCANNER_SOPHOS,   "sophos" },
        { -1,                           NULL }
@@ -157,6 +159,7 @@ static bool quarantine_create_dir(
                                                        new_dir,
                                                        NULL,
                                                        NULL,
+                                                       0,
                                                        0);
                        if (smb_fname == NULL) {
                                goto done;
@@ -198,7 +201,16 @@ static int virusfilter_vfs_connect(
        int snum = SNUM(handle->conn);
        struct virusfilter_config *config = NULL;
        const char *exclude_files = NULL;
+       const char *infected_files = NULL;
        const char *temp_quarantine_dir_mode = NULL;
+       const char *infected_file_command = NULL;
+       const char *scan_error_command = NULL;
+       const char *quarantine_dir = NULL;
+       const char *quarantine_prefix = NULL;
+       const char *quarantine_suffix = NULL;
+       const char *rename_prefix = NULL;
+       const char *rename_suffix = NULL;
+       const char *socket_path = NULL;
        char *sret = NULL;
        char *tmp = NULL;
        enum virusfilter_scanner_enum backend;
@@ -246,6 +258,12 @@ static int virusfilter_vfs_connect(
                set_namearray(&config->exclude_files, exclude_files);
        }
 
+       infected_files = lp_parm_const_string(
+               snum, "virusfilter", "infected files", NULL);
+       if (infected_files != NULL) {
+               set_namearray(&config->infected_files, infected_files);
+       }
+
        config->cache_entry_limit = lp_parm_int(
                snum, "virusfilter", "cache entry limit", 100);
 
@@ -256,11 +274,27 @@ static int virusfilter_vfs_connect(
                snum, "virusfilter", "infected file action",
                virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
 
-       config->infected_file_command = lp_parm_const_string(
+       infected_file_command = lp_parm_const_string(
                snum, "virusfilter", "infected file command", NULL);
-
-       config->scan_error_command = lp_parm_const_string(
+       if (infected_file_command != NULL) {
+               config->infected_file_command = talloc_strdup(
+                                                       config,
+                                                       infected_file_command);
+               if (config->infected_file_command == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
+       scan_error_command = lp_parm_const_string(
                snum, "virusfilter", "scan error command", NULL);
+       if (scan_error_command != NULL) {
+               config->scan_error_command = talloc_strdup(config,
+                                                          scan_error_command);
+               if (config->scan_error_command == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
        config->block_access_on_error = lp_parm_bool(
                snum, "virusfilter", "block access on error", false);
@@ -268,9 +302,16 @@ static int virusfilter_vfs_connect(
        tmp = talloc_asprintf(config, "%s/.quarantine",
                handle->conn->connectpath);
 
-       config->quarantine_dir = lp_parm_const_string(
+       quarantine_dir = lp_parm_const_string(
                snum, "virusfilter", "quarantine directory",
                tmp ? tmp : "/tmp/.quarantine");
+       if (quarantine_dir != NULL) {
+               config->quarantine_dir = talloc_strdup(config, quarantine_dir);
+               if (config->quarantine_dir == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
        if (tmp != config->quarantine_dir) {
                TALLOC_FREE(tmp);
@@ -284,35 +325,55 @@ static int virusfilter_vfs_connect(
                config->quarantine_dir_mode = mode;
        }
 
-       config->quarantine_prefix = lp_parm_const_string(
+       quarantine_prefix = lp_parm_const_string(
                snum, "virusfilter", "quarantine prefix",
                VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
+       if (quarantine_prefix != NULL) {
+               config->quarantine_prefix = talloc_strdup(config,
+                                                         quarantine_prefix);
+               if (config->quarantine_prefix == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
-       config->quarantine_suffix = lp_parm_const_string(
+       quarantine_suffix = lp_parm_const_string(
                snum, "virusfilter", "quarantine suffix",
                VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
+       if (quarantine_suffix != NULL) {
+               config->quarantine_suffix = talloc_strdup(config,
+                                                         quarantine_suffix);
+               if (config->quarantine_suffix == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
        /*
         * Make sure prefixes and suffixes do not contain directory
         * delimiters
         */
-       sret = strstr(config->quarantine_prefix, "/");
-       if (sret != NULL) {
-               DBG_ERR("quarantine prefix must not contain directory "
-                       "delimiter(s) such as '/' (%s replaced with %s)\n",
-                       config->quarantine_prefix,
-                       VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
-               config->quarantine_prefix =
-                       VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
-       }
-       sret = strstr(config->quarantine_suffix, "/");
-       if (sret != NULL) {
-               DBG_ERR("quarantine suffix must not contain directory "
-                       "delimiter(s) such as '/' (%s replaced with %s)\n",
-                       config->quarantine_suffix,
-                       VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
-               config->quarantine_suffix =
-                       VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
+       if (config->quarantine_prefix != NULL) {
+               sret = strstr(config->quarantine_prefix, "/");
+               if (sret != NULL) {
+                       DBG_ERR("quarantine prefix must not contain directory "
+                               "delimiter(s) such as '/' (%s replaced with %s)\n",
+                               config->quarantine_prefix,
+                               VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
+                       config->quarantine_prefix =
+                               VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
+               }
+       }
+       if (config->quarantine_suffix != NULL) {
+               sret = strstr(config->quarantine_suffix, "/");
+               if (sret != NULL) {
+                       DBG_ERR("quarantine suffix must not contain directory "
+                               "delimiter(s) such as '/' (%s replaced with %s)\n",
+                               config->quarantine_suffix,
+                               VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
+                       config->quarantine_suffix =
+                               VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
+               }
        }
 
        config->quarantine_keep_tree = lp_parm_bool(
@@ -321,35 +382,53 @@ static int virusfilter_vfs_connect(
        config->quarantine_keep_name = lp_parm_bool(
                snum, "virusfilter", "quarantine keep name", true);
 
-       config->rename_prefix = lp_parm_const_string(
+       rename_prefix = lp_parm_const_string(
                snum, "virusfilter", "rename prefix",
                VIRUSFILTER_DEFAULT_RENAME_PREFIX);
+       if (rename_prefix != NULL) {
+               config->rename_prefix = talloc_strdup(config, rename_prefix);
+               if (config->rename_prefix == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
-       config->rename_suffix = lp_parm_const_string(
+       rename_suffix = lp_parm_const_string(
                snum, "virusfilter", "rename suffix",
                VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
+       if (rename_suffix != NULL) {
+               config->rename_suffix = talloc_strdup(config, rename_suffix);
+               if (config->rename_suffix == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
        /*
         * Make sure prefixes and suffixes do not contain directory
         * delimiters
         */
-       sret = strstr(config->rename_prefix, "/");
-       if (sret != NULL) {
-               DBG_ERR("rename prefix must not contain directory "
-                       "delimiter(s) such as '/' (%s replaced with %s)\n",
-                       config->rename_prefix,
-                       VIRUSFILTER_DEFAULT_RENAME_PREFIX);
-               config->rename_prefix =
-                       VIRUSFILTER_DEFAULT_RENAME_PREFIX;
-       }
-       sret = strstr(config->rename_suffix, "/");
-       if (sret != NULL) {
-               DBG_ERR("rename suffix must not contain directory "
-                       "delimiter(s) such as '/' (%s replaced with %s)\n",
-                       config->rename_suffix,
-                       VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
-               config->rename_suffix =
-                       VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
+       if (config->rename_prefix != NULL) {
+               sret = strstr(config->rename_prefix, "/");
+               if (sret != NULL) {
+                       DBG_ERR("rename prefix must not contain directory "
+                               "delimiter(s) such as '/' (%s replaced with %s)\n",
+                               config->rename_prefix,
+                               VIRUSFILTER_DEFAULT_RENAME_PREFIX);
+                       config->rename_prefix =
+                               VIRUSFILTER_DEFAULT_RENAME_PREFIX;
+               }
+       }
+       if (config->rename_suffix != NULL) {
+               sret = strstr(config->rename_suffix, "/");
+               if (sret != NULL) {
+                       DBG_ERR("rename suffix must not contain directory "
+                               "delimiter(s) such as '/' (%s replaced with %s)\n",
+                               config->rename_suffix,
+                               VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
+                       config->rename_suffix =
+                               VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
+               }
        }
 
        config->infected_open_errno = lp_parm_int(
@@ -364,15 +443,22 @@ static int virusfilter_vfs_connect(
        config->scan_error_close_errno = lp_parm_int(
                snum, "virusfilter", "scan error errno on close", 0);
 
-       config->socket_path = lp_parm_const_string(
+       socket_path = lp_parm_const_string(
                snum, "virusfilter", "socket path", NULL);
+       if (socket_path != NULL) {
+               config->socket_path = talloc_strdup(config, socket_path);
+               if (config->socket_path == NULL) {
+                       DBG_ERR("virusfilter-vfs: out of memory!\n");
+                       return -1;
+               }
+       }
 
        /* canonicalize socket_path */
        if (config->socket_path != NULL && config->socket_path[0] != '/') {
                DBG_ERR("socket path must be an absolute path. "
                        "Using backend default\n");
                config->socket_path = NULL;
-        }
+       }
        if (config->socket_path != NULL) {
                config->socket_path = canonicalize_absolute_path(
                        handle, config->socket_path);
@@ -389,7 +475,7 @@ static int virusfilter_vfs_connect(
 
        config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
        if (config->io_h == NULL) {
-               DBG_ERR("virusfilter_io_new failed");
+               DBG_ERR("virusfilter_io_new failed\n");
                return -1;
        }
 
@@ -460,6 +546,9 @@ static int virusfilter_vfs_connect(
        case VIRUSFILTER_SCANNER_CLAMAV:
                ret = virusfilter_clamav_init(config);
                break;
+       case VIRUSFILTER_SCANNER_DUMMY:
+               ret = virusfilter_dummy_init(config);
+               break;
        default:
                DBG_ERR("Unhandled scanner %d\n", backend);
                return -1;
@@ -670,6 +759,7 @@ static virusfilter_action infected_file_action_quarantine(
                                          q_filepath,
                                          smb_fname->stream_name,
                                          NULL,
+                                         0,
                                          smb_fname->flags);
        if (q_smb_fname == NULL) {
                action = VIRUSFILTER_ACTION_DO_NOTHING;
@@ -751,6 +841,7 @@ static virusfilter_action infected_file_action_rename(
 
        q_smb_fname = synthetic_smb_fname(frame, q_filepath,
                                          smb_fname->stream_name, NULL,
+                                         0,
                                          smb_fname->flags);
        if (q_smb_fname == NULL) {
                action = VIRUSFILTER_ACTION_DO_NOTHING;
@@ -1130,7 +1221,7 @@ virusfilter_scan_result_eval:
                                        scan_result, scan_report);
                        if (!ok) {
                                DBG_ERR("Cannot create cache entry: "
-                                       "virusfilter_cache_entry_new failed");
+                                       "virusfilter_cache_entry_new failed\n");
                                goto virusfilter_scan_return;
                        }
                } else if (is_cache) {
@@ -1142,16 +1233,15 @@ virusfilter_scan_return:
        return scan_result;
 }
 
-static int virusfilter_vfs_open(
-       struct vfs_handle_struct *handle,
-       struct smb_filename *smb_fname,
-       files_struct *fsp,
-       int flags,
-       mode_t mode)
+static int virusfilter_vfs_openat(struct vfs_handle_struct *handle,
+                                 const struct files_struct *dirfsp,
+                                 const struct smb_filename *smb_fname_in,
+                                 struct files_struct *fsp,
+                                 const struct vfs_open_how *how)
 {
        TALLOC_CTX *mem_ctx = talloc_tos();
-       struct virusfilter_config *config;
-       const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
+       struct virusfilter_config *config = NULL;
+       const char *cwd_fname = dirfsp->fsp_name->base_name;
        virusfilter_result scan_result;
        const char *fname = fsp->fsp_name->base_name;
        char *dir_name = NULL;
@@ -1163,6 +1253,8 @@ static int virusfilter_vfs_open(
        int ret;
        bool ok1;
        char *sret = NULL;
+       struct smb_filename *smb_fname = NULL;
+       SMB_STRUCT_STAT sbuf = smb_fname_in->st;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct virusfilter_config, return -1);
@@ -1181,6 +1273,11 @@ static int virusfilter_vfs_open(
                rename_trap_count++;
        }
 
+       smb_fname = cp_smb_filename(mem_ctx, smb_fname_in);
+       if (smb_fname == NULL) {
+               goto virusfilter_vfs_open_fail;
+       }
+
        if (is_named_stream(smb_fname)) {
                DBG_INFO("Not scanned: only file backed streams can be scanned:"
                         " %s/%s\n", cwd_fname, fname);
@@ -1193,13 +1290,13 @@ static int virusfilter_vfs_open(
                goto virusfilter_vfs_open_next;
        }
 
-       if (flags & O_TRUNC) {
+       if (how->flags & O_TRUNC) {
                DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
                         cwd_fname, fname);
                goto virusfilter_vfs_open_next;
        }
 
-       ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+       ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf);
        if (ret != 0) {
 
                /*
@@ -1211,21 +1308,21 @@ static int virusfilter_vfs_open(
                 */
                goto virusfilter_vfs_open_next;
        }
-       ret = S_ISREG(smb_fname->st.st_ex_mode);
+       ret = S_ISREG(sbuf.st_ex_mode);
        if (ret == 0) {
                DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
                         cwd_fname, fname);
                goto virusfilter_vfs_open_next;
        }
        if (config->max_file_size > 0 &&
-           smb_fname->st.st_ex_size > config->max_file_size)
+           sbuf.st_ex_size > config->max_file_size)
        {
                DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
                         cwd_fname, fname);
                goto virusfilter_vfs_open_next;
        }
        if (config->min_file_size > 0 &&
-           smb_fname->st.st_ex_size < config->min_file_size)
+           sbuf.st_ex_size < config->min_file_size)
        {
                DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
                      cwd_fname, fname);
@@ -1301,10 +1398,13 @@ static int virusfilter_vfs_open(
                goto virusfilter_vfs_open_fail;
        }
 
+       TALLOC_FREE(smb_fname);
+
 virusfilter_vfs_open_next:
-       return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname_in, fsp, how);
 
 virusfilter_vfs_open_fail:
+       TALLOC_FREE(smb_fname);
        errno = (scan_errno != 0) ? scan_errno : EACCES;
        return -1;
 }
@@ -1358,7 +1458,7 @@ static int virusfilter_vfs_close(
                return close_result;
        }
 
-       if (is_named_stream(fsp->fsp_name)) {
+       if (fsp_is_alternate_stream(fsp)) {
                if (config->scan_on_open && fsp->fsp_flags.modified) {
                        if (config->cache) {
                                DBG_DEBUG("Removing cache entry (if existent)"
@@ -1395,9 +1495,7 @@ static int virusfilter_vfs_close(
                return close_result;
        }
 
-       if (config->exclude_files && is_in_path(fname,
-           config->exclude_files, false))
-       {
+       if (is_in_path(fname, config->exclude_files, false)) {
                DBG_INFO("Not scanned: exclude files: %s/%s\n",
                         cwd_fname, fname);
                return close_result;
@@ -1446,8 +1544,9 @@ static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
                        smb_fname,
                        flags);
        struct virusfilter_config *config = NULL;
+       struct smb_filename *full_fname = NULL;
        char *fname = NULL;
-       char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
+       char *cwd_fname = dirfsp->fsp_name->base_name;
 
        if (ret != 0 && errno != ENOENT) {
                return ret;
@@ -1460,11 +1559,19 @@ static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
                return 0;
        }
 
-       fname = smb_fname->base_name;
+       full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                 dirfsp,
+                                                 smb_fname);
+       if (full_fname == NULL) {
+               return -1;
+       }
+
+       fname = full_fname->base_name;
 
        DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
        virusfilter_cache_remove(config->cache, cwd_fname, fname);
 
+       TALLOC_FREE(full_fname);
        return 0;
 }
 
@@ -1484,6 +1591,8 @@ static int virusfilter_vfs_renameat(
        char *fname = NULL;
        char *dst_fname = NULL;
        char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
+       struct smb_filename *full_src = NULL;
+       struct smb_filename *full_dst = NULL;
 
        if (ret != 0) {
                return ret;
@@ -1496,16 +1605,39 @@ static int virusfilter_vfs_renameat(
                return 0;
        }
 
-       fname = smb_fname_src->base_name;
-       dst_fname = smb_fname_dst->base_name;
+       full_src = full_path_from_dirfsp_atname(talloc_tos(),
+                                               srcfsp,
+                                               smb_fname_src);
+       if (full_src == NULL) {
+               errno = ENOMEM;
+               ret = -1;
+               goto out;
+       }
+
+       full_dst = full_path_from_dirfsp_atname(talloc_tos(),
+                                               dstfsp,
+                                               smb_fname_dst);
+       if (full_dst == NULL) {
+               errno = ENOMEM;
+               ret = -1;
+               goto out;
+       }
+
+       fname = full_src->base_name;
+       dst_fname = full_dst->base_name;
 
        DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
                  fname, dst_fname);
        virusfilter_cache_entry_rename(config->cache,
-                                      cwd_fname, fname,
+                                      cwd_fname,
+                                      fname,
                                       dst_fname);
 
-       return 0;
+       ret = 0;
+  out:
+       TALLOC_FREE(full_src);
+       TALLOC_FREE(full_dst);
+       return ret;
 }
 
 
@@ -1513,7 +1645,7 @@ static int virusfilter_vfs_renameat(
 static struct vfs_fn_pointers vfs_virusfilter_fns = {
        .connect_fn     = virusfilter_vfs_connect,
        .disconnect_fn  = virusfilter_vfs_disconnect,
-       .open_fn        = virusfilter_vfs_open,
+       .openat_fn      = virusfilter_vfs_openat,
        .close_fn       = virusfilter_vfs_close,
        .unlinkat_fn    = virusfilter_vfs_unlinkat,
        .renameat_fn    = virusfilter_vfs_renameat,