From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/modules/vfs_virusfilter_utils.c | 1036 +++++++++++++++++++++++++++++++ 1 file changed, 1036 insertions(+) create mode 100644 source3/modules/vfs_virusfilter_utils.c (limited to 'source3/modules/vfs_virusfilter_utils.c') diff --git a/source3/modules/vfs_virusfilter_utils.c b/source3/modules/vfs_virusfilter_utils.c new file mode 100644 index 0000000..b467779 --- /dev/null +++ b/source3/modules/vfs_virusfilter_utils.c @@ -0,0 +1,1036 @@ +/* + Samba-VirusFilter VFS modules + Copyright (C) 2010-2016 SATOH Fumiyasu @ OSS Technology Corp., Japan + Copyright (C) 2016-2017 Trever L. Adams + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "modules/vfs_virusfilter_common.h" +#include "modules/vfs_virusfilter_utils.h" + +struct iovec; + +#include "lib/util/iov_buf.h" +#include +#include "lib/tsocket/tsocket.h" +#include "source3/lib/substitute.h" + +int virusfilter_debug_class = DBGC_VFS; + +/* ====================================================================== */ + +char *virusfilter_string_sub( + TALLOC_CTX *mem_ctx, + connection_struct *conn, + const char *str) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + return talloc_sub_full(mem_ctx, + lp_servicename(mem_ctx, lp_sub, SNUM(conn)), + conn->session_info->unix_info->unix_name, + conn->connectpath, + conn->session_info->unix_token->gid, + conn->session_info->unix_info->sanitized_username, + conn->session_info->info->domain_name, + str); +} + +int virusfilter_vfs_next_move( + struct vfs_handle_struct *vfs_h, + const struct smb_filename *smb_fname_src, + const struct smb_filename *smb_fname_dst) +{ + int result; + + result = SMB_VFS_NEXT_RENAMEAT(vfs_h, + vfs_h->conn->cwd_fsp, + smb_fname_src, + vfs_h->conn->cwd_fsp, + smb_fname_dst); + if (result == 0 || errno != EXDEV) { + return result; + } + + /* + * For now, do not handle EXDEV as poking around violates + * stackability. Return -1, simply refuse access. + */ + return -1; +} + +/* Line-based socket I/O + * ====================================================================== + */ + +struct virusfilter_io_handle *virusfilter_io_new( + TALLOC_CTX *mem_ctx, + int connect_timeout, + int io_timeout) +{ + struct virusfilter_io_handle *io_h = talloc_zero(mem_ctx, + struct virusfilter_io_handle); + + if (io_h == NULL) { + return NULL; + } + + io_h->stream = NULL; + io_h->r_len = 0; + + virusfilter_io_set_connect_timeout(io_h, connect_timeout); + virusfilter_io_set_io_timeout(io_h, io_timeout); + virusfilter_io_set_writel_eol(io_h, "\x0A", 1); + virusfilter_io_set_readl_eol(io_h, "\x0A", 1); + + return io_h; +} + +int virusfilter_io_set_connect_timeout( + struct virusfilter_io_handle *io_h, + int timeout) +{ + int timeout_old = io_h->connect_timeout; + + /* timeout <= 0 means infinite */ + io_h->connect_timeout = (timeout > 0) ? timeout : -1; + + return timeout_old; +} + +int virusfilter_io_set_io_timeout( + struct virusfilter_io_handle *io_h, + int timeout) +{ + int timeout_old = io_h->io_timeout; + + /* timeout <= 0 means infinite */ + io_h->io_timeout = (timeout > 0) ? timeout : -1; + + return timeout_old; +} + +void virusfilter_io_set_writel_eol( + struct virusfilter_io_handle *io_h, + const char *eol, + int eol_size) +{ + if (eol_size < 1 || eol_size > VIRUSFILTER_IO_EOL_SIZE) { + return; + } + + memcpy(io_h->w_eol, eol, eol_size); + io_h->w_eol_size = eol_size; +} + +void virusfilter_io_set_readl_eol( + struct virusfilter_io_handle *io_h, + const char *eol, + int eol_size) +{ + if (eol_size < 1 || eol_size > VIRUSFILTER_IO_EOL_SIZE) { + return; + } + + memcpy(io_h->r_eol, eol, eol_size); + io_h->r_eol_size = eol_size; +} + +bool virusfilter_io_connect_path( + struct virusfilter_io_handle *io_h, + const char *path) +{ + struct sockaddr_un addr; + NTSTATUS status; + int socket, ret; + size_t len; + bool ok; + + ZERO_STRUCT(addr); + addr.sun_family = AF_UNIX; + + len = strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); + if (len >= sizeof(addr.sun_path)) { + io_h->stream = NULL; + return false; + } + + status = open_socket_out((struct sockaddr_storage *)&addr, 0, + io_h->connect_timeout, + &socket); + if (!NT_STATUS_IS_OK(status)) { + io_h->stream = NULL; + return false; + } + + /* We must not block */ + ret = set_blocking(socket, false); + if (ret == -1) { + close(socket); + io_h->stream = NULL; + return false; + } + + ok = smb_set_close_on_exec(socket); + if (!ok) { + close(socket); + io_h->stream = NULL; + return false; + } + + ret = tstream_bsd_existing_socket(io_h, socket, &io_h->stream); + if (ret == -1) { + close(socket); + DBG_ERR("Could not convert socket to tstream: %s.\n", + strerror(errno)); + io_h->stream = NULL; + return false; + } + + return true; +} + +static void disconnect_done(struct tevent_req *req) +{ + uint64_t *perr = tevent_req_callback_data(req, uint64_t); + int ret; + int err_ret; + + ret = tstream_disconnect_recv(req, &err_ret); + TALLOC_FREE(req); + if (ret == -1) { + *perr = err_ret; + } +} + +bool virusfilter_io_disconnect( + struct virusfilter_io_handle *io_h) +{ + struct tevent_req *req; + struct tevent_context *ev; + uint64_t *perror = NULL; + bool ok = true; + TALLOC_CTX *frame = talloc_stackframe(); + + if (io_h->stream == NULL) { + io_h->r_len = 0; + TALLOC_FREE(frame); + return VIRUSFILTER_RESULT_OK; + } + + ev = tevent_context_init(frame); + if (ev == NULL) { + DBG_ERR("Failed to setup event context.\n"); + ok = false; + goto fail; + } + + /* Error return - must be talloc'ed. */ + perror = talloc_zero(frame, uint64_t); + if (perror == NULL) { + goto fail; + } + + req = tstream_disconnect_send(io_h, ev, io_h->stream); + + /* Callback when disconnect is done. */ + tevent_req_set_callback(req, disconnect_done, perror); + + /* Set timeout. */ + ok = tevent_req_set_endtime(req, ev, timeval_current_ofs_msec( + io_h->connect_timeout)); + if (!ok) { + DBG_ERR("Can't set endtime\n"); + goto fail; + } + + /* Loop waiting for req to finish. */ + ok = tevent_req_poll(req, ev); + if (!ok) { + DBG_ERR("tevent_req_poll failed\n"); + goto fail; + } + + /* Emit debug error if failed. */ + if (*perror != 0) { + DBG_DEBUG("Error %s\n", strerror((int)*perror)); + goto fail; + } + + /* Here we know we disconnected. */ + + io_h->stream = NULL; + io_h->r_len = 0; + + fail: + TALLOC_FREE(frame); + return ok; +} + +static void writev_done(struct tevent_req *req) +{ + uint64_t *perr = tevent_req_callback_data(req, uint64_t); + int ret; + int err_ret; + + ret = tstream_writev_recv(req, &err_ret); + TALLOC_FREE(req); + if (ret == -1) { + *perr = err_ret; + } +} + +/**************************************************************************** + Write all data from an iov array, with msec timeout (per write) + NB. This can be called with a non-socket fd, don't add dependencies + on socket calls. +****************************************************************************/ + +bool write_data_iov_timeout( + struct tstream_context *stream, + const struct iovec *iov, + size_t iovcnt, + int ms_timeout) +{ + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + uint64_t *perror = NULL; + bool ok = false; + TALLOC_CTX *frame = talloc_stackframe(); + + ev = tevent_context_init(frame); + if (ev == NULL) { + DBG_ERR("Failed to setup event context.\n"); + goto fail; + } + + /* Error return - must be talloc'ed. */ + perror = talloc_zero(frame, uint64_t); + if (perror == NULL) { + goto fail; + } + + /* Send the data. */ + req = tstream_writev_send(frame, ev, stream, iov, iovcnt); + if (req == NULL) { + DBG_ERR("Out of memory.\n"); + goto fail; + } + + /* Callback when *all* data sent. */ + tevent_req_set_callback(req, writev_done, perror); + + /* Set timeout. */ + ok = tevent_req_set_endtime(req, ev, + timeval_current_ofs_msec(ms_timeout)); + if (!ok) { + DBG_ERR("Can't set endtime\n"); + goto fail; + } + + /* Loop waiting for req to finish. */ + ok = tevent_req_poll(req, ev); + if (!ok) { + DBG_ERR("tevent_req_poll failed\n"); + goto fail; + } + + /* Done with req - freed by the callback. */ + req = NULL; + + /* Emit debug error if failed. */ + if (*perror != 0) { + DBG_DEBUG("Error %s\n", strerror((int)*perror)); + goto fail; + } + + /* Here we know we correctly wrote all data. */ + TALLOC_FREE(frame); + return true; + + fail: + TALLOC_FREE(frame); + return false; +} + +bool virusfilter_io_write( + struct virusfilter_io_handle *io_h, + const char *data, + size_t data_size) +{ + struct iovec iov; + + if (data_size == 0) { + return VIRUSFILTER_RESULT_OK; + } + + iov.iov_base = discard_const_p(void, data); + iov.iov_len = data_size; + + return write_data_iov_timeout(io_h->stream, &iov, 1, io_h->io_timeout); +} + +bool virusfilter_io_writel( + struct virusfilter_io_handle *io_h, + const char *data, + size_t data_size) +{ + bool ok; + + ok = virusfilter_io_write(io_h, data, data_size); + if (!ok) { + return ok; + } + + return virusfilter_io_write(io_h, io_h->w_eol, io_h->w_eol_size); +} + +bool PRINTF_ATTRIBUTE(2, 3) virusfilter_io_writefl( + struct virusfilter_io_handle *io_h, + const char *data_fmt, ...) +{ + va_list ap; + char data[VIRUSFILTER_IO_BUFFER_SIZE + VIRUSFILTER_IO_EOL_SIZE]; + int data_size; + + va_start(ap, data_fmt); + data_size = vsnprintf(data, VIRUSFILTER_IO_BUFFER_SIZE, data_fmt, ap); + va_end(ap); + + if (unlikely (data_size < 0)) { + DBG_ERR("vsnprintf failed: %s\n", strerror(errno)); + return false; + } + + memcpy(data + data_size, io_h->w_eol, io_h->w_eol_size); + data_size += io_h->w_eol_size; + + return virusfilter_io_write(io_h, data, data_size); +} + +bool PRINTF_ATTRIBUTE(2, 0) virusfilter_io_vwritefl( + struct virusfilter_io_handle *io_h, + const char *data_fmt, va_list ap) +{ + char data[VIRUSFILTER_IO_BUFFER_SIZE + VIRUSFILTER_IO_EOL_SIZE]; + int data_size; + + data_size = vsnprintf(data, VIRUSFILTER_IO_BUFFER_SIZE, data_fmt, ap); + + if (unlikely (data_size < 0)) { + DBG_ERR("vsnprintf failed: %s\n", strerror(errno)); + return false; + } + + memcpy(data + data_size, io_h->w_eol, io_h->w_eol_size); + data_size += io_h->w_eol_size; + + return virusfilter_io_write(io_h, data, data_size); +} + +bool virusfilter_io_writev( + struct virusfilter_io_handle *io_h, ...) +{ + va_list ap; + struct iovec iov[VIRUSFILTER_IO_IOV_MAX], *iov_p; + int iov_n; + + va_start(ap, io_h); + for (iov_p = iov, iov_n = 0; + iov_n < VIRUSFILTER_IO_IOV_MAX; + iov_p++, iov_n++) + { + iov_p->iov_base = va_arg(ap, void *); + if (iov_p->iov_base == NULL) { + break; + } + iov_p->iov_len = va_arg(ap, int); + } + va_end(ap); + + return write_data_iov_timeout(io_h->stream, iov, iov_n, + io_h->io_timeout); +} + +bool virusfilter_io_writevl( + struct virusfilter_io_handle *io_h, ...) +{ + va_list ap; + struct iovec iov[VIRUSFILTER_IO_IOV_MAX + 1], *iov_p; + int iov_n; + + va_start(ap, io_h); + for (iov_p = iov, iov_n = 0; iov_n < VIRUSFILTER_IO_IOV_MAX; + iov_p++, iov_n++) + { + iov_p->iov_base = va_arg(ap, void *); + if (iov_p->iov_base == NULL) { + break; + } + iov_p->iov_len = va_arg(ap, int); + } + va_end(ap); + + iov_p->iov_base = io_h->r_eol; + iov_p->iov_len = io_h->r_eol_size; + iov_n++; + + return write_data_iov_timeout(io_h->stream, iov, iov_n, + io_h->io_timeout); +} + +static bool return_existing_line(TALLOC_CTX *ctx, + struct virusfilter_io_handle *io_h, + char **read_line) +{ + size_t read_line_len = 0; + char *end_p = NULL; + char *eol = NULL; + + eol = memmem(io_h->r_buffer, io_h->r_len, + io_h->r_eol, io_h->r_eol_size); + if (eol == NULL) { + return false; + } + end_p = eol + io_h->r_eol_size; + + *eol = '\0'; + read_line_len = strlen(io_h->r_buffer) + 1; + *read_line = talloc_memdup(ctx, + io_h->r_buffer, + read_line_len); + if (*read_line == NULL) { + return false; + } + + /* + * Copy the remaining buffer over the line + * we returned. + */ + memmove(io_h->r_buffer, + end_p, + io_h->r_len - (end_p - io_h->r_buffer)); + + /* And reduce the size left in the buffer. */ + io_h->r_len -= (end_p - io_h->r_buffer); + return true; +} + +static void readv_done(struct tevent_req *req) +{ + uint64_t *perr = tevent_req_callback_data(req, uint64_t); + int ret; + int err_ret; + + ret = tstream_readv_recv(req, &err_ret); + TALLOC_FREE(req); + if (ret == -1) { + *perr = err_ret; + } +} + +bool virusfilter_io_readl(TALLOC_CTX *ctx, + struct virusfilter_io_handle *io_h, + char **read_line) +{ + struct tevent_context *ev = NULL; + bool ok = false; + uint64_t *perror = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Search for an existing complete line. */ + ok = return_existing_line(ctx, io_h, read_line); + if (ok) { + goto finish; + } + + /* + * No complete line in the buffer. We must read more + * from the server. + */ + ev = tevent_context_init(frame); + if (ev == NULL) { + DBG_ERR("Failed to setup event context.\n"); + goto finish; + } + + /* Error return - must be talloc'ed. */ + perror = talloc_zero(frame, uint64_t); + if (perror == NULL) { + goto finish; + } + + for (;;) { + ssize_t pending = 0; + size_t read_size = 0; + struct iovec iov; + struct tevent_req *req = NULL; + + /* + * How much can we read ? + */ + pending = tstream_pending_bytes(io_h->stream); + if (pending < 0) { + DBG_ERR("tstream_pending_bytes failed (%s).\n", + strerror(errno)); + goto finish; + } + + read_size = pending; + /* Must read at least one byte. */ + read_size = MIN(read_size, 1); + + /* And max remaining buffer space. */ + read_size = MAX(read_size, + (sizeof(io_h->r_buffer) - io_h->r_len)); + + if (read_size == 0) { + /* Buffer is full with no EOL. Error out. */ + DBG_ERR("Line buffer full.\n"); + goto finish; + } + + iov.iov_base = io_h->r_buffer + io_h->r_len; + iov.iov_len = read_size; + + /* Read the data. */ + req = tstream_readv_send(frame, + ev, + io_h->stream, + &iov, + 1); + if (req == NULL) { + DBG_ERR("out of memory.\n"); + goto finish; + } + + /* Callback when *all* data read. */ + tevent_req_set_callback(req, readv_done, perror); + + /* Set timeout. */ + ok = tevent_req_set_endtime(req, ev, + timeval_current_ofs_msec(io_h->io_timeout)); + if (!ok) { + DBG_ERR("can't set endtime\n"); + goto finish; + } + + /* Loop waiting for req to finish. */ + ok = tevent_req_poll(req, ev); + if (!ok) { + DBG_ERR("tevent_req_poll failed\n"); + goto finish; + } + + /* Done with req - freed by the callback. */ + req = NULL; + + /* + * Emit debug error if failed. + * EPIPE may be success so, don't exit. + */ + if (*perror != 0 && *perror != EPIPE) { + DBG_DEBUG("Error %s\n", strerror((int)*perror)); + errno = (int)*perror; + goto finish; + } + + /* + * We read read_size bytes. Extend the usable + * buffer length. + */ + io_h->r_len += read_size; + + /* Paranoia... */ + SMB_ASSERT(io_h->r_len <= sizeof(io_h->r_buffer)); + + /* Exit if we have a line to return. */ + ok = return_existing_line(ctx, io_h, read_line); + if (ok) { + goto finish; + } + /* No eol - keep reading. */ + } + + finish: + + TALLOC_FREE(frame); + return ok; +} + +bool PRINTF_ATTRIBUTE(3, 4) virusfilter_io_writefl_readl( + struct virusfilter_io_handle *io_h, + char **read_line, + const char *fmt, ...) +{ + bool ok; + + if (fmt) { + va_list ap; + + va_start(ap, fmt); + ok = virusfilter_io_vwritefl(io_h, fmt, ap); + va_end(ap); + + if (!ok) { + return ok; + } + } + + ok = virusfilter_io_readl(talloc_tos(), io_h, read_line); + if (!ok) { + DBG_ERR("virusfilter_io_readl not OK: %d\n", ok); + return false; + } + if (io_h->r_len == 0) { /* EOF */ + DBG_ERR("virusfilter_io_readl EOF\n"); + return false; + } + + return true; +} + +struct virusfilter_cache *virusfilter_cache_new( + TALLOC_CTX *ctx, + int entry_limit, + time_t time_limit) +{ + struct virusfilter_cache *cache; + + if (time_limit == 0) { + return NULL; + } + + cache = talloc_zero(ctx, struct virusfilter_cache); + if (cache == NULL) { + DBG_ERR("talloc_zero failed.\n"); + return NULL; + } + + cache->cache = memcache_init(cache->ctx, entry_limit * + (sizeof(struct virusfilter_cache_entry) + + VIRUSFILTER_CACHE_BUFFER_SIZE)); + if (cache->cache == NULL) { + DBG_ERR("memcache_init failed.\n"); + return NULL; + } + cache->ctx = ctx; + cache->time_limit = time_limit; + + return cache; +} + +bool virusfilter_cache_entry_add( + struct virusfilter_cache *cache, + const char *directory, + const char *fname, + virusfilter_result result, + char *report) +{ + int blob_size = sizeof(struct virusfilter_cache_entry); + struct virusfilter_cache_entry *cache_e = + talloc_zero_size(NULL, blob_size); + int fname_len = 0; + + if (fname == NULL || directory == NULL) { + TALLOC_FREE(report); + return false; + } + + fname = talloc_asprintf(talloc_tos(), "%s/%s", directory, fname); + + if (fname == NULL) { + TALLOC_FREE(report); + return false; + } + + fname_len = strlen(fname); + + if (cache_e == NULL|| cache->time_limit == 0) { + TALLOC_FREE(report); + return false; + } + + cache_e->result = result; + if (report != NULL) { + cache_e->report = talloc_steal(cache_e, report); + } + if (cache->time_limit > 0) { + cache_e->time = time(NULL); + } + + memcache_add_talloc(cache->cache, + VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC, + data_blob_const(fname, fname_len), &cache_e); + + return true; +} + +bool virusfilter_cache_entry_rename( + struct virusfilter_cache *cache, + const char *directory, + char *old_fname, + char *new_fname) +{ + int old_fname_len = 0; + int new_fname_len = 0; + struct virusfilter_cache_entry *new_data = NULL; + struct virusfilter_cache_entry *old_data = NULL; + + if (old_fname == NULL || new_fname == NULL || directory == NULL) { + return false; + } + + old_fname = talloc_asprintf(talloc_tos(), "%s/%s", directory, old_fname); + new_fname = talloc_asprintf(talloc_tos(), "%s/%s", directory, new_fname); + + if (old_fname == NULL || new_fname == NULL) { + TALLOC_FREE(old_fname); + TALLOC_FREE(new_fname); + return false; + } + + old_fname_len = strlen(old_fname); + new_fname_len = strlen(new_fname); + + old_data = memcache_lookup_talloc( + cache->cache, + VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC, + data_blob_const(old_fname, old_fname_len)); + + if (old_data == NULL) { + return false; + } + + new_data = talloc_memdup(cache->ctx, old_data, + sizeof(struct virusfilter_cache_entry)); + if (new_data == NULL) { + return false; + } + new_data->report = talloc_strdup(new_data, old_data->report); + + memcache_add_talloc(cache->cache, + VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC, + data_blob_const(new_fname, new_fname_len), &new_data); + + memcache_delete(cache->cache, VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC, + data_blob_const(old_fname, old_fname_len)); + + return true; +} + +void virusfilter_cache_purge(struct virusfilter_cache *cache) +{ + memcache_flush(cache->cache, VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC); +} + +struct virusfilter_cache_entry *virusfilter_cache_get( + struct virusfilter_cache *cache, + const char *directory, + const char *fname) +{ + int fname_len = 0; + struct virusfilter_cache_entry *cache_e = NULL; + struct virusfilter_cache_entry *data = NULL; + + if (fname == NULL || directory == NULL) { + return 0; + } + + fname = talloc_asprintf(talloc_tos(), "%s/%s", directory, fname); + + if (fname == NULL) { + return 0; + } + + fname_len = strlen(fname); + + data = memcache_lookup_talloc(cache->cache, + VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC, + data_blob_const(fname, fname_len)); + + if (data == NULL) { + return cache_e; + } + + if (cache->time_limit > 0) { + if (time(NULL) - data->time > cache->time_limit) { + DBG_DEBUG("Cache entry is too old: %s\n", + fname); + virusfilter_cache_remove(cache, directory, fname); + return cache_e; + } + } + cache_e = talloc_memdup(cache->ctx, data, + sizeof(struct virusfilter_cache_entry)); + if (cache_e == NULL) { + return NULL; + } + if (data->report != NULL) { + cache_e->report = talloc_strdup(cache_e, data->report); + } else { + cache_e->report = NULL; + } + + return cache_e; +} + +void virusfilter_cache_remove(struct virusfilter_cache *cache, + const char *directory, + const char *fname) +{ + DBG_DEBUG("Purging cache entry: %s/%s\n", directory, fname); + + if (fname == NULL || directory == NULL) { + return; + } + + fname = talloc_asprintf(talloc_tos(), "%s/%s", directory, fname); + + if (fname == NULL) { + return; + } + + memcache_delete(cache->cache, VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC, + data_blob_const(fname, strlen(fname))); +} + +void virusfilter_cache_entry_free(struct virusfilter_cache_entry *cache_e) +{ + if (cache_e != NULL) { + TALLOC_FREE(cache_e->report); + cache_e->report = NULL; + } + TALLOC_FREE(cache_e); +} + +/* Shell scripting + * ====================================================================== + */ + +int virusfilter_env_set( + TALLOC_CTX *mem_ctx, + char **env_list, + const char *name, + const char *value) +{ + char *env_new; + int ret; + + env_new = talloc_asprintf(mem_ctx, "%s=%s", name, value); + if (env_new == NULL) { + DBG_ERR("talloc_asprintf failed\n"); + return -1; + } + + ret = strv_add(mem_ctx, env_list, env_new); + + TALLOC_FREE(env_new); + + return ret; +} + +/* virusfilter_env version Samba's *_sub_advanced() in substitute.c */ +int virusfilter_shell_set_conn_env( + TALLOC_CTX *mem_ctx, + char **env_list, + connection_struct *conn) +{ + int snum = SNUM(conn); + char *server_addr_p; + char *client_addr_p; + const char *local_machine_name = get_local_machine_name(); + fstring pidstr; + int ret; + + server_addr_p = tsocket_address_inet_addr_string( + conn->sconn->local_address, talloc_tos()); + + if (server_addr_p != NULL) { + ret = strncmp("::ffff:", server_addr_p, 7); + if (ret == 0) { + server_addr_p += 7; + } + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_SERVER_IP", + server_addr_p); + } + TALLOC_FREE(server_addr_p); + + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_SERVER_NAME", + myhostname()); + virusfilter_env_set(mem_ctx, env_list, + "VIRUSFILTER_SERVER_NETBIOS_NAME", + local_machine_name); + slprintf(pidstr,sizeof(pidstr)-1, "%ld", (long)getpid()); + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_SERVER_PID", + pidstr); + + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_SERVICE_NAME", + lp_const_servicename(snum)); + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_SERVICE_PATH", + conn->cwd_fsp->fsp_name->base_name); + + client_addr_p = tsocket_address_inet_addr_string( + conn->sconn->remote_address, talloc_tos()); + + if (client_addr_p != NULL) { + ret = strncmp("::ffff:", client_addr_p, 7); + if (ret == 0) { + client_addr_p += 7; + } + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_CLIENT_IP", + client_addr_p); + } + TALLOC_FREE(client_addr_p); + + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_CLIENT_NAME", + conn->sconn->remote_hostname); + virusfilter_env_set(mem_ctx, env_list, + "VIRUSFILTER_CLIENT_NETBIOS_NAME", + get_remote_machine_name()); + + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_USER_NAME", + get_current_username()); + virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_USER_DOMAIN", + get_current_user_info_domain()); + + return 0; +} + +/* Wrapper to Samba's smbrun() in smbrun.c */ +int virusfilter_shell_run( + TALLOC_CTX *mem_ctx, + const char *cmd, + char **env_list, + connection_struct *conn, + bool sanitize) +{ + int ret; + + if (conn != NULL) { + ret = virusfilter_shell_set_conn_env(mem_ctx, env_list, conn); + if (ret == -1) { + return -1; + } + } + + if (sanitize) { + return smbrun(cmd, NULL, strv_to_env(talloc_tos(), *env_list)); + } else { + return smbrun_no_sanitize(cmd, NULL, strv_to_env(talloc_tos(), + *env_list)); + } +} -- cgit v1.2.3