static void htmlnorm_setup(void) { cl_init(CL_INIT_DEFAULT); dconf_setup(); dir = cli_gentemp(NULL); fail_unless(!!dir, "cli_gentemp failed"); }
int cli_scannulsft(cli_ctx *ctx, off_t offset) { int ret; struct nsis_st nsist; cli_dbgmsg("in scannulsft()\n"); memset(&nsist, 0, sizeof(struct nsis_st)); nsist.off = offset; if (!(nsist.dir = cli_gentemp(ctx->engine->tmpdir))) return CL_ETMPDIR; if(mkdir(nsist.dir, 0700)) { cli_dbgmsg("NSIS: Can't create temporary directory %s\n", nsist.dir); free(nsist.dir); return CL_ETMPDIR; } nsist.map = *ctx->fmap; if(ctx->engine->keeptmp) cli_dbgmsg("NSIS: Extracting files to %s\n", nsist.dir); do { ret = cli_nsis_unpack(&nsist, ctx); if (ret == CL_SUCCESS && nsist.opened == 0) { /* Don't scan a non-existent file */ continue; } if (ret == CL_SUCCESS) { cli_dbgmsg("NSIS: Successully extracted file #%u\n", nsist.fno); if (lseek(nsist.ofd, 0, SEEK_SET) == -1) { cli_dbgmsg("NSIS: call to lseek() failed\n"); free(nsist.dir); return CL_ESEEK; } if(nsist.fno == 1) ret=cli_scandesc(nsist.ofd, ctx, 0, 0, NULL, AC_SCAN_VIR, NULL); else ret=cli_magic_scandesc(nsist.ofd, ctx); close(nsist.ofd); if(!ctx->engine->keeptmp) if(cli_unlink(nsist.ofn)) ret = CL_EUNLINK; } else if(ret == CL_EMAXSIZE) { ret = nsist.solid ? CL_BREAK : CL_SUCCESS; } } while(ret == CL_SUCCESS); if(ret == CL_BREAK || ret == CL_EMAXFILES) ret = CL_CLEAN; nsis_shutdown(&nsist); if(!ctx->engine->keeptmp) cli_rmdirs(nsist.dir); free(nsist.dir); return ret; }
static void jstest_setup(void) { cl_init(CL_INIT_DEFAULT); state = cli_js_init(); fail_unless(!!state, "js init"); tmpdir = cli_gentemp(NULL); fail_unless(!!tmpdir,"js tmp dir"); fail_unless_fmt(mkdir(tmpdir, 0700) == 0, "tempdir mkdir of %s failed: %s", tmpdir, strerror(errno)); }
char *read_stream(void) { char *filename; char buf[512]; size_t nread, nwritten; FILE *fp; filename = cli_gentemp(NULL); if (!(filename)) { return NULL; } fp = fopen(filename, "w"); if (!(fp)) { free(filename); return NULL; } while (!feof(stdin)) { nwritten = 0; nread = fread(buf, 1, sizeof(buf), stdin); if (nread == 0) { fclose(fp); remove(filename); free(filename); return NULL; } while (nwritten < nread) { size_t i; i = fwrite(buf, 1, nread, fp); if (i == 0) { fclose(fp); remove(filename); free(filename); return NULL; } nwritten += i; } } fclose(fp); return filename; }
/* TODO: field in ctx, id of last bytecode that called magicscandesc, reset * after hooks/other bytecodes are run. TODO: need a more generic solution * to avoid uselessly recursing on bytecode-unpacked files, but also a way to * override the limit if we need it in a special situation */ int32_t cli_bcapi_write(struct cli_bc_ctx *ctx, uint8_t*data, int32_t len) { char err[128]; int32_t res; cli_ctx *cctx = (cli_ctx*)ctx->ctx; if (len < 0) { cli_warnmsg("Bytecode API: called with negative length!\n"); API_MISUSE(); return -1; } if (!ctx->outfd) { ctx->tempfile = cli_gentemp(cctx ? cctx->engine->tmpdir : NULL); if (!ctx->tempfile) { cli_dbgmsg("Bytecode API: Unable to allocate memory for tempfile\n"); cli_event_error_oom(EV, 0); return -1; } ctx->outfd = open(ctx->tempfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if (ctx->outfd == -1) { ctx->outfd = 0; cli_warnmsg("Bytecode API: Can't create file %s: %s\n", ctx->tempfile, cli_strerror(errno, err, sizeof(err))); cli_event_error_str(EV, "cli_bcapi_write: Can't create temporary file"); free(ctx->tempfile); return -1; } cli_dbgmsg("bytecode opened new tempfile: %s\n", ctx->tempfile); } cli_event_fastdata(ctx->bc_events, BCEV_WRITE, data, len); if (cli_checklimits("bytecode api", cctx, ctx->written + len, 0, 0)) return -1; res = cli_writen(ctx->outfd, data, len); if (res > 0) ctx->written += res; if (res == -1) { cli_warnmsg("Bytecode API: write failed: %s\n", cli_strerror(errno, err, sizeof(err))); cli_event_error_str(EV, "cli_bcapi_write: write failed"); } return res; }
/* * TODO: * - strptime() is most likely not portable enough */ int submitstats(const char *clamdcfg, const struct optstruct *opts) { int fd, sd, bread, lread = 0, cnt, ret; char post[SUBMIT_MIN_ENTRIES * 256 + 512]; char query[SUBMIT_MIN_ENTRIES * 256]; char buff[512], statsdat[512], newstatsdat[512], uastr[128]; char logfile[256], fbuff[FILEBUFF]; char *pt, *pt2, *auth = NULL; const char *line, *country = NULL, *user, *proxy = NULL; struct optstruct *clamdopt; const struct optstruct *opt; struct stat sb; struct tm tms; time_t epoch; unsigned int qcnt, entries, submitted = 0, permfail = 0, port = 0; if((opt = optget(opts, "DetectionStatsCountry"))->enabled) { if(strlen(opt->strarg) != 2 || !isalpha(opt->strarg[0]) || !isalpha(opt->strarg[1])) { logg("!SubmitDetectionStats: DetectionStatsCountry requires a two-letter country code\n"); return 56; } country = opt->strarg; } if(!(clamdopt = optparse(clamdcfg, 0, NULL, 1, OPT_CLAMD, 0, NULL))) { logg("!SubmitDetectionStats: Can't open or parse configuration file %s\n", clamdcfg); return 56; } if(!(opt = optget(clamdopt, "LogFile"))->enabled) { logg("!SubmitDetectionStats: LogFile needs to be enabled in %s\n", clamdcfg); optfree(clamdopt); return 56; } strncpy(logfile, opt->strarg, sizeof(logfile)); logfile[sizeof(logfile) - 1] = 0; if(!optget(clamdopt, "LogTime")->enabled) { logg("!SubmitDetectionStats: LogTime needs to be enabled in %s\n", clamdcfg); optfree(clamdopt); return 56; } optfree(clamdopt); if((fd = open("stats.dat", O_RDONLY)) != -1) { if((bread = read(fd, statsdat, sizeof(statsdat) - 1)) == -1) { logg("^SubmitDetectionStats: Can't read stats.dat\n"); bread = 0; } statsdat[bread] = 0; close(fd); } else { *statsdat = 0; } if((fd = open(logfile, O_RDONLY)) == -1) { logg("!SubmitDetectionStats: Can't open %s for reading\n", logfile); return 56; } if(fstat(fd, &sb) == -1) { logg("!SubmitDetectionStats: fstat() failed\n"); close(fd); return 56; } while((line = readbline(fd, fbuff, FILEBUFF, sb.st_size, &lread))) if(strlen(line) >= 32 && !strcmp(&line[strlen(line) - 6], " FOUND")) break; if(!line) { logg("SubmitDetectionStats: No detection records found\n"); close(fd); return 1; } if(*statsdat && !strcmp(line, statsdat)) { logg("SubmitDetectionStats: No new detection records found\n"); close(fd); return 1; } else { strncpy(newstatsdat, line, sizeof(newstatsdat)); } if((opt = optget(opts, "HTTPUserAgent"))->enabled) strncpy(uastr, opt->strarg, sizeof(uastr)); else snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")%s%s", get_version(), country ? ":" : "", country ? country : ""); uastr[sizeof(uastr) - 1] = 0; if((opt = optget(opts, "HTTPProxyServer"))->enabled) { proxy = opt->strarg; if(!strncasecmp(proxy, "http://", 7)) proxy += 7; if((opt = optget(opts, "HTTPProxyUsername"))->enabled) { user = opt->strarg; if(!(opt = optget(opts, "HTTPProxyPassword"))->enabled) { logg("!SubmitDetectionStats: HTTPProxyUsername requires HTTPProxyPassword\n"); close(fd); return 56; } auth = proxyauth(user, opt->strarg); if(!auth) { close(fd); return 56; } } if((opt = optget(opts, "HTTPProxyPort"))->enabled) port = opt->numarg; logg("*Connecting via %s\n", proxy); } ret = 0; memset(query, 0, sizeof(query)); qcnt = 0; entries = 0; do { if(strlen(line) < 32 || strcmp(&line[strlen(line) - 6], " FOUND")) continue; if(*statsdat && !strcmp(line, statsdat)) break; strncpy(buff, line, sizeof(buff)); buff[sizeof(buff) - 1] = 0; if(!(pt = strstr(buff, " -> "))) { logg("*SubmitDetectionStats: Skipping detection entry logged without time\b"); continue; } *pt = 0; pt += 4; if(!strptime(buff, "%a %b %d %H:%M:%S %Y", &tms) || (epoch = mktime(&tms)) == -1) { logg("!SubmitDetectionStats: Failed to convert date string\n"); ret = 1; break; } pt2 = &pt[strlen(pt) - 6]; *pt2 = 0; if(!(pt2 = strrchr(pt, ':'))) { logg("!SubmitDetectionStats: Incorrect format of the log file (1)\n"); ret = 1; break; } *pt2 = 0; pt2 += 2; #ifdef C_WINDOWS if((pt = strrchr(pt, '\\'))) #else if((pt = strrchr(pt, '/'))) #endif *pt++ = 0; if(!pt) pt = (char*) "NOFNAME"; qcnt += snprintf(&query[qcnt], sizeof(query) - qcnt, "ts[]=%u&fname[]=%s&virus[]=%s&", (unsigned int) epoch, pt, pt2); entries++; if(entries == SUBMIT_MIN_ENTRIES) { sd = wwwconnect("stats.clamav.net", proxy, port, NULL, optget(opts, "LocalIPAddress")->strarg, optget(opts, "ConnectTimeout")->numarg, NULL, 0, 0); if(sd == -1) { logg("!SubmitDetectionStats: Can't connect to server\n"); ret = 52; break; } query[sizeof(query) - 1] = 0; snprintf(post, sizeof(post), "POST http://stats.clamav.net/submit.php HTTP/1.0\r\n" "Host: stats.clamav.net\r\n%s" "Content-Type: application/x-www-form-urlencoded\r\n" "User-Agent: %s\r\n" "Content-Length: %u\r\n\n" "%s", auth ? auth : "", uastr, (unsigned int) strlen(query), query); if(send(sd, post, strlen(post), 0) < 0) { logg("!SubmitDetectionStats: Can't write to socket\n"); ret = 52; closesocket(sd); break; } pt = post; cnt = sizeof(post) - 1; #ifdef SO_ERROR while((bread = wait_recv(sd, pt, cnt, 0, optget(opts, "ReceiveTimeout")->numarg)) > 0) { #else while((bread = recv(sd, pt, cnt, 0)) > 0) { #endif pt += bread; cnt -= bread; if(cnt <= 0) break; } *pt = 0; closesocket(sd); if(bread < 0) { logg("!SubmitDetectionStats: Can't read from socket\n"); ret = 52; break; } if(strstr(post, "SUBMIT_OK")) { submitted += entries; if(submitted + SUBMIT_MIN_ENTRIES > SUBMIT_MAX_ENTRIES) break; qcnt = 0; entries = 0; memset(query, 0, sizeof(query)); continue; } ret = 52; if((pt = strstr(post, "SUBMIT_PERMANENT_FAILURE"))) { if(!submitted) { permfail = 1; if((pt + 32 <= post + sizeof(post)) && pt[24] == ':') logg("!SubmitDetectionStats: Remote server reported permanent failure: %s\n", &pt[25]); else logg("!SubmitDetectionStats: Remote server reported permanent failure\n"); } } else if((pt = strstr(post, "SUBMIT_TEMPORARY_FAILURE"))) { if(!submitted) { if((pt + 32 <= post + sizeof(post)) && pt[24] == ':') logg("!SubmitDetectionStats: Remote server reported temporary failure: %s\n", &pt[25]); else logg("!SubmitDetectionStats: Remote server reported temporary failure\n"); } } else { if(!submitted) logg("!SubmitDetectionStats: Incorrect answer from server\n"); } break; } } while((line = readbline(fd, fbuff, FILEBUFF, sb.st_size, &lread))); close(fd); if(auth) free(auth); if(submitted || permfail) { if((fd = open("stats.dat", O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1) { logg("^SubmitDetectionStats: Can't open stats.dat for writing\n"); } else { if((bread = write(fd, newstatsdat, sizeof(newstatsdat))) != sizeof(newstatsdat)) logg("^SubmitDetectionStats: Can't write to stats.dat\n"); close(fd); } } if(ret == 0) { if(!submitted) logg("SubmitDetectionStats: Not enough recent data for submission\n"); else logg("SubmitDetectionStats: Submitted %u records\n", submitted); } return ret; } static int Rfc2822DateTime(char *buf, time_t mtime) { struct tm *gmt; gmt = gmtime(&mtime); return strftime(buf, 36, "%a, %d %b %Y %X GMT", gmt); } static struct cl_cvd *remote_cvdhead(const char *cvdfile, const char *localfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int *ims, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { char cmd[512], head[513], buffer[FILEBUFF], ipaddr[46], *ch, *tmp; int bread, cnt, sd; unsigned int i, j; char *remotename = NULL, *authorization = NULL; struct cl_cvd *cvd; char last_modified[36], uastr[128]; if(proxy) { remotename = malloc(strlen(hostname) + 8); if(!remotename) { logg("!remote_cvdhead: Can't allocate memory for 'remotename'\n"); return NULL; } sprintf(remotename, "http://%s", hostname); if(user) { authorization = proxyauth(user, pass); if(!authorization) { free(remotename); return NULL; } } } if(!access(localfile, R_OK)) { cvd = cl_cvdhead(localfile); if(!cvd) { logg("!remote_cvdhead: Can't parse file %s\n", localfile); free(remotename); free(authorization); return NULL; } Rfc2822DateTime(last_modified, (time_t) cvd->stime); cl_cvdfree(cvd); } else { time_t mtime = 1104119530; Rfc2822DateTime(last_modified, mtime); logg("*Assuming modification time in the past\n"); } logg("*If-Modified-Since: %s\n", last_modified); logg("Reading CVD header (%s): ", cvdfile); if(uas) strncpy(uastr, uas, sizeof(uastr)); else snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")", get_version()); uastr[sizeof(uastr) - 1] = 0; snprintf(cmd, sizeof(cmd), "GET %s/%s HTTP/1.0\r\n" "Host: %s\r\n%s" "User-Agent: %s\r\n" "Connection: close\r\n" "Range: bytes=0-511\r\n" "If-Modified-Since: %s\r\n" "\r\n", (remotename != NULL) ? remotename : "", cvdfile, hostname, (authorization != NULL) ? authorization : "", uastr, last_modified); free(remotename); free(authorization); memset(ipaddr, 0, sizeof(ipaddr)); if(ip[0]) /* use ip to connect */ sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); else sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); if(sd < 0) { return NULL; } else { logg("*Connected to %s (IP: %s).\n", hostname, ipaddr); logg("*Trying to retrieve CVD header of http://%s/%s\n", hostname, cvdfile); } if(!ip[0]) strcpy(ip, ipaddr); if(send(sd, cmd, strlen(cmd), 0) < 0) { logg("%cremote_cvdhead: write failed\n", logerr ? '!' : '^'); closesocket(sd); return NULL; } tmp = buffer; cnt = FILEBUFF; #ifdef SO_ERROR while((bread = wait_recv(sd, tmp, cnt, 0, rtimeout)) > 0) { #else while((bread = recv(sd, tmp, cnt, 0)) > 0) { #endif tmp += bread; cnt -= bread; if(cnt <= 0) break; } closesocket(sd); if(bread == -1) { logg("%cremote_cvdhead: Error while reading CVD header from %s\n", logerr ? '!' : '^', hostname); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } if((strstr(buffer, "HTTP/1.1 404")) != NULL || (strstr(buffer, "HTTP/1.0 404")) != NULL) { logg("%cCVD file not found on remote server\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 2); return NULL; } /* check whether the resource is up-to-date */ if((strstr(buffer, "HTTP/1.1 304")) != NULL || (strstr(buffer, "HTTP/1.0 304")) != NULL) { *ims = 0; logg("OK (IMS)\n"); mirman_update(mdat->currip, mdat->af, mdat, 0); return NULL; } else { *ims = 1; } if(!strstr(buffer, "HTTP/1.1 200") && !strstr(buffer, "HTTP/1.0 200") && !strstr(buffer, "HTTP/1.1 206") && !strstr(buffer, "HTTP/1.0 206")) { logg("%cUnknown response from remote server\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } i = 3; ch = buffer + i; while(i < sizeof(buffer)) { if (*ch == '\n' && *(ch - 1) == '\r' && *(ch - 2) == '\n' && *(ch - 3) == '\r') { ch++; i++; break; } ch++; i++; } if(sizeof(buffer) - i < 512) { logg("%cremote_cvdhead: Malformed CVD header (too short)\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } memset(head, 0, sizeof(head)); for(j = 0; j < 512; j++) { if(!ch || (ch && !*ch) || (ch && !isprint(ch[j]))) { logg("%cremote_cvdhead: Malformed CVD header (bad chars)\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } head[j] = ch[j]; } if(!(cvd = cl_cvdparse(head))) { logg("%cremote_cvdhead: Malformed CVD header (can't parse)\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); } else { logg("OK\n"); mirman_update(mdat->currip, mdat->af, mdat, 0); } return cvd; } static int getfile(const char *srcfile, const char *destfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { char cmd[512], uastr[128], buffer[FILEBUFF], *ch; int bread, fd, totalsize = 0, rot = 0, totaldownloaded = 0, percentage = 0, sd; unsigned int i; char *remotename = NULL, *authorization = NULL, *headerline, ipaddr[46]; const char *rotation = "|/-\\"; #ifdef FILTER_RULES pid_t pid = -1; #endif if(proxy) { remotename = malloc(strlen(hostname) + 8); if(!remotename) { logg("!getfile: Can't allocate memory for 'remotename'\n"); return 75; /* FIXME */ } sprintf(remotename, "http://%s", hostname); if(user) { authorization = proxyauth(user, pass); if(!authorization) { free(remotename); return 75; /* FIXME */ } } } if(uas) strncpy(uastr, uas, sizeof(uastr)); else snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")", get_version()); uastr[sizeof(uastr) - 1] = 0; snprintf(cmd, sizeof(cmd), "GET %s/%s HTTP/1.0\r\n" "Host: %s\r\n%s" "User-Agent: %s\r\n" #ifdef FRESHCLAM_NO_CACHE "Cache-Control: no-cache\r\n" #endif "Connection: close\r\n" "\r\n", (remotename != NULL) ? remotename : "", srcfile, hostname, (authorization != NULL) ? authorization : "", uastr); if(remotename) free(remotename); if(authorization) free(authorization); memset(ipaddr, 0, sizeof(ipaddr)); if(ip[0]) /* use ip to connect */ sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); else sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); if(sd < 0) { return 52; } else { logg("*Trying to download http://%s/%s (IP: %s)\n", hostname, srcfile, ipaddr); } if(!ip[0]) strcpy(ip, ipaddr); if(send(sd, cmd, strlen(cmd), 0) < 0) { logg("%cgetfile: Can't write to socket\n", logerr ? '!' : '^'); closesocket(sd); return 52; } /* read http headers */ ch = buffer; i = 0; while(1) { /* recv one byte at a time, until we reach \r\n\r\n */ #ifdef SO_ERROR if((i >= sizeof(buffer) - 1) || wait_recv(sd, buffer + i, 1, 0, rtimeout) == -1) { #else if((i >= sizeof(buffer) - 1) || recv(sd, buffer + i, 1, 0) == -1) { #endif logg("%cgetfile: Error while reading database from %s (IP: %s)\n", logerr ? '!' : '^', hostname, ipaddr); mirman_update(mdat->currip, mdat->af, mdat, 1); closesocket(sd); return 52; } if(i > 2 && *ch == '\n' && *(ch - 1) == '\r' && *(ch - 2) == '\n' && *(ch - 3) == '\r') { i++; break; } ch++; i++; } buffer[i] = 0; /* check whether the resource actually existed or not */ if((strstr(buffer, "HTTP/1.1 404")) != NULL || (strstr(buffer, "HTTP/1.0 404")) != NULL) { logg("^getfile: %s not found on remote server (IP: %s)\n", srcfile, ipaddr); mirman_update(mdat->currip, mdat->af, mdat, 2); closesocket(sd); return 58; } if(!strstr(buffer, "HTTP/1.1 200") && !strstr(buffer, "HTTP/1.0 200") && !strstr(buffer, "HTTP/1.1 206") && !strstr(buffer, "HTTP/1.0 206")) { logg("%cgetfile: Unknown response from remote server (IP: %s)\n", logerr ? '!' : '^', ipaddr); mirman_update(mdat->currip, mdat->af, mdat, 1); closesocket(sd); return 58; } /* get size of resource */ for(i = 0; (headerline = cli_strtok(buffer, i, "\n")); i++){ if(strstr(headerline, "Content-Length:")) { if((ch = cli_strtok(headerline, 1, ": "))) { totalsize = atoi(ch); free(ch); } else { totalsize = 0; } } free(headerline); } #ifdef FILTER_RULES { int pfd[2]; if (pipe(pfd) == -1) { logg("!getfile: Can't create pipe to filter\n"); closesocket(sd); return 57; } pid = fork(); if (pid == -1) { logg("!getfile: Can't filter fork failed\n"); closesocket(sd); return 57; } if (pid == 0) { /* Child */ close(pfd[1]); dup2(pfd[0], 0); close(pfd[0]); execl("/bin/clamfilter", "clamfilter", destfile, NULL); _exit(1); } /* Parent */ close(pfd[0]); fd = pfd[1]; destfile = "clam-filter"; } #else if((fd = open(destfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644)) == -1) { char currdir[512]; if(getcwd(currdir, sizeof(currdir))) logg("!getfile: Can't create new file %s in %s\n", destfile, currdir); else logg("!getfile: Can't create new file %s in the current directory\n", destfile); logg("Hint: The database directory must be writable for UID %d or GID %d\n", getuid(), getgid()); closesocket(sd); return 57; } #endif #ifdef SO_ERROR while((bread = wait_recv(sd, buffer, FILEBUFF, 0, rtimeout)) > 0) { #else while((bread = recv(sd, buffer, FILEBUFF, 0)) > 0) { #endif if(write(fd, buffer, bread) != bread) { logg("getfile: Can't write %d bytes to %s\n", bread, destfile); #ifdef FILTER_RULES // if (pid == -1) // unlink(destfile); #else unlink(destfile); #endif close(fd); closesocket(sd); return 57; /* FIXME */ } if(totalsize > 0) { totaldownloaded += bread; percentage = (int) (100 * (float) totaldownloaded / totalsize); } if(!mprintf_quiet) { if(totalsize > 0) { mprintf("Downloading %s [%3i%%]\r", srcfile, percentage); } else { mprintf("Downloading %s [%c]\r", srcfile, rotation[rot]); rot++; rot %= 4; } fflush(stdout); } } closesocket(sd); close(fd); #ifdef FILTER_RULES /*if (pid != -1)*/ { int status; waitpid(pid, &status, 0); if (! WIFEXITED(status) || WEXITSTATUS(status) != 0) { logg("!getfile: Filter failed\n"); return 57; } } #endif if(totalsize > 0) logg("Downloading %s [%i%%]\n", srcfile, percentage); else logg("Downloading %s [*]\n", srcfile); mirman_update(mdat->currip, mdat->af, mdat, 0); return 0; } static int getcvd(const char *cvdfile, const char *newfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, unsigned int newver, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { struct cl_cvd *cvd; int ret; logg("*Retrieving http://%s/%s\n", hostname, cvdfile); if((ret = getfile(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist))) { logg("%cCan't download %s from %s\n", logerr ? '!' : '^', cvdfile, hostname); unlink(newfile); return ret; } if((ret = cl_cvdverify(newfile))) { logg("!Verification: %s\n", cl_strerror(ret)); unlink(newfile); return 54; } if(!(cvd = cl_cvdhead(newfile))) { logg("!Can't read CVD header of new %s database.\n", cvdfile); unlink(newfile); return 54; } if(cvd->version < newver) { logg("^Mirror %s is not synchronized.\n", ip); mirman_update(mdat->currip, mdat->af, mdat, 2); cl_cvdfree(cvd); unlink(newfile); return 59; } cl_cvdfree(cvd); return 0; } static int chdir_tmp(const char *dbname, const char *tmpdir) { char cvdfile[32]; if(access(tmpdir, R_OK|W_OK) == -1) { sprintf(cvdfile, "%s.cvd", dbname); if(access(cvdfile, R_OK) == -1) { sprintf(cvdfile, "%s.cld", dbname); if(access(cvdfile, R_OK) == -1) { logg("!chdir_tmp: Can't access local %s database\n", dbname); return -1; } } if(mkdir(tmpdir, 0755) == -1) { logg("!chdir_tmp: Can't create directory %s\n", tmpdir); return -1; } if(cli_cvdunpack(cvdfile, tmpdir) == -1) { logg("!chdir_tmp: Can't unpack %s into %s\n", cvdfile, tmpdir); cli_rmdirs(tmpdir); return -1; } } if(chdir(tmpdir) == -1) { logg("!chdir_tmp: Can't change directory to %s\n", tmpdir); return -1; } return 0; } static int getpatch(const char *dbname, const char *tmpdir, int version, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { char *tempname, patch[32], olddir[512]; int ret, fd; if(!getcwd(olddir, sizeof(olddir))) { logg("!getpatch: Can't get path of current working directory\n"); return 50; /* FIXME */ } if(chdir_tmp(dbname, tmpdir) == -1) return 50; tempname = cli_gentemp("."); snprintf(patch, sizeof(patch), "%s-%d.cdiff", dbname, version); logg("*Retrieving http://%s/%s\n", hostname, patch); if((ret = getfile(patch, tempname, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist))) { logg("%cgetpatch: Can't download %s from %s\n", logerr ? '!' : '^', patch, hostname); unlink(tempname); free(tempname); CHDIR_ERR(olddir); return ret; } if((fd = open(tempname, O_RDONLY|O_BINARY)) == -1) { logg("!getpatch: Can't open %s for reading\n", tempname); unlink(tempname); free(tempname); CHDIR_ERR(olddir); return 55; } if(cdiff_apply(fd, 1) == -1) { logg("!getpatch: Can't apply patch\n"); close(fd); unlink(tempname); free(tempname); CHDIR_ERR(olddir); return 70; /* FIXME */ } close(fd); unlink(tempname); free(tempname); if(chdir(olddir) == -1) { logg("!getpatch: Can't chdir to %s\n", olddir); return 50; /* FIXME */ } return 0; }
static int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, const struct optstruct *opts, const char *dnsreply, char *localip, int outdated, struct mirdat *mdat, int logerr) { struct cl_cvd *current, *remote; const struct optstruct *opt; unsigned int nodb = 0, currver = 0, newver = 0, port = 0, i, j; int ret, ims = -1; char *pt, cvdfile[32], localname[32], *tmpdir = NULL, *newfile, newdb[32], cwd[512]; const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL; unsigned int flevel = cl_retflevel(), remote_flevel = 0, maxattempts; unsigned int can_whitelist = 0; int ctimeout, rtimeout; snprintf(cvdfile, sizeof(cvdfile), "%s.cvd", dbname); if(!(current = currentdb(dbname, localname))) { nodb = 1; } else { mdat->dbflevel = current->fl; } if(!nodb && dnsreply) { int field = 0; if(!strcmp(dbname, "main")) { field = 1; } else if(!strcmp(dbname, "daily")) { field = 2; } else if(!strcmp(dbname, "safebrowsing")) { field = 6; } else { logg("!updatedb: Unknown database name (%s) passed.\n", dbname); cl_cvdfree(current); return 70; } if(field && (pt = cli_strtok(dnsreply, field, ":"))) { if(!cli_isnumber(pt)) { logg("^Broken database version in TXT record.\n"); } else { newver = atoi(pt); logg("*%s version from DNS: %d\n", cvdfile, newver); } free(pt); } else { logg("^Invalid DNS reply. Falling back to HTTP mode.\n"); } } if(dnsreply) { if((pt = cli_strtok(dnsreply, 5, ":"))) { remote_flevel = atoi(pt); free(pt); if(remote_flevel && (remote_flevel - flevel < 4)) can_whitelist = 1; } } /* Initialize proxy settings */ if((opt = optget(opts, "HTTPProxyServer"))->enabled) { proxy = opt->strarg; if(strncasecmp(proxy, "http://", 7) == 0) proxy += 7; if((opt = optget(opts, "HTTPProxyUsername"))->enabled) { user = opt->strarg; if((opt = optget(opts, "HTTPProxyPassword"))->enabled) { pass = opt->strarg; } else { logg("HTTPProxyUsername requires HTTPProxyPassword\n"); if(current) cl_cvdfree(current); return 56; } } if((opt = optget(opts, "HTTPProxyPort"))->enabled) port = opt->numarg; logg("Connecting via %s\n", proxy); } if((opt = optget(opts, "HTTPUserAgent"))->enabled) uas = opt->strarg; ctimeout = optget(opts, "ConnectTimeout")->numarg; rtimeout = optget(opts, "ReceiveTimeout")->numarg; if(!nodb && !newver) { remote = remote_cvdhead(cvdfile, localname, hostname, ip, localip, proxy, port, user, pass, uas, &ims, ctimeout, rtimeout, mdat, logerr, can_whitelist); if(!nodb && !ims) { logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", localname, current->version, current->sigs, current->fl, current->builder); *signo += current->sigs; cl_cvdfree(current); return 1; } if(!remote) { logg("^Can't read %s header from %s (IP: %s)\n", cvdfile, hostname, ip); cl_cvdfree(current); return 58; } newver = remote->version; cl_cvdfree(remote); } if(!nodb && (current->version >= newver)) { logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", localname, current->version, current->sigs, current->fl, current->builder); if(!outdated && flevel < current->fl) { /* display warning even for already installed database */ logg("^Current functionality level = %d, recommended = %d\n", flevel, current->fl); logg("Please check if ClamAV tools are linked against the proper version of libclamav\n"); logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n"); } *signo += current->sigs; cl_cvdfree(current); return 1; } if(current) { currver = current->version; cl_cvdfree(current); } /* if(ipaddr[0]) { hostfd = wwwconnect(ipaddr, proxy, port, NULL, localip); } else { hostfd = wwwconnect(hostname, proxy, port, ipaddr, localip); if(!ip[0]) strcpy(ip, ipaddr); } if(hostfd < 0) { if(ipaddr[0]) logg("Connection with %s (IP: %s) failed.\n", hostname, ipaddr); else logg("Connection with %s failed.\n", hostname); return 52; }; */ if(!optget(opts, "ScriptedUpdates")->enabled) nodb = 1; if(!getcwd(cwd, sizeof(cwd))) { logg("!updatedb: Can't get path of current working directory\n"); return 50; /* FIXME */ } newfile = cli_gentemp(cwd); if(nodb) { ret = getcvd(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, newver, ctimeout, rtimeout, mdat, logerr, can_whitelist); if(ret) { memset(ip, 0, 16); free(newfile); return ret; } snprintf(newdb, sizeof(newdb), "%s.cvd", dbname); } else { ret = 0; tmpdir = cli_gentemp("."); maxattempts = optget(opts, "MaxAttempts")->numarg; for(i = currver + 1; i <= newver; i++) { for(j = 0; j < maxattempts; j++) { int llogerr = logerr; if(logerr) llogerr = (j == maxattempts - 1); ret = getpatch(dbname, tmpdir, i, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, llogerr, can_whitelist); if(ret == 52 || ret == 58) { memset(ip, 0, 16); continue; } else { break; } } if(ret) break; } if(ret) { cli_rmdirs(tmpdir); free(tmpdir); logg("^Incremental update failed, trying to download %s\n", cvdfile); mirman_whitelist(mdat, 2); ret = getcvd(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, newver, ctimeout, rtimeout, mdat, logerr, can_whitelist); if(ret) { free(newfile); return ret; } snprintf(newdb, sizeof(newdb), "%s.cvd", dbname); } else { if(buildcld(tmpdir, dbname, newfile, optget(opts, "CompressLocalDatabase")->enabled) == -1) { logg("!Can't create local database\n"); cli_rmdirs(tmpdir); free(tmpdir); free(newfile); return 70; /* FIXME */ } snprintf(newdb, sizeof(newdb), "%s.cld", dbname); cli_rmdirs(tmpdir); free(tmpdir); } } if(!(current = cl_cvdhead(newfile))) { logg("!Can't parse new database %s\n", newfile); unlink(newfile); free(newfile); return 55; /* FIXME */ } if(!nodb && !access(localname, R_OK) && unlink(localname)) { logg("!Can't unlink %s. Please fix it and try again.\n", localname); unlink(newfile); free(newfile); return 53; } #ifdef C_WINDOWS if(!access(newdb, R_OK) && unlink(newdb)) { logg("!Can't unlink %s. Please fix the problem manually and try again.\n", newdb); unlink(newfile); free(newfile); return 53; } #endif if(rename(newfile, newdb) == -1) { logg("!Can't rename %s to %s: %s\n", newfile, newdb, strerror(errno)); unlink(newfile); free(newfile); return 57; } free(newfile); logg("%s updated (version: %d, sigs: %d, f-level: %d, builder: %s)\n", newdb, current->version, current->sigs, current->fl, current->builder); if(flevel < current->fl) { logg("Your ClamAV installation is out of date.\n"); logg("Current functionality level = %d, recommended = %d\n", flevel, current->fl); logg("A firmware upgrade might resolve this issue.\n"); logg("Also read http://sgkb.securecomputing.com/article.asp?article=11282&p=2\n"); } *signo += current->sigs; cl_cvdfree(current); return 0; }
/* Base scan function for scanning HFS+ or HFSX partitions */ int cli_scanhfsplus(cli_ctx *ctx) { char *targetdir = NULL; int ret = CL_CLEAN; hfsPlusVolumeHeader *volHeader = NULL; hfsNodeDescriptor catFileDesc; hfsHeaderRecord catFileHeader; hfsNodeDescriptor extentFileDesc; hfsHeaderRecord extentFileHeader; if (!ctx || !ctx->fmap) { cli_errmsg("cli_scanhfsplus: Invalid context\n"); return CL_ENULLARG; } cli_dbgmsg("cli_scanhfsplus: scanning partition content\n"); /* first, read volume header contents */ ret = hfsplus_volumeheader(ctx, &volHeader); if (ret != CL_CLEAN) { goto freeHeader; } /* cli_dbgmsg("sizeof(hfsUniStr255) is %lu\n", sizeof(hfsUniStr255)); cli_dbgmsg("sizeof(hfsPlusBSDInfo) is %lu\n", sizeof(hfsPlusBSDInfo)); cli_dbgmsg("sizeof(hfsPlusExtentDescriptor) is %lu\n", sizeof(hfsPlusExtentDescriptor)); cli_dbgmsg("sizeof(hfsPlusExtentRecord) is %lu\n", sizeof(hfsPlusExtentRecord)); cli_dbgmsg("sizeof(hfsPlusForkData) is %lu\n", sizeof(hfsPlusForkData)); cli_dbgmsg("sizeof(hfsPlusVolumeHeader) is %lu\n", sizeof(hfsPlusVolumeHeader)); cli_dbgmsg("sizeof(hfsNodeDescriptor) is %lu\n", sizeof(hfsNodeDescriptor)); */ /* Get root node (header node) of extent overflow file */ ret = hfsplus_readheader(ctx, volHeader, &extentFileDesc, &extentFileHeader, HFS_FILETREE_EXTENTS, "extentFile"); if (ret != CL_CLEAN) { goto freeHeader; } /* Get root node (header node) of catalog file */ ret = hfsplus_readheader(ctx, volHeader, &catFileDesc, &catFileHeader, HFS_FILETREE_CATALOG, "catalogFile"); if (ret != CL_CLEAN) { goto freeHeader; } /* Create temp folder for contents */ if (!(targetdir = cli_gentemp(ctx->engine->tmpdir))) { cli_errmsg("cli_scandmg: cli_gentemp failed\n"); ret = CL_ETMPDIR; goto freeHeader; } if (mkdir(targetdir, 0700)) { cli_errmsg("cli_scandmg: Cannot create temporary directory %s\n", targetdir); ret = CL_ETMPDIR; goto freeDirname; } cli_dbgmsg("cli_scandmg: Extracting into %s\n", targetdir); /* Can build and scan catalog file if we want *** ret = hfsplus_scanfile(ctx, volHeader, &extentFileHeader, &(volHeader->catalogFile), targetdir); */ if (ret == CL_CLEAN) { ret = hfsplus_validate_catalog(ctx, volHeader, &catFileHeader); if (ret == CL_CLEAN) { cli_dbgmsg("cli_scandmg: validation successful\n"); } else { cli_dbgmsg("cli_scandmg: validation returned %d : %s\n", ret, cl_strerror(ret)); } } /* Walk through catalog to identify files to scan */ if (ret == CL_CLEAN) { ret = hfsplus_walk_catalog(ctx, volHeader, &catFileHeader, &extentFileHeader, targetdir); cli_dbgmsg("cli_scandmg: walk catalog finished\n"); } /* Clean up extracted content, if needed */ if (!ctx->engine->keeptmp) { cli_rmdirs(targetdir); } freeDirname: free(targetdir); freeHeader: free(volHeader); return ret; }
int cli_tnef(const char *dir, cli_ctx *ctx) { uint32_t i32; uint16_t i16; fileblob *fb; int ret, alldone; off_t fsize, pos = 0; STATBUF statb; fsize = ctx->fmap[0]->len; if(fsize < (off_t) MIN_SIZE) { cli_dbgmsg("cli_tngs: file too small, ignoring\n"); return CL_CLEAN; } if (fmap_readn(*ctx->fmap, &i32, pos, sizeof(uint32_t)) != sizeof(uint32_t)) { /* The file is at least MIN_SIZE bytes, so it "can't" fail */ return CL_EREAD; } pos += sizeof(uint32_t); if(host32(i32) != TNEF_SIGNATURE) { return CL_EFORMAT; } if(fmap_readn(*ctx->fmap, &i16, pos, sizeof(uint16_t)) != sizeof(uint16_t)) { /* The file is at least MIN_SIZE bytes, so it "can't" fail */ return CL_EREAD; } pos += sizeof(uint16_t); fb = NULL; ret = CL_CLEAN; /* we don't know if it's clean or not :-) */ alldone = 0; do { uint8_t part = 0; uint16_t type = 0, tag = 0; int32_t length = 0; switch(tnef_header(*ctx->fmap, &pos, &part, &type, &tag, &length)) { case 0: alldone = 1; break; case 1: break; default: /* * Assume truncation, not file I/O error */ cli_warnmsg("cli_tnef: file truncated, returning CLEAN\n"); ret = CL_CLEAN; alldone = 1; break; } if(length == 0) continue; if(length < 0) { cli_warnmsg("Corrupt TNEF header detected - length %d\n", (int)length); ret = CL_EFORMAT; break; } if(alldone) break; switch(part) { case LVL_MESSAGE: cli_dbgmsg("TNEF - found message\n"); if(fb != NULL) { fileblobDestroy(fb); fb = NULL; } fb = fileblobCreate(); if(tnef_message(*ctx->fmap, &pos, type, tag, length, fsize) != 0) { cli_dbgmsg("TNEF: Error reading TNEF message\n"); ret = CL_EFORMAT; alldone = 1; } break; case LVL_ATTACHMENT: cli_dbgmsg("TNEF - found attachment\n"); if(tnef_attachment(*ctx->fmap, &pos, type, tag, length, dir, &fb, fsize) != 0) { cli_dbgmsg("TNEF: Error reading TNEF attachment\n"); ret = CL_EFORMAT; alldone = 1; } break; case 0: break; default: cli_warnmsg("TNEF - unknown level %d tag 0x%x\n", (int)part, (int)tag); /* * Dump the file incase it was part of an * email that's about to be deleted */ if(cli_debug_flag) { int fout = -1; char *filename = cli_gentemp(ctx->engine->tmpdir); char buffer[BUFSIZ]; if(filename) fout = open(filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if(fout >= 0) { int count; cli_warnmsg("Saving dump to %s: refer to http://www.clamav.net/bugs\n", filename); pos = 0; while ((count = fmap_readn(*ctx->fmap, buffer, pos, sizeof(buffer))) > 0) { pos += count; cli_writen(fout, buffer, count); } close(fout); } free(filename); } ret = CL_EFORMAT; alldone = 1; break; } } while(!alldone); if(fb) { cli_dbgmsg("cli_tnef: flushing final data\n"); if(fileblobGetFilename(fb) == NULL) { cli_dbgmsg("Saving TNEF portion with an unknown name\n"); fileblobSetFilename(fb, dir, "tnef"); } fileblobDestroy(fb); fb = NULL; } cli_dbgmsg("cli_tnef: returning %d\n", ret); return ret; }
static int sigtool_scandir (const char *dirname, int hex_output) { DIR *dd; struct dirent *dent; struct stat statbuf; char *fname; const char *tmpdir; char *dir; int ret = CL_CLEAN, desc; if ((dd = opendir (dirname)) != NULL) { while ((dent = readdir (dd))) { if (dent->d_ino) { if (strcmp (dent->d_name, ".") && strcmp (dent->d_name, "..")) { /* build the full name */ fname = (char *) cli_calloc (strlen (dirname) + strlen (dent->d_name) + 2, sizeof (char)); sprintf (fname, "%s/%s", dirname, dent->d_name); /* stat the file */ if (lstat (fname, &statbuf) != -1) { if (S_ISDIR (statbuf.st_mode) && !S_ISLNK (statbuf.st_mode)) { if (sigtool_scandir (fname, hex_output)) { free (fname); closedir (dd); return CL_VIRUS; } } else { if (S_ISREG (statbuf.st_mode)) { tmpdir = getenv ("TMPDIR"); if (tmpdir == NULL) #ifdef P_tmpdir tmpdir = P_tmpdir; #else tmpdir = "/tmp"; #endif /* generate the temporary directory */ dir = cli_gentemp (tmpdir); if (mkdir (dir, 0700)) { printf ("Can't create temporary directory %s\n", dir); return CL_ETMPDIR; } if ((desc = open (fname, O_RDONLY)) == -1) { printf ("Can't open file %s\n", fname); return 1; } if ((ret = cli_ole2_extract (desc, dir, NULL))) { printf ("ERROR %s\n", cl_strerror (ret)); cli_rmdirs (dir); free (dir); return ret; } sigtool_vba_scandir (dir, hex_output); cli_rmdirs (dir); free (dir); } } } free (fname); } } } } else { cli_errmsg ("Can't open directory %s.\n", dirname); return CL_EOPEN; } closedir (dd); return 0; }
int cli_scandmg(cli_ctx *ctx) { struct dmg_koly_block hdr; int ret, namelen, ofd; size_t maplen, nread; off_t pos = 0; char *dirname, *tmpfile; const char *outdata; unsigned int file = 0; struct dmg_mish_with_stripes *mish_list = NULL, *mish_list_tail = NULL; enum dmgReadState state = DMG_FIND_BASE_PLIST; int stateDepth[DMG_MAX_STATE]; #if HAVE_LIBXML2 xmlTextReaderPtr reader; #endif if (!ctx || !ctx->fmap) { cli_errmsg("cli_scandmg: Invalid context\n"); return CL_ENULLARG; } maplen = (*ctx->fmap)->real_len; pos = maplen - 512; if (pos <= 0) { cli_dbgmsg("cli_scandmg: Sizing problem for DMG archive.\n"); return CL_CLEAN; } /* Grab koly block */ if (fmap_readn(*ctx->fmap, &hdr, pos, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scandmg: Invalid DMG trailer block\n"); return CL_EFORMAT; } /* Check magic */ hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == 0x6b6f6c79) { cli_dbgmsg("cli_scandmg: Found koly block @ %ld\n", (long) pos); } else { cli_dbgmsg("cli_scandmg: No koly magic, %8x\n", hdr.magic); return CL_EFORMAT; } hdr.dataForkOffset = be64_to_host(hdr.dataForkOffset); hdr.dataForkLength = be64_to_host(hdr.dataForkLength); cli_dbgmsg("cli_scandmg: data offset %lu len %d\n", (unsigned long)hdr.dataForkOffset, (int)hdr.dataForkLength); hdr.xmlOffset = be64_to_host(hdr.xmlOffset); hdr.xmlLength = be64_to_host(hdr.xmlLength); if (hdr.xmlLength > (uint64_t)INT_MAX) { cli_dbgmsg("cli_scandmg: The embedded XML is way larger than necessary, and probably corrupt or tampered with.\n"); return CL_EFORMAT; } if ((hdr.xmlOffset > (uint64_t)maplen) || (hdr.xmlLength > (uint64_t)maplen) || (hdr.xmlOffset + hdr.xmlLength) > (uint64_t)maplen) { cli_dbgmsg("cli_scandmg: XML out of range for this file\n"); return CL_EFORMAT; } cli_dbgmsg("cli_scandmg: XML offset %lu len %d\n", (unsigned long)hdr.xmlOffset, (int)hdr.xmlLength); if (hdr.xmlLength == 0) { cli_dbgmsg("cli_scandmg: Embedded XML length is zero.\n"); return CL_EFORMAT; } /* Create temp folder for contents */ if (!(dirname = cli_gentemp(ctx->engine->tmpdir))) { return CL_ETMPDIR; } if (mkdir(dirname, 0700)) { cli_errmsg("cli_scandmg: Cannot create temporary directory %s\n", dirname); free(dirname); return CL_ETMPDIR; } cli_dbgmsg("cli_scandmg: Extracting into %s\n", dirname); /* Dump XML to tempfile, if needed */ if (ctx->engine->keeptmp) { int xret; xret = dmg_extract_xml(ctx, dirname, &hdr); if (xret != CL_SUCCESS) { /* Printed err detail inside dmg_extract_xml */ free(dirname); return xret; } } /* scan XML with cli_map_scandesc */ ret = cli_map_scandesc(*ctx->fmap, (off_t)hdr.xmlOffset, (size_t)hdr.xmlLength, ctx); if (ret != CL_CLEAN) { cli_dbgmsg("cli_scandmg: retcode from scanning TOC xml: %s\n", cl_strerror(ret)); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return ret; } /* page data from map */ outdata = fmap_need_off_once_len(*ctx->fmap, hdr.xmlOffset, hdr.xmlLength, &nread); if (!outdata || (nread != hdr.xmlLength)) { cli_errmsg("cli_scandmg: Failed getting XML from map, len %d\n", (int)hdr.xmlLength); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return CL_EMAP; } /* time to walk the tree */ /* plist -> dict -> (key:resource_fork) dict -> (key:blkx) array -> dict */ /* each of those bottom level dict should have 4 parts */ /* [ Attributes, Data, ID, Name ], where Data is Base64 mish block */ /* This is the block where we require libxml2 */ #if HAVE_LIBXML2 /* XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_COMPACT */ #define DMG_XML_PARSE_OPTS (1 << 1 | 1 << 11 | 1 << 16) reader = xmlReaderForMemory(outdata, (int)hdr.xmlLength, "toc.xml", NULL, DMG_XML_PARSE_OPTS); if (!reader) { cli_dbgmsg("cli_scandmg: Failed parsing XML!\n"); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return CL_EFORMAT; } stateDepth[DMG_FIND_BASE_PLIST] = -1; // May need to check for (xmlTextReaderIsEmptyElement(reader) == 0) /* Break loop if have return code or reader can't read any more */ while ((ret == CL_CLEAN) && (xmlTextReaderRead(reader) == 1)) { xmlReaderTypes nodeType; nodeType = xmlTextReaderNodeType(reader); if (nodeType == XML_READER_TYPE_ELEMENT) { // New element, do name check xmlChar *nodeName; int depth; depth = xmlTextReaderDepth(reader); if (depth < 0) { break; } if ((depth > 50) && SCAN_ALGO) { // Possible heuristic, should limit runaway cli_dbgmsg("cli_scandmg: Excessive nesting in DMG TOC.\n"); break; } nodeName = xmlTextReaderLocalName(reader); if (!nodeName) continue; dmg_parsemsg("read: name %s depth %d\n", nodeName, depth); if ((state == DMG_FIND_DATA_MISH) && (depth == stateDepth[state-1])) { xmlChar * textValue; struct dmg_mish_with_stripes *mish_set; /* Reset state early, for continue cases */ stateDepth[DMG_FIND_KEY_DATA] = -1; state--; if (xmlStrcmp(nodeName, "data") != 0) { cli_dbgmsg("cli_scandmg: Not blkx data element\n"); xmlFree(nodeName); continue; } dmg_parsemsg("read: Found blkx data element\n"); /* Pull out data content from text */ if (xmlTextReaderIsEmptyElement(reader)) { cli_dbgmsg("cli_scandmg: blkx data element is empty\n"); xmlFree(nodeName); continue; } if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Next node not text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { xmlFree(nodeName); continue; } /* Have encoded mish block */ mish_set = cli_malloc(sizeof(struct dmg_mish_with_stripes)); if (mish_set == NULL) { ret = CL_EMEM; xmlFree(textValue); xmlFree(nodeName); break; } ret = dmg_decode_mish(ctx, &file, textValue, mish_set); xmlFree(textValue); if (ret == CL_EFORMAT) { /* Didn't decode, or not a mish block */ ret = CL_CLEAN; xmlFree(nodeName); continue; } else if (ret != CL_CLEAN) { xmlFree(nodeName); continue; } /* Add mish block to list */ if (mish_list_tail != NULL) { mish_list_tail->next = mish_set; mish_list_tail = mish_set; } else { mish_list = mish_set; mish_list_tail = mish_set; } mish_list_tail->next = NULL; } if ((state == DMG_FIND_KEY_DATA) && (depth > stateDepth[state-1]) && (xmlStrcmp(nodeName, "key") == 0)) { xmlChar * textValue; dmg_parsemsg("read: Found key - checking for Data\n"); if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Key node no text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n"); xmlFree(nodeName); continue; } if (xmlStrcmp(textValue, "Data") == 0) { dmg_parsemsg("read: Matched data\n"); stateDepth[DMG_FIND_KEY_DATA] = depth; state++; } else { dmg_parsemsg("read: text value is %s\n", textValue); } xmlFree(textValue); } if ((state == DMG_FIND_BLKX_CONTAINER) && (depth == stateDepth[state-1])) { if (xmlStrcmp(nodeName, "array") == 0) { dmg_parsemsg("read: Found array blkx\n"); stateDepth[DMG_FIND_BLKX_CONTAINER] = depth; state++; } else if (xmlStrcmp(nodeName, "dict") == 0) { dmg_parsemsg("read: Found dict blkx\n"); stateDepth[DMG_FIND_BLKX_CONTAINER] = depth; state++; } else { cli_dbgmsg("cli_scandmg: Bad blkx, not container\n"); stateDepth[DMG_FIND_KEY_BLKX] = -1; state--; } } if ((state == DMG_FIND_KEY_BLKX) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "key") == 0)) { xmlChar * textValue; dmg_parsemsg("read: Found key - checking for blkx\n"); if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Key node no text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n"); xmlFree(nodeName); continue; } if (xmlStrcmp(textValue, "blkx") == 0) { cli_dbgmsg("cli_scandmg: Matched blkx\n"); stateDepth[DMG_FIND_KEY_BLKX] = depth; state++; } else { cli_dbgmsg("cli_scandmg: wanted blkx, text value is %s\n", textValue); } xmlFree(textValue); } if ((state == DMG_FIND_DICT_RESOURCE_FORK) && (depth == stateDepth[state-1])) { if (xmlStrcmp(nodeName, "dict") == 0) { dmg_parsemsg("read: Found resource-fork dict\n"); stateDepth[DMG_FIND_DICT_RESOURCE_FORK] = depth; state++; } else { dmg_parsemsg("read: Not resource-fork dict\n"); stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = -1; state--; } } if ((state == DMG_FIND_KEY_RESOURCE_FORK) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "key") == 0)) { dmg_parsemsg("read: Found resource-fork key\n"); stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = depth; state++; } if ((state == DMG_FIND_BASE_DICT) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "dict") == 0)) { dmg_parsemsg("read: Found dict start\n"); stateDepth[DMG_FIND_BASE_DICT] = depth; state++; } if ((state == DMG_FIND_BASE_PLIST) && (xmlStrcmp(nodeName, "plist") == 0)) { dmg_parsemsg("read: Found plist start\n"); stateDepth[DMG_FIND_BASE_PLIST] = depth; state++; } xmlFree(nodeName); } else if ((nodeType == XML_READER_TYPE_END_ELEMENT) && (state > DMG_FIND_BASE_PLIST)) { int significantEnd = 0; int depth = xmlTextReaderDepth(reader); if (depth < 0) { break; } else if (depth < stateDepth[state-1]) { significantEnd = 1; } else if ((depth == stateDepth[state-1]) && (state-1 == DMG_FIND_BLKX_CONTAINER)) { /* Special case, ending blkx container */ significantEnd = 1; } if (significantEnd) { dmg_parsemsg("read: significant end tag, state %d\n", state); stateDepth[state-1] = -1; state--; if ((state-1 == DMG_FIND_KEY_RESOURCE_FORK) || (state-1 == DMG_FIND_KEY_BLKX)) { /* Keys end their own tag (validly) and the next state depends on the following tag */ // cli_dbgmsg("read: significant end tag ending prior key state\n"); stateDepth[state-1] = -1; state--; } } else { dmg_parsemsg("read: not significant end tag, state %d depth %d prior depth %d\n", state, depth, stateDepth[state-1]); } } } xmlFreeTextReader(reader); xmlCleanupParser(); #else cli_dbgmsg("cli_scandmg: libxml2 support is compiled out. It is required for full DMG support.\n"); #endif /* Loop over mish array */ file = 0; while ((ret == CL_CLEAN) && (mish_list != NULL)) { /* Handle & scan mish block */ ret = dmg_handle_mish(ctx, file++, dirname, hdr.xmlOffset, mish_list); free(mish_list->mish); mish_list_tail = mish_list; mish_list = mish_list->next; free(mish_list_tail); } /* Cleanup */ /* If error occurred, need to free mish items and mish blocks */ while (mish_list != NULL) { free(mish_list->mish); mish_list_tail = mish_list; mish_list = mish_list->next; free(mish_list_tail); } if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return ret; }
static int scanstdin(const struct cl_engine *engine, const struct optstruct *opts, int options) { int ret; unsigned int fsize = 0; const char *virname, *tmpdir; char *file, buff[FILEBUFF]; size_t bread; FILE *fs; struct clamscan_cb_data data; if(optget(opts, "tempdir")->enabled) { tmpdir = optget(opts, "tempdir")->strarg; } else { /* check write access */ tmpdir = cli_gettmpdir(); } if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) { logg("!Can't write to temporary directory\n"); return 2; } if(!(file = cli_gentemp(tmpdir))) { logg("!Can't generate tempfile name\n"); return 2; } if(!(fs = fopen(file, "wb"))) { logg("!Can't open %s for writing\n", file); free(file); return 2; } while((bread = fread(buff, 1, FILEBUFF, stdin))) { fsize += bread; if(fwrite(buff, 1, bread, fs) < bread) { logg("!Can't write to %s\n", file); free(file); fclose(fs); return 2; } } fclose(fs); logg("*Checking %s\n", file); info.files++; info.rblocks += fsize / CL_COUNT_PRECISION; data.filename = "stdin"; data.chain = NULL; if((ret = cl_scanfile_callback(file, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) { info.ifiles++; if(bell) fprintf(stderr, "\007"); } else if(ret == CL_CLEAN) { if(!printinfected) mprintf("stdin: OK\n"); } else { if(!printinfected) logg("stdin: %s ERROR\n", cl_strerror(ret)); info.errors++; } unlink(file); free(file); return ret; }
int cli_scanrtf(cli_ctx *ctx) { char* tempname; const unsigned char* ptr; const unsigned char* ptr_end; int ret = CL_CLEAN; struct rtf_state state; struct stack stack; size_t bread; table_t* actiontable; uint8_t main_symbols[256]; size_t offset = 0; cli_dbgmsg("in cli_scanrtf()\n"); memset(main_symbols, 0, 256); main_symbols['{']=1; main_symbols['}']=1; main_symbols['\\']=1; stack.stack_cnt = 0; stack.stack_size = 16; stack.elements = 0; stack.warned = 0; stack.states = cli_malloc(stack.stack_size*sizeof(*stack.states)); if(!stack.states) { cli_errmsg("ScanRTF: Unable to allocate memory for stack states\n"); return CL_EMEM; } if(!(tempname = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM; if(mkdir(tempname, 0700)) { cli_dbgmsg("ScanRTF -> Can't create temporary directory %s\n", tempname); free(stack.states); free(tempname); return CL_ETMPDIR; } actiontable = tableCreate(); if((ret = load_actions(actiontable))) { cli_dbgmsg("RTF: Unable to load rtf action table\n"); free(stack.states); if(!ctx->engine->keeptmp) cli_rmdirs(tempname); free(tempname); tableDestroy(actiontable); return ret; } init_rtf_state(&state); for (offset = 0; (ptr = fmap_need_off_once_len(*ctx->fmap, offset, BUFF_SIZE, &bread)) && bread; offset += bread) { ptr_end = ptr + bread; while(ptr < ptr_end) { switch(state.parse_state) { case PARSE_MAIN: switch(*ptr++) { case '{': if(( ret = push_state(&stack,&state) )) { cli_dbgmsg("RTF:Push failure!\n"); SCAN_CLEANUP; return ret; } break; case '}': if(state.cb_data && state.cb_end) if(( ret = state.cb_end(&state, ctx) )) { SCAN_CLEANUP; return ret; } if(( ret = pop_state(&stack,&state) )) { cli_dbgmsg("RTF:pop failure!\n"); SCAN_CLEANUP; return ret; } break; case '\\': state.parse_state = PARSE_CONTROL_; break; default: ptr--; { size_t i; size_t left = ptr_end - ptr; size_t use = left; for(i = 1;i < left; i++) if(main_symbols[ptr[i]]) { use = i; break; } if(state.cb_begin) { if(!state.cb_data) if(( ret = state.cb_begin(&state, ctx,tempname) )) { SCAN_CLEANUP; return ret; } if(( ret = state.cb_process(&state, ptr, use) )) { if(state.cb_end) { state.cb_end(&state,ctx); } SCAN_CLEANUP; return ret; } } ptr += use; } } break; case PARSE_CONTROL_: if(isalpha(*ptr)) { state.parse_state = PARSE_CONTROL_WORD; state.controlword_cnt = 0; } else state.parse_state = PARSE_CONTROL_SYMBOL; break; case PARSE_CONTROL_SYMBOL: ptr++; /* Do nothing */ state.parse_state = PARSE_MAIN; break; case PARSE_CONTROL_WORD: if(state.controlword_cnt == 32) { cli_dbgmsg("Invalid control word: maximum size exceeded:%s\n",state.controlword); state.parse_state = PARSE_MAIN; } else if(isalpha(*ptr)) state.controlword[state.controlword_cnt++] = *ptr++; else { if(isspace(*ptr)) { state.controlword[state.controlword_cnt++] = *ptr++; state.parse_state = PARSE_INTERPRET_CONTROLWORD; } else if (isdigit(*ptr)) { state.parse_state = PARSE_CONTROL_WORD_PARAM; state.controlword_param = 0; state.controlword_param_sign = 1; } else if(*ptr == '-') { ptr++; state.parse_state = PARSE_CONTROL_WORD_PARAM; state.controlword_param = 0; state.controlword_param_sign = -1; } else { state.parse_state = PARSE_INTERPRET_CONTROLWORD; } } break; case PARSE_CONTROL_WORD_PARAM: if(isdigit(*ptr)) { state.controlword_param = state.controlword_param*10 + *ptr++ - '0'; } else if(isalpha(*ptr)) { ptr++; } else { if(state.controlword_param_sign < 0) state.controlword_param = -state.controlword_param; state.parse_state = PARSE_INTERPRET_CONTROLWORD; } break; case PARSE_INTERPRET_CONTROLWORD: { int action; state.controlword[state.controlword_cnt] = '\0'; action = tableFind(actiontable, state.controlword); if(action != -1) { if(state.cb_data && state.cb_end) {/* premature end of previous block */ state.cb_end(&state,ctx); state.cb_begin = NULL; state.cb_end = NULL; state.cb_data = NULL; } rtf_action(&state,action); } state.parse_state = PARSE_MAIN; break; } } } } SCAN_CLEANUP; return ret; }
static int unz(const uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, uint16_t flags, unsigned int *fu, cli_ctx *ctx, char *tmpd, zip_cb zcb) { char name[1024], obuf[BUFSIZ]; char *tempfile = name; int of, ret=CL_CLEAN; unsigned int res=1, written=0; if(tmpd) { snprintf(name, sizeof(name), "%s"PATHSEP"zip.%03u", tmpd, *fu); name[sizeof(name)-1]='\0'; } else { if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM; } if((of = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR))==-1) { cli_warnmsg("cli_unzip: failed to create temporary file %s\n", tempfile); if(!tmpd) free(tempfile); return CL_ECREAT; } switch (method) { case ALG_STORED: if(csize<usize) { unsigned int fake = *fu + 1; cli_dbgmsg("cli_unzip: attempting to inflate stored file with inconsistent size\n"); if ((ret=unz(src, csize, usize, ALG_DEFLATE, 0, &fake, ctx, tmpd, zcb))==CL_CLEAN) { (*fu)++; res=fake-(*fu); } else break; } if(res==1) { if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize); csize = ctx->engine->maxfilesize; } if(cli_writen(of, src, csize)!=(int)csize) ret = CL_EWRITE; else res=0; } break; case ALG_DEFLATE: case ALG_DEFLATE64: { union { z_stream64 strm64; z_stream strm; } strm; typedef int (*unz_init_) (void *, int); typedef int (*unz_unz_) (void *, int); typedef int (*unz_end_) (void *); unz_init_ unz_init; unz_unz_ unz_unz; unz_end_ unz_end; int wbits; void **next_in; void **next_out; unsigned int *avail_in; unsigned int *avail_out; if(method == ALG_DEFLATE64) { unz_init = (unz_init_)inflate64Init2; unz_unz = (unz_unz_)inflate64; unz_end = (unz_end_)inflate64End; next_in = (void *)&strm.strm64.next_in; next_out = (void *)&strm.strm64.next_out; avail_in = &strm.strm64.avail_in; avail_out = &strm.strm64.avail_out; wbits=MAX_WBITS64; } else { unz_init = (unz_init_)wrap_inflateinit2; unz_unz = (unz_unz_)inflate; unz_end = (unz_end_)inflateEnd; next_in = (void *)&strm.strm.next_in; next_out = (void *)&strm.strm.next_out; avail_in = &strm.strm.avail_in; avail_out = &strm.strm.avail_out; wbits=MAX_WBITS; } memset(&strm, 0, sizeof(strm)); *next_in = (void*) src; *next_out = obuf; *avail_in = csize; *avail_out = sizeof(obuf); if (unz_init(&strm, -wbits)!=Z_OK) { cli_dbgmsg("cli_unzip: zinit failed\n"); break; } while(1) { while((res = unz_unz(&strm, Z_NO_FLUSH))==Z_OK) {}; if(*avail_out!=sizeof(obuf)) { written+=sizeof(obuf)-(*avail_out); if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize); res = Z_STREAM_END; break; } if(cli_writen(of, obuf, sizeof(obuf)-(*avail_out)) != (int)(sizeof(obuf)-(*avail_out))) { cli_warnmsg("cli_unzip: falied to write %lu inflated bytes\n", (unsigned long int)sizeof(obuf)-(*avail_out)); ret = CL_EWRITE; res = 100; break; } *next_out = obuf; *avail_out = sizeof(obuf); continue; } break; } unz_end(&strm); if (res == Z_STREAM_END) res=0; break; } #if HAVE_BZLIB_H #ifdef NOBZ2PREFIX #define BZ2_bzDecompress bzDecompress #define BZ2_bzDecompressEnd bzDecompressEnd #define BZ2_bzDecompressInit bzDecompressInit #endif case ALG_BZIP2: { bz_stream strm; memset(&strm, 0, sizeof(strm)); strm.next_in = (char *)src; strm.next_out = obuf; strm.avail_in = csize; strm.avail_out = sizeof(obuf); if (BZ2_bzDecompressInit(&strm, 0, 0)!=BZ_OK) { cli_dbgmsg("cli_unzip: bzinit failed\n"); break; } while((res = BZ2_bzDecompress(&strm))==BZ_OK || res==BZ_STREAM_END) { if(strm.avail_out!=sizeof(obuf)) { written+=sizeof(obuf)-strm.avail_out; if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize); res = BZ_STREAM_END; break; } if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { cli_warnmsg("cli_unzip: falied to write %lu bunzipped bytes\n", (long unsigned int)sizeof(obuf)-strm.avail_out); ret = CL_EWRITE; res = 100; break; } strm.next_out = obuf; strm.avail_out = sizeof(obuf); if (res == BZ_OK) continue; /* after returning BZ_STREAM_END once, decompress returns an error */ } break; } BZ2_bzDecompressEnd(&strm); if (res == BZ_STREAM_END) res=0; break; } #endif /* HAVE_BZLIB_H */ case ALG_IMPLODE: { struct xplstate strm; strm.next_in = (void*)src; strm.next_out = (uint8_t *)obuf; strm.avail_in = csize; strm.avail_out = sizeof(obuf); if (explode_init(&strm, flags)!=EXPLODE_OK) { cli_dbgmsg("cli_unzip: explode_init() failed\n"); break; } while((res = explode(&strm))==EXPLODE_OK) { if(strm.avail_out!=sizeof(obuf)) { written+=sizeof(obuf)-strm.avail_out; if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize); res = 0; break; } if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { cli_warnmsg("cli_unzip: falied to write %lu exploded bytes\n", (unsigned long int) sizeof(obuf)-strm.avail_out); ret = CL_EWRITE; res = 100; break; } strm.next_out = (uint8_t *)obuf; strm.avail_out = sizeof(obuf); continue; } break; } break; } case ALG_LZMA: /* easy but there's not a single sample in the zoo */ #if !HAVE_BZLIB_H case ALG_BZIP2: #endif case ALG_SHRUNK: case ALG_REDUCE1: case ALG_REDUCE2: case ALG_REDUCE3: case ALG_REDUCE4: case ALG_TOKENZD: case ALG_OLDTERSE: case ALG_RSVD1: case ALG_RSVD2: case ALG_RSVD3: case ALG_RSVD4: case ALG_RSVD5: case ALG_NEWTERSE: case ALG_LZ77: case ALG_WAVPACK: case ALG_PPMD: cli_dbgmsg("cli_unzip: unsupported method (%d)\n", method); break; default: cli_dbgmsg("cli_unzip: unknown method (%d)\n", method); break; } if(!res) { (*fu)++; cli_dbgmsg("cli_unzip: extracted to %s\n", tempfile); if (lseek(of, 0, SEEK_SET) == -1) { cli_dbgmsg("cli_unzip: call to lseek() failed\n"); if (!(tmpd)) free(tempfile); close(of); return CL_ESEEK; } ret = zcb(of, ctx); close(of); if(!ctx->engine->keeptmp) if(cli_unlink(tempfile)) ret = CL_EUNLINK; if(!tmpd) free(tempfile); return ret; } close(of); if(!ctx->engine->keeptmp) if(cli_unlink(tempfile)) ret = CL_EUNLINK; if(!tmpd) free(tempfile); cli_dbgmsg("cli_unzip: extraction failed\n"); return ret; }
int cli_unzip(cli_ctx *ctx) { unsigned int fc=0, fu=0; int ret=CL_CLEAN; uint32_t fsize, lhoff = 0, coff = 0; fmap_t *map = *ctx->fmap; char *tmpd; const char *ptr; int virus_found = 0; #if HAVE_JSON int toval = 0; #endif cli_dbgmsg("in cli_unzip\n"); fsize = (uint32_t)map->len; if(sizeof(off_t)!=sizeof(uint32_t) && (size_t)fsize!=map->len) { cli_dbgmsg("cli_unzip: file too big\n"); return CL_CLEAN; } if (fsize < SIZEOF_CH) { cli_dbgmsg("cli_unzip: file too short\n"); return CL_CLEAN; } if (!(tmpd = cli_gentemp(ctx->engine->tmpdir))) { return CL_ETMPDIR; } if (mkdir(tmpd, 0700)) { cli_dbgmsg("cli_unzip: Can't create temporary directory %s\n", tmpd); free(tmpd); return CL_ETMPDIR; } for(coff=fsize-22 ; coff>0 ; coff--) { /* sizeof(EOC)==22 */ if(!(ptr = fmap_need_off_once(map, coff, 20))) continue; if(cli_readint32(ptr)==0x06054b50) { uint32_t chptr = cli_readint32(&ptr[16]); if(!CLI_ISCONTAINED(0, fsize, chptr, SIZEOF_CH)) continue; coff=chptr; break; } } if(coff) { cli_dbgmsg("cli_unzip: central @%x\n", coff); while(ret==CL_CLEAN && (coff=chdr(map, coff, fsize, &fu, fc+1, &ret, ctx, tmpd, NULL))) { fc++; if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) { cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles); ret=CL_EMAXFILES; } #if HAVE_JSON if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) { return CL_ETIMEOUT; } #endif } } else cli_dbgmsg("cli_unzip: central not found, using localhdrs\n"); if(fu<=(fc/4)) { /* FIXME: make up a sane ratio or remove the whole logic */ fc = 0; while (ret==CL_CLEAN && lhoff<fsize && (coff=lhdr(map, lhoff, fsize-lhoff, &fu, fc+1, NULL, &ret, ctx, tmpd, 1, zip_scan_cb))) { fc++; lhoff+=coff; if (SCAN_ALL && ret == CL_VIRUS) { ret = CL_CLEAN; virus_found = 1; } if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) { cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles); ret=CL_EMAXFILES; } #if HAVE_JSON if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) { return CL_ETIMEOUT; } #endif } } if (!ctx->engine->keeptmp) cli_rmdirs(tmpd); free(tmpd); if (ret == CL_CLEAN && virus_found) ret = CL_VIRUS; return ret; }
int cli_cvdload(FILE *fs, struct cl_engine **engine, unsigned int *signo, short warn, unsigned int options) { char *dir; struct cl_cvd cvd; int ret; time_t s_time; int cfd; cli_dbgmsg("in cli_cvdload()\n"); /* verify */ if((ret = cli_cvdverify(fs, &cvd))) return ret; if(cvd.stime && warn) { time(&s_time); if((int) s_time - cvd.stime > 604800) { cli_warnmsg("**************************************************\n"); cli_warnmsg("*** The virus database is older than 7 days! ***\n"); cli_warnmsg("*** Please update it as soon as possible. ***\n"); cli_warnmsg("**************************************************\n"); } } if(cvd.fl > cl_retflevel()) { cli_warnmsg("***********************************************************\n"); cli_warnmsg("*** This version of the ClamAV engine is outdated. ***\n"); cli_warnmsg("*** DON'T PANIC! Read http://www.clamav.net/support/faq ***\n"); cli_warnmsg("***********************************************************\n"); } dir = cli_gentemp(NULL); if(mkdir(dir, 0700)) { cli_errmsg("cli_cvdload(): Can't create temporary directory %s\n", dir); free(dir); return CL_ETMPDIR; } cfd = fileno(fs); /* use only operations on file descriptors, and not on the FILE* from here on * if we seek the FILE*, the underlying descriptor may not seek as expected * (for example on OpenBSD, cygwin, etc.). * So seek the descriptor directly. */ if(lseek(cfd, 512, SEEK_SET) == -1) { cli_errmsg("cli_cvdload(): lseek(fs, 512, SEEK_SET) failed\n"); return CL_EIO; } if(cli_untgz(cfd, dir)) { cli_errmsg("cli_cvdload(): Can't unpack CVD file.\n"); free(dir); return CL_ECVDEXTR; } /* load extracted directory */ ret = cl_load(dir, engine, signo, options); cli_rmdirs(dir); free(dir); return ret; }
int cli_scannulsft(int desc, cli_ctx *ctx, off_t offset) { int ret; struct nsis_st nsist; cli_dbgmsg("in scannulsft()\n"); if(ctx->limits && ctx->limits->maxreclevel && ctx->arec >= ctx->limits->maxreclevel) { cli_dbgmsg("Archive recursion limit exceeded (arec == %u).\n", ctx->arec+1); return CL_EMAXREC; } memset(&nsist, 0, sizeof(struct nsis_st)); nsist.ifd = desc; nsist.off = offset; if (!(nsist.dir = cli_gentemp(NULL))) return CL_ETMPDIR; if(mkdir(nsist.dir, 0700)) { cli_dbgmsg("NSIS: Can't create temporary directory %s\n", nsist.dir); free(nsist.dir); return CL_ETMPDIR; } if(cli_leavetemps_flag) cli_dbgmsg("NSIS: Extracting files to %s\n", nsist.dir); ctx->arec++; do { ret = cli_nsis_unpack(&nsist, ctx); if(ret != CL_SUCCESS) { if(ret == CL_EMAXSIZE) { if(BLOCKMAX) { *ctx->virname = "NSIS.ExceededFileSize"; ret=CL_VIRUS; } else { ret = nsist.solid ? CL_BREAK : CL_SUCCESS; } } } else { cli_dbgmsg("NSIS: Successully extracted file #%u\n", nsist.fno); lseek(nsist.ofd, 0, SEEK_SET); if(nsist.fno == 1) ret=cli_scandesc(nsist.ofd, ctx, 0, 0, 0, NULL); else ret=cli_magic_scandesc(nsist.ofd, ctx); close(nsist.ofd); if(!cli_leavetemps_flag) unlink(nsist.ofn); } } while(ret == CL_SUCCESS); if(ret == CL_BREAK) ret = CL_CLEAN; cli_nsis_free(&nsist); if(!cli_leavetemps_flag) cli_rmdirs(nsist.dir); free(nsist.dir); ctx->arec--; return ret; }
static int sigtool_scandir (const char *dirname, int hex_output) { DIR *dd; struct dirent *dent; STATBUF statbuf; char *fname; const char *tmpdir; char *dir; int ret = CL_CLEAN, desc; cli_ctx *ctx; fname = NULL; if ((dd = opendir (dirname)) != NULL) { while ((dent = readdir (dd))) { if (dent->d_ino) { if (strcmp (dent->d_name, ".") && strcmp (dent->d_name, "..")) { /* build the full name */ fname = (char *) cli_calloc (strlen (dirname) + strlen (dent->d_name) + 2, sizeof (char)); if(!fname){ closedir(dd); return -1; } sprintf (fname, "%s"PATHSEP"%s", dirname, dent->d_name); /* stat the file */ if (LSTAT (fname, &statbuf) != -1) { if (S_ISDIR (statbuf.st_mode) && !S_ISLNK (statbuf.st_mode)) { if (sigtool_scandir (fname, hex_output)) { free (fname); closedir (dd); return CL_VIRUS; } } else { if (S_ISREG (statbuf.st_mode)) { struct uniq *vba = NULL; tmpdir = cli_gettmpdir(); /* generate the temporary directory */ dir = cli_gentemp (tmpdir); if(!dir) { printf("cli_gentemp() failed\n"); free(fname); closedir (dd); return -1; } if (mkdir (dir, 0700)) { printf ("Can't create temporary directory %s\n", dir); free(fname); closedir (dd); free(dir); return CL_ETMPDIR; } if ((desc = open (fname, O_RDONLY|O_BINARY)) == -1) { printf ("Can't open file %s\n", fname); free(fname); closedir (dd); free(dir); return 1; } if(!(ctx = convenience_ctx(desc))) { free(fname); close(desc); closedir(dd); free(dir); return 1; } if ((ret = cli_ole2_extract (dir, ctx, &vba))) { printf ("ERROR %s\n", cl_strerror (ret)); destroy_ctx(desc, ctx); cli_rmdirs (dir); free (dir); closedir (dd); free(fname); return ret; } if(vba) sigtool_vba_scandir (dir, hex_output, vba); destroy_ctx(desc, ctx); cli_rmdirs (dir); free (dir); } } } free (fname); } } } } else { logg("!Can't open directory %s.\n", dirname); return CL_EOPEN; } closedir (dd); return 0; }