--- avcheck.c Sat Jul 27 23:22:05 2002 +++ avcheck.c_Varadi Thu Apr 8 13:26:25 2004 @@ -52,6 +52,12 @@ static char buf[8192]; /* i/o buffer */ +#if AVP +extern char vers[]; +extern char lastupdate[]; +extern char info[]; +#endif + static void cleanup() { if (path) unlink(path); @@ -276,6 +282,12 @@ static const char *htemplate = "X-AV-Checked: %.24s %s%s"; +#if AVP + static const char *htemplate2 = "X-AV-Updated: %s%s"; + static const char *htemplate3 = "X-AV-Version: %s%s"; + static const char *htemplate4 = "X-AV-Info: %s%s"; +#endif + /* open mail stream */ if (sendmail_argv[0][0] != '/') { @@ -304,6 +316,17 @@ if (header) { time_t now = time(NULL); fprintf(f, htemplate, ctime(&now), header, "\r\n"); +#if AVP + if( lastupdate[0] ) { + fprintf( f, htemplate2, lastupdate, "\r\n" ); + } + if( vers[0] ) { + fprintf( f, htemplate3, vers, "\r\n" ); + } + if( info[0] ) { + fprintf( f, htemplate4, info, "\r\n" ); + } +#endif } /* nl = 1 means we're at a start of a line (watch for dots!) */ for (nl = 1, bp = be = buf;;) { @@ -409,108 +432,197 @@ } #if AVP -static int -scan_avp(int fd, const char *path, const char *avname) { +static char avebuf[8192]; +static char vers[32]; +static char lastupdate[32]; //'YYYY-mm-dd / nn' +static char expire[32]; //'YYYY-mm-dd ( nn day )' +static char keys[32]; +static char msg[8192]; +static char last325[128]; +static char info[128]; +static int clean = 1; +static int xcode = 0; - unsigned short r; /*XXX size/endiannes problem */ - char *p, *t, *n; - int l, len; - time_t now = time(NULL); - - l = 3 + 15 + 1 + strlen(path); - p = (char*)alloca(l + 1); - sprintf(p, "<0>%.15s:%s", ctime(&now) + 4, path); - - errno = 0; - if (iwrite(fd, p, l) < 0) - err(errno, "unable to send command to %s", avname); - if (read(fd, &r, 2) != 2) /*XXX endiannes problem */ - err(errno, "unable to read %s responce", avname); - /*XXX avp uses very bogus protocol. - Here we're ignoring it's text answer(s) unless code says that - it found some virus(es). */ - switch(r & 0xcf) { - case 0: /* the only one OK result. */ - /*XXX unfortunately avp is so buggy that it will report success - if it encountered some error(s), so that this is not really - "OK result." */ - return 0; - case 1: /* virus scan was incomplete */ - /* this returned when AVP is unable to parse file structure for - various reasons. */ - case 8: /* file is corrupted (e.g. archive) - do not treat it as bad */ - warn("unexpected %s return code %d (0x%04x)", avname, r & 0xcf, - ((r & 0xcf) == 1) ? "virus scan was incomplete" : "file is corrupted"); - return 0; - case 4: /* known virus(es) detected */ - case 3: /* suspicious object(s) found */ - case 2: /* Found corrupted or changed virus (usually false positive, - controlled by Warnings=Yes|No in defUnix.prf) */ - break; - default: - /* some more additional codes to help diagnose problem(s) */ - if ((r & 0xcf) == 7) p = "kavdaemon is corrupted"; - else if ((r & 0xcf) == 0x0f) - p = "kavdaemon wants confirmation -- set InfectedAction to 0"; - else if (r & 0x80) p = "kavdaemon integrity failed"; - else if (r & 0x40) p = "kavdaemon av bases not found"; - else if (r & 0x10) p = "kavdaemon key file not found or expired"; - else - err(0, "unexpected %s return code %d (0x%04x)", avname, r & 0xcf, r); - err(0, "unexpected %s return code %d (0x%04x) (%s)", avname, r & 0xcf, r, p); +static void aveparse( int code ) +{ + char c; + unsigned int u, m, d, y, r, da; + char temp_buf[8192]; + +/* +220 File is clean +221 File was infected and has been cured + +230 File is infected +231 File is infected and can't be cured +232 File is suspicious +233 File has warnings +234 File is corrupted + +240 File is encrypted +241 Scan error has been detected +*/ + + switch ( code ) { + case 220: + case 221: + case 230: + case 231: + case 232: + case 233: + case 234: + case 240: + case 241: + xcode = code; + if( code >= 230 && code <= 239 ) + clean = 0; + bzero( avebuf, sizeof( avebuf ) ); + break; + case 201: + sscanf( avebuf, "%u %u %u %2u%2u%4u %u %19s", &u, &u, &u, &d, &m, &y, &r, vers ); + snprintf( lastupdate, sizeof( lastupdate ), "%04d-%02d-%02d / %d", y, m, d, r ); + bzero( avebuf, sizeof( avebuf ) ); + break; + case 301: + sscanf( avebuf, "%20s %2u%2u%4u %u %c %u", keys, &d, &m, &y, &da, &c, &u ); + snprintf( expire, sizeof( expire ), "%04d-%02d-%02d (%d day)", y, m, d, da ); + bzero( avebuf, sizeof( avebuf ) ); + break; + case 322: + bzero( temp_buf, sizeof( temp_buf ) ); + m = sizeof( temp_buf ) - 1; + for( u = 0; u < m; u++ ) { + if( avebuf[u] ) { + if( avebuf[u] != '/' ) { + temp_buf[u] = avebuf[u]; + } else { + break; + } + } + } + if( msg[0] ) + strncat( msg, "; ", sizeof( msg ) ); + strncat( msg, temp_buf, sizeof( msg ) ); + bzero( avebuf, sizeof( avebuf ) ); + break; + case 200: + case 310: + case 311: + case 312: + case 321: + case 325: + if( code == 325 ) { + char *pp = strrchr( buf, '/' ); + if( pp != ( char * ) NULL ) { + strncpy( last325, pp + 1, sizeof( last325 ) ); + } + } + bzero( avebuf, sizeof( avebuf ) ); + break; + default: + warn( "Aveserver:: not parsed return code %d: %s", code, avebuf ); + break; } - /* here we're if infected or suspicious object(s) was found by avp */ +} - /* since we will work here with only one file (message), - we will not deal with memory deallocation for msg and *msgp. */ - if (!(r & 0x0100)) { /* no message(s) */ - buf[0] = '\0'; - return 1; - } +/* read ~SMTP~ dialog responce from opened filedescriptor fd */ +static int averead( int fd, int parse ) + { + int l; + char *p; - if (r & 0x0200) { - if (read(fd, &len, 4) != 4) /* read 4-byte junk */ - err(errno, "unable to read %s junk response", avname); + bzero( avebuf, sizeof( avebuf ) ); + l = read( fd, buf, sizeof( buf ) ); + if( l < 3 ) + err( l < 0 ? errno : 0, "unable to read ave response" ); + if( buf[--l] != '\n' ) + err( 0, "long line in ave response" ); + if( buf[l - 1] == '\r' ) + --l; + buf[l] = '\0'; + l = 0; + for( p = buf; p < buf + 3; ++p ) + if( *p >= '0' && *p <= '9' ) + l = l * 10 + ( *p - '0' ); + else + break; + if( parse ) { + strncpy( avebuf, buf + 4, sizeof( avebuf ) ); + aveparse( l ); } + return l; +} - l = 0; - if (read(fd, &len, 4) != 4) /*XXX endiannes problem */ - err(errno, "unable to read avp answer length"); - if (len >= sizeof(buf)) /* limit response len to avoid DoSes */ - len = sizeof(buf) - 1; - p = buf; - while(len && (l = read(fd, p, len)) > 0) { - p += l; - len -= l; - } - if (len || l < 0) - err(errno, "unable to read %s answer", avname); - *p = '\0'; - /* now interpret avp response. */ - /* we will cut junk from each line before tab character (the only - interesting information, rest of line is a crap) */ - t = NULL; /* last tab seen */ - p = buf; /* current write pointer */ - n = buf; /* current read pointer */ +static void avecmd( int fd, const char *cmd, const char *avname ) +{ + int l; + static char p[8192]; + + snprintf( p, 8192, "%s\r\n", cmd ); + l = strlen( p ); + + if (iwrite(fd, p, l) < 0) + err( errno, "unable to send command <%s> to %s", cmd, avname ); +} + +static int scan_avp( int fd, const char *path, const char *avname ) +{ + int l; + const char *opt = "xmQPRSTUWabcdefghi"; + static char cmd[8192]; + + bzero( msg, sizeof( msg ) ); + bzero( keys, sizeof( keys ) ); + bzero( expire, sizeof( expire ) ); + bzero( lastupdate, sizeof( lastupdate ) ); + bzero( vers, sizeof( vers ) ); + bzero( info, sizeof( info ) ); + bzero( last325, sizeof( last325 ) ); + clean = 1; + + averead( fd, 0 ); + + snprintf( cmd, sizeof( cmd ), "SCAN %s %s", opt, path ); + avecmd( fd, cmd, avname ); + for( ;; ) { + l = averead( fd, 1 ); + if( l < 300 || l >= 400 ) + break; + } + snprintf( cmd, sizeof( cmd ), "INFO" ); + avecmd( fd, cmd, avname ); + averead( fd, 1 ); + + snprintf( cmd, sizeof( cmd ), "KEYS" ); + avecmd( fd, cmd, avname ); + for( ;; ) { + l = averead( fd, 1 ); + if( l == 200 ) + break; + } + snprintf( cmd, sizeof( cmd ), "QUIT" ); + avecmd( fd, cmd, avname ); for(;;) { - if (*n == '\t') - t = ++n; - else if (*n == '\n' || *n == '\0') { - if (t && t != n) { - memcpy(p, t, n - t); - p += n - t; - *p++ = '\n'; - } - if (!*n) - break; - ++n; - t = NULL; - } - else - ++n; + l = averead( fd, 1 ); + if( l == 200 ) + break; } - *p = '\0'; - return 1; + if( xcode == 240 ) { + warn( "File is protected/encrypted: '%s'", last325 ); + strncpy( info, "File is protected/encrypted: ", sizeof(info) ); + strncat( info, last325, sizeof(info) ); + } + if( xcode == 241 ) { + warn( "Scan error has been detected '%s'", last325 ); + strncpy( info, "Scan error has been detected: ", sizeof(info) ); + strncat( info, last325, sizeof(info) ); + } + if( clean ) { + buf[0] = '\0'; + } else { + strncpy( buf, msg, sizeof( buf ) ); + } + return !clean; } #endif /* AVP */