def event_history(self, columns, *, indent=4, sieve=None, repres=None): self._events_to_show = _filter_events(self._all_events, sieve) if not self._events_to_show: print_info('No USB events found!') return if not cfg.QUIET and cfg.ISATTY: choice, abs_filename = _output_choice('event history', 'history.json', 'history/') if choice == '2': try: _dump_events(self._events_to_show, 'event history', abs_filename, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return # elif choice == '1' or choice == '': if columns: table_data = [[COLUMN_NAMES[name] for name in columns]] else: columns = [key for key in COLUMN_NAMES.keys()] table_data = [[val for val in COLUMN_NAMES.values()]] _represent_events(self._events_to_show, columns, table_data, 'USB-History-Events', repres)
def _get_latest_version(): connected, errcode, e = _check_connection('www.google.com') if not connected: return (None, -1, -1, errcode, e) print_info('Getting latest version and date') try: html = urlopen('http://www.linux-usb.org/usb.ids', timeout=10).read() #from requests import get #resp = get('http://www.linux-usb.org/usb.ids', timeout=10) except socket.timeout as e: #except requests.exceptions.Timeout as e: return (None, -1, -1, USBIDs._SERVER_TIMEOUT_ERROR, str(e)) db = html.decode('cp1252') #soup = BeautifulSoup(resp.text, 'html.parser') #db = soup.text try: latest_ver = re.search(r'^# Version:\s*(.*?$)', db, re.MULTILINE).group(1) latest_date = re.search(r'^# Date:\s*(.*?$)', db, re.MULTILINE).group(1) except AttributeError as e: return (None, -1, -1, USBIDs._SERVER_CONTENT_ERROR, str(e)) return (db, latest_ver, latest_date, 0, '')
def get_config_parser(): config_parser = ConfigParser(allow_no_value=True) config_parser.optionxform = str os.makedirs(CONFIG_FILE.rsplit('/', 1)[0], exist_ok=True) if os.path.isfile(CONFIG_FILE): config_parser.read(CONFIG_FILE, encoding='utf-8') print_info(f'Configuration loaded: "{CONFIG_FILE}"') else: print_warning('No configuration file found, creating new one...') config_parser.add_section('history') config_parser.set('history', 'password', 'r1pp3r') config_parser.add_section('violations') config_parser.set('violations', 'password', 'r1pp3r') print_info(f'New configuration file: "{CONFIG_FILE}"') with open(CONFIG_FILE, 'w', encoding='utf-8') as f: config_parser.write(f) return config_parser
def _dump_events(events_to_show, list_name, abs_filename, indent): print_info(f'Generating {list_name} list (JSON)') out = [] for event in events_to_show: tmp_event_dict = OrderedDict() for key in ('conn', 'host', 'vid', 'pid', 'prod', 'manufact', 'serial', 'port', 'disconn'): tmp_event_dict[key] = event[key] out.append(tmp_event_dict) try: with open(abs_filename, 'w', encoding='utf-8') as out_json: json.dump(out, out_json, indent=indent) except PermissionError as e: raise USBRipError( f'Permission denied: "{abs_filename}". Retry with sudo', errors={'initial_error': str(e)} ) os.chmod(abs_filename, stat.S_IRUSR | stat.S_IWUSR) # 600 print_info(f'New {list_name} list: "{abs_filename}"')
def _get_filtered_history(): filtered_history = [] print_info( 'Searching for log files: C:\\Users\\eyver-dev\\Documents\\python\\usbrip\\test\\syslog* or C:\\Users\\eyver-dev\\Documents\\python\\usbrip\\test\\messages*' ) syslog_files = sorted([ filename for filename in list_files( 'C:\\Users\\eyver-dev\\Documents\\python\\usbrip\\test') if filename.rsplit('\\', 1)[1].startswith('syslog') ]) if syslog_files: for syslog in syslog_files: filtered_history.extend(_read_log_file(syslog)) else: messages_files = sorted([ filename for filename in list_files('/var/log/') if filename.rsplit('/', 1)[1].startswith('messages') ]) if messages_files: for messages in messages_files: filtered_history.extend(_read_log_file(messages)) else: raise USBRipError('None of log file types was found!') return filtered_history
def _read_log_file(filename): filtered = DefaultOrderedDict(default_factory=list) if filename.endswith('.gz'): print_info(f'Unpacking "{filename}"') try: log = gzip.open(filename, 'rb') except PermissionError as e: print_warning(f'Permission denied: "{filename}". Retry with sudo', initial_error=str(e)) return filtered else: end_of_file = b'' filename = filename[:-3] else: log = codecs.open(filename, 'r', encoding='utf-8', errors='ignore') end_of_file = '' print_info(f'Reading "{filename}"') regex = re.compile(r'usb') for line in iter(log.readline, end_of_file): if isinstance(line, bytes): line = line.decode(encoding='utf-8', errors='ignore') if regex.search(line): filtered[line[:15]].append(line) # line[:15] == 'Mon dd hh:mm:ss' log.close() return filtered
def _get_raw_history(): raw_history = DefaultOrderedDict(default_factory=list) print_info( 'Searching for log files: "/var/log/syslog*" or "/var/log/messages*"') syslog_files = sorted([ filename for filename in list_files('/var/log/') if filename.rsplit('/', 1)[1].startswith('syslog') ]) if syslog_files: for syslog in syslog_files: raw_history.update(_read_log_file(syslog)) else: messages_files = sorted([ filename for filename in list_files('/var/log/') if filename.rsplit('/', 1)[1].startswith('messages') ]) if messages_files: for messages in messages_files: raw_history.update(_read_log_file(messages)) else: raise USBRipError('None of log file types was found!') return raw_history
def open_dump(input_dump, columns, *, sieve=None, repres=None): abs_input_dump = os.path.abspath(input_dump) print_info(f'Opening USB event dump: "{abs_input_dump}"') try: with open(abs_input_dump, 'r', encoding='utf-8') as dump: events_dumped = json.load(dump) except json.decoder.JSONDecodeError as e: print_critical('Failed to decode event dump (JSON)', initial_error=str(e)) return if not events_dumped: print_critical('This dump is empty!') return events_to_show = _filter_events(events_dumped, sieve) if not events_to_show: print_info('No USB events found!') return if columns: table_data = [[COLUMN_NAMES[name] for name in columns]] else: columns = [key for key in COLUMN_NAMES.keys()] table_data = [[val for val in COLUMN_NAMES.values()]] _represent_events(events_to_show, columns, table_data, 'USB-Event-Dump', repres)
def _get_filtered_history(): filtered_history = [] print_info( 'Searching for log files: "/var/log/syslog*" or "/var/log/messages*"') syslog_files = sorted([ filename for filename in list_files('/var/log/') if filename.rsplit('/', 1)[1].startswith('syslog') ]) if syslog_files: for syslog in syslog_files: filtered_history.extend(_read_log_file(syslog)) else: messages_files = sorted([ filename for filename in list_files('/var/log/') if filename.rsplit('/', 1)[1].startswith('messages') ]) if messages_files: for messages in messages_files: filtered_history.extend(_read_log_file(messages)) else: raise USBRipError('None of log file types was found!') return filtered_history
def _read_log_file(filename): filtered = [] abs_filename = os.path.abspath(filename) if abs_filename.endswith('.gz'): print_info(f'Unpacking "{abs_filename}"') try: log = gzip.open(abs_filename, 'rb') except PermissionError as e: print_warning( f'Permission denied: "{abs_filename}". Retry with sudo', initial_error=str(e)) return filtered else: end_of_file = b'' abs_filename = os.path.splitext(abs_filename) else: log = codecs.open(abs_filename, 'r', encoding='utf-8', errors='ignore') end_of_file = '' print_info(f'Reading "{abs_filename}"') regex = re.compile(r'] usb (.*?): ') for line in iter(log.readline, end_of_file): if isinstance(line, bytes): line = line.decode(encoding='utf-8', errors='ignore') if regex.search(line): date = line[:32] if date.count(':') > 2: date = ''.join( line[:32].rsplit(':', 1) ) # rreplace(':', 1) to remove the last ':' from "2019-08-09T06:15:49.655261-04:00" timestamp if there is one try: date = datetime.strptime( date, '%Y-%m-%dT%H:%M:%S.%f%z' ) # ex. 2019-08-09T06:15:49.655261-0400 except ValueError as e: raise USBRipError( f'Wrong timestamp format found in "{abs_filename}"', errors={'initial_error': str(e)}) else: date = date.strftime('%Y-%m-%d %H:%M:%S') logline = line[32:].strip() if any(pat in line for pat in ('New USB device found, ', 'Product: ', 'Manufacturer: ', 'SerialNumber: ')): filtered.append((date, 'c', logline)) elif 'disconnect' in line: filtered.append((date, 'd', logline)) log.close() return filtered
def change_password(storage_type, *, compression_level='5'): storage_full_path = f'{USBStorage._STORAGE_BASE}/{storage_type}.7z' if not os.path.isfile(storage_full_path): print_critical(f'Storage not found: "{storage_full_path}"') return old_password = getpass('Old password: '******'New password: '******'Confirm new password: '******'Passwords does not match, try again') return try: out = _7zip_unpack(storage_full_path, old_password) if 'Everything is Ok' in out: base_filename = re.search( r'([^\n ]*\.json)', _7zip_list(storage_full_path, old_password), re.MULTILINE).group(1) json_file = f'{USBStorage._STORAGE_BASE}/{base_filename}' os.remove(storage_full_path) out = _7zip_pack(storage_full_path, json_file, new_password, compression_level) if 'Everything is Ok' in out: print_info('Password was successfully changed') conf_parser = ConfigParser(allow_no_value=True) conf_parser.optionxform = str conf_parser.read(CONFIG_FILE, encoding='utf-8') conf_parser.set(storage_type, 'password', new_password) with open(CONFIG_FILE, 'w', encoding='utf-8') as f: conf_parser.write(f) print_info('Configuration file updated') else: print_critical( 'Undefined behaviour while creating storage', initial_error=out) os.remove(json_file) else: print_critical('Undefined behaviour while unpacking storage', initial_error=out) except USBRipError as e: print_critical(str(e), errcode=e.errors['errcode'], initial_error=e.errors['initial_error']) return
def create_storage(storage_type, password, *, input_auth=None, attributes=None, compression_level='5', indent=4, sieve=None): if storage_type == 'history': events_to_show = _get_history_events(sieve) elif storage_type == 'violations': try: events_to_show = _get_violation_events(sieve, input_auth, attributes, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 if events_to_show is None: return 1 if events_to_show: min_date, max_date = _get_dates(events_to_show) json_file = f'{USBStorage._STORAGE_BASE}/{min_date}-{max_date}.json' else: json_file = f'{USBStorage._STORAGE_BASE}/{datetime.now().strftime("%Y%m%dT%H%M%S")}.json' try: _dump_events(events_to_show, storage_type, json_file, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 storage_full_path = f'{USBStorage._STORAGE_BASE}/{storage_type}.7z' if os.path.exists(storage_full_path): os.remove(storage_full_path) try: out = _7zip_pack(storage_full_path, json_file, password, compression_level) except USBRipError as e: os.remove(json_file) print_critical(str(e), errcode=e.errors['errcode'], initial_error=e.errors['initial_error']) return 1 if 'Everything is Ok' in out: print_info(f'New {storage_type} storage: "{storage_full_path}"') print_secret('Your password is', secret=password) os.remove(json_file) else: print_critical('Undefined behaviour while creating storage', initial_error=out)
def _output_choice(list_name, default_filename, dirname): while True: print( f'[?] How would you like your {list_name} list to be generated?\n') print(' 1. JSON-file') print(' 2. Terminal stdout') number = input('\n[>] Please enter the number of your choice: ') if number == '1': while True: filename = input( f'[>] Please enter the base name for the output file ' f'(default is "{default_filename}"): ') if all(c in printable for c in filename) and len(filename) < 256: if not filename: filename = default_filename elif filename[-5:] != '.json': filename = filename + '.json' filename = root_dir_join(dirname + filename) try: dirname = os.path.dirname(filename) os_makedirs(dirname) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return (None, '') else: print_info(f'Created "{dirname}"') overwrite = True if os.path.isfile(filename): while True: overwrite = input( '[?] File exists. Would you like to overwrite it? [Y/n]: ' ) if len(overwrite) == 1 and overwrite in 'Yy': overwrite = True break elif len(overwrite) == 1 and overwrite in 'Nn': overwrite = False break if overwrite: return (int(number), filename) elif number == '2': return (int(number), '')
def __new__(cls, files=None): try: if files: filtered_history = [] for file in files: filtered_history.extend(_read_log_file(file)) else: print_info('Trying to run journalctl...') # child_env = os.environ.copy() # child_env['LANG'] = 'en_US.utf-8' # journalctl_out = check_output(['journalctl'], env=child_env).decode('utf-8') try: journalctl_out = check_output([ 'journalctl', '-o', 'short-iso-precise' ]).decode('utf-8') except Exception as e: print_warning(f'Failed to run journalctl: {str(e)}') filtered_history = _get_filtered_history() else: if '-- Logs begin at' in journalctl_out: print_info('Successfully ran journalctl') filtered_history = _read_log_file( None, log=StringIO(journalctl_out), total=journalctl_out.count('\n') ) else: print_warning(f'An error occurred when running journalctl: {journalctl_out}') filtered_history = _get_filtered_history() except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return None all_events = _parse_history(filtered_history) instance = super().__new__(cls) instance._all_events = all_events # self._all_events instance._violations = [] # self._violations instance._events_to_show = None # self._events_to_show return instance
def _7zip_list(archive, password): print_info(f'Listing archive: "{archive}"') cmd = ['7z', 'l', archive, '-p' + password] out, errcode, errmsg, e = _7zip_subprocess_handler(cmd) if errcode: raise USBRipError(errmsg, errors={ 'errcode': errcode, 'initial_error': e }) return out
def generate_auth_json(self, output_auth, attributes, *, indent=4, sieve=None): self._events_to_show = _filter_events(self._all_events, sieve) if not self._events_to_show: print_info('No USB devices found!') rand_id = f'usbrip-{randint(1000, 9999)}' self._events_to_show += [{ 'conn': rand_id, 'host': rand_id, 'vid': rand_id, 'pid': rand_id, 'prod': rand_id, 'manufact': rand_id, 'serial': rand_id, 'port': rand_id, 'disconn': rand_id }] abs_output_auth = os.path.abspath(output_auth) try: dirname = os.path.dirname(abs_output_auth) os_makedirs(dirname) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 else: print_info(f'Created directory "{dirname}/"') try: auth_json = open(abs_output_auth, 'w', encoding='utf-8') except PermissionError as e: print_critical(f'Permission denied: "{abs_output_auth}". Retry with sudo', initial_error=str(e)) return 1 print_info('Generating authorized device list (JSON)') if not attributes: attributes = ('vid', 'pid', 'prod', 'manufact', 'serial') auth = defaultdict(set) for event in tqdm(self._events_to_show, ncols=80, unit='dev'): for key, val in event.items(): if key in attributes and val is not None: auth[key].add(val) auth = {key: list(vals) for key, vals in auth.items()} for key in auth.keys(): auth[key].sort() json.dump(auth, auth_json, sort_keys=True, indent=indent) auth_json.close() os.chmod(abs_output_auth, stat.S_IRUSR | stat.S_IWUSR) # 600 print_info(f'New authorized device list: "{abs_output_auth}"')
def _7zip_pack(archive, file, password, compression_level): print_info(f'Creating storage (7-Zip): "{archive}"') cmd = [ '7z', 'a', archive, file, '-mhe=on', '-p' + password, '-mx=' + compression_level ] out, errcode, errmsg, e = _7zip_subprocess_handler(cmd) if errcode: raise USBRipError(errmsg, errors={ 'errcode': errcode, 'initial_error': e }) return out
def _7zip_unpack(archive, password): print_info(f'Unpacking archive: "{archive}"') cmd = [ '7z', 'e', archive, '-p' + password, '-o' + USBStorage._STORAGE_BASE, '-y' ] out, errcode, errmsg, e = _7zip_subprocess_handler(cmd) if errcode: raise USBRipError(errmsg, errors={'errcode': errcode, 'initial_error': e}) return out
def _download_database(filename): try: dirname = os.path.dirname(filename) os_makedirs(dirname) except USBRipError as e: raise USBRipError(str(e), errors={'initial_error': e.errors['initial_error']}) else: print_info(f'Created directory "{dirname}/"') try: usb_ids = open(filename, 'w+', encoding='utf-8') except PermissionError as e: raise USBRipError(f'Permission denied: "{filename}"', errors={'initial_error': str(e)}) db, latest_ver, latest_date, errcode, e = _get_latest_version() if errcode: usb_ids.close() os.remove(filename) if errcode == USBIDs._INTERNET_CONNECTION_ERROR: errmsg = 'No internet connection' elif errcode == USBIDs._SERVER_TIMEOUT_ERROR: errmsg = 'Server timeout' elif errcode == USBIDs._SERVER_CONTENT_ERROR: errmsg = 'Server content error: no version or date found' raise USBRipError(errmsg, errors={ 'errcode': errcode, 'initial_error': e }) usb_ids.write(db) usb_ids.seek(0) print_info('Database downloaded') print(f'Version: {latest_ver}') print(f'Date: {latest_date}') return usb_ids
def change_password(storage_type, old_password, new_password, *, compression_level='5'): storage_full_path = '{}/{}.7z'.format(USBStorage._STORAGE_BASE, storage_type) if not os.path.isfile(storage_full_path): print_critical( 'Storage not found: \'{}\''.format(storage_full_path)) return try: out = _7zip_unpack(storage_full_path, old_password) if 'Everything is Ok' in out: os.remove(storage_full_path) base_filename = re.search(r'Extracting\s*(.*?$)', out, re.MULTILINE).group(1) json_file = '{}/{}'.format(USBStorage._STORAGE_BASE, base_filename) out = _7zip_pack(storage_full_path, json_file, new_password, compression_level) if 'Everything is Ok' in out: print_info('Password was successfully changed') else: print_critical( 'Undefined behaviour while creating storage', initial_error=out) os.remove(json_file) else: print_critical('Undefined behaviour while unpacking storage', initial_error=out) except USBRipError as e: print_critical(str(e), errcode=e.errors['errcode'], initial_error=e.errors['initial_error']) return
def _dump_events(events_to_show, list_name, filename, indent): print_info('Generating {} list (JSON)'.format(list_name)) out = [] for event in events_to_show: tmp_event_dict = OrderedDict() for key in ('conn', 'user', 'vid', 'pid', 'prod', 'manufact', 'serial', 'port', 'disconn'): tmp_event_dict[key] = event[key] out.append(tmp_event_dict) try: with open(filename, 'w', encoding='utf-8') as out_json: json.dump(out, out_json, indent=indent) except PermissionError as e: raise USBRipError( 'Permission denied: \'{}\'. Retry with sudo'.format(filename), errors={'initial_error': str(e)} ) print_info('New {} list: \'{}\''.format(list_name, os.path.abspath(filename)))
def _filter_events(all_events, sieve): if sieve is None: sieve = {'external': False, 'number': -1, 'dates': [], 'fields': {}} if sieve != {'external': False, 'number': -1, 'dates': [], 'fields': {}}: print_info('Filtering events') events_to_show = all_events if sieve['fields']: events_to_show = [] for key, vals in sieve['fields'].items(): events_to_show += [ event for event in all_events for val in vals if event[key] == val ] if sieve['external']: events_to_show = [ event for event in all_events if event['disconn'] is not None ] if sieve['dates']: events_to_show = [ event for date in sieve['dates'] for event in events_to_show if event['conn'][:6] == date ] if not events_to_show: return [] SIZE = len(events_to_show) if sieve['number'] == -1 or sieve['number'] >= SIZE: if sieve['number'] > SIZE: print_warning( f'USB action history has only {SIZE} entries instead of requested {sieve["number"]}, ' f'displaying all of them...') sieve['number'] = SIZE return [events_to_show[SIZE - i] for i in range(sieve['number'], 0, -1)]
def search_violations(self, input_auth, attributes, columns, *, indent=4, sieve=None, repres=None): print_info( f'Opening authorized device list: "{os.path.abspath(input_auth)}"') try: auth = _process_auth_list(input_auth, indent) except json.decoder.JSONDecodeError as e: print_critical('Failed to decode authorized device list (JSON)', initial_error=str(e)) return print_info('Searching for violations') if not attributes: attributes = auth.keys() for event in self._all_events: try: if any(event[key] not in vals and event[key] is not None for key, vals in zip(attributes, auth.values())): self._violations.append(event) except KeyError as e: print_critical('No such attribute in authorized device list', initial_error=str(e)) return self._events_to_show = _filter_events(self._violations, sieve) if not self._events_to_show: print_info('No USB violation events found!') return if not cfg.QUIET and cfg.ISATTY: number, filename = _output_choice('violation', 'viol.json', 'violations/') if number is None: return elif number == 1: try: _dump_events(self._events_to_show, 'violations', filename, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return if columns: table_data = [[COLUMN_NAMES[name] for name in columns]] else: columns = [key for key in COLUMN_NAMES.keys()] table_data = [[val for val in COLUMN_NAMES.values()]] _represent_events(self._events_to_show, columns, table_data, 'USB-Violation-Events', repres)
def generate_auth_json(self, output_auth, attributes, *, indent=4, sieve=None): self._events_to_show = _filter_events(self._all_events, sieve) if not self._events_to_show: print_info('No USB devices found!') return 1 abs_output_auth = os.path.abspath(output_auth) try: dirname = os.path.dirname(abs_output_auth) os_makedirs(dirname) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 else: print_info(f'Created "{dirname}"') try: auth_json = open(abs_output_auth, 'w', encoding='utf-8') except PermissionError as e: print_critical( f'Permission denied: "{abs_output_auth}". Retry with sudo', initial_error=str(e)) return 1 print_info('Generating authorized device list (JSON)') if not attributes: attributes = ('vid', 'pid', 'prod', 'manufact', 'serial') auth = defaultdict(list) for event in tqdm(self._events_to_show, ncols=80, unit='dev'): for key, val in event.items(): if (key in attributes and val is not None and val not in auth[key]): auth[key].append(val) for key in auth.keys(): auth[key].sort() json.dump(auth, auth_json, sort_keys=True, indent=indent) auth_json.close() print_info(f'New authorized device list: "{abs_output_auth}"')
def _update_database(filename): try: usb_ids = open(filename, 'r+', encoding='utf-8') except PermissionError as e: raise USBRipError( f'Permission denied: "{filename}"', errors={'initial_error': str(e)} ) print_info('Getting current database version') curr_ver, curr_date = _get_current_version(usb_ids) print(f'Version: {curr_ver}') print(f'Date: {curr_date}') print_info('Checking local database for update') db, latest_ver, latest_date, errcode, e = _get_latest_version() if errcode: if errcode == USBIDs._INTERNET_CONNECTION_ERROR: print_warning( 'No internet connection, using current version', errcode=errcode ) elif errcode == USBIDs._SERVER_TIMEOUT_ERROR: print_warning( 'Server timeout, using current version', errcode=errcode, initial_error=e ) elif errcode == USBIDs._SERVER_CONTENT_ERROR: print_warning( 'Server error, using current version', errcode=errcode, initial_error=e ) return usb_ids if curr_ver != latest_ver and curr_date != latest_date: # if there's newer database version print('Updating database... ', end='') usb_ids.write(db) usb_ids.truncate() usb_ids.seek(0) print('Done\n') print(f'Version: {latest_ver}') print(f'Date: {latest_date}') print_info('Local database is up-to-date') return usb_ids
def _represent_events(events_to_show, columns, table_data, title, repres): print_info('Preparing gathered events') if repres is None: repres = {'table': False, 'list': False, 'smart': True} max_len = { 'conn': 19, 'user': max(max(len(event['user']) for event in events_to_show), len('User')), 'vid': 4, 'pid': 4, 'prod': max(max(len(str(event['prod'])) for event in events_to_show), len('Product')), 'manufact': max(max(len(str(event['manufact'])) for event in events_to_show), len('Manufacturer')), 'serial': max(max(len(str(event['serial'])) for event in events_to_show), len('Serial Number')), 'port': max(max(len(event['port']) for event in events_to_show), len('Port')), 'disconn': 19 } prev_cday = '' for event in events_to_show: if 'conn' in columns: curr_cday = event['conn'][:10] if prev_cday != curr_cday: cday = [ f'{curr_cday} {BULLET * (len(event["conn"])-len(curr_cday)-1)}' ] table_data.append(cday + [ SEPARATOR * max_len[name] for name in columns if name != 'conn' ]) prev_cday = curr_cday row = [] for name in columns: if event[name] is None: event[name] = ABSENCE item = event[name] if name == 'conn' and cfg.ISATTY: item = colored(item, 'green') elif name == 'disconn' and cfg.ISATTY: item = colored(item, 'red') row.append(item) table_data.append(row) event_table = _build_single_table(USBEvents.TableClass, table_data, colored(title, 'white', attrs=['bold'])) # Display as table if cfg.ISATTY and (repres['smart'] and event_table.ok or repres['table']): print_info('Representation: Table') print('\n' + event_table.table) # Display as list elif not cfg.ISATTY or (repres['smart'] and not event_table.ok or repres['list']): if not event_table.ok: print_warning( 'Terminal window is too small to display table properly') print_warning('Representation: List') else: print_info('Representation: List') max_len = max( len(str(val)) for event in events_to_show for val in event.values()) + len( 'Serial Number: ') # max length string if not max_len // 2: max_len += 1 if cfg.ISATTY: cprint('\n' + title, 'white', attrs=['bold']) else: print('\n' + title) print(SEPARATOR * max_len) for event in events_to_show: if cfg.ISATTY: print( colored('Connected: ', 'magenta', attrs=['bold']) + colored(event['conn'], 'green')) print( colored('User: '******'magenta', attrs=['bold']) + event['user']) print( colored('VID: ', 'magenta', attrs=['bold']) + event['vid']) print( colored('PID: ', 'magenta', attrs=['bold']) + event['pid']) print( colored('Product: ', 'magenta', attrs=['bold']) + str(event['prod'])) print( colored('Manufacturer: ', 'magenta', attrs=['bold']) + str(event['manufact'])) print( colored('Serial Number: ', 'magenta', attrs=['bold']) + str(event['serial'])) print( colored('Bus-Port: ', 'magenta', attrs=['bold']) + event['port']) print( colored('Disconnected: ', 'magenta', attrs=['bold']) + colored(event['disconn'], 'red')) else: print('Connected: ' + event['conn']) print('User: '******'user']) print('VID: ' + event['vid']) print('PID: ' + event['pid']) print('Product: ' + str(event['prod'])) print('Manufacturer: ' + str(event['manufact'])) print('Serial Number: ' + str(event['serial'])) print('Bus-Port: ' + event['port']) print('Disconnected: ' + event['disconn']) print(SEPARATOR * max_len)
def _filter_events(all_events, sieve): # sieve = { # 'external': False, # 'dates': [], # 'fields': {}, # 'number': -1 # } if sieve is None: return all_events else: print_info('Filtering events') events_by_external = [] if sieve['external']: for event in all_events: if event['disconn'] is not None: events_by_external.append(event) continue else: events_by_external = all_events events_by_date = [] if sieve['dates']: for event in all_events: for date in sieve['dates']: if event['conn'].startswith(date): events_by_date.append(event) break continue else: events_by_date = all_events event_intersection = intersect_event_sets(events_by_external, events_by_date) events_to_show = [] if sieve['fields']: for event in event_intersection: for key, vals in sieve['fields'].items(): if any(event[key] == val for val in vals): events_to_show.append(event) break else: events_to_show = event_intersection if not events_to_show: return [] SIZE = len(events_to_show) if sieve['number'] <= -1 or sieve['number'] > SIZE: if sieve['number'] < -1: print_warning( f'usbrip can\'t handle dark matter \"--number={sieve["number"]}\", so it will show ' f'all {SIZE} USB history entries available') elif sieve['number'] > SIZE: print_warning( f'USB history has only {SIZE} entries instead of requested {sieve["number"]}, ' f'displaying all of them...') sieve['number'] = SIZE return [ events_to_show[SIZE - i] for i in range(sieve['number'], 0, -1) ]
def create_storage(storage_type, *, password=None, input_auth=None, attributes=None, compression_level='5', indent=4, sieve=None): if storage_type == 'history': events_to_show = _get_history_events(sieve) elif storage_type == 'violations': try: events_to_show = _get_violation_events(sieve, input_auth, attributes, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 if events_to_show is None: return 1 if events_to_show: min_date, max_date = _get_dates(events_to_show) json_file = '{}/{}-{}.json'.format(USBStorage._STORAGE_BASE, min_date, max_date) else: json_file = '{}/{}.json'.format(USBStorage._STORAGE_BASE, datetime.now().strftime('%m%d')) try: _dump_events(events_to_show, storage_type, json_file, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 if password is None: print_warning('No password provided, generating random one') password = _gen_random_password(12) storage_full_path = '{}/{}.7z'.format(USBStorage._STORAGE_BASE, storage_type) if os.path.exists(storage_full_path): os.remove(storage_full_path) try: out = _7zip_pack(storage_full_path, json_file, password, compression_level) except USBRipError as e: os.remove(json_file) print_critical(str(e), errcode=e.errors['errcode'], initial_error=e.errors['initial_error']) return 1 if 'Everything is Ok' in out: print_info('New {} storage: \'{}\''.format(storage_type, storage_full_path)) print_secret('Your password is', secret=password) os.remove(json_file) else: print_critical('Undefined behaviour while creating storage', initial_error=out)
def _read_log_file(filename, log=None): filtered = [] if log is None: abs_filename = os.path.abspath(filename) if abs_filename.endswith('.gz'): print_info(f'Unpacking "{abs_filename}"') try: log = gzip.open(abs_filename, 'rb') except PermissionError as e: print_warning( f'Permission denied: "{abs_filename}". Retry with sudo', initial_error=str(e)) return filtered else: end_of_file = b'' abs_filename = os.path.splitext(abs_filename) else: log = codecs.open(abs_filename, 'r', encoding='utf-8', errors='ignore') end_of_file = '' print_info(f'Reading "{abs_filename}"') else: abs_filename = 'journalctl output' end_of_file = '' print_info(f'Reading journalctl output') regex = re.compile(r'(?:]|:) usb (.*?): ') for line in tqdm(iter(log.readline, end_of_file), unit='line'): if isinstance(line, bytes): line = line.decode('utf-8', errors='ignore') if regex.search(line): # Case 1 -- Modified Timestamp ("%Y-%m-%dT%H:%M:%S.%f%z") date = line[:32].strip() if date.count(':') == 3: date = ''.join( line[:32].rsplit(':', 1) ) # rreplace(':', '', 1) to remove the last ':' from "2019-08-09T06:15:49.655261-04:00" timestamp if there is one try: date = datetime.strptime( date, '%Y-%m-%dT%H:%M:%S.%f%z' ) # ex. "2019-08-09T06:15:49.655261-0400" except ValueError: # Case 2 -- Non-Modified Timestamp ("%b %d %H:%M:%S") date = line[:15].strip() if ' ' in date: date = date.replace(' ', ' 0', 1) # pad day of the week with zero try: date = datetime.strptime( date, '%b %d %H:%M:%S') # ex. "Mar 18 13:56:07" except ValueError as e: raise USBRipError( f'Wrong timestamp format found in "{abs_filename}"', errors={'initial_error': str(e)}) else: date = date.strftime('????-%m-%d %H:%M:%S') logline = line[15:].strip() else: date = date.strftime('%Y-%m-%d %H:%M:%S') logline = line[32:].strip() if any(pat in line for pat in ('New USB device found, ', 'Product: ', 'Manufacturer: ', 'SerialNumber: ')): filtered.append((date, 'c', logline)) elif 'disconnect' in line: filtered.append((date, 'd', logline)) log.close() return filtered
def update_storage( storage_type, password, *, input_auth=None, attributes=None, compression_level='5', indent=4, sieve=None ): if storage_type == 'history': events_to_show = _get_history_events(sieve) elif storage_type == 'violations': try: events_to_show = _get_violation_events(sieve, input_auth, attributes, indent) except USBRipError as e: print_critical(str(e), initial_error=e.errors['initial_error']) return 1 if events_to_show is None: return 1 if events_to_show: min_date, max_date = _get_dates(events_to_show) else: print_info('No events to append') return 1 storage_full_path = f'{USBStorage._STORAGE_BASE}/{storage_type}.7z' if not os.path.isfile(storage_full_path): print_critical(f'Storage not found: "{storage_full_path}"') return 1 print_info(f'Updating storage: "{storage_full_path}"') try: out = _7zip_unpack(storage_full_path, password) except USBRipError as e: print_critical(str(e), errcode=e.errors['errcode'], initial_error=e.errors['initial_error']) return 1 if 'Everything is Ok' in out: base_filename = re.search(r'([^\n ]*\.json)', _7zip_list(storage_full_path, password), re.MULTILINE).group(1) json_file = f'{USBStorage._STORAGE_BASE}/{base_filename}' os.remove(storage_full_path) with open(json_file, 'r', encoding='utf-8') as dump: events_dumped = json.load(dump) os.remove(json_file) merged_events = _merge_json_events(events_dumped, events_to_show) if len(base_filename) > 9: # len('mmdd.json') == 9 min_date = base_filename[:4] new_json_file = f'{USBStorage._STORAGE_BASE}/{min_date}-{max_date}.json' _dump_events(merged_events, storage_type, new_json_file, indent) try: out = _7zip_pack(storage_full_path, new_json_file, password, compression_level) except USBRipError as e: os.remove(new_json_file) print_critical(str(e), errcode=e.errors['errcode'], initial_error=e.errors['initial_error']) return 1 if 'Everything is Ok' in out: print_info('Storage was successfully updated') else: print_critical('Undefined behaviour while creating storage', initial_error=out) os.remove(new_json_file) else: print_critical('Undefined behaviour while unpacking storage', initial_error=out)