/* * Copyright (C)2008 Sourcefire, Inc. * * Author: aCaB <acab@clamav.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #if HAVE_CONFIG_H #include "clamav-config.h" #endif #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <libmilter/mfapi.h> #include "shared/output.h" #include "connpool.h" #include "netcode.h" uint64_t maxfilesize; #define CLAMFIBUFSZ 1424 struct CLAMFI { char buffer[CLAMFIBUFSZ]; int local; int main; int alt; unsigned int altsz; unsigned int bufsz; }; #define FREECF freecf(ctx, cf) static void freecf(SMFICTX *ctx, struct CLAMFI *cf) { close(cf->main); close(cf->alt); smfi_setpriv(ctx, NULL); free(cf); } static sfsistat sendchunk(struct CLAMFI *cf, unsigned char *bodyp, size_t len, SMFICTX *ctx) { if(cf->altsz > maxfilesize) return SMFIS_CONTINUE; /* FIXME: SMFIS_SKIP needs negotiation (only for _body() */ if(cf->altsz + len > maxfilesize) len = maxfilesize - cf->altsz; if(cf->local) { while(len) { int n = write(cf->alt, bodyp, len); if (n==-1) { logg("!clamfi_body: Failed to write temporary file\n"); FREECF; return SMFIS_TEMPFAIL; } len -= n; bodyp += n; } } else { int sendfailed = 0; if(len < CLAMFIBUFSZ - cf->bufsz) { memcpy(&cf->buffer[cf->bufsz], bodyp, len); cf->bufsz += len; } else if(len < CLAMFIBUFSZ) { memcpy(&cf->buffer[cf->bufsz], bodyp, CLAMFIBUFSZ - cf->bufsz); sendfailed = nc_send(cf->alt, cf->buffer, CLAMFIBUFSZ); len -= (CLAMFIBUFSZ - cf->bufsz); memcpy(cf->buffer, &bodyp[CLAMFIBUFSZ - cf->bufsz], len); cf->bufsz = len; } else { sendfailed = nc_send(cf->alt, cf->buffer, cf->bufsz); sendfailed += nc_send(cf->alt, bodyp, len); cf->bufsz = 0; } if(sendfailed) { logg("!clamfi_body: Streaming failed\n"); FREECF; return SMFIS_TEMPFAIL; } } cf->altsz += len; return SMFIS_CONTINUE; } sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len) { struct CLAMFI *cf; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) { sfsistat ret; cf = (struct CLAMFI *)malloc(sizeof(*cf)); if(!cf) { logg("!clamfi_body: Failed to allocate CLAMFI struct\n"); return SMFIS_TEMPFAIL; } cf->altsz = 0; cf->bufsz = 0; if(nc_connect_rand(&cf->main, &cf->alt, &cf->local)) { logg("!clamfi_body: Failed to initiate streaming/fdpassing\n"); free(cf); return SMFIS_TEMPFAIL; } smfi_setpriv(ctx, (void *)cf); if((ret = sendchunk(cf, (unsigned char *)"From clamav-milter\n", 19, ctx)) != SMFIS_CONTINUE) return ret; } return sendchunk(cf, bodyp, len, ctx); } sfsistat clamfi_eom(SMFICTX *ctx) { struct CLAMFI *cf; char *reply; int len, ret; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(cf->local) { struct iovec iov[1]; struct msghdr msg; struct cmsghdr *cmsg; unsigned char fdbuf[CMSG_SPACE(sizeof(int))]; char dummy[]=""; if(nc_send(cf->main, "nFILDES\n", 8)) { logg("!clamfi_eom: FD scan request failed\n"); FREECF; return SMFIS_TEMPFAIL; } lseek(cf->alt, 0, SEEK_SET); iov[0].iov_base = dummy; iov[0].iov_len = 1; memset(&msg, 0, sizeof(msg)); msg.msg_control = fdbuf; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_controllen = CMSG_LEN(sizeof(int)); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = cf->alt; if(sendmsg(cf->main, &msg, 0) == -1) { /* FIXME: nonblock code needed (?) */ logg("!clamfi_eom: FD send failed\n"); FREECF; return SMFIS_TEMPFAIL; } } else { if(cf->bufsz && nc_send(cf->alt, cf->buffer, cf->bufsz)) { logg("!clamfi_eom: Flushing failed\n"); FREECF; return SMFIS_TEMPFAIL; } close(cf->alt); } reply = nc_recv(cf->main); if(cf->local) close(cf->alt); close(cf->main); close(cf->alt); smfi_setpriv(ctx, NULL); free(cf); if(!reply) { logg("!clamfi_eom: no reply to scan request\n"); return SMFIS_TEMPFAIL; } len = strlen(reply); if(len>5 && !strcmp(reply + len - 5, ": OK\n")) ret = SMFIS_ACCEPT; else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) ret = SMFIS_REJECT; else { logg("!clamfi_eom: unknown reply from clamd\n"); ret = SMFIS_TEMPFAIL; } free(reply); return ret; } /* * Local Variables: * mode: c * c-basic-offset: 4 * tab-width: 8 * End: * vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8: */