Exemple #1
0
static int cauth_scanfile(const char *fname, int extinfo, struct thrarg *tharg)
{
	struct cb_context context;
	const char *virname;
	int ret = 0, fd;

    context.filename = fname;
    context.virsize = 0;

    fd = open(fname, O_RDONLY);
    if(fd == -1)
	return -1;

    if(cl_scandesc_callback(fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
	if(context.virsize)
	    detstats_add(virname, fname, context.virsize, context.virhash);
	if(extinfo && context.virsize)
	    logg("ClamAuth: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
	else
	    logg("ClamAuth: %s: %s FOUND\n", fname, virname);
	virusaction(fname, virname, tharg->opts);
    }
    close(fd);
    return ret;
}
Exemple #2
0
static int fan_scanfile(int fan_fd, const char *fname, struct fanotify_event_metadata *fmd, int scan, int extinfo, struct thrarg *tharg)
{
	struct fanotify_response res;
	struct cb_context context;
	const char *virname;
	int ret = 0;

    res.fd = fmd->fd;
    res.response = FAN_ALLOW;
    context.filename = fname;
    context.virsize = 0;
    if(scan && cl_scandesc_callback(fmd->fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
	if(context.virsize)
	    detstats_add(virname, fname, context.virsize, context.virhash);
	if(extinfo && context.virsize)
	    logg("ScanOnAccess: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
	else
	    logg("ScanOnAccess: %s: %s FOUND\n", fname, virname);
	virusaction(fname, virname, tharg->opts);
	res.response = FAN_DENY;
    }

    if(fmd->mask & FAN_ALL_PERM_EVENTS) {
	ret = write(fan_fd, &res, sizeof(res));
	if(ret == -1)
	    logg("!ScanOnAccess: Internal error (can't write to fanotify)\n");
    }

    return ret;
}
Exemple #3
0
int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term)
{
    int ret, sockfd, acceptd;
    int tmpd, bread, retval, firsttimeout, timeout, btread;
    unsigned int port = 0, portscan, min_port, max_port;
    unsigned long int quota = 0, maxsize = 0;
    short bound = 0;
    const char *virname;
    char buff[FILEBUFF];
    char peer_addr[32];
    struct cb_context context;
    struct sockaddr_in server;
    struct sockaddr_in peer;
    socklen_t addrlen;
    char *tmpname;


    min_port = optget(opts, "StreamMinPort")->numarg;
    max_port = optget(opts, "StreamMaxPort")->numarg;

    /* search for a free port to bind to */
    port = cli_rndnum(max_port - min_port);
    bound = 0;
    for (portscan = 0; portscan < 1000; portscan++) {
        port = (port - 1) % (max_port - min_port + 1);

        memset((char *) &server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(min_port + port);
        server.sin_addr.s_addr = htonl(INADDR_ANY);

        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
            continue;

        if(bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) == -1)
            closesocket(sockfd);
        else {
            bound = 1;
            break;
        }
    }
    port += min_port;

    timeout = optget(opts, "ReadTimeout")->numarg;
    firsttimeout = optget(opts, "CommandReadTimeout")->numarg;

    if(!bound) {
        logg("!ScanStream: Can't find any free port.\n");
        mdprintf(odesc, "Can't find any free port. ERROR%c", term);
        return -1;
    } else {
        listen(sockfd, 1);
        if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) {
            logg("!ScanStream: error transmitting port.\n");
            closesocket(sockfd);
            return -1;
        }
    }

    retval = poll_fd(sockfd, firsttimeout, 0);
    if (!retval || retval == -1) {
        const char *reason = !retval ? "timeout" : "poll";
        mdprintf(odesc, "Accept %s. ERROR%c", reason, term);
        logg("!ScanStream %u: accept %s.\n", port, reason);
        closesocket(sockfd);
        return -1;
    }

    addrlen = sizeof(peer);
    if((acceptd = accept(sockfd, (struct sockaddr *) &peer, &addrlen)) == -1) {
        closesocket(sockfd);
        mdprintf(odesc, "accept() ERROR%c", term);
        logg("!ScanStream %u: accept() failed.\n", port);
        return -1;
    }

    *peer_addr = '\0';
    inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr));
    logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd);

    if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) {
        shutdown(sockfd, 2);
        closesocket(sockfd);
        closesocket(acceptd);
        mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term);
        logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port);
        return -1;
    }

    quota = maxsize = optget(opts, "StreamMaxLength")->numarg;

    while((retval = poll_fd(acceptd, timeout, 0)) == 1) {
        /* only read up to max */
        btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff);
        if (!btread) {
            logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize);
            break; /* Scan what we have */
        }
        bread = recv(acceptd, buff, btread, 0);
        if(bread <= 0)
            break;

        quota -= bread;

        if(writen(tmpd, buff, bread) != bread) {
            shutdown(sockfd, 2);
            closesocket(sockfd);
            closesocket(acceptd);
            mdprintf(odesc, "Temporary file -> write ERROR%c", term);
            logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port);
            close(tmpd);
            if(!optget(opts, "LeaveTemporaryFiles")->enabled)
                unlink(tmpname);
            free(tmpname);
            return -1;
        }
    }

    switch(retval) {
    case 0: /* timeout */
        mdprintf(odesc, "read timeout ERROR%c", term);
        logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port);
        break;
    case -1:
        mdprintf(odesc, "read poll ERROR%c", term);
        logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port);
        break;
    }

    if(retval == 1) {
        lseek(tmpd, 0, SEEK_SET);
        thrmgr_setactivetask(peer_addr, NULL);
        context.filename = peer_addr;
        context.virsize = 0;
        ret = cl_scandesc_callback(tmpd, &virname, scanned, engine, options, &context);
        thrmgr_setactivetask(NULL, NULL);
    } else {
        ret = -1;
    }
    close(tmpd);
    if(!optget(opts, "LeaveTemporaryFiles")->enabled)
        unlink(tmpname);
    free(tmpname);

    closesocket(acceptd);
    closesocket(sockfd);

    if(ret == CL_VIRUS) {
        if(context.virsize)
            detstats_add(virname, "NOFNAME", context.virsize, context.virhash);
        if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) {
            mdprintf(odesc, "stream: %s(%s:%llu) FOUND%c", virname, context.virhash, context.virsize, term);
            logg("stream(%s@%u): %s(%s:%llu) FOUND\n", peer_addr, port, virname, context.virhash, context.virsize);
        } else {
            mdprintf(odesc, "stream: %s FOUND%c", virname, term);
            logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname);
        }
        virusaction("stream", virname, opts);
    } else if(ret != CL_CLEAN) {
        if(retval == 1) {
            mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term);
            logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret));
        }
    } else {
        mdprintf(odesc, "stream: OK%c", term);
        if(logok)
            logg("stream(%s@%u): OK\n", peer_addr, port);
    }

    return ret;
}
Exemple #4
0
int scanfd(const client_conn_t *conn, unsigned long int *scanned,
           const struct cl_engine *engine,
           unsigned int options, const struct optstruct *opts, int odesc, int stream)
{
    int ret, fd = conn->scanfd;
    const char *virname;
    STATBUF statbuf;
    struct cb_context context;
    char fdstr[32];
    const char*reply_fdstr;

    if (stream) {
        struct sockaddr_in sa;
        socklen_t salen = sizeof(sa);
        if(getpeername(conn->sd, (struct sockaddr *)&sa, &salen) || salen > sizeof(sa) || sa.sin_family != AF_INET)
            strncpy(fdstr, "instream(local)", sizeof(fdstr));
        else
            snprintf(fdstr, sizeof(fdstr), "instream(%s@%u)", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
        reply_fdstr = "stream";
    } else {
        snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd);
        reply_fdstr = fdstr;
    }
    if(FSTAT(fd, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
        logg("%s: Not a regular file. ERROR\n", fdstr);
        if (conn_reply(conn, reply_fdstr, "Not a regular file", "ERROR") == -1)
            return CL_ETIMEOUT;
        return -1;
    }

    thrmgr_setactivetask(fdstr, NULL);
    context.filename = fdstr;
    context.virsize = 0;
    ret = cl_scandesc_callback(fd, &virname, scanned, engine, options, &context);
    thrmgr_setactivetask(NULL, NULL);

    if (thrmgr_group_need_terminate(conn->group)) {
        logg("*Client disconnected while scanjob was active\n");
        return ret == CL_ETIMEOUT ? ret : CL_BREAK;
    }

    if(ret == CL_VIRUS) {
        if (conn_reply_virus(conn, reply_fdstr, virname) == -1)
            ret = CL_ETIMEOUT;
        if(context.virsize)
            detstats_add(virname, "NOFNAME", context.virsize, context.virhash);
        if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled)
            logg("%s: %s(%s:%llu) FOUND\n", fdstr, virname, context.virhash, context.virsize);
        else
            logg("%s: %s FOUND\n", fdstr, virname);
        virusaction(reply_fdstr, virname, opts);
    } else if(ret != CL_CLEAN) {
        if (conn_reply(conn, reply_fdstr, cl_strerror(ret), "ERROR") == -1)
            ret = CL_ETIMEOUT;
        logg("%s: %s ERROR\n", fdstr, cl_strerror(ret));
    } else {
        if (conn_reply_single(conn, reply_fdstr, "OK") == CL_ETIMEOUT)
            ret = CL_ETIMEOUT;
        if(logok)
            logg("%s: OK\n", fdstr);
    }
    return ret;
}
Exemple #5
0
int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
{
    struct scan_cb_data *scandata = data->data;
    const char *virname;
    const char **virpp = &virname;
    int ret;
    int type = scandata->type;
    struct cb_context context;

    /* detect disconnected socket,
     * this should NOT detect half-shutdown sockets (SHUT_WR) */
    if (send(scandata->conn->sd, &ret, 0, 0) == -1 && errno != EINTR) {
        logg("$Client disconnected while command was active!\n");
        thrmgr_group_terminate(scandata->conn->group);
        if (reason == visit_file)
            free(filename);
        return CL_BREAK;
    }

    if (thrmgr_group_need_terminate(scandata->conn->group)) {
        logg("^Client disconnected while scanjob was active\n");
        if (reason == visit_file)
            free(filename);
        return CL_BREAK;
    }
    scandata->total++;
    switch (reason) {
    case error_mem:
        if (msg)
            logg("!Memory allocation failed during cli_ftw() on %s\n",
                 msg);
        else
            logg("!Memory allocation failed during cli_ftw()\n");
        scandata->errors++;
        return CL_EMEM;
    case error_stat:
        conn_reply_errno(scandata->conn, msg, "lstat() failed:");
        logg("^lstat() failed on: %s\n", msg);
        scandata->errors++;
        return CL_SUCCESS;
    case warning_skipped_dir:
        logg("^Directory recursion limit reached, skipping %s\n",
             msg);
        return CL_SUCCESS;
    case warning_skipped_link:
        logg("$Skipping symlink: %s\n", msg);
        return CL_SUCCESS;
    case warning_skipped_special:
        if (msg == scandata->toplevel_path)
            conn_reply(scandata->conn, msg, "Not supported file type", "ERROR");
        logg("*Not supported file type: %s\n", msg);
        return CL_SUCCESS;
    case visit_directory_toplev:
        return CL_SUCCESS;
    case visit_file:
        break;
    }

    /* check whether the file is excluded */
#ifdef C_LINUX
    if(procdev && sb && (sb->st_dev == procdev)) {
        free(filename);
        return CL_SUCCESS;
    }
#endif

    if(sb && sb->st_size == 0) { /* empty file */
        if (msg == scandata->toplevel_path)
            conn_reply_single(scandata->conn, filename, "Empty file");
        free(filename);
        return CL_SUCCESS;
    }

    if (type == TYPE_MULTISCAN) {
        client_conn_t *client_conn = (client_conn_t *) calloc(1, sizeof(struct client_conn_tag));
        if(client_conn) {
            client_conn->scanfd = -1;
            client_conn->sd = scandata->odesc;
            client_conn->filename = filename;
            client_conn->cmdtype = COMMAND_MULTISCANFILE;
            client_conn->term = scandata->conn->term;
            client_conn->options = scandata->options;
            client_conn->opts = scandata->opts;
            client_conn->group = scandata->group;
            if(cl_engine_addref(scandata->engine)) {
                logg("!cl_engine_addref() failed\n");
                free(filename);
                free(client_conn);
                return CL_EMEM;
            } else {
                client_conn->engine = scandata->engine;
                pthread_mutex_lock(&reload_mutex);
                client_conn->engine_timestamp = reloaded_time;
                pthread_mutex_unlock(&reload_mutex);
                if(!thrmgr_group_dispatch(scandata->thr_pool, scandata->group, client_conn, 1)) {
                    logg("!thread dispatch failed\n");
                    cl_engine_free(scandata->engine);
                    free(filename);
                    free(client_conn);
                    return CL_EMEM;
                }
            }
        } else {
            logg("!Can't allocate memory for client_conn\n");
            scandata->errors++;
            free(filename);
            return CL_EMEM;
        }
        return CL_SUCCESS;
    }

    if (access(filename, R_OK)) {
        if (conn_reply(scandata->conn, filename, "Access denied.", "ERROR") == -1) {
            free(filename);
            return CL_ETIMEOUT;
        }
        logg("*Access denied: %s\n", filename);
        scandata->errors++;
        free(filename);
        return CL_SUCCESS;
    }

    thrmgr_setactivetask(filename, NULL);
    context.filename = filename;
    context.virsize = 0;
    ret = cl_scanfile_callback(filename, virpp, &scandata->scanned, scandata->engine, scandata->options, &context);
    thrmgr_setactivetask(NULL, NULL);

    if (scandata->options & CL_SCAN_ALLMATCHES) {
        virpp = (const char **)*virpp; /* temp hack for scanall mode until api augmentation */
        virname = virpp[0];
    }

    if (thrmgr_group_need_terminate(scandata->conn->group)) {
        free(filename);
        if (ret == CL_VIRUS && scandata->options & CL_SCAN_ALLMATCHES)
            free((void *)virpp);
        logg("*Client disconnected while scanjob was active\n");
        return ret == CL_ETIMEOUT ? ret : CL_BREAK;
    }

    if (ret == CL_VIRUS) {
        scandata->infected++;
        if (conn_reply_virus(scandata->conn, filename, virname) == -1) {
            free(filename);
            if (scandata->options & CL_SCAN_ALLMATCHES)
                free((void *)virpp);
            return CL_ETIMEOUT;
        }
        if (scandata->options & CL_SCAN_ALLMATCHES && virpp[1] != NULL) {
            int i = 1;
            while (NULL != virpp[i])
                if (conn_reply_virus(scandata->conn, filename, virpp[i++]) == -1) {
                    free(filename);
                    free((void *)virpp);
                    return CL_ETIMEOUT;
                }
        }
        if(context.virsize)
            detstats_add(virname, filename, context.virsize, context.virhash);
        if(context.virsize && optget(scandata->opts, "ExtendedDetectionInfo")->enabled)
            logg("~%s: %s(%s:%llu) FOUND\n", filename, virname, context.virhash, context.virsize);
        else
            logg("~%s: %s FOUND\n", filename, virname);
        virusaction(filename, virname, scandata->opts);
        if (scandata->options & CL_SCAN_ALLMATCHES && virpp[1] != NULL) {
            int i = 1;
            while (NULL != virpp[i])
                logg("~%s: %s FOUND\n", filename, virpp[i++]);
        }
    } else if (ret != CL_CLEAN) {
        scandata->errors++;
        if (conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR") == -1) {
            free(filename);
            return CL_ETIMEOUT;
        }
        logg("~%s: %s ERROR\n", filename, cl_strerror(ret));
    } else if (logok) {
        logg("~%s: OK\n", filename);
    }

    free(filename);
    if (ret == CL_VIRUS && scandata->options & CL_SCAN_ALLMATCHES)
        free((void *)virpp);
    if(ret == CL_EMEM) /* stop scanning */
        return ret;

    if (type == TYPE_SCAN) {
        /* virus -> break */
        return ret;
    }

    /* keep scanning always */
    return CL_SUCCESS;
}
Exemple #6
0
static void *clamukolegacyth(void *arg)
{
	struct thrarg *tharg = (struct thrarg *) arg;
	sigset_t sigset;
	const char *virname;
        struct sigaction act;
	unsigned long mask = 0;
	const struct optstruct *pt;
	short int scan;
	int sizelimit = 0, extinfo;
	struct stat sb;
	struct cb_context context;


    clamuko_scanning = 0;

    /* ignore all signals except SIGUSR1 */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGUSR1);
    /* The behavior of a process is undefined after it ignores a 
     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
    sigdelset(&sigset, SIGFPE);
    sigdelset(&sigset, SIGILL);
    sigdelset(&sigset, SIGSEGV);
#ifdef SIGBUS    
    sigdelset(&sigset, SIGBUS);
#endif
    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = clamuko_exit;
    sigfillset(&(act.sa_mask));
    sigaction(SIGUSR1, &act, NULL);
    sigaction(SIGSEGV, &act, NULL);

    /* register */
    if(dazukoRegister("ClamAV", "r+")) {
	logg("!Clamuko: Can't register with Dazuko\n");
	return NULL;
    } else
	logg("Clamuko: Correctly registered with Dazuko.\n");

    /* access mask */
    if(optget(tharg->opts, "ClamukoScanOnOpen")->enabled) {
	logg("Clamuko: Scan-on-open mode activated.\n");
	mask |= DAZUKO_ON_OPEN;
    }
    if(optget(tharg->opts, "ClamukoScanOnClose")->enabled) {
	logg("Clamuko: Scan-on-close mode activated.\n");
	mask |= DAZUKO_ON_CLOSE;
    }
    if(optget(tharg->opts, "ClamukoScanOnExec")->enabled) {
	logg("Clamuko: Scan-on-exec mode activated.\n");
	mask |= DAZUKO_ON_EXEC;
    }

    if(!mask) {
	logg("!Access mask is not configured properly.\n");
	dazukoUnregister();
	return NULL;
    }

    if(dazukoSetAccessMask(mask)) {
	logg("!Clamuko: Can't set access mask in Dazuko.\n");
	dazukoUnregister();
	return NULL;
    }

    if((pt = optget(tharg->opts, "ClamukoIncludePath"))->enabled) {
	while(pt) {
	    if((dazukoAddIncludePath(pt->strarg))) {
		logg("!Clamuko: Dazuko -> Can't include path %s\n", pt->strarg);
		dazukoUnregister();
		return NULL;
	    } else
		logg("Clamuko: Included path %s\n", pt->strarg);

	    pt = (struct optstruct *) pt->nextarg;
	}
    } else {
	logg("!Clamuko: please include at least one path.\n");
	dazukoUnregister();
	return NULL;
    }

    if((pt = optget(tharg->opts, "ClamukoExcludePath"))->enabled) {
	while(pt) {
	    if((dazukoAddExcludePath(pt->strarg))) {
		logg("!Clamuko: Dazuko -> Can't exclude path %s\n", pt->strarg);
		dazukoUnregister();
		return NULL;
	    } else
		logg("Clamuko: Excluded path %s\n", pt->strarg);

	    pt = (struct optstruct *) pt->nextarg;
	}
    }

    sizelimit = optget(tharg->opts, "ClamukoMaxFileSize")->numarg;
    if(sizelimit)
	logg("Clamuko: Max file size limited to %d bytes.\n", sizelimit);
    else
	logg("Clamuko: File size limit disabled.\n");

    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;

    while(1) {

	if(dazukoGetAccess(&acc) == 0) {
	    clamuko_scanning = 1;
	    scan = 1;

	    if(sizelimit) {
		stat(acc->filename, &sb);
		if(sb.st_size > sizelimit) {
		    scan = 0;
		    logg("*Clamuko: %s skipped (too big)\n", acc->filename);
		}
	    }

	    if(clamuko_checkowner(acc->pid, tharg->opts)) {
		scan = 0;
		logg("*Clamuko: %s skipped (excluded UID)\n", acc->filename);
	    }

	    context.filename = acc->filename;
	    context.virsize = 0;
	    if(scan && cl_scanfile_callback(acc->filename, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
		if(context.virsize)
		    detstats_add(virname, acc->filename, context.virsize, context.virhash);
		if(extinfo && context.virsize)
		    logg("Clamuko: %s: %s(%s:%llu) FOUND\n", acc->filename, virname, context.virhash, context.virsize);
		else
		    logg("Clamuko: %s: %s FOUND\n", acc->filename, virname);
		virusaction(acc->filename, virname, tharg->opts);
		acc->deny = 1;
	    } else
		acc->deny = 0;

	    if(dazukoReturnAccess(&acc)) {
		logg("!Can't return access to Dazuko.\n");
		logg("Clamuko stopped.\n");
		dazukoUnregister();
		clamuko_scanning = 0;
		return NULL;
	    }

	    clamuko_scanning = 0;
	}
    }

    /* can't be ;) */
    return NULL;
}