// check data received from ICAP server and interpret as virus name & return value int icapinstance::doScan(Socket & icapsock, HTTPHeader * docheader, const char* object, unsigned int objectsize, NaughtyFilter * checkme) { char *data = new char[8192]; try { String line; int rc = icapsock.getLine(data, 8192, o.content_scanner_timeout); if (rc == 0) return ICAP_NODATA; line = data; #ifdef DGDEBUG std::cout << "reply from icap: " << line << std::endl; #endif // reply is of the format: // ICAP/1.0 204 No Content Necessary (etc) String returncode(line.after(" ").before(" ")); if (returncode == "204") { #ifdef DGDEBUG std::cerr << "ICAP says clean!" << std::endl; #endif delete[]data; return DGCS_CLEAN; } else if (returncode == "100") { #ifdef DGDEBUG std::cerr << "ICAP says continue!" << std::endl; #endif // discard rest of headers (usually just a blank line) // this is so we are in the right place in the data stream to // call doScan() again later, because people like Symantec seem // to think sending code 100 then code 204 one after the other // is not an abuse of the ICAP specification. while (icapsock.getLine(data, 8192, o.content_scanner_timeout) > 0) { if (data[0] == 13) break; } delete[]data; return ICAP_CONTINUE; } else if (returncode == "200") { #ifdef DGDEBUG std::cerr << "ICAP says maybe not clean!" << std::endl; #endif while (icapsock.getLine(data, 8192, o.content_scanner_timeout) > 0) { if (data[0] == 13) // end marker break; line = data; // Symantec's engine gives us the virus name in the ICAP headers if (supportsXIF && line.startsWith("X-Infection-Found")) { #ifdef DGDEBUG std::cout << "ICAP says infected! (X-Infection-Found)" << std::endl; #endif lastvirusname = line.after("Threat=").before(";"); delete[]data; blockFile(NULL,NULL,checkme); return DGCS_INFECTED; } } // AVIRA's Antivir gives us 200 in all cases, so // - unfortunately - we must pay attention to the encapsulated // header/body. if (needsBody) { // grab & compare the HTTP return code from modified response // if it's been modified, assume there's an infection icapsock.getLine(data, 8192, o.content_scanner_timeout); line = data; #ifdef DGDEBUG std::cout << "Comparing original return code to modified:" << std::endl << docheader->header.front() << std::endl << line << std::endl; #endif int respmodReturnCode = line.after(" ").before(" ").toInteger(); if (respmodReturnCode != docheader->returnCode()) { #ifdef DGDEBUG std::cerr << "ICAP says infected! (returned header comparison)" << std::endl; #endif delete[] data; lastvirusname = "Unknown"; blockFile(NULL,NULL,checkme); return DGCS_INFECTED; } // ok - headers were identical, so look at encapsulated body // discard the rest of the encapsulated headers while (icapsock.getLine(data, 8192, o.content_scanner_timeout) > 0) { if (data[0] == 13) break; } // grab body chunk size #ifdef DGDEBUG std::cout << "Comparing original body data to modified" << std::endl; #endif icapsock.getLine(data, 8192, o.content_scanner_timeout); line = data; int bodysize = line.hexToInteger(); // get, say, the first 100 bytes and compare them to what we // originally sent to see if it has been modified unsigned int chunksize = (bodysize < 100) ? bodysize : 100; if (chunksize > objectsize) chunksize = objectsize; icapsock.readFromSocket(data, chunksize, 0, o.content_scanner_timeout); if (memcmp(data, object, chunksize) == 0) { #ifdef DGDEBUG std::cerr << "ICAP says clean!" << std::endl; #endif delete[]data; return DGCS_CLEAN; } else { #ifdef DGDEBUG std::cerr << "ICAP says infected! (body byte comparison)" << std::endl; #endif delete[] data; lastvirusname = "Unknown"; blockFile(NULL,NULL,checkme); return DGCS_INFECTED; } } // even if we don't find an X-Infection-Found header, // the file is still infected! #ifdef DGDEBUG std::cerr << "ICAP says infected! (no further tests)" << std::endl; #endif delete[] data; lastvirusname = "Unknown"; blockFile(NULL,NULL,checkme); return DGCS_INFECTED; } else if (returncode == "404") { #ifdef DGDEBUG std::cerr << "ICAP says no such service!" << std::endl; #endif lastmessage = "ICAP reports no such service"; syslog(LOG_ERR, "ICAP reports no such service; check your server URL"); delete[]data; return DGCS_SCANERROR; } else { #ifdef DGDEBUG std::cerr << "ICAP returned unrecognised response code: " << returncode << std::endl; #endif lastmessage = "ICAP returned unrecognised response code."; syslog(LOG_ERR, "ICAP returned unrecognised response code: %s", returncode.toCharArray()); delete[]data; return DGCS_SCANERROR; } delete[]data; } catch(std::exception & e) { #ifdef DGDEBUG std::cerr << "Exception getting reply from ICAP: " << e.what() << std::endl; #endif lastmessage = "Exception getting reply from ICAP."; syslog(LOG_ERR, "Exception getting reply from ICAP: %s", e.what()); delete[]data; return DGCS_SCANERROR; } // it is generally NOT a good idea, when using virus scanning, // to continue as if nothing went wrong by default! return DGCS_SCANERROR; }
// no need to replace the inheritied scanMemory() which just calls scanFile() // there is no capability to scan memory with kavdscan as we pass it // a file name to scan. So we save the memory to disk and pass that. // Then delete the temp file. int kavdinstance::scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, int filtergroup, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { lastvirusname = lastmessage = ""; // mkstemp seems to only set owner permissions, so our AV daemon won't be // able to read the file, unless it's running as the same user as us. that's // not usually very convenient. so instead, just allow group read on the // file, and tell users to make sure the daemongroup option is friendly to // the AV daemon's group membership. // chmod can error with EINTR, ignore this? if (chmod(filename, S_IRGRP | S_IRUSR) != 0) { syslog(LOG_ERR, "Could not change file ownership to give kavd read access: %s", strerror(errno)); return DGCS_SCANERROR; }; String command("SCAN bPQRSTUW "); if (pathprefix.length()) { String fname(filename); command += fname.after(pathprefix.toCharArray()); } else { command += filename; } command += "\r\n"; #ifdef DGDEBUG std::cerr << "kavdscan command:" << command << std::endl; #endif UDSocket stripedsocks; if (stripedsocks.getFD() < 0) { syslog(LOG_ERR, "%s", "Error creating socket for talking to kavdscan"); return DGCS_SCANERROR; } if (stripedsocks.connect(udspath.toCharArray()) < 0) { syslog(LOG_ERR, "%s", "Error connecting to kavdscan socket"); stripedsocks.close(); return DGCS_SCANERROR; } char *buff = new char[4096]; memset(buff, 0, 4096); int rc; try { // read kaspersky kavdscan (AV Enging Server) - format: 2xx greeting rc = stripedsocks.getLine(buff, 4096, o.content_scanner_timeout); } catch (std::exception &e) { } if (buff[0] != '2') { delete[] buff; stripedsocks.close(); syslog(LOG_ERR, "%s", "kavdscan did not return ok"); return DGCS_SCANERROR; } try { stripedsocks.writeString(command.toCharArray()); } catch (std::exception &e) { delete[] buff; stripedsocks.close(); syslog(LOG_ERR, "%s", "unable to write to kavdscan"); return DGCS_SCANERROR; } try { rc = stripedsocks.getLine(buff, 4096, o.content_scanner_timeout); } catch (std::exception &e) { delete[] buff; stripedsocks.close(); syslog(LOG_ERR, "%s", "Error reading kavdscan socket"); return DGCS_SCANERROR; } String reply(buff); #ifdef DGDEBUG std::cout << "Got from kavdscan:" << reply << std::endl; #endif if (reply[0] == '2') { // clean #ifdef DGDEBUG std::cerr << "kavdscan - clean" << std::endl; #endif delete[] buff; stripedsocks.close(); return DGCS_CLEAN; } if (reply.startsWith("322")) { // infected // patch to handle multiple virii in kavd response // originally submitted by cahya <*****@*****.**> while (reply[0] != '2' && rc != 0) { reply.removeWhiteSpace(); lastvirusname = lastvirusname + " " + reply.after("322-").before(" "); try { rc = stripedsocks.getLine(buff, 4096, o.content_scanner_timeout); } catch (std::exception &e) { delete[] buff; stripedsocks.close(); syslog(LOG_ERR, "%s", "Error reading kavdscan socket"); return DGCS_SCANERROR; } reply = buff; #ifdef DGDEBUG std::cout << "Got from kavdscan:" << reply << std::endl; #endif } std::cout << "lastvirusname: " << lastvirusname << std::endl; delete[] buff; stripedsocks.close(); // format: 322 nastyvirus blah blockFile(NULL, NULL, checkme); return DGCS_INFECTED; } delete[] buff; stripedsocks.close(); // must be an error then lastmessage = reply; return DGCS_SCANERROR; }