clamdscan can't read from tmp directory - php

I was wondering what's wrong with my code, if I use clamscan, it works fine both reading from /tmp, or manually specified the path. but if I use clamdscan, any path from /tmp will result in error (the int result is 2). This is the code.
$command = 'clamdscan ' . escapeshellarg($_FILES['file']['tmp_name']);
$out = '';
$int = -1;
exec($command, $out, $int);
echo "\n" . $command;
echo "\n" . $out;
echo "\n This is int = " . $int;
if ($int == 0) {
// all good, code goes here uploads file as normal IE move to
//echo "File path : ".$file."Return code : ".cl_pretcode($retcode);
//echo "\n";
move_uploaded_file($_FILES["file"]["tmp_name"], "filesave/" . $_FILES["file"]["name"]);
echo "Stored in: " . "filesave/" . $_FILES["file"]["name"];
} else {
echo "\n FAILED";
}
based on above code, it will failed because $int = 2. But, if I change the command to
//some file that is saved already in the directory
$command = 'clamdscan ' . '/abc/abc.txt';
It works perfectly fine.
It only failed if the command is clamdscan. if I use clamscan, temp directory is fine
any idea?

You should really just use one of the many clamd clients out there instead of relying on exec'ing a command and parsing its output, that's super fragile and will bring you nothing but headache in the future. For example:
http://torquecp.sourceforge.net/phpclamcli.html
If you are the do-it-yourself type, the clamd wire protocol is super simple (http://linux.die.net/man/8/clamd) and you could potentially write up a simple client in a couple of hours. Again, the benefit here is that it's a well defined protocol and has some nice features like a streaming call that allows you to operate the clamd service and your webapp with completely difference security credentials (heck they can even run on different boxes).
I hope this helps.

Just a quick remark on using http://torquecp.sourceforge.net/phpclamcli.html as a supposedly better alternative to a DIY cli exec. The aforementioned php script does entirely rely on the syntax of the clamav response text as well.

Old question but I've just had the same issue. clamdscan runs as user clamav which doesn't have permission to files in /tmp. There is an additional parameter --fdpass which runs the command as the user running the script.
Using $command = 'clamdscan --fdpass' . escapeshellarg($_FILES['file']['tmp_name']); should run the command as the www user which will have access to the temporary file.

Related

Detect broken symlink in PHP on Windows 7 + Apache environment

I have a Windows 7 dev environment and I am using symlinks (not junctions) with php's symlink() function. Everything works fine until a target of a symlink gets deleted. When that happens all the PHP's file functions (file_exists(), is_file(), is_dir(), is_link(),...) return false although the symlink is still there.
This gets even more troubling if the broken symlink was originally targetting a directory. I can do file_put_contents() on the symlink path, which creates a file on the original target directory path. That's quite unfortunate and unpredictable.
So to my question: is there a way to detect a broken symlink in PHP on Windows 7 environment? I need whatever decent solution possible (an exec() or something like that). The production server is running standard LAMP configuration which works fine as expected.
I am using XAMPP with PHP 5.5.3.
A sample script:
$dir_path = __DIR__ . '/temporary_directory';
$link_path = __DIR__ . '/broken_symlink';
mkdir($dir_path);
symlink($dir_path, $link_path);
rmdir($dir_path);
echo 'file_exists(): ';
var_dump(file_exists($link_path));// false
echo "<br>\n";
echo 'is_file(): ';
var_dump(is_file($link_path));// false
echo "<br>\n";
echo 'is_dir(): ';
var_dump(is_dir($link_path));// false
echo "<br>\n";
echo 'is_link(): ';
var_dump(is_link($link_path));// false
echo "<br>\n";
echo 'readlink(): ';
var_dump(readlink($link_path));// false
echo "<br>\n";
// Now it is possible to create file:
file_put_contents($link_path, '');
// which creates a new file on the $dir_path. That makes the symlink somewhat hybrid as Windows Explorer thinks it is a directory symlink but it points to a file (so it's unable to resolve it).
I have come up with a solution. I am not completely happy about it but it should work.
function is_link_on_windows($path) {
$parent_dir_path = dirname($path);
if (false === file_exists($parent_dir_path)) return false;
$filename_regex = preg_quote(substr($path, strlen($parent_dir_path) + 1), '#');
// DOS' dir command is unlike PHP sensitive about the path format.
$parent_dir_path = realpath($parent_dir_path);
exec('dir ' . $parent_dir_path, $dir_output);
foreach ($dir_output as $line) {
// Symlink's entry should look like this:
// broken_symlink [C:\xampp\htdocs\symlink_test\temporary_directory]
// where the part within square brackets is the symlink's target.
if (preg_match('#\\s' . $filename_regex . '\\s\\[[^\\]]+\\]#', $line)) {
return true;
}
}
return false;
}

Get directory of current php file, which might have changed since the script started to run

I hope you can help. I need to find to way to get the current directory of the currently running PHP script.
The location of the script itself might have changed since the script was started.
I cannot use any magic constants such as __FILE__ or __DIR__ as a basis as they appear to be defined by the PHP processor at the beginning of execution and so continue to point to the original location of the script.
To test out ideas, I'm doing the following (from the command line on linux):
Have a file named test.php in directory test_1 i.e. test_1/test.php
Run this file on the command line with: php test_1/test.php
Open another terminal session and rename the directory with mv test_1 test_2
Keep an eye on the original terminal and hope you can find that it is reporting the new test_2 dir not test_1
The script used for this test was as follows:
<?php
while (true) {
echo "__FILE__: " . __FILE__ . "\n";
echo "__DIR__: " . __DIR__ . "\n";
echo "realpath(__FILE__): " . realpath(__FILE__) . "\n";
echo "realpath(__DIR__): " . realpath(__DIR__) . "\n";
echo '-----'. "\n";
sleep(5);
}
So, the question: How do you find out the current directory of the script running now (not where it was when the script started)?
The inode of a script will not change, even if it is moved to a different location in the same filesystem.
You could get the inode and pathname of the file at the beginning of the script, using the built-in function getmyinode:
<?php
$myinode = getmyinode();
$mypath = realpath(__FILE__);
?>
Then whenever you want to get the file path, call a function which compares the two and updates the path if necessary:
<?php
function getMyPath() {
global $myinode, $mypath;
if (fileinode($mypath) != $myinode) {
// File moved!
$searchfrom = '/'; // Maybe you can change this to $_SERVER['DOCUMENT_ROOT']
$mypath = shell_exec("find " . $searchfrom . " -inum " . $myinode . " -print -quit 2>/dev/null");
if (fileinode($mypath) != $myinode) {
// error; not found
}
}
return $mypath;
}
?>
It will be very slow to search for the inode if/when the file is moved but I don't see any way around that. Also note that if there are hardlinks then there could be multiple paths to the same file; the -print -quit above simply stops after the first one.
This will only work on Linux due to the use of inodes and the find shell command.
dirname(__FILE__);
Maybe this will be helpful?

Is there anyway to detect unix command "cat" finish merging files?

I was wondering, is there any way to detect cat function finish merging a file, so I can zip the files?
for example if I have simple case like this, folder with multiple files like this:
/var/www/testing/file1.iso0
/var/www/testing/file1.iso1
/var/www/testing/file1.iso2
/var/www/testing/file2.xls0
/var/www/testing/file2.xls1
basicly, using my web app, user will upload file1.iso and file2.xls, and using html5 I will slice each file and upload it in part which will result at the above structure. After all parts of each file finish uploading, I will use an ajax call to merge the file using cat unix command.
$command = '(files=(' . $list_file_copy . '); cat ' . '"${files[#]}" > ' . '"' . $file_name . '"' . '; rm ' . '"${files[#]}")';
something like this.
exec($cmd . " > /dev/null &");
My question is how can I know the file1.iso and file2.xls finish merging, so I can send another command to zip the file? as in file1.iso and file2.xls become filexxx.zip for example.
Please note that, the file could be multiple and each individual file can be huge (I will say 4GB at most, which is the reason why it will be slice 100MB to small part)
you could try and put a check for the exit code .......
when a command completes it does return a success/faliure indicator to the OS or calling prog....
in shell testing for the value of $? (would give you the exit code of the last command executed) , if thats a success it will return 0 as exit code - do the zip , else throw a warning or error as you like .
i do that in lot of my scripts.
hope this helps

Dynamically running ClamAV's clamscan on file uploads with PHP

Stack,
I want to scan each file that gets uploaded via my php upload script with clam anti-virus's clamscan tool. I think I've got a good script written but I wanted to run it past you guys.
So assuming that the file I'm sending to this php upload script is named "uploadedfile" does the following code make sense?
<?php
$safe_path = escapeshellarg('/tmp/' . $_FILES['uploadedfile']['tmp_name']);
$command = 'clamscan ' . $safe_path;
$out = '';
$int = -1;
exec($command, $out, $int);
if ($int == 0) {
// all good, code goes here uploads file as normal IE move to
permanent directory etc;
} else {
unlink('/tmp/' . $_FILES['uploadedfile']['tmp_name']);
header(Location: http://www.domain.com/uploadform.php?error=your-file-was-infected-pal);
}
?>
Also, will clamscan find php shells as well as traditional good old malware?
Thanks!
Update - found the answer
I answered my own question but don't have the reputation to officially do so. Here is the anser:
For those who come after. I've tested this script using the EICAR test virus file http://eicar.org/86-0-Intended-use.html and after a few tweaks it works. The return variable $int is what tells you whether or not the file is safe or not. If $int is 0, no virus was found, if $int is 1, a virus was found. However, there are some changes that I had to make the script work (I updated the $safe_path variable to be correct), here is the working script:
<?php
$safe_path = escapeshellarg($_FILES['uploadedfile']['tmp_name']);
$command = 'clamscan ' . $safe_path;
$out = '';
$int = -1;
exec($command, $out, $int);
if ($int == 0) {
// all good, code goes here uploads file as normal IE move to
permanent directory etc;
} else {
//whatever you need to do if a virus is found.
}
?>
Note that if your server runs a clamav daemon (clamd) it may be possible to use clamdscan instead of clamscan as proposed, this usage is faster since use virus signatures already loaded by clamd.
Just be careful. If your clamscan becomes outdated you'll get feedback in the output:
It will look like this:
LibClamAV Warning: ***********************************************************
LibClamAV Warning: *** This version of the ClamAV engine is outdated. ***
LibClamAV Warning: *** DON'T PANIC! Read http://www.clamav.net/support/faq ***
LibClamAV Warning: ***********************************************************
Also depending on the version of clamscan the "result" might look like this (and you'll need to parse it accordingly):
[filename]: OK
----------- SCAN SUMMARY -----------
Known viruses: x
Engine version: x.x.x
Scanned directories: 0
Scanned files: 1
Infected files: 0
Data scanned: x.xx MB
Data read: x.xx MB (ratio 0.00:1)
Time: x.xx sec (0 m x s)
I had a lot of trouble with permissions when trying to run this with clamdscan. I found a solution for the permissions problem here: https://wiki.archlinux.org/index.php/ClamAV
This changed this line:
$command = 'clamscan ' . $safe_path;
to:
$command = 'clamdscan --fdpass ' . $safe_path;
Seemed to successfully pass a good file and flag an EICAR file.

exec() not working as expected in PHP

I have a PHP script in which I need to execute a shell script 'add_article.sh' which reads a parameter file and adds the news article contents to a search index (Lemur/Indri).
Executing the 'add_article.sh' script on its own (from the shell) works perfectly, but running it from within the php script I get this:
$blah = exec("./add_article.sh", $out, $ret_val);
echo $out . "<BR />";
echo $ret_val . "<BR />";
This produces
Array
255
It is in the correct directory and fails even with an absolute path. I use exactly the same format in another function in the same file that executes another script in the same directory and all is fine.
Any ideas?
Have you checked the execute permissions of your shell script? Apache runs as a user with very few permissions in most operating systems, so that may be the cause.
$out is supposed to be an array. You should probably print_r() or var_dump() it to see what's coming back from the script; it may be telling you what's going wrong.
In general, there's probably some environmental dependency that isn't being satisfied when PHP is running the script. This is especially common if it's being run from inside Apache.
Try:
$blah = exec("./add_article.sh", $out, $ret_val);
print_r($out);
echo '<br />';
echo $ret_val . "<br />";

Categories