From 8f66c71974d9c070da3b2990bdba18e1fce67c17 Mon Sep 17 00:00:00 2001 From: mullenm Date: Mon, 25 Feb 2008 17:08:37 +0000 Subject: [PATCH] Created a virus scanning procedure. Download file to server, perform AV check, re-import whatever's left. If the file isn't there, delete it from MIP as it may be malicious. git-svn-id: http://locode01.ad.dom/svn/WEBMIP/trunk@3696 248e525c-4dfb-0310-94bc-949c084e9493 --- Modules/mip_virus_check.pck | 314 ++++++++++++++++++++++++++---------- 1 file changed, 232 insertions(+), 82 deletions(-) diff --git a/Modules/mip_virus_check.pck b/Modules/mip_virus_check.pck index 8c9f432..ddb6fe2 100644 --- a/Modules/mip_virus_check.pck +++ b/Modules/mip_virus_check.pck @@ -4,29 +4,52 @@ CREATE OR REPLACE PACKAGE mip_virus_check IS -- Created : 23/01/2008 09:55:52 -- Purpose : Virus checking package -- Updates : MULLENMD - added functionality to run the virus checker for a file - -- - PROCEDURE write_file(p_name IN wwv_flow_files.NAME%TYPE - ,p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS' - ,p_fs_name IN VARCHAR2); - + -- added load file (to replace BLOB) + PROCEDURE write_file(p_name IN wwv_flow_files.NAME%TYPE, + p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS', + p_fs_name IN VARCHAR2); + /** - check_file - allows MIP to run an anti-virus scan on a given file - - %param p_name - the APEX name of the file - %param p_location - the directory on the server where the file will be tested - %param p_fs_name - the actual filename of the file to test - */ - PROCEDURE check_file(p_name IN wwv_flow_files.NAME%TYPE - ,p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS' - ,p_fs_name IN VARCHAR2); + check_file - allows MIP to run an anti-virus scan on a given file + + %param p_name - the APEX name of the file + %param p_location - the directory on the server where the file will be tested + */ + PROCEDURE check_file(p_name IN wwv_flow_files.NAME%TYPE, + p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS'); -- + + /** + get_fs_name - returns the file name from teh APEX doc's URI + + %param p_name - the APEX doc URI (in format unique ID/filename) + %return VARCHAR2 - the actual name of the file + */ + FUNCTION get_fs_name(p_name IN documents.uri%TYPE) RETURN VARCHAR2; + -- + PROCEDURE pl(p_in VARCHAR2); END mip_virus_check; / CREATE OR REPLACE PACKAGE BODY mip_virus_check IS + /** + Updates: + MM 25-Feb-2008 added the load file + */ + PROCEDURE pl(p_in VARCHAR2) IS + l_fh utl_file.file_type; + BEGIN + dbms_application_info.set_module('mip_virus_check', p_in); + l_fh := utl_file.fopen(location => 'WEBMIP_VIRUS', + filename => 'WEBMIP_VIRUS.txt', + open_mode => 'A'); + utl_file.put_line(l_fh, + to_char(SYSDATE, 'DD/MM/YYYY HH24:MI:SS') || ',' || p_in); + utl_file.fclose(l_fh); + END pl; - PROCEDURE write_file(p_name IN wwv_flow_files.NAME%TYPE - ,p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS' - ,p_fs_name IN VARCHAR2) IS + PROCEDURE write_file(p_name IN wwv_flow_files.NAME%TYPE, + p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS', + p_fs_name IN VARCHAR2) IS l_lob_loc BLOB; l_buffer RAW(32767); l_buffer_size BINARY_INTEGER; @@ -36,12 +59,12 @@ CREATE OR REPLACE PACKAGE BODY mip_virus_check IS l_out_file utl_file.file_type; BEGIN - + pl('Writing file ' || p_name || ', ' || p_fs_name); SELECT blob_content INTO l_lob_loc FROM wwv_flow_files - WHERE NAME = p_name; - + WHERE upper(NAME) = upper(p_name); + pl('Got file from APEX. Downloading'); l_chunksize := dbms_lob.getchunksize(l_lob_loc); IF (l_chunksize < 32767) THEN @@ -52,28 +75,27 @@ CREATE OR REPLACE PACKAGE BODY mip_virus_check IS l_amount := l_buffer_size; - dbms_lob.OPEN(l_lob_loc - ,dbms_lob.lob_readonly); + dbms_lob.OPEN(l_lob_loc, dbms_lob.lob_readonly); - l_out_file := utl_file.fopen(location => p_location - ,filename => p_fs_name - ,open_mode => 'wb' - ,max_linesize => 32767); + l_out_file := utl_file.fopen(location => p_location, + filename => p_fs_name, + open_mode => 'wb', + max_linesize => 32767); WHILE l_amount >= l_buffer_size LOOP - dbms_lob.READ(lob_loc => l_lob_loc - ,amount => l_amount - ,offset => l_offset - ,buffer => l_buffer); + dbms_lob.READ(lob_loc => l_lob_loc, + amount => l_amount, + offset => l_offset, + buffer => l_buffer); l_offset := l_offset + l_amount; - utl_file.put_raw(file => l_out_file - ,buffer => l_buffer - ,autoflush => TRUE); + utl_file.put_raw(file => l_out_file, + buffer => l_buffer, + autoflush => TRUE); - --utl_file.fflush(file => l_out_file); + --utl_file.fflush(file => l_out_file); END LOOP; @@ -83,90 +105,218 @@ CREATE OR REPLACE PACKAGE BODY mip_virus_check IS dbms_lob.CLOSE(l_lob_loc); END write_file; - -- - FUNCTION trigger_av_scan(p_location IN VARCHAR2 - ,p_fs_name IN VARCHAR2) RETURN BOOLEAN IS + + /** + load_file - procedure to reload the resultant file after virus checking is complete. + + %param p_uri - the file's unique URI as stored in the Apex view + */ + PROCEDURE load_file(p_uri wwv_flow_files.NAME%TYPE) IS + l_directory_path all_directories.directory_path%TYPE; + l_source_file BFILE; + l_source_file_length BINARY_INTEGER; + l_blob BLOB; + l_filename VARCHAR2(250); BEGIN - NULL; + + pl('reload_after_av'); + dbms_lob.createtemporary(lob_loc => l_blob, cache => TRUE); + -- lock the table by selecting for update + SELECT f.blob_content + INTO l_blob + FROM wwv_flow_files f + + WHERE f.NAME = p_uri + FOR UPDATE; + + SELECT directory_path + INTO l_directory_path + FROM all_directories + WHERE directory_name = 'WEBMIP_VIRUS'; + + l_filename := REPLACE(p_uri, '/', '!'); + + l_source_file := bfilename('WEBMIP_VIRUS', l_filename); + l_source_file_length := dbms_lob.getlength(l_source_file); + + dbms_lob.OPEN(file_loc => l_source_file, + open_mode => dbms_lob.lob_readonly); + + dbms_lob.loadfromfile(dest_lob => l_blob, + src_lob => l_source_file, + amount => l_source_file_length); + + -- update the BLOB + UPDATE wwv_flow_files f + SET f.blob_content = l_blob + WHERE f.NAME = p_uri; + + --close the file + dbms_lob.fileclose(file_loc => l_source_file); + + pl('Loaded:' || l_filename); + END load_file; + -- + + /*\** + trigger_av_scan - triggers the anti-virus scan on the file + + %param p_location - the location of the file on the file server + %param p_fs_name - the name of the file to check + *\ + FUNCTION trigger_av_scan(p_fs_name IN VARCHAR2) RETURN BOOLEAN IS + av_cmd VARCHAR2(250); + av_checker VARCHAR2(250); + l_location VARCHAR2(250); + BEGIN + dbms_output.put_line('Checking file.'); + av_checker := cout_system_configuration.get_configuration_item('AV_LOCATION'); + l_location := cout_system_configuration.get_configuration_item('AV_DIRECTORY'); + + av_cmd := '"' || av_checker ||'" /automation '||l_location||'\'||p_fs_name; + + dbms_output.put_line('about to check'); + dbms_output.put_line(av_cmd); + + mip_systemcall.syscall(p_cmd_str => av_cmd); + + dbms_output.put_line('Finished checking'); + + RETURN TRUE; EXCEPTION WHEN OTHERS THEN + dbms_output.put_line(SQLERRM); RETURN NULL; END trigger_av_scan; - -- + --*/ - -- - FUNCTION check_file_exists(p_location IN VARCHAR2 - ,p_fs_name IN VARCHAR2) RETURN BOOLEAN IS + /** + check_file_exists - tests if the file is still on the server + if the file is no longer there then it has + failed the virus scan and been quarantined + + %param p_location - the location of the file on the file server + %param p_fs_name - the name of the file to check + */ + FUNCTION check_file_exists(p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS', + p_fs_name IN VARCHAR2) RETURN BOOLEAN IS l_file_exists BOOLEAN; l_file_length NUMBER; l_block_size NUMBER; BEGIN - utl_file.fgetattr(location => p_location - ,filename => p_fs_name - ,fexists => l_file_exists - ,file_length => l_file_length - ,block_size => l_block_size); - + utl_file.fgetattr(location => p_location, + filename => p_fs_name, + fexists => l_file_exists, + file_length => l_file_length, + block_size => l_block_size); + pl('File exists:' || sys.diutil.bool_to_int(l_file_exists)); RETURN l_file_exists; EXCEPTION - WHEN OTHERS THEN + WHEN OTHERS THEN RETURN FALSE; END check_file_exists; -- - - -- - PROCEDURE delete_file_from_server(p_location IN VARCHAR2 - ,p_fs_name IN VARCHAR2) IS + + /** + delete_file_from_server - removes the files from the server once AV + scan is complete. Housekeeping. + + %param p_location - the location of the file on the file server + %param p_fs_name - the name of the file to check + */ + PROCEDURE delete_file_from_server(p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS', + p_fs_name IN VARCHAR2) IS BEGIN - utl_file.fremove(location => p_location - ,filename => p_fs_name); + utl_file.fremove(location => p_location, filename => p_fs_name); END delete_file_from_server; -- - - -- - PROCEDURE delete_file_from_MIP(p_name IN wwv_flow_files.NAME%TYPE) IS + + /** + delete_file_from_MIP - removes an infected file from the MIP system + + %param p_location - the location of the file on the file server + %param p_fs_name - the name of the file to check + */ + PROCEDURE delete_file_from_mip(p_doc_id documents.id%TYPE) IS + l_result BOOLEAN; BEGIN - NULL; - END delete_file_from_MIP; - -- + l_result := mip_files.delete_file(p_id => p_doc_id, + p_doc_status => 'REMOVED MALICIOUS', + p_reason => 'Malicious content found - deleted by Anti-virus.'); + END delete_file_from_mip; -- + + /** + check_file - allows MIP to run an anti-virus scan on a given file + + %param p_name - the APEX name of the file + %param p_location - the directory on the server where the file will be tested + */ PROCEDURE check_file(p_name IN wwv_flow_files.NAME%TYPE, - p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS', - p_fs_name IN VARCHAR2) IS + p_location IN VARCHAR2 DEFAULT 'WEBMIP_VIRUS') IS av_scan_result BOOLEAN; file_result BOOLEAN; + l_fs_name VARCHAR2(250); + l_doc_id documents.id%TYPE; BEGIN + -- need to remove the slash from the URI so we can store multiple files + -- change it to a "!" for now. + l_fs_name := REPLACE(p_name, '/', '!'); + + SELECT doc.id INTO l_doc_id FROM documents doc WHERE doc.uri = p_name; + -- write the file to the file system, trigger a scan, check if the file is still there -- and then act accordingly if it isn't. write_file(p_name => p_name, p_location => p_location, - p_fs_name => p_fs_name); + p_fs_name => l_fs_name); - av_scan_result := trigger_av_scan(p_location => p_location, - p_fs_name => p_fs_name); + --dbms_lock.sleep(5); - IF av_scan_result THEN - file_result := check_file_exists(p_location => p_location, - p_fs_name => p_fs_name); - - IF NOT file_result THEN - delete_file_from_mip(p_name => p_name); - ELSE - delete_file_from_server(p_location => p_location, - p_fs_name => p_fs_name); - END IF; + pl('Checking file exists:' || l_fs_name); + file_result := check_file_exists(p_fs_name => l_fs_name); + + IF NOT file_result THEN + delete_file_from_mip(p_doc_id => l_doc_id); + pl('File removed from MIP'); ELSE - raise_application_error(-20099, - 'Error whilst running anti-virus on file ' || - p_fs_name); + -- reload the leftovers into MIP + load_file(p_uri => p_name); + + pl('Delete file from server'); + delete_file_from_server(p_location => p_location, + p_fs_name => l_fs_name); + + mip_documents.set_doc_available(p_docu_id => l_doc_id, + p_description => 'Virus checked by MIP'); + END IF; + END check_file; -- + + /** + get_fs_name - returns the file name from the APEX doc's URI -BEGIN - -- Initialization - NULL; + %param p_name - the APEX doc URI (in format unique ID/filename) + %return VARCHAR2 - the actual name of the file + */ + FUNCTION get_fs_name(p_name IN documents.uri%TYPE) RETURN VARCHAR2 IS + ret_fs_name VARCHAR2(250); + seperator_pos NUMBER; + BEGIN + seperator_pos := instr(p_name, '/', -1); + + IF NOT seperator_pos = 0 THEN + ret_fs_name := substr(p_name, seperator_pos + 1); + + RETURN ret_fs_name; + ELSE + RETURN NULL; + END IF; + END get_fs_name; + -- END mip_virus_check; /