X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/8768d5483a5894400ae1f70cda1beb44ed9b087c..1e1ddfac79fbcd052f199500a6493c7f79cb8462:/src/src/malware.c diff --git a/src/src/malware.c b/src/src/malware.c index 4b28760af..5ed469f30 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -4,7 +4,7 @@ /* Copyright (c) Tom Kistner 2003 - 2015 * License: GPL - * Copyright (c) The Exim Maintainers 2015 - 2018 + * Copyright (c) The Exim Maintainers 2015 - 2020 */ /* Code for calling virus (malware) scanners. Called from acl.c. */ @@ -49,6 +49,7 @@ typedef enum { #ifndef DISABLE_MAL_CMDLINE M_CMDL, #endif + M_DUMMY } scanner_t; typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t; static struct scan @@ -106,16 +107,16 @@ static struct scan void features_malware(void) { -const struct scan * sc; const uschar * s; uschar * t; uschar buf[64]; spf(buf, sizeof(buf), US"_HAVE_MALWARE_"); -for (sc = m_scans; sc->scancode != -1; sc++) +for (const struct scan * sc = m_scans; sc->scancode != -1; sc++) { - for(s = sc->name, t = buf+14; *s; s++) if (*s != '-') *t++ = toupper(*s); + for (s = sc->name, t = buf+14; *s; s++) if (*s != '-') + *t++ = toupper(*s); *t = '\0'; builtin_macro_create(buf); } @@ -191,11 +192,12 @@ static const pcre * fprot6d_re_virus = NULL; /******************************************************************************/ +#ifndef DISABLE_MAL_KAV /* Routine to check whether a system is big- or little-endian. Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html Needed for proper kavdaemon implementation. Sigh. */ -#define BIG_MY_ENDIAN 0 -#define LITTLE_MY_ENDIAN 1 +# define BIG_MY_ENDIAN 0 +# define LITTLE_MY_ENDIAN 1 static int test_byte_order(void); static inline int test_byte_order() @@ -204,6 +206,7 @@ test_byte_order() char *byte = CS &word; return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN); } +#endif BOOL malware_ok = FALSE; @@ -262,13 +265,6 @@ m_panic_defer_3(struct scan * scanent, const uschar * hostport, (void) close(fd_to_close); return m_panic_defer(scanent, hostport, str); } -static inline int -m_log_defer_3(struct scan * scanent, const uschar * hostport, - const uschar * str, int fd_to_close) -{ -(void) close(fd_to_close); -return m_log_defer(scanent, hostport, str); -} /*************************************************/ @@ -306,8 +302,7 @@ const uschar * rerror; int roffset; const pcre * cre; -cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL); -if (!cre) +if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL))) *errstr= string_sprintf("regular expression error in '%s': %s at offset %d", re, rerror, roffset); return cre; @@ -321,7 +316,7 @@ int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0, ovector, nelem(ovector)); uschar * substr = NULL; if (i >= 2) /* Got it */ - pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr); + pcre_get_substring(CS text, ovector, i, 1, CCSS &substr); return substr; } @@ -353,13 +348,13 @@ return cre; -2 on timeout or error */ static int -recv_line(int fd, uschar * buffer, int bsize, int tmo) +recv_line(int fd, uschar * buffer, int bsize, time_t tmo) { uschar * p = buffer; ssize_t rcv; BOOL ok = FALSE; -if (!fd_ready(fd, tmo-time(NULL))) +if (!fd_ready(fd, tmo)) return -2; /*XXX tmo handling assumes we always get a whole line */ @@ -386,9 +381,9 @@ return p - buffer; /* return TRUE iff size as requested */ static BOOL -recv_len(int sock, void * buf, int size, int tmo) +recv_len(int sock, void * buf, int size, time_t tmo) { -return fd_ready(sock, tmo-time(NULL)) +return fd_ready(sock, tmo) ? recv(sock, buf, size, 0) == size : FALSE; } @@ -434,7 +429,7 @@ for (;;) } static inline int -mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo) +mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo) { client_conn_ctx cctx = {.sock = sock}; int offset = 0; @@ -442,7 +437,7 @@ int i; do { - i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL)); + i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo); if (i <= 0) { (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); @@ -501,7 +496,7 @@ switch (*line) static int mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename, - int tmo) + time_t tmo) { struct iovec iov[3]; const char *cmd = "MSQ\n"; @@ -659,11 +654,11 @@ if (!malware_ok) switch(scanent->conn) { case MC_TCP: - malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5); break; + malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break; case MC_UNIX: malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break; case MC_STRM: - malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5); break; + malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break; default: /* compiler quietening */ break; } @@ -750,7 +745,7 @@ if (!malware_ok) if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0) return m_panic_defer(scanent, CUS callout_address, errstr); - bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); if (bread <= 0) return m_panic_defer_3(scanent, CUS callout_address, @@ -796,7 +791,7 @@ if (!malware_ok) if (*scanner_options != '/') { /* calc file size */ - if ((drweb_fd = open(CCS eml_filename, O_RDONLY)) == -1) + if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1) return m_panic_defer_3(scanent, NULL, string_sprintf("can't open spool file %s: %s", eml_filename, strerror(errno)), @@ -840,7 +835,7 @@ badseek: err = errno; malware_daemon_ctx.sock); } - if (!(drweb_fbuf = US malloc(fsize_uint))) + if (!(drweb_fbuf = store_malloc(fsize_uint))) { (void)close(drweb_fd); return m_panic_defer_3(scanent, NULL, @@ -853,7 +848,7 @@ badseek: err = errno; { int err = errno; (void)close(drweb_fd); - free(drweb_fbuf); + store_free(drweb_fbuf); return m_panic_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), @@ -864,11 +859,12 @@ badseek: err = errno; /* send file body to socket */ if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0) { - free(drweb_fbuf); + store_free(drweb_fbuf); return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send file body to socket (%s)", scanner_options), malware_daemon_ctx.sock); } + store_free(drweb_fbuf); } else { @@ -902,7 +898,6 @@ badseek: err = errno; /* "virus(es) found" if virus number is > 0 */ if (drweb_vnum) { - int i; gstring * g = NULL; /* setup default virus name */ @@ -913,7 +908,7 @@ badseek: err = errno; drweb_re = m_pcre_compile(drweb_re_str, &errstr); /* read and concatenate virus names into one string */ - for (i = 0; i < drweb_vnum; i++) + for (int i = 0; i < drweb_vnum; i++) { int ovector[10*3]; @@ -922,7 +917,9 @@ badseek: err = errno; return m_panic_defer_3(scanent, CUS callout_address, US"cannot read report size", malware_daemon_ctx.sock); drweb_slen = ntohl(drweb_slen); - tmpbuf = store_get(drweb_slen); + + /* assume tainted, since it is external input */ + tmpbuf = store_get(drweb_slen, TRUE); /* read report body */ if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo)) @@ -1048,7 +1045,7 @@ badseek: err = errno; #ifndef DISABLE_MAL_FSECURE case M_FSEC: /* "fsecure" scanner type ---------------------------------- */ { - int i, j, bread = 0; + int i, bread = 0; uschar * file_name; uschar av_buffer[1024]; static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n", @@ -1068,13 +1065,13 @@ badseek: err = errno; if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) return m_panic_defer(scanent, CUS callout_address, errstr); - bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); if (bread > 0) av_buffer[bread]='\0'; if (bread < 0) return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), malware_daemon_ctx.sock); - for (j = 0; j < bread; j++) + for (int j = 0; j < bread; j++) if (av_buffer[j] == '\r' || av_buffer[j] == '\n') av_buffer[j] ='@'; } @@ -1100,7 +1097,7 @@ badseek: err = errno; { errno = ETIMEDOUT; i = av_buffer+sizeof(av_buffer)-p; - if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo-time(NULL))) < 0) + if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0) return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read result (%s)", strerror(errno)), malware_daemon_ctx.sock); @@ -1365,13 +1362,10 @@ badseek: err = errno; malware_name = US"unknown"; /* re-open the scanner output file, look for name match */ - scanner_record = fopen(CS file_name, "rb"); - while (fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) - { - /* try match */ - if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) + scanner_record = Ufopen(file_name, "rb"); + while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record)) + if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */ malware_name = s; - } (void)fclose(scanner_record); } else /* no virus found */ @@ -1405,7 +1399,7 @@ badseek: err = errno; /* wait for result */ memset(av_buffer, 0, sizeof(av_buffer)); - if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) + if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0) return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read from UNIX socket (%s)", scanner_options), malware_daemon_ctx.sock); @@ -1468,7 +1462,7 @@ badseek: err = errno; /* Local file; so we def want to use_scan_command and don't want to try * passing IP/port combinations */ use_scan_command = TRUE; - cd = (clamd_address *) store_get(sizeof(clamd_address)); + cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE); /* extract socket-path part */ sublist = scanner_options; @@ -1502,7 +1496,7 @@ badseek: err = errno; continue; } - cd = (clamd_address *) store_get(sizeof(clamd_address)); + cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE); /* extract host and port part */ sublist = scanner_options; @@ -1578,6 +1572,7 @@ badseek: err = errno; * on both connections (as one host could resolve to multiple ips) */ for (;;) { + /*XXX we trust that the cmd_str is ideempotent */ if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port, &connhost, &errstr, &cmd_str)) >= 0) { @@ -1640,7 +1635,7 @@ badseek: err = errno; malware_daemon_ctx.sock); /* calc file size */ - if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) + if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0) { int err = errno; return m_panic_defer_3(scanent, NULL, @@ -1670,7 +1665,7 @@ b_seek: err = errno; if (lseek(clam_fd, 0, SEEK_SET) < 0) goto b_seek; - if (!(clamav_fbuf = US malloc(fsize_uint))) + if (!(clamav_fbuf = store_malloc(fsize_uint))) { (void)close(clam_fd); return m_panic_defer_3(scanent, NULL, @@ -1682,7 +1677,7 @@ b_seek: err = errno; if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) { int err = errno; - free(clamav_fbuf); (void)close(clam_fd); + store_free(clamav_fbuf); (void)close(clam_fd); return m_panic_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), @@ -1697,13 +1692,12 @@ b_seek: err = errno; (send(malware_daemon_ctx.sock, clamav_fbuf, fsize_uint, 0) < 0) || (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) { - free(clamav_fbuf); + store_free(clamav_fbuf); return m_panic_defer_3(scanent, NULL, string_sprintf("unable to send file body to socket (%s)", hostname), malware_daemon_ctx.sock); } - - free(clamav_fbuf); + store_free(clamav_fbuf); } else { /* use scan command */ @@ -1740,7 +1734,7 @@ b_seek: err = errno; /* Read the result */ memset(av_buffer, 0, sizeof(av_buffer)); - bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); (void)close(malware_daemon_ctx.sock); malware_daemon_ctx.sock = -1; malware_daemon_ctx.tls_ctx = NULL; @@ -1898,7 +1892,7 @@ b_seek: err = errno; return m_panic_defer(scanent, CUS callout_address, errstr); /* Read the result */ - bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); if (bread <= 0) return m_panic_defer_3(scanent, CUS callout_address, @@ -1980,7 +1974,7 @@ b_seek: err = errno; and the [ ] marker. [+] - not infected [L] - infected - [E] - some error occured + [E] - some error occurred Such marker follows the first non-escaped TAB. For more information see avast-protocol(5) @@ -2282,13 +2276,13 @@ if (!fprot6d_re_virus) } -void -malware_show_supported(FILE * f) +gstring * +malware_show_supported(gstring * g) { -struct scan * sc; -fprintf(f, "Malware:"); -for (sc = m_scans; sc->scancode != -1; sc++) fprintf(f, " %s", sc->name); -fprintf(f, "\n"); +g = string_cat(g, US"Malware:"); +for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++) + g = string_fmt_append(g, " %s", sc->name); +return string_cat(g, US"\n"); }