(root)/vsftpd-clamav.patch - Rev 1
Rev 2 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
Add support for scanning uploaded files with clamav. Not all features are
implemented (ex. file inclusion/exclusion for scanning). Every uploaded file is
saved in random named file, and moved to destination file after scanning. Side
effects: when uploaded *new* file was infected, 0-size file left.
Written by Marek Marczykowski <m.marczykowski@fiok.pl>
Downloaded from: http://cvs.pld-linux.org/cgi-bin/cvsweb.cgi/packages/vsftpd/vsftpd-clamav.patch
diff -Naru vsftpd-2.2.2.orig/Makefile vsftpd-2.2.2/Makefile
--- vsftpd-2.2.2.orig/Makefile 2009-05-22 21:44:52.000000000 +0200
+++ vsftpd-2.2.2/Makefile 2010-04-29 19:46:54.435448038 +0200
@@ -14,7 +14,7 @@
banner.o filestr.o parseconf.o secutil.o \
ascii.o oneprocess.o twoprocess.o privops.o standalone.o hash.o \
tcpwrap.o ipaddrparse.o access.o features.o readwrite.o opts.o \
- ssl.o sslslave.o ptracesandbox.o ftppolicy.o sysutil.o sysdeputil.o
+ ssl.o sslslave.o ptracesandbox.o ftppolicy.o sysutil.o sysdeputil.o clamav.o
.c.o:
diff -Naru vsftpd-2.2.2.orig/clamav.c vsftpd-2.2.2/clamav.c
--- vsftpd-2.2.2.orig/clamav.c 1970-01-01 01:00:00.000000000 +0100
+++ vsftpd-2.2.2/clamav.c 2010-04-29 19:46:54.435448038 +0200
@@ -0,0 +1,221 @@
+#include <sys/types.h>
+#include <regex.h>
+#include <sys/socket.h>
+#include <linux/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include "clamav.h"
+#include "tunables.h"
+#include "utility.h"
+#include "sysutil.h"
+#include "logging.h"
+#include "sysstr.h"
+
+regex_t av_include_files_regex, av_exclude_files_regex;
+
+int av_init() {
+ int ret;
+
+ if (tunable_av_enable) {
+ if (tunable_av_include_files) {
+ if ((ret=regcomp(&av_include_files_regex, tunable_av_include_files, REG_NOSUB)) != 0)
+ die("regex compilation failed for AvIncludeFiles");
+ }
+ if (tunable_av_exclude_files) {
+ if ((ret=regcomp(&av_exclude_files_regex, tunable_av_exclude_files, REG_NOSUB)) != 0)
+ die("regex compilation failed for AvExcludeFiles");
+ }
+ }
+ return 0;
+}
+
+int av_will_scan(const char *filename) {
+ if (!tunable_av_enable)
+ return 0;
+ if (tunable_av_include_files && (regexec(&av_include_files_regex, filename, 0, 0, 0)!=0))
+ return 0;
+ if (tunable_av_exclude_files && (regexec(&av_exclude_files_regex, filename, 0, 0, 0)==0))
+ return 0;
+ return 1;
+}
+
+int av_init_scanner (struct vsf_session* p_sess) {
+ struct mystr debug_str = INIT_MYSTR;
+
+ if (p_sess->clamd_sock < 0) {
+
+ /* connect to clamd through local unix socket */
+ if (tunable_av_clamd_socket) {
+ struct sockaddr_un server_local;
+
+ vsf_sysutil_memclr((char*)&server_local, sizeof(server_local));
+
+ server_local.sun_family = AF_UNIX;
+ vsf_sysutil_strcpy(server_local.sun_path, tunable_av_clamd_socket, sizeof(server_local.sun_path));
+ if ((p_sess->clamd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ str_alloc_text(&debug_str, "av: error opening unix socket");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ p_sess->clamd_sock = -2;
+ return 0;
+ }
+
+ if (connect(p_sess->clamd_sock, (struct sockaddr *)&server_local, sizeof(struct sockaddr_un)) < 0) {
+ str_alloc_text(&debug_str, "av: error connecting to clamd");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ p_sess->clamd_sock = -2;
+ return 0;
+ }
+
+ } else if (tunable_av_clamd_host) {
+ struct sockaddr_in server_inet;
+ struct hostent *he;
+
+ vsf_sysutil_memclr((char*)&server_inet, sizeof(server_inet));
+
+ /* Remote Socket */
+ server_inet.sin_family = AF_INET;
+ server_inet.sin_port = htons(tunable_av_clamd_port);
+
+ if ((p_sess->clamd_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ str_alloc_text(&debug_str, "av: error opening inet socket");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ p_sess->clamd_sock = -2;
+ return 0;
+ }
+
+ if ((he = gethostbyname(tunable_av_clamd_host)) == 0) {
+ str_alloc_text(&debug_str, "av: unable to locate clamd host");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ vsf_sysutil_close_failok(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ return 0;
+ }
+
+ server_inet.sin_addr = *(struct in_addr *) he->h_addr_list[0];
+
+ if (connect(p_sess->clamd_sock, (struct sockaddr *)&server_inet, sizeof(struct sockaddr_in)) < 0) {
+ str_alloc_text(&debug_str, "av: error connecting to clamd host");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ vsf_sysutil_close_failok(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ return 0;
+ }
+ }
+
+ if (vsf_sysutil_write(p_sess->clamd_sock, "nIDSESSION\n", 11) <= 0) {
+ str_alloc_text(&debug_str, "av: error starting clamd session");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ vsf_sysutil_close_failok(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int av_scan_file(struct vsf_session* p_sess, struct mystr *filename, struct mystr *virname) {
+ struct mystr cwd = INIT_MYSTR;
+ struct mystr clamcmd = INIT_MYSTR;
+ struct mystr response = INIT_MYSTR;
+ char recv_buff[4096];
+ int recv_count;
+ struct str_locate_result locate_res;
+ struct mystr debug_str = INIT_MYSTR;
+ int retry = 0;
+
+init_scan:
+ if (av_init_scanner(p_sess)) {
+
+ str_alloc_text(&clamcmd, "nSCAN ");
+ if (!str_isempty(&p_sess->chroot_str)) {
+ str_append_str(&clamcmd, &p_sess->chroot_str);
+ }
+ if (str_get_char_at(filename, 0) != '/') {
+ str_getcwd(&cwd);
+ str_append_str(&clamcmd, &cwd);
+ }
+ if (str_get_char_at(&clamcmd, str_getlen(&clamcmd) - 1) != '/') {
+ str_append_char(&clamcmd, '/');
+ }
+ str_append_str(&clamcmd, filename);
+ str_append_char(&clamcmd, '\n');
+
+// sprintf(recv_buff, "sockfd: %d", p_sess->clamd_sock);
+// str_alloc_text(&debug_str, recv_buff);
+// vsf_log_line(p_sess, kVSFLogEntryDebug, &p_sess->chroot_str);
+// vsf_log_line(p_sess, kVSFLogEntryDebug, &clamcmd);
+
+ if (vsf_sysutil_write(p_sess->clamd_sock, str_getbuf(&clamcmd), str_getlen(&clamcmd)) <= 0) {
+ str_alloc_text(&debug_str, "av: failed to scan file");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ vsf_sysutil_close_failok(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ return 2;
+ }
+
+ str_free(&clamcmd);
+
+ /* receive and interpret answer */
+ while ((recv_count=vsf_sysutil_read(p_sess->clamd_sock, recv_buff, 4095)) > 0) {
+ recv_buff[recv_count]=0;
+ str_append_text(&response, recv_buff);
+ if (recv_buff[recv_count-1] == '\n')
+ break;
+ }
+ if (recv_count < 0 || str_getlen(&response) == 0) {
+ if (!retry) {
+ retry = 1;
+ vsf_sysutil_close_failok(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ goto init_scan;
+ } else {
+ str_alloc_text(&debug_str, "av: failed to scan file (read failed)");
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ vsf_sysutil_close(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ return 2;
+ }
+ }
+
+ if (str_equal_text(&response, "COMMAND READ TIMED OUT\n")) {
+ if (!retry) {
+ retry = 1;
+ vsf_sysutil_close_failok(p_sess->clamd_sock);
+ p_sess->clamd_sock = -2;
+ goto init_scan;
+ } else {
+ str_alloc_text(&debug_str, "av: got: ");
+ str_append_str(&debug_str, &response);
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ return 2;
+ }
+ }
+
+
+
+ locate_res = str_locate_text(&response, " FOUND\n");
+ /* virus found */
+ if (locate_res.found) {
+ str_trunc(&response, locate_res.index);
+ str_split_text_reverse(&response, virname, ": ");
+ return 1;
+ }
+ locate_res = str_locate_text(&response, " ERROR\n");
+ if (locate_res.found) {
+ str_alloc_text(&debug_str, "av: got: ");
+ str_append_str(&debug_str, &response);
+ vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
+ return 2;
+ }
+ return 0;
+ }
+
+ return 2;
+}
+
+
+
+
diff -Naru vsftpd-2.2.2.orig/clamav.h vsftpd-2.2.2/clamav.h
--- vsftpd-2.2.2.orig/clamav.h 1970-01-01 01:00:00.000000000 +0100
+++ vsftpd-2.2.2/clamav.h 2010-04-29 19:46:54.435448038 +0200
@@ -0,0 +1,12 @@
+#ifndef _CLAMAV_H
+#define _CLAMAV_H
+
+#include "str.h"
+#include "session.h"
+
+extern int av_init();
+extern int av_will_scan(const char *filename);
+extern int av_init_scanner (struct vsf_session* p_sess);
+extern int av_scan_file(struct vsf_session* p_sess, struct mystr *filename, struct mystr *virname);
+
+#endif
diff -Naru vsftpd-2.2.2.orig/main.c vsftpd-2.2.2/main.c
--- vsftpd-2.2.2.orig/main.c 2009-07-18 07:55:53.000000000 +0200
+++ vsftpd-2.2.2/main.c 2010-04-29 19:46:54.435448038 +0200
@@ -64,7 +64,9 @@
/* Secure connection state */
0, 0, 0, 0, 0, INIT_MYSTR, 0, -1, -1,
/* Login fails */
- 0
+ 0,
+ /* av */
+ -1, INIT_MYSTR
};
int config_loaded = 0;
int i;
diff -Naru vsftpd-2.2.2.orig/parseconf.c vsftpd-2.2.2/parseconf.c
--- vsftpd-2.2.2.orig/parseconf.c 2009-08-07 20:46:40.000000000 +0200
+++ vsftpd-2.2.2/parseconf.c 2010-04-29 19:46:54.435448038 +0200
@@ -100,6 +100,7 @@
{ "delete_failed_uploads", &tunable_delete_failed_uploads },
{ "implicit_ssl", &tunable_implicit_ssl },
{ "sandbox", &tunable_sandbox },
+ { "av_enable", &tunable_av_enable },
{ "require_ssl_reuse", &tunable_require_ssl_reuse },
{ "isolate", &tunable_isolate },
{ "isolate_network", &tunable_isolate_network },
@@ -133,6 +134,7 @@
{ "delay_successful_login", &tunable_delay_successful_login },
{ "max_login_fails", &tunable_max_login_fails },
{ "chown_upload_mode", &tunable_chown_upload_mode },
+ { "av_clamd_port", &tunable_av_clamd_port },
{ 0, 0 }
};
@@ -175,6 +177,10 @@
{ "dsa_private_key_file", &tunable_dsa_private_key_file },
{ "ca_certs_file", &tunable_ca_certs_file },
{ "cmds_denied", &tunable_cmds_denied },
+ { "av_clamd_socket", &tunable_av_clamd_socket },
+ { "av_clamd_host", &tunable_av_clamd_host },
+ { "av_include_files", &tunable_av_include_files },
+ { "av_exclude_files", &tunable_av_exclude_files },
{ 0, 0 }
};
diff -Naru vsftpd-2.2.2.orig/postlogin.c vsftpd-2.2.2/postlogin.c
--- vsftpd-2.2.2.orig/postlogin.c 2009-11-07 05:55:12.000000000 +0100
+++ vsftpd-2.2.2/postlogin.c 2010-04-29 19:46:54.438781445 +0200
@@ -27,6 +27,7 @@
#include "ssl.h"
#include "vsftpver.h"
#include "opts.h"
+#include "clamav.h"
/* Private local functions */
static void handle_pwd(struct vsf_session* p_sess);
@@ -972,12 +973,15 @@
static struct vsf_sysutil_statbuf* s_p_statbuf;
static struct mystr s_filename;
struct mystr* p_filename;
+ struct mystr tmp_filename = INIT_MYSTR;
struct vsf_transfer_ret trans_ret;
int new_file_fd;
+ int av_orig_file_fd = -1;
int remote_fd;
int success = 0;
int created = 0;
int do_truncate = 0;
+ int do_av = 0;
filesize_t offset = p_sess->restart_pos;
p_sess->restart_pos = 0;
if (!data_transfer_checks_ok(p_sess))
@@ -991,6 +995,7 @@
get_unique_filename(&s_filename, p_filename);
p_filename = &s_filename;
}
+
vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
prepend_path_to_filename(&p_sess->log_str);
@@ -1022,6 +1027,24 @@
return;
}
created = 1;
+
+ if (av_will_scan(str_getbuf(p_filename))) {
+ do_av = 1;
+ str_copy(&tmp_filename, p_filename);
+ str_append_text(&tmp_filename, ".XXXXXX");
+ av_orig_file_fd = new_file_fd;
+ /* FIXME: various permissions issues... ex. writable file in non-writable directory */
+ new_file_fd = mkstemp(str_getbuf(&tmp_filename));
+ if (vsf_sysutil_retval_is_error(new_file_fd))
+ {
+ vsf_sysutil_close(av_orig_file_fd);
+ vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create temp file.");
+ return;
+ }
+ /* mkstemp creates file with 0600 */
+ vsf_sysutil_fchmod(new_file_fd, 0666 &(~vsf_sysutil_get_umask()));
+ }
+
vsf_sysutil_fstat(new_file_fd, &s_p_statbuf);
if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
{
@@ -1047,6 +1070,8 @@
if (tunable_lock_upload_files)
{
vsf_sysutil_lock_file_write(new_file_fd);
+ if (do_av)
+ vsf_sysutil_lock_file_write(av_orig_file_fd);
}
/* Must truncate the file AFTER locking it! */
if (do_truncate)
@@ -1054,6 +1079,22 @@
vsf_sysutil_ftruncate(new_file_fd);
vsf_sysutil_lseek_to(new_file_fd, 0);
}
+ if (do_av && (is_append || offset != 0)) {
+ char buf[4096];
+ int count;
+
+ /* copy original file */
+ vsf_sysutil_lseek_to(av_orig_file_fd, 0);
+ while ((count=vsf_sysutil_read(av_orig_file_fd, buf, 4096)) > 0) {
+ if (vsf_sysutil_write_loop(new_file_fd, buf, count) < 0) {
+ vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not copy temp file.");
+ vsf_sysutil_close(new_file_fd);
+ vsf_sysutil_close(av_orig_file_fd);
+ vsf_sysutil_unlink(str_getbuf(&tmp_filename));
+ return;
+ }
+ }
+ }
if (!is_append && offset != 0)
{
/* XXX - warning, allows seek past end of file! Check for seek > size? */
@@ -1077,6 +1118,7 @@
}
if (vsf_sysutil_retval_is_error(remote_fd))
{
+ vsf_sysutil_unlink(str_getbuf(&tmp_filename));
goto port_pasv_cleanup_out;
}
if (tunable_ascii_upload_enable && p_sess->is_ascii)
@@ -1097,7 +1139,6 @@
if (trans_ret.retval == 0)
{
success = 1;
- vsf_log_do_log(p_sess, 1);
}
if (trans_ret.retval == -1)
{
@@ -1109,7 +1150,43 @@
}
else
{
- vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
+ if (do_av) {
+ struct mystr virname = INIT_MYSTR;
+ struct mystr resp_str = INIT_MYSTR;
+
+ switch (av_scan_file(p_sess, &tmp_filename, &virname)) {
+ case 1:
+ str_alloc_text(&resp_str, "Virus found: ");
+ str_append_str(&resp_str, &virname);
+ vsf_log_line(p_sess, kVSFLogEntryUpload, &resp_str);
+ vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, str_getbuf(&resp_str));
+ str_free(&resp_str);
+
+ str_unlink(&tmp_filename);
+ break;
+ case 2:
+ vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure scanning file.");
+ str_unlink(&tmp_filename);
+ break;
+ default:
+ /* FIXME: race condition */
+ if (vsf_sysutil_rename(str_getbuf(&tmp_filename), str_getbuf(p_filename)) < 0) {
+ vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure writing to local file .");
+ str_unlink(&tmp_filename);
+ }
+ else
+ {
+ vsf_log_do_log(p_sess, 1);
+ vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "File receive OK.");
+ }
+ break;
+ }
+ }
+ else
+ {
+ vsf_log_do_log(p_sess, 1);
+ vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "File receive OK.");
+ }
}
check_abor(p_sess);
port_pasv_cleanup_out:
@@ -1117,9 +1194,15 @@
pasv_cleanup(p_sess);
if (tunable_delete_failed_uploads && created && !success)
{
- str_unlink(p_filename);
+ if (do_av) {
+ str_unlink(&tmp_filename);
+ } else {
+ str_unlink(p_filename);
+ }
}
vsf_sysutil_close(new_file_fd);
+ if (do_av)
+ vsf_sysutil_close(av_orig_file_fd);
}
static void
@@ -1898,3 +1981,5 @@
{
vsf_cmdio_write(p_sess, FTP_LOGINOK, "Already logged in.");
}
+
+// vim: sw=2:
diff -Naru vsftpd-2.2.2.orig/secutil.c vsftpd-2.2.2/secutil.c
--- vsftpd-2.2.2.orig/secutil.c 2009-05-27 08:20:36.000000000 +0200
+++ vsftpd-2.2.2/secutil.c 2010-04-29 19:46:54.438781445 +0200
@@ -34,6 +34,7 @@
if (p_dir_str == 0 || str_isempty(p_dir_str))
{
str_alloc_text(&dir_str, vsf_sysutil_user_get_homedir(p_user));
+ str_copy(p_dir_str, &dir_str);
}
else
{
diff -Naru vsftpd-2.2.2.orig/session.h vsftpd-2.2.2/session.h
--- vsftpd-2.2.2.orig/session.h 2008-02-12 03:39:38.000000000 +0100
+++ vsftpd-2.2.2/session.h 2010-04-29 19:46:54.438781445 +0200
@@ -93,6 +93,10 @@
int ssl_slave_fd;
int ssl_consumer_fd;
unsigned int login_fails;
+
+ /* data for av scanner */
+ int clamd_sock;
+ struct mystr chroot_str;
};
#endif /* VSF_SESSION_H */
diff -Naru vsftpd-2.2.2.orig/tunables.c vsftpd-2.2.2/tunables.c
--- vsftpd-2.2.2.orig/tunables.c 2009-07-15 22:08:27.000000000 +0200
+++ vsftpd-2.2.2/tunables.c 2010-04-29 19:48:44.265437093 +0200
@@ -85,6 +85,8 @@
int tunable_isolate;
int tunable_isolate_network;
+int tunable_av_enable;
+
unsigned int tunable_accept_timeout;
unsigned int tunable_connect_timeout;
unsigned int tunable_local_umask;
@@ -105,6 +107,7 @@
unsigned int tunable_delay_successful_login;
unsigned int tunable_max_login_fails;
unsigned int tunable_chown_upload_mode;
+unsigned int tunable_av_clamd_port;
const char* tunable_secure_chroot_dir;
const char* tunable_ftp_username;
@@ -139,6 +142,11 @@
const char* tunable_dsa_private_key_file;
const char* tunable_ca_certs_file;
+const char* tunable_av_clamd_socket;
+const char* tunable_av_clamd_host;
+const char* tunable_av_include_files;
+const char* tunable_av_exclude_files;
+
static void install_str_setting(const char* p_value, const char** p_storage);
void
@@ -219,9 +227,10 @@
tunable_sandbox = 0;
tunable_require_ssl_reuse = 1;
tunable_isolate = 1;
- tunable_isolate_network = 1;
+ tunable_isolate_network = 0;
tunable_ftp_enable = 1;
tunable_http_enable = 0;
+ tunable_av_enable = 0;
tunable_accept_timeout = 60;
tunable_connect_timeout = 60;
@@ -245,6 +254,7 @@
tunable_max_login_fails = 3;
/* -rw------- */
tunable_chown_upload_mode = 0600;
+ tunable_av_clamd_port = 3310;
install_str_setting("/usr/share/empty", &tunable_secure_chroot_dir);
install_str_setting("ftp", &tunable_ftp_username);
@@ -280,6 +290,11 @@
install_str_setting(0, &tunable_rsa_private_key_file);
install_str_setting(0, &tunable_dsa_private_key_file);
install_str_setting(0, &tunable_ca_certs_file);
+
+ install_str_setting(0, &tunable_av_clamd_socket);
+ install_str_setting("127.0.0.1", &tunable_av_clamd_host);
+ install_str_setting(0, &tunable_av_include_files);
+ install_str_setting(0, &tunable_av_exclude_files);
}
void
diff -Naru vsftpd-2.2.2.orig/tunables.h vsftpd-2.2.2/tunables.h
--- vsftpd-2.2.2.orig/tunables.h 2009-07-07 03:37:28.000000000 +0200
+++ vsftpd-2.2.2/tunables.h 2010-04-29 19:46:54.438781445 +0200
@@ -83,6 +83,7 @@
extern int tunable_implicit_ssl; /* Use implicit SSL protocol */
extern int tunable_sandbox; /* Deploy ptrace sandbox */
extern int tunable_require_ssl_reuse; /* Require re-used data conn */
+extern int tunable_av_enable; /* Scan av incomming files */
extern int tunable_isolate; /* Use container clone() flags */
extern int tunable_isolate_network; /* Use CLONE_NEWNET */
@@ -107,6 +108,7 @@
extern unsigned int tunable_delay_successful_login;
extern unsigned int tunable_max_login_fails;
extern unsigned int tunable_chown_upload_mode;
+extern unsigned int tunable_av_clamd_port;
/* String defines */
extern const char* tunable_secure_chroot_dir;
@@ -141,6 +143,10 @@
extern const char* tunable_dsa_private_key_file;
extern const char* tunable_ca_certs_file;
extern const char* tunable_cmds_denied;
+extern const char* tunable_av_clamd_socket;
+extern const char* tunable_av_clamd_host;
+extern const char* tunable_av_include_files;
+extern const char* tunable_av_exclude_files;
#endif /* VSF_TUNABLES_H */
diff -Naru vsftpd-2.2.2.orig/twoprocess.c vsftpd-2.2.2/twoprocess.c
--- vsftpd-2.2.2.orig/twoprocess.c 2009-07-18 07:56:44.000000000 +0200
+++ vsftpd-2.2.2/twoprocess.c 2010-04-29 19:46:54.438781445 +0200
@@ -428,6 +428,13 @@
p_user_str, p_orig_user_str);
vsf_secutil_change_credentials(p_user_str, &userdir_str, &chroot_str,
0, secutil_option);
+
+ if (do_chroot) {
+ str_copy(&p_sess->chroot_str, &userdir_str);
+ } else {
+ str_empty(&p_sess->chroot_str);
+ }
+
if (!str_isempty(&chdir_str))
{
(void) str_chdir(&chdir_str);
diff -Naru vsftpd-2.2.2.orig/vsftpd.conf.5 vsftpd-2.2.2/vsftpd.conf.5
--- vsftpd-2.2.2.orig/vsftpd.conf.5 2009-10-19 04:46:30.000000000 +0200
+++ vsftpd-2.2.2/vsftpd.conf.5 2010-04-29 19:46:54.438781445 +0200
@@ -105,6 +105,11 @@
Default: NO
.TP
+.B av_enable
+If enabled, all uploaded files are scanned with clamav (through clamd).
+
+Default: NO
+.TP
.B background
When enabled, and vsftpd is started in "listen" mode, vsftpd will background
the listener process. i.e. control will immediately be returned to the shell
@@ -643,6 +648,11 @@
Default: 077
.TP
+.B av_clamd_port
+Port number where clamd listen on.
+
+Default: 3310
+.TP
.B chown_upload_mode
The file mode to force for chown()ed anonymous uploads. (Added in v2.0.6).
@@ -758,6 +768,18 @@
Default: (none)
.TP
+.B av_clamd_host
+IP where clamd listen. It must be on the same host (or have access to same
+filesystem).
+
+Default: 127.0.0.1
+.TP
+.B av_clamd_socket
+UNIX socket of clamd. Warning: When using chroot you should use TCP instead of
+UNIX socket.
+
+Default: (none)
+.TP
.B banned_email_file
This option is the name of a file containing a list of anonymous e-mail
passwords which are not permitted. This file is consulted if the option