a8b7c1dd |
/* |
2023340a |
* Copyright (C) 2007-2008 Sourcefire, Inc.
*
* Authors: Nigel Horne |
a8b7c1dd |
*
* This program is free software; you can redistribute it and/or modify |
2023340a |
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. |
a8b7c1dd |
*
* 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 |
48b7b4a7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
a8b7c1dd |
*/ |
2023340a |
|
95e11e5a |
static char const rcsid[] = "$Id: untar.c,v 1.35 2007/02/12 20:46:09 njh Exp $"; |
dea34e7d |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif |
95fb46e5 |
#include <stdio.h>
#include <errno.h>
#include <string.h> |
45b28aba |
#ifdef HAVE_UNISTD_H |
95fb46e5 |
#include <unistd.h> |
45b28aba |
#endif |
95fb46e5 |
#include <sys/stat.h> |
bb3fdd1b |
#include <fcntl.h> |
45b28aba |
#ifdef HAVE_SYS_PARAM_H |
7c5a7a47 |
#include <sys/param.h> /* for NAME_MAX */ |
45b28aba |
#endif |
95fb46e5 |
#include "clamav.h"
#include "others.h" |
bb3fdd1b |
#include "untar.h" |
11b50569 |
#include "mbox.h"
#include "blob.h" |
96522097 |
#include "scanners.h" |
95fb46e5 |
#define BLOCKSIZE 512
|
834f22d7 |
#ifndef O_BINARY
#define O_BINARY 0
#endif
|
bb3fdd1b |
static int
octal(const char *str) |
95fb46e5 |
{ |
ed734e16 |
int ret; |
95fb46e5 |
|
ed734e16 |
if(sscanf(str, "%o", (unsigned int *)&ret) != 1)
return -1; |
95fb46e5 |
return ret;
}
int |
d91ab809 |
cli_untar(const char *dir, int desc, unsigned int posix, cli_ctx *ctx) |
95fb46e5 |
{ |
d0d1afd7 |
int size = 0, ret, fout=-1; |
95fb46e5 |
int in_block = 0; |
9140eb10 |
unsigned int files = 0; |
bb3fdd1b |
char fullname[NAME_MAX + 1]; |
95fb46e5 |
|
d0d1afd7 |
cli_dbgmsg("In untar(%s, %d)\n", dir, desc); |
95fb46e5 |
for(;;) {
char block[BLOCKSIZE]; |
9c107190 |
const int nread = cli_readn(desc, block, (unsigned int)sizeof(block)); |
95fb46e5 |
if(!in_block && nread == 0)
break;
|
f0931086 |
if(nread < 0) { |
d0d1afd7 |
if(fout>=0)
close(fout); |
f0931086 |
cli_errmsg("cli_untar: block read error\n"); |
871177cd |
return CL_EREAD; |
95fb46e5 |
}
if(!in_block) {
char type; |
d0d1afd7 |
int directory, skipEntry = 0; |
bb3fdd1b |
char magic[7], name[101], osize[13];
|
d0d1afd7 |
if(fout>=0) {
lseek(fout, 0, SEEK_SET);
ret = cli_magic_scandesc(fout, ctx);
close(fout); |
33068e09 |
if (!ctx->engine->keeptmp) |
871177cd |
if (cli_unlink(fullname)) return CL_EUNLINK; |
d0d1afd7 |
if (ret==CL_VIRUS)
return CL_VIRUS;
fout = -1; |
bb3fdd1b |
} |
95fb46e5 |
|
7c5a7a47 |
if(block[0] == '\0') /* We're done */ |
95fb46e5 |
break; |
d91ab809 |
if((ret=cli_checklimits("cli_untar", ctx, 0, 0, 0))!=CL_CLEAN)
return ret; |
9140eb10 |
|
90e80a54 |
/* Notice assumption that BLOCKSIZE > 262 */ |
a7f5fd00 |
if(posix) {
strncpy(magic, block+257, 5);
magic[5] = '\0';
if(strcmp(magic, "ustar") != 0) { |
9fc9db81 |
cli_dbgmsg("cli_untar: Incorrect magic string '%s' in tar header\n", magic); |
a7f5fd00 |
return CL_EFORMAT;
} |
95fb46e5 |
}
type = block[156];
switch(type) { |
45f8ad14 |
default: |
9fc9db81 |
cli_dbgmsg("cli_untar: unknown type flag %c\n", type); |
63943caf |
case '0': /* plain file */
case '\0': /* plain file */
case '7': /* contiguous file */ |
45f8ad14 |
case 'M': /* continuation of a file from another volume; might as well scan it. */ |
9140eb10 |
files++; |
95fb46e5 |
directory = 0;
break; |
ab592ce9 |
case '1': /* Link to already archived file */ |
63943caf |
case '5': /* directory */
case '2': /* sym link */
case '3': /* char device */
case '4': /* block device */
case '6': /* fifo special */ |
d2888b89 |
case 'V': /* Volume header */ |
95fb46e5 |
directory = 1;
break; |
d2888b89 |
case 'K':
case 'L':
/* GNU extension - ././@LongLink
* Discard the blocks with the extended filename,
* the last header will contain parts of it anyway
*/ |
45f8ad14 |
case 'N': /* Old GNU format way of storing long filenames. */
case 'A': /* Solaris ACL */
case 'E': /* Solaris Extended attribute s*/
case 'I': /* Inode only */
case 'g': /* Global extended header */
case 'x': /* Extended attributes */
case 'X': /* Extended attributes (POSIX) */ |
d2888b89 |
directory = 0;
skipEntry = 1;
break; |
95fb46e5 |
}
|
63943caf |
if(directory) {
in_block = 0; |
95fb46e5 |
continue; |
63943caf |
} |
95fb46e5 |
|
d2888b89 |
strncpy(osize, block+124, 12);
osize[12] = '\0';
size = octal(osize);
if(size < 0) { |
9fc9db81 |
cli_dbgmsg("cli_untar: Invalid size in tar header\n"); |
9140eb10 |
skipEntry++; |
9fc9db81 |
} else {
cli_dbgmsg("cli_untar: size = %d\n", size);
if((ret=cli_checklimits("cli_untar", ctx, size, 0, 0))!=CL_CLEAN)
skipEntry++; |
9140eb10 |
} |
d2888b89 |
if(skipEntry) {
const int nskip = (size % BLOCKSIZE || !size) ? size + BLOCKSIZE - (size % BLOCKSIZE) : size; |
2995f1c7 |
if(nskip < 0) {
cli_dbgmsg("cli_untar: got nagative skip size, giving up\n");
return CL_CLEAN;
} |
9140eb10 |
cli_dbgmsg("cli_untar: skipping entry\n"); |
d2888b89 |
lseek(desc, nskip, SEEK_CUR);
continue;
}
|
bb3fdd1b |
strncpy(name, block, 100);
name[100] = '\0'; |
d0d1afd7 |
snprintf(fullname, sizeof(fullname)-1, "%s/tar%02u", dir, files);
fullname[sizeof(fullname)-1] = '\0';
fout = open(fullname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); |
95fb46e5 |
|
d0d1afd7 |
if(fout < 0) { |
e68d70e7 |
char err[128];
cli_errmsg("cli_untar: Can't create temporary file %s: %s\n", fullname, cli_strerror(errno, err, sizeof(err))); |
bb3fdd1b |
return CL_ETMPFILE;
} |
e68d70e7 |
|
d0d1afd7 |
cli_dbgmsg("cli_untar: extracting to %s\n", fullname); |
bb3fdd1b |
in_block = 1; |
95fb46e5 |
} else { /* write or continue writing file contents */
const int nbytes = size>512? 512:size; |
d0d1afd7 |
const int nwritten = (int)write(fout, block, (size_t)nbytes); |
95fb46e5 |
if(nwritten != nbytes) { |
585756f1 |
cli_errmsg("cli_untar: only wrote %d bytes to file %s (out of disc space?)\n", |
95fb46e5 |
nwritten, fullname); |
d0d1afd7 |
close(fout); |
871177cd |
return CL_EWRITE; |
95fb46e5 |
}
size -= nbytes;
} |
d4112005 |
if (size == 0)
in_block = 0; |
d91ab809 |
} |
d0d1afd7 |
if(fout>=0) {
lseek(fout, 0, SEEK_SET);
ret = cli_magic_scandesc(fout, ctx);
close(fout); |
33068e09 |
if (!ctx->engine->keeptmp) |
871177cd |
if (cli_unlink(fullname)) return CL_EUNLINK; |
d0d1afd7 |
if (ret==CL_VIRUS)
return CL_VIRUS;
}
return CL_CLEAN; |
95fb46e5 |
} |