public function verify_file_contents() { $uzd = $this->importertransport->get('tempdir') . 'extract/'; $includedfiles = get_dir_contents($uzd); $okfiles = array(); $badfiles = array(); // check what arrived in the directory foreach ($includedfiles as $k => $f) { // @todo penny later we might need this if (is_dir($f)) { $badfiles[] = $f; unset($includedfiles[$k]); continue; } if (get_config('viruschecking')) { $pathtoclam = escapeshellcmd(trim(get_config('pathtoclam'))); if ($pathtoclam && file_exists($pathtoclam) && is_executable($pathtoclam)) { if ($errormsg = mahara_clam_scan_file($uzd . $f)) { throw new ImportException($this, $errormsg); } } else { clam_mail_admins(get_string('clamlost', 'mahara', $pathtoclam)); } } $sha1 = sha1_file($uzd . $f); if (array_key_exists($sha1, $this->manifest)) { $tmp = new StdClass(); $tmp->sha1 = $sha1; $tmp->wantsfilename = $this->manifest[$sha1]['filename']; $tmp->actualfilename = $f; $okfiles[] = $tmp; unset($includedfiles[$k]); continue; } $badfiles[] = $f; unset($includedfiles[$k]); } $ok_c = count($okfiles); $bad_c = count($badfiles); $man_c = count($this->manifest); if ($ok_c != $man_c) { throw new ImportException($this, 'Files receieved did not exactly match what was in the manifest'); // @todo penny later - better reporting (missing files, too many files, etc) } $this->files = $okfiles; }
/** * If $CFG->runclamonupload is set, we scan a given file. (called from {@link preprocess_files()}) * * This function will add on a uploadlog index in $file. * @param mixed $file The file to scan from $files. or an absolute path to a file. * @param course $course {@link $COURSE} * @return int 1 if good, 0 if something goes wrong (opposite from actual error code from clam) */ function clam_scan_moodle_file(&$file, $course) { global $CFG, $USER; if (is_array($file) && is_uploaded_file($file['tmp_name'])) { // it's from $_FILES $appendlog = true; $fullpath = $file['tmp_name']; } else { if (repository_file_exists($file)) { // it's a path to somewhere on the filesystem! $fullpath = $file; } else { return false; // erm, what is this supposed to be then, huh? } } $CFG->pathtoclam = trim($CFG->pathtoclam); # HU, CS Repository plugin support if (!$CFG->pathtoclam || !repository_file_exists($CFG->pathtoclam) || !is_executable($CFG->pathtoclam)) { $newreturn = 1; $notice = get_string('clamlost', 'moodle', $CFG->pathtoclam); if ($CFG->clamfailureonupload == 'actlikevirus') { $notice .= "\n" . get_string('clamlostandactinglikevirus'); $notice .= "\n" . clam_handle_infected_file($fullpath); $newreturn = false; } clam_mail_admins($notice); if ($appendlog) { $file['uploadlog'] .= "\n" . get_string('clambroken'); $file['clam'] = 1; } return $newreturn; // return 1 if we're allowing clam failures } $cmd = $CFG->pathtoclam . ' ' . $fullpath . " 2>&1"; // before we do anything we need to change perms so that clamscan can read the file (clamdscan won't work otherwise) chmod($fullpath, 0644); exec($cmd, $output, $return); switch ($return) { case 0: // glee! we're ok. return 1; // translate clam return code into reasonable return code consistent with everything else. // translate clam return code into reasonable return code consistent with everything else. case 1: // bad wicked evil, we have a virus. if (!empty($course)) { $info->course = $course->fullname; } else { $info->course = 'No course'; } $info->user = fullname($USER); $notice = get_string('virusfound', 'moodle', $info); $notice .= "\n\n" . implode("\n", $output); $notice .= "\n\n" . clam_handle_infected_file($fullpath); clam_mail_admins($notice); if ($appendlog) { $info->filename = $file['originalname']; $file['uploadlog'] .= "\n" . get_string('virusfounduser', 'moodle', $info); $file['virus'] = 1; } return false; // in this case, 0 means bad. // in this case, 0 means bad. default: // error - clam failed to run or something went wrong $notice .= get_string('clamfailed', 'moodle', get_clam_error_code($return)); $notice .= "\n\n" . implode("\n", $output); $newreturn = true; if ($CFG->clamfailureonupload == 'actlikevirus') { $notice .= "\n" . clam_handle_infected_file($fullpath); $newreturn = false; } clam_mail_admins($notice); if ($appendlog) { $file['uploadlog'] .= "\n" . get_string('clambroken'); $file['clam'] = 1; } return $newreturn; // return 1 if we're allowing failures. } }
/** * If $CFG->runclamonupload is set, we scan a given file. (called from {@link preprocess_files()}) * * This function will add on a uploadlog index in $file. * @param mixed $file The file to scan from $files. or an absolute path to a file. * @return int 1 if good, 0 if something goes wrong (opposite from actual error code from clam) */ function clam_scan_file(&$file) { global $CFG, $USER; if (is_array($file) && is_uploaded_file($file['tmp_name'])) { // it's from $_FILES $appendlog = true; $fullpath = $file['tmp_name']; } else { if (file_exists($file)) { // it's a path to somewhere on the filesystem! $fullpath = $file; } else { return false; // erm, what is this supposed to be then, huh? } } $CFG->pathtoclam = trim($CFG->pathtoclam); if (!$CFG->pathtoclam || !file_exists($CFG->pathtoclam) || !is_executable($CFG->pathtoclam)) { $newreturn = 1; $notice = sprintf(__gettext('Yupana is configured to run clam on file upload, but the path supplied to Clam AV, %s, is invalid.'), $CFG->pathtoclam); if ($CFG->clamfailureonupload == 'actlikevirus') { $notice .= "\n" . __gettext('In addition, Yupana is configured so that if clam fails to run, files are treated like viruses. This essentially means that no student can upload a file successfully until you fix this.'); $notice .= "\n" . clam_handle_infected_file($fullpath); $newreturn = false; } clam_mail_admins($notice); if ($appendlog) { $file['uploadlog'] .= "\n" . __gettext('Your administrator has enabled virus checking for file uploads but has misconfigured something.<br />Your file upload was NOT successful. Your administrator has been emailed to notify them so they can fix it.<br />Maybe try uploading this file later.'); $file['clam'] = 1; } return $newreturn; // return 1 if we're allowing clam failures } $cmd = $CFG->pathtoclam . ' ' . $fullpath . " 2>&1"; // before we do anything we need to change perms so that clamscan can read the file (clamdscan won't work otherwise) chmod($fullpath, 0644); exec($cmd, $output, $return); switch ($return) { case 0: // glee! we're ok. return 1; // translate clam return code into reasonable return code consistent with everything else. // translate clam return code into reasonable return code consistent with everything else. case 1: // bad wicked evil, we have a virus. $info->user = $USER->name; $notice = sprintf(__gettext('Attention administrator! Clam AV has found a virus in a file uploaded by %s. Here is the output of clamscan:'), $info->user); $notice .= "\n\n" . implode("\n", $output); $notice .= "\n\n" . clam_handle_infected_file($fullpath); clam_mail_admins($notice); if ($appendlog) { $info->filename = $file['originalname']; $file['uploadlog'] .= "\n" . sprintf(__gettext('The file you have uploaded, %s, has been scanned by a virus checker and found to be infected! Your file upload was NOT successful.'), $info->filename); $file['virus'] = 1; } return false; // in this case, 0 means bad. // in this case, 0 means bad. default: // error - clam failed to run or something went wrong $notice .= sprintf(__gettext('Clam AV has failed to run. The return error message was %s. Here is the output from Clam:'), get_clam_error_code($return)); $notice .= "\n\n" . implode("\n", $output); $newreturn = true; if ($CFG->clamfailureonupload == 'actlikevirus') { $notice .= "\n" . clam_handle_infected_file($fullpath); $newreturn = false; } clam_mail_admins($notice); if ($appendlog) { $file['uploadlog'] .= "\n" . __gettext('Your administrator has enabled virus checking for file uploads but has misconfigured something.<br />Your file upload was NOT successful. Your administrator has been emailed to notify them so they can fix it.<br />Maybe try uploading this file later.'); $file['clam'] = 1; } return $newreturn; // return 1 if we're allowing failures. } }
/** * Gets file information out of $_FILES and stores it locally in $files. * Checks file against max upload file size. * Scans file for viruses. * @return false for no errors, or a string describing the error */ public function preprocess_file() { $name = $this->inputname; if (!isset($_FILES[$name])) { if ($this->optional) { $this->optionalandnotsupplied = true; return false; } else { return get_string('noinputnamesupplied'); } } $file = $_FILES[$name]; $maxsize = get_config('maxuploadsize'); if (isset($this->inputindex)) { $size = $file['size'][$this->inputindex]; $error = $file['error'][$this->inputindex]; $tmpname = $file['tmp_name'][$this->inputindex]; } else { $size = $file['size']; $error = $file['error']; $tmpname = $file['tmp_name']; } if ($maxsize && $size > $maxsize) { return get_string('uploadedfiletoobig'); } if ($error != UPLOAD_ERR_OK) { $errormsg = get_string('phpuploaderror', 'mahara', get_string('phpuploaderror_' . $error), $error); if ($error == UPLOAD_ERR_NO_TMP_DIR || $error == UPLOAD_ERR_CANT_WRITE) { // The admin probably needs to fix this; notify them // @TODO: Create a new activity type for general admin messages. $message = (object) array('users' => get_column('usr', 'id', 'admin', 1), 'subject' => get_string('adminphpuploaderror'), 'message' => $errormsg); require_once 'activity.php'; activity_occurred('maharamessage', $message); } else { if ($error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE) { return get_string('uploadedfiletoobig'); } } } if (!is_uploaded_file($tmpname)) { if ($this->optional) { $this->optionalandnotsupplied = true; return false; } else { return get_string('notphpuploadedfile'); } } if (get_config('viruschecking')) { $pathtoclam = escapeshellcmd(trim(get_config('pathtoclam'))); if ($pathtoclam && file_exists($pathtoclam) && is_executable($pathtoclam)) { if ($errormsg = mahara_clam_scan_file($file, $this->inputindex)) { return $errormsg; } } else { clam_mail_admins(get_string('clamlost', 'mahara', $pathtoclam)); } } $this->file = $file; return false; }
/** * Scan a file for viruses using clamav. * * @param mixed $file The file to scan from $files. or an absolute path to a file. * @return false if no errors, or a string if there's an error. */ function mahara_clam_scan_file($file) { if (is_array($file) && is_uploaded_file($file['tmp_name'])) { // it's from $_FILES $fullpath = $file['tmp_name']; } else { if (file_exists($file)) { $fullpath = $file; } else { throw new SystemException('mahara_clam_scan_file: not called correctly, read phpdoc for this function'); } } $pathtoclam = escapeshellcmd(trim(get_config('pathtoclam'))); if (!$pathtoclam || !file_exists($pathtoclam) || !is_executable($pathtoclam)) { clam_mail_admins(get_string('clamlost', 'mahara', $pathtoclam)); clam_handle_infected_file($fullpath); return get_string('clambroken'); } $cmd = $pathtoclam . ' ' . escapeshellarg($fullpath) . " 2>&1"; // before we do anything we need to change perms so that clamscan // can read the file (clamdscan won't work otherwise) chmod($fullpath, 0644); exec($cmd, $output, $return); switch ($return) { case 0: // glee! we're ok. return false; // no error // no error case 1: // bad wicked evil, we have a virus. global $USER; $userid = $USER->get('id'); clam_handle_infected_file($fullpath); // Notify admins if user has uploaded more than 3 infected // files in the last month if (count_records_sql(' SELECT COUNT(*) FROM {usr_infectedupload} WHERE usr = ? AND time > ?', array($userid, db_format_timestamp(time() - 60 * 60 * 24 * 30))) >= 2) { log_debug('sending virusrepeat notification'); $data = (object) array('username' => $USER->get('username'), 'userid' => $userid, 'fullname' => full_name()); require_once 'activity.php'; activity_occurred('virusrepeat', $data); } $data = (object) array('usr' => $userid, 'time' => db_format_timestamp(time())); insert_record('usr_infectedupload', $data, 'id'); return get_string('virusfounduser', 'mahara', display_name($USER)); default: // error - clam failed to run or something went wrong $notice = get_string('clamfailed', 'mahara', get_clam_error_code($return)); $notice .= "\n\n" . implode("\n", $output); $notice .= "\n" . clam_handle_infected_file($fullpath); clam_mail_admins($notice); return get_string('clambroken'); } }