debian/0000755000000000000000000000000012213721245007165 5ustar debian/watch0000644000000000000000000000014211645322134010215 0ustar version=3 http://www.thrallingpenguin.com/resources/mod_clamav.htm mod_clamav-([\d\.]+)\.tar\.gz debian/templates/0000755000000000000000000000000011645322134011165 5ustar debian/templates/clamav.conf0000644000000000000000000000016011645322134013274 0ustar ClamAV on ClamServer localhost ClamPort 3310 ClamMaxSize 250 Mb debian/proftpd-mod-clamav.links0000644000000000000000000000012511645322134013723 0ustar usr/share/doc/proftpd-mod-clamav/clamav.conf usr/share/proftpd/templates/clamav.conf debian/proftpd-mod-clamav.install0000644000000000000000000000007711645322134014257 0ustar debian/templates/clamav.conf usr/share/doc/proftpd-mod-clamav/ debian/rules0000755000000000000000000000116611645322134010253 0ustar #!/usr/bin/make -f # -*- makefile -*- export DH_VERBOSE=1 MODULE_NAME=mod_clamav DEBNAME=proftpd-mod-clamav %: dh $@ override_dh_auto_build: DESTDIR=$(CURDIR)/debian/$(DEBNAME) prxs -c $(MODULE_NAME).c override_dh_auto_install: DESTDIR=$(CURDIR)/debian/$(DEBNAME) prxs -i -c $(MODULE_NAME).c rm -f $(CURDIR)/debian/$(DEBNAME)/usr/lib/proftpd/*.la \ $(CURDIR)/debian/$(DEBNAME)/usr/lib/proftpd/*.a override_dh_gencontrol: cat /usr/share/proftpd/proftpd-substvars >> $(CURDIR)/debian/$(DEBNAME).substvars dh_gencontrol override_dh_autoclean: DESTDIR=$(CURDIR)/debian/$(DEBNAME) prxs -d $(MODULE_NAME).c debian/README.Debian0000644000000000000000000000043311645322134011230 0ustar Add-on mod_clamav module for ProFTPD in Debian ---------------------------------------------- Note that the Debian version of this module is patched to avoid changing the mod_core ProFTPD module and to work better with mod_sftp. Thanks TJ Saunders and the ProFTPD team about that. debian/proftpd-mod-clamav.dirs0000644000000000000000000000005411645322134013545 0ustar usr/lib/proftpd usr/share/proftpd/templates debian/copyright0000644000000000000000000000217411645322134011126 0ustar Format: http://dep.debian.net/deps/dep5/ Upstream-Name: mod_clamav Upstream-Contact: Joseph Benden Source: http://www.thrallingpenguin.com/resources/mod_clamav.htm Files: * Copyright: 2007-2010, Joseph Benden License: GPL-2+ License: GPL-2+ This package 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 2 of the License, or (at your option) any later version. . This package 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 . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". Files: debian/* Copyright: 2011, Mahyuddin Susanto License: GPL-2+ debian/source/0000755000000000000000000000000011645322134010467 5ustar debian/source/format0000644000000000000000000000001411645322134011675 0ustar 3.0 (quilt) debian/patches/0000755000000000000000000000000011645322134010616 5ustar debian/patches/cleanup0000644000000000000000000002656311645322134012204 0ustar Index: proftpd-mod-clamav/mod_clamav.c =================================================================== --- proftpd-mod-clamav.orig/mod_clamav.c 2011-04-20 17:38:03.000000000 +0200 +++ proftpd-mod-clamav/mod_clamav.c 2011-10-12 15:30:48.000000000 +0200 @@ -29,12 +29,12 @@ #include "conf.h" #include "privs.h" #include -#include "mod_clamav.h" /** * Module version and declaration */ #define MOD_CLAMAV_VERSION "mod_clamav/0.10" + module clamav_module; /** @@ -50,11 +50,13 @@ * Local declarations */ static unsigned long parse_nbytes(char *nbytes_str, char *units_str); +static int clamavd_connect(void); +static int clamavd_scan(int, const char *, const char *); /** * Read the returned information from Clamavd. */ -int clamavd_result(int sockd, const char *abs_filename, const char *rel_filename) { +static int clamavd_result(int sockd, const char *abs_filename, const char *rel_filename) { int infected = 0, waserror = 0, ret; char buff[4096], *pt, *pt1; FILE *fd = 0; @@ -65,15 +67,26 @@ errno); return -1; } - - if (fgets(buff, sizeof(buff), fd)) { + + memset(buff, '\0', sizeof(buff)); + if (fgets(buff, sizeof(buff)-1, fd)) { if (strstr(buff, "FOUND\n")) { ++infected; - - pt = strrchr(buff, ':'); - if (pt) - *pt = 0; - + + /* Advance past the portion of the response, + * and the path name, and the colon and space that + * follow the path name. + */ + pt = strchr(buff, ':'); + pt++; + pt += strlen(abs_filename); + pt += 3; + + pt1 = strchr(pt, '('); + if (pt1 != NULL) { + *pt1 = '\0'; + } + /* Delete the infected upload */ if ((ret=pr_fsio_unlink(rel_filename))!=0) { pr_log_pri(PR_LOG_ERR, @@ -81,20 +94,14 @@ errno, strerror(errno)); } - /* clean up the response */ - pt += 2; - pt1 = strstr(pt, " FOUND"); - if (pt1) { - *pt1 = 0; - } - /* Inform the client the file contained a virus */ pr_response_add_err(R_550, "Virus Detected and Removed: %s", pt); /* Log the fact */ pr_log_pri(PR_LOG_ERR, MOD_CLAMAV_VERSION ": Virus '%s' found in '%s'", pt, abs_filename); - } else if (strstr(buff, "ERROR\n")) { + } else if (strstr(buff, "ERROR\n") != NULL || + strstr(buff, "UNKNOWN COMMAND") != NULL) { pr_log_pri(PR_LOG_ERR, MOD_CLAMAV_VERSION ": Clamd Error: %s", buff); waserror = 1; } @@ -106,8 +113,8 @@ /** * Start a session with Clamavd. */ -int clamavd_session_start(int sockd) { - if (sockd != -1 && write(sockd, "SESSION\n", 8) <= 0) { +static int clamavd_session_start(int sockd) { + if (sockd != -1 && write(sockd, "nIDSESSION\n", 11) <= 0) { pr_log_pri(PR_LOG_ERR, MOD_CLAMAV_VERSION ": error: Clamd didn't accept the session request."); return -1; @@ -118,8 +125,8 @@ /** * End session. */ -int clamavd_session_stop(int sockd) { - if (sockd != -1 && write(sockd, "END\n", 4) <= 0) { +static int clamavd_session_stop(int sockd) { + if (sockd != -1 && write(sockd, "nEND\n", 5) <= 0) { pr_log_pri(PR_LOG_INFO, MOD_CLAMAV_VERSION ": info: Clamd didn't accept the session end request."); return -1; @@ -130,7 +137,7 @@ /** * Test the connection with Clamd. */ -int clamavd_connect_check(int sockd) { +static int clamavd_connect_check(int sockd) { FILE *fd = NULL; char buff[32]; @@ -138,8 +145,8 @@ return 0; if (write(sockd, "PING\n", 5) <= 0) { - pr_log_debug(DEBUG4, "Clamd did not accept PING (%d): %s", - errno, strerror(errno)); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION + ": Clamd did not accept PING (%d): %s", errno, strerror(errno)); close(sockd); clamd_sockd = -1; clam_errno = errno; @@ -147,7 +154,7 @@ } if ((fd = fdopen(dup(sockd), "r")) == NULL) { - pr_log_debug(DEBUG4, "Clamd can not open descriptor for reading (%d): %s", + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": Clamd can not open descriptor for reading (%d): %s", errno, strerror(errno)); close(sockd); clamd_sockd = -1; @@ -160,10 +167,10 @@ fclose(fd); return 1; } - pr_log_debug(DEBUG4, "Clamd return unknown response to PING: '%s'", buff); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": Clamd return unknown response to PING: '%s'", buff); } - pr_log_debug(DEBUG4, "Clamd did not respond to fgets (%d): %s", errno, strerror(errno)); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": Clamd did not respond to fgets (%d): %s", errno, strerror(errno)); fclose(fd); close(sockd); clamd_sockd = -1; @@ -174,7 +181,7 @@ /** * Request Clamavd to perform a scan. */ -int clamavd_scan(int sockd, const char *abs_filename, const char *rel_filename) { +static int clamavd_scan(int sockd, const char *abs_filename, const char *rel_filename) { char *scancmd = NULL; scancmd = calloc(strlen(abs_filename) + 20, sizeof(char)); @@ -183,22 +190,24 @@ return -1; } - sprintf(scancmd, "SCAN %s\n", abs_filename); + sprintf(scancmd, "nSCAN %s\n", abs_filename); if (!clamavd_connect_check(sockd)) { if ((clamd_sockd = clamavd_connect()) < 0) { pr_log_pri(PR_LOG_ERR, MOD_CLAMAV_VERSION ": error: Cannot re-connect to Clamd (%d): %s", errno, strerror(errno)); + free(scancmd); + scancmd = NULL; clam_errno = errno; return -1; } clamavd_session_start(clamd_sockd); sockd = clamd_sockd; - pr_log_debug(DEBUG4, "Successfully reconnected to Clamd."); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": Successfully reconnected to Clamd"); clam_errno = 0; } - + if (write(sockd, scancmd, strlen(scancmd)) <= 0) { pr_log_pri(PR_LOG_ERR, MOD_CLAMAV_VERSION ": error: Cannot write to the Clamd socket: %d", errno); @@ -216,7 +225,7 @@ /** * Connect a socket to ClamAVd. */ -int clamavd_connect(void) { +static int clamavd_connect(void) { struct sockaddr_un server; struct sockaddr_in server2; struct hostent *he; @@ -314,61 +323,60 @@ return sockd; } -/** - * Entry point of mod_xfer during an upload - */ -int clamav_scan(cmd_rec *cmd) { - char absolutepath[4096]; - char *file, *rel_file; +static int clamav_fsio_close(pr_fh_t *fh, int fd) { + char *abs_path, *rel_path; struct stat st; + int do_scan = FALSE; config_rec *c = NULL; unsigned long *minsize, *maxsize; - + + /* We're only interested in STOR, APPE, and maybe STOU commands. */ + if (session.curr_cmd) { + if (strcmp(session.curr_cmd, C_STOR) == 0 || + strcmp(session.curr_cmd, C_APPE) == 0 || + strcmp(session.curr_cmd, C_STOU) == 0) { + do_scan = TRUE; + } + } + + if (!do_scan) { + return close(fd); + } + + /* Make sure the data is written to disk, so that the fstat(2) picks + * up the size properly. + */ + if (fsync(fd) < 0) { + return -1; + } + + pr_fs_clear_cache(); + if (pr_fsio_fstat(fh, &st) < 0) { + return -1; + } + + if (close(fd) < 0) { + return -1; + } + c = find_config(CURRENT_CONF, CONF_PARAM, "ClamAV", FALSE); if (!c || !*(int*)(c->argv[0])) return 0; - + /** * Figure out the absolute path of our directory */ - if (session.xfer.path && session.xfer.path_hidden) { - /* Hidden Store Condition */ - if (session.chroot_path) { - sstrncpy(absolutepath, - pdircat(cmd->tmp_pool, session.chroot_path, session.xfer.path_hidden, NULL), - sizeof(absolutepath) - 1); - pr_log_debug(DEBUG4, "session.chroot_path is '%s'.", session.chroot_path); - } else { - sstrncpy(absolutepath, session.xfer.path_hidden, sizeof(absolutepath) - 1); - } - file = absolutepath; - rel_file = session.xfer.path_hidden; - - pr_log_debug(DEBUG4, "session.xfer.path_hidden is '%s'.", session.xfer.path_hidden); - } else if (session.xfer.path) { - - /* Default Condition */ - if (session.chroot_path) { - sstrncpy(absolutepath, pdircat(cmd->tmp_pool, session.chroot_path, session.xfer.path, NULL), - sizeof(absolutepath) - 1); - pr_log_debug(DEBUG4, "session.chroot_path is '%s'.", session.chroot_path); - } else { - sstrncpy(absolutepath, session.xfer.path, sizeof(absolutepath) - 1); - } - file = absolutepath; - rel_file = session.xfer.path; - - pr_log_debug(DEBUG4, "session.xfer.path is '%s'.", session.xfer.path); + if (session.chroot_path) { + abs_path = pdircat(fh->fh_pool, session.chroot_path, fh->fh_path, NULL); } else { - - /* Error! */ - pr_log_pri(PR_LOG_ERR, - MOD_CLAMAV_VERSION ": error: 'session.xfer.path' does not contain a valid filename."); - pr_response_add_err(R_500, "'session.xfer.path' does not contain a valid filename."); - return 1; + abs_path = pstrdup(fh->fh_pool, fh->fh_path); } - + + rel_path = pstrdup(fh->fh_pool, fh->fh_path); + + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": absolute path is '%s', relative path is '%s'", abs_path, rel_path); + /** * Handle min/max settings */ @@ -384,18 +392,19 @@ if (clamd_minsize > 0 || clamd_maxsize > 0) { /* Stat the file to acquire the size */ - if (pr_fsio_stat(rel_file, &st) == -1) { + pr_fs_clear_cache(); + if (pr_fsio_fstat(fh, &st) == -1) { pr_log_pri(PR_LOG_ERR, MOD_CLAMAV_VERSION ": error: Can not stat file (%d): %s", errno, strerror(errno)); - return 1; + return -1; } - pr_log_debug(DEBUG4, "ClamMinSize=%lu ClamMaxSize=%lu Filesize=%" PR_LU, clamd_minsize, clamd_maxsize, (pr_off_t) st.st_size); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": ClamMinSize=%lu ClamMaxSize=%lu Filesize=%" PR_LU, clamd_minsize, clamd_maxsize, (pr_off_t) st.st_size); } if (clamd_minsize > 0) { /* test the minimum size */ if (st.st_size < clamd_minsize) { - pr_log_debug(DEBUG4, "File is too small, skipping virus scan. min = %lu size = %" PR_LU, + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": File is too small, skipping virus scan. min = %lu size = %" PR_LU, clamd_minsize, (pr_off_t) st.st_size); return 0; } @@ -404,24 +413,25 @@ if (clamd_maxsize > 0) { /* test the maximum size */ if (st.st_size > clamd_maxsize) { - pr_log_debug(DEBUG4, "File is too large, skipping virus scan. max = %lu size = %" PR_LU, + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": File is too large, skipping virus scan. max = %lu size = %" PR_LU, clamd_maxsize, (pr_off_t) st.st_size); return 0; } } - pr_log_debug(DEBUG4, - "Going to virus scan absolute filename = '%s' with relative filename = '%s'.", file, rel_file); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION + ": Going to virus scan absolute filename = '%s' with relative filename = '%s'.", abs_path, rel_path); clam_errno = 0; - if (clamavd_scan(clamd_sockd, file, rel_file) > 0) { - return 1; + if (clamavd_scan(clamd_sockd, abs_path, rel_path) > 0) { + errno = EPERM; + return -1; } if (clam_errno == 0) - pr_log_debug(DEBUG4, "No virus detected in filename = '%s'.", file); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": No virus detected in filename = '%s'.", abs_path); else - pr_log_debug(DEBUG4, "Skipped virus scan due to errno = %d", clam_errno); + pr_log_debug(DEBUG4, MOD_CLAMAV_VERSION ": Skipped virus scan due to errno = %d", clam_errno); return 0; } @@ -634,11 +644,17 @@ * Start FTP Session */ static int clamav_sess_init(void) { - + pr_fs_t *fs; + is_remote = 0; clamd_sockd = -1; pr_event_register(&clamav_module, "core.exit", clamav_shutdown, NULL); - + + fs = pr_register_fs(main_server->pool, "clamav", "/"); + if (fs) { + fs->close = clamav_fsio_close; + } + return 0; } @@ -661,6 +677,7 @@ NULL, NULL, /* auth function table */ NULL, /* init function */ - clamav_sess_init /* session init function */ + clamav_sess_init, /* session init function */ + MOD_CLAMAV_VERSION }; debian/patches/series0000644000000000000000000000001011645322134012022 0ustar cleanup debian/compat0000644000000000000000000000000211645322134010365 0ustar 7 debian/control0000644000000000000000000000231711645322134010575 0ustar Source: proftpd-mod-clamav Section: net Priority: optional Maintainer: ProFTPD Maintainance Team Uploaders: Mahyuddin Susanto , Francesco Paolo Lovergine DM-Upload-Allowed: yes Build-Depends: autotools-dev, debhelper (>= 7.0.50~), libacl1-dev, proftpd-dev (>= 1.3.3d-5~) Standards-Version: 3.9.2 Homepage: http://www.thrallingpenguin.com/resources/mod_clamav.htm Vcs-Git: git://git.debian.org/pkg-proftpd/proftpd-mod-clamav.git Vcs-Browser: http://git.debian.org/?p=pkg-proftpd/proftpd-mod-clamav.git;a=summary Package: proftpd-mod-clamav Architecture: any Depends: clamav, ${misc:Depends}, ${proftpd:Depends}, ${shlibs:Depends} Description: ProFTPD module mod_clamav Mod_Clamav can be configured to either use Clamd via local unix sockets or TCP sockets. This allows one to combine ProFTPd with Mod_Clamav and Clamd on a system with local unix sockets for minimal administrative overhead and decent security. however, also allows for a large scale deployment with many ProFTPd servers utilizing a separate Clamd host over TCP for scalability and lower administrative overhead. debian/changelog0000644000000000000000000000132612213721245011041 0ustar proftpd-mod-clamav (0.10-1build2) saucy; urgency=low * Rebuild for ProFTPd 1.3.5~rc3. -- Colin Watson Tue, 10 Sep 2013 23:52:21 +0100 proftpd-mod-clamav (0.10-1build1) precise; urgency=low * No change to rebuild agian current proftpd ABI -- Mahyuddin Susanto Thu, 19 Jan 2012 18:19:01 +0700 proftpd-mod-clamav (0.10-1) unstable; urgency=low [ Mahyuddin Susanto ] * Initial release (Closes: #618431). [ Francesco Paolo Lovergine ] * Added a patch `cleanup' to avoid patching proftpd mod_core. Thanks TJ. * Fixed debian/watch file to work. * Do not install *.{la,a} files. -- Francesco Paolo Lovergine Wed, 12 Oct 2011 16:29:18 +0200