libclamav/cpio.c
e6417b01
 /*
c442ca9c
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2009-2013 Sourcefire, Inc.
e6417b01
  *
  *  Authors: Tomasz Kojm <tkojm@clamav.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
f1e02ed2
 #ifdef HAVE_UNISTD_H
e6417b01
 #include <unistd.h>
f1e02ed2
 #endif
e6417b01
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
60d8d2c3
 #include "clamav.h"
e6417b01
 #include "others.h"
 #include "cpio.h"
 #include "scanners.h"
570b1d00
 #include "matcher.h"
e6417b01
 
 struct cpio_hdr_old {
     uint16_t magic;
     uint16_t dev;
     uint16_t ino;
     uint16_t mode;
     uint16_t uid;
     uint16_t gid;
     uint16_t nlink;
     uint16_t rdev;
     uint16_t mtime[2];
     uint16_t namesize;
     uint16_t filesize[2];
 };
 
 struct cpio_hdr_odc {
     char magic[6];
     char dev[6];
     char ino[6];
     char mode[6];
     char uid[6];
     char gid[6];
     char nlink[6];
     char rdev[6];
     char mtime[11];
     char namesize[6];
     char filesize[11];
 };
 
 struct cpio_hdr_newc {
     char magic[6];
     char ino[8];
     char mode[8];
     char uid[8];
     char gid[8];
     char nlink[8];
     char mtime[8];
     char filesize[8];
     char devmajor[8];
     char devminor[8];
     char rdevmajor[8];
     char rdevminor[8];
     char namesize[8];
     char check[8];
 };
 
3222a096
 #define EC16(v, conv)   (conv ? cbswap16(v) : v)
e6417b01
 
 static void sanitname(char *name)
 {
     while(*name) {
 	if(!isascii(*name) || strchr("%\\\t\n\r", *name))
 	    *name = '_';
 	name++;
     }
 }
 
87af1d57
 int cli_scancpio_old(cli_ctx *ctx)
e6417b01
 {
 	struct cpio_hdr_old hdr_old;
 	char name[513];
 	unsigned int file = 0, trailer = 0;
 	uint32_t filesize, namesize, hdr_namesize;
7a307529
 	int ret = CL_CLEAN, conv;
87af1d57
 	off_t pos = 0;
7a307529
         int virus_found = 0;
e6417b01
 
 
87af1d57
     while(fmap_readn(*ctx->fmap, &hdr_old, pos, sizeof(hdr_old)) == sizeof(hdr_old)) {
 	pos += sizeof(hdr_old);
7a307529
 	if(!hdr_old.magic && trailer) {
             ret = CL_SUCCESS;
 	    goto leave;
         }
e6417b01
 
 	if(hdr_old.magic == 070707) {
 	    conv = 0;
 	} else if(hdr_old.magic == 0143561) {
 	    conv = 1;
 	} else {
 	    cli_dbgmsg("cli_scancpio_old: Invalid magic number\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 
 	cli_dbgmsg("CPIO: -- File %u --\n", ++file);
 
 	if(hdr_old.namesize) {
 	    hdr_namesize = EC16(hdr_old.namesize, conv);
 	    namesize = MIN(sizeof(name), hdr_namesize);
cd94be7a
 	    if ((uint32_t)fmap_readn(*ctx->fmap, &name, pos, namesize) != namesize) {
e6417b01
 		cli_dbgmsg("cli_scancpio_old: Can't read file name\n");
 		return CL_EFORMAT;
 	    }
87af1d57
 	    pos += namesize;
e6417b01
 	    name[namesize - 1] = 0;
 	    sanitname(name);
 	    cli_dbgmsg("CPIO: Name: %s\n", name);
 	    if(!strcmp(name, "TRAILER!!!"))
 		trailer = 1;
 
 	    if(namesize < hdr_namesize) {
 		if(hdr_namesize % 2)
 		    hdr_namesize++;
87af1d57
 		pos += hdr_namesize - namesize;
e6417b01
 	    } else if(hdr_namesize % 2)
87af1d57
 		pos++;
e6417b01
 	}
f1d82e2f
         filesize = (uint32_t)((uint32_t)EC16(hdr_old.filesize[0], conv) << 16 | EC16(hdr_old.filesize[1], conv));
e6417b01
 	cli_dbgmsg("CPIO: Filesize: %u\n", filesize);
 	if(!filesize)
 	    continue;
 
7a307529
 	if(cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) {
d7979d4f
             if (!SCAN_ALLMATCHES)
7a307529
                 return CL_VIRUS;
             virus_found = 1;
         }
570b1d00
 
e6417b01
 
 	if((EC16(hdr_old.mode, conv) & 0170000) != 0100000) {
 	    cli_dbgmsg("CPIO: Not a regular file, skipping\n");
 	} else {
 	    ret = cli_checklimits("cli_scancpio_old", ctx, filesize, 0, 0);
 	    if(ret == CL_EMAXFILES) {
7a307529
 		goto leave;
e6417b01
 	    } else if(ret == CL_SUCCESS) {
328a3325
 		ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY);
7a307529
 		if(ret == CL_VIRUS) {
d7979d4f
                     if (!SCAN_ALLMATCHES)
7a307529
                         return ret;
                     virus_found = 1;
                 }
e6417b01
 	    }
 	}
 	if(filesize % 2)
 	    filesize++;
 
87af1d57
 	pos += filesize;
e6417b01
     }
 
7a307529
  leave:
     if (virus_found != 0)
         return CL_VIRUS;
     return ret;
e6417b01
 }
 
87af1d57
 int cli_scancpio_odc(cli_ctx *ctx)
e6417b01
 {
 	struct cpio_hdr_odc hdr_odc;
 	char name[513], buff[12];
 	unsigned int file = 0, trailer = 0;
 	uint32_t filesize, namesize, hdr_namesize;
7a307529
 	int ret = CL_CLEAN;
87af1d57
 	off_t pos = 0;
7a307529
         int virus_found = 0;
e6417b01
 
 
87af1d57
     while(fmap_readn(*ctx->fmap, &hdr_odc, pos, sizeof(hdr_odc)) == sizeof(hdr_odc)) {
 	pos += sizeof(hdr_odc);
e6417b01
 	if(!hdr_odc.magic[0] && trailer)
7a307529
 	    goto leave;
e6417b01
 
 	if(strncmp(hdr_odc.magic, "070707", 6)) {
 	    cli_dbgmsg("cli_scancpio_odc: Invalid magic string\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 
 	cli_dbgmsg("CPIO: -- File %u --\n", ++file);
 
 	strncpy(buff, hdr_odc.namesize, 6);
 	buff[6] = 0;
 	if(sscanf(buff, "%o", &hdr_namesize) != 1) {
 	    cli_dbgmsg("cli_scancpio_odc: Can't convert name size\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 	if(hdr_namesize) {
 	    namesize = MIN(sizeof(name), hdr_namesize);
cd94be7a
 	    if ((uint32_t)fmap_readn(*ctx->fmap, &name, pos, namesize) != namesize) {
e6417b01
 		cli_dbgmsg("cli_scancpio_odc: Can't read file name\n");
7a307529
 		ret = CL_EFORMAT;
                 goto leave;
e6417b01
 	    }
87af1d57
 	    pos += namesize;
e6417b01
 	    name[namesize - 1] = 0;
 	    sanitname(name);
 	    cli_dbgmsg("CPIO: Name: %s\n", name);
 	    if(!strcmp(name, "TRAILER!!!"))
 		trailer = 1;
 
 	    if(namesize < hdr_namesize)
87af1d57
 		pos += hdr_namesize - namesize;
e6417b01
 	}
 
 	strncpy(buff, hdr_odc.filesize, 11);
 	buff[11] = 0;
 	if(sscanf(buff, "%o", &filesize) != 1) {
 	    cli_dbgmsg("cli_scancpio_odc: Can't convert file size\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 	cli_dbgmsg("CPIO: Filesize: %u\n", filesize);
 	if(!filesize)
 	    continue;
 
7a307529
 	if(cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) {
d7979d4f
             if (!SCAN_ALLMATCHES)
7a307529
                 return CL_VIRUS;
             virus_found = 1;
         }
570b1d00
 
e6417b01
 
 	ret = cli_checklimits("cli_scancpio_odc", ctx, filesize, 0, 0);
 	if(ret == CL_EMAXFILES) {
7a307529
 	    goto leave;
e6417b01
 	} else if(ret == CL_SUCCESS) {
328a3325
 	    ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY);
7a307529
 	    if(ret == CL_VIRUS) {
d7979d4f
                 if (!SCAN_ALLMATCHES)
7a307529
                     return ret;
                 virus_found = 1;
             }
e6417b01
 	}
 
87af1d57
 	pos += filesize;
e6417b01
     }
 
7a307529
  leave:
     if (virus_found != 0)
         return CL_VIRUS;
     return ret;
e6417b01
 }
 
87af1d57
 int cli_scancpio_newc(cli_ctx *ctx, int crc)
e6417b01
 {
 	struct cpio_hdr_newc hdr_newc;
 	char name[513], buff[9];
 	unsigned int file = 0, trailer = 0;
 	uint32_t filesize, namesize, hdr_namesize, pad;
7a307529
 	int ret = CL_CLEAN;
87af1d57
 	off_t pos = 0;
7a307529
         int virus_found = 0;
e6417b01
 
c76e6305
     memset(name, 0, 513);
e6417b01
 
87af1d57
     while(fmap_readn(*ctx->fmap, &hdr_newc, pos, sizeof(hdr_newc)) == sizeof(hdr_newc)) {
 	pos += sizeof(hdr_newc);
e6417b01
 	if(!hdr_newc.magic[0] && trailer)
7a307529
 	    goto leave;
e6417b01
 
 	if((!crc && strncmp(hdr_newc.magic, "070701", 6)) || (crc && strncmp(hdr_newc.magic, "070702", 6))) {
 	    cli_dbgmsg("cli_scancpio_newc: Invalid magic string\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 
 	cli_dbgmsg("CPIO: -- File %u --\n", ++file);
 
 	strncpy(buff, hdr_newc.namesize, 8);
 	buff[8] = 0;
 	if(sscanf(buff, "%x", &hdr_namesize) != 1) {
 	    cli_dbgmsg("cli_scancpio_newc: Can't convert name size\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 	if(hdr_namesize) {
 	    namesize = MIN(sizeof(name), hdr_namesize);
cd94be7a
 	    if ((uint32_t)fmap_readn(*ctx->fmap, &name, pos, namesize) != namesize) {
e6417b01
 		cli_dbgmsg("cli_scancpio_newc: Can't read file name\n");
7a307529
 		ret = CL_EFORMAT;
                 goto leave;
e6417b01
 	    }
87af1d57
 	    pos += namesize;
e6417b01
 	    name[namesize - 1] = 0;
 	    sanitname(name);
 	    cli_dbgmsg("CPIO: Name: %s\n", name);
 	    if(!strcmp(name, "TRAILER!!!"))
 		trailer = 1;
 
 	    pad = (4 - (sizeof(hdr_newc) + hdr_namesize) % 4) % 4;
 	    if(namesize < hdr_namesize) {
 		if(pad)
 		    hdr_namesize += pad;
87af1d57
 		pos += hdr_namesize - namesize;
e6417b01
 	    } else if(pad)
87af1d57
 		pos += pad;
e6417b01
 	}
 
 	strncpy(buff, hdr_newc.filesize, 8);
 	buff[8] = 0;
 	if(sscanf(buff, "%x", &filesize) != 1) {
 	    cli_dbgmsg("cli_scancpio_newc: Can't convert file size\n");
7a307529
 	    ret = CL_EFORMAT;
             goto leave;
e6417b01
 	}
 	cli_dbgmsg("CPIO: Filesize: %u\n", filesize);
 	if(!filesize)
 	    continue;
 
7a307529
 	if(cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) {
d7979d4f
             if (!SCAN_ALLMATCHES)
7a307529
                 return CL_VIRUS;
             virus_found = 1;
         }
e6417b01
 
 	ret = cli_checklimits("cli_scancpio_newc", ctx, filesize, 0, 0);
 	if(ret == CL_EMAXFILES) {
7a307529
 	    goto leave;
e6417b01
 	} else if(ret == CL_SUCCESS) {
328a3325
 	    ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY);
7a307529
 	    if(ret == CL_VIRUS) {
d7979d4f
                 if (!SCAN_ALLMATCHES)
7a307529
                     return ret;
                 virus_found = 1;
             }
e6417b01
 	}
 
 	if((pad = filesize % 4))
 	    filesize += (4 - pad);
 
87af1d57
 	pos += filesize;
e6417b01
     }
 
7a307529
  leave:
     if (virus_found != 0)
         return CL_VIRUS;
     return ret;
e6417b01
 }