def pipe_receive(peer_type): if peer_type not in ('client', 'server'): raise Exception('Invalid peer type "{}"'.format(peer_type)) data = json.loads(request.form['data']) uuid = data['pipe_id'] if uuid not in pipes: log.error('Attempt to receive from nonexistent pipe {}', uuid) abort(404) with pipes_lock: pipe = pipes[uuid] pipe['activity'] = time.time() try: other_peer_type = 'server' if peer_type == 'client' else 'client' data_field = other_peer_type + '_to_' + peer_type if pipe[data_field]: encryptor = encryptors[uuid][peer_type]['receive'] encrypted_data = encryptor.encrypt(pipe[data_field]) encoded_data = b64encode(encrypted_data).decode('utf8') ret = json.dumps({'data': encoded_data}) if peer_type == 'server': PipeLogger.log(uuid, 'receive', pipe[data_field]) pipe[data_field] = b'' return ret closed_field = other_peer_type + '_closed' if pipe[closed_field]: return json.dumps({'eof': True}) return json.dumps({'status': 'ok'}) finally: # DictProxy doesn't detect updates to nested dicts. pipes[uuid] = pipe
def pipe_close(peer_type): if peer_type not in ('client', 'server'): raise Exception('Invalid peer type "{}"'.format(peer_type)) data = json.loads(request.form['data']) uuid = data['pipe_id'] if uuid not in pipes: log.error('Attempt to close nonexistent pipe {}', uuid) abort(404) with pipes_lock: pipe = pipes[uuid] try: other_peer_type = 'server' if peer_type == 'client' else 'client' closed_field = peer_type + '_closed' other_closed_field = other_peer_type + '_closed' pipe[closed_field] = True client_opened = peer_type == 'client' or pipe['client_opened'] if not client_opened or pipe[other_closed_field]: del pipes[uuid] encryptors.pop(uuid, None) if peer_type == 'server': PipeLogger.finish(uuid) return json.dumps({'status': 'ok'}) finally: # DictProxy doesn't detect updates to nested dicts. if uuid in pipes: # i.e., it wasn't deleted above pipes[uuid] = pipe
def pipe_create(): data = json.loads(request.form['data']) client_hostname = data['client_hostname'] db = get_db() if not db.clients.find_one({'hostname': client_hostname}): raise Exception('Attempt to create pipe for invalid client {}'.format( client_hostname)) key = data['encryption_key'] iv = data['encryption_iv'] uuid = uuid4().hex encryptors[uuid]['server'] = { 'send': Encryptor(key, iv), 'receive': Encryptor(key, iv) } pipes[uuid] = { 'client_opened': False, 'client_closed': False, 'server_closed': False, 'client_to_server': b'', 'server_to_client': b'', 'created': time.time(), 'activity': None, 'client_hostname': client_hostname, } log.debug('Created pipe {}', uuid) return json.dumps({'pipe_id': uuid})
def main(): args = parse_args() if not (args.plugins or args.commands): args.plugins = args.commands = True results = { 'hostname': socket.gethostname(), 'collected_at': datetime.datetime.utcnow(), } before_runlevel = runlevel_info() if args.plugins: results['plugins'] = run_dir(plugins_dir, submit_failures=True) if args.commands: results['commands'] = run_dir(commands_dir, parse_output=False, delete_after_success=True, submit_failures=True) after_runlevel = runlevel_info() if not (results.get('plugins', False) or results.get('commands', False)): return if before_runlevel != after_runlevel: # If the runlevel changes while we are running, it's probaby because # the machine is in the process of rebooting, in which case the odds # are that some of the plugins returned bad data and we shouldn't send # anything to the server. before_lines = set(before_runlevel.split('\n')) after_lines = set(after_runlevel.split('\n')) only_before = sorted(before_lines - after_lines) only_after = sorted(after_lines - before_lines) sys.exit('Aborting because runlevel changed: removed={!r}, added={!r}'. format(only_before, only_after)) results, updates = encrypt_document(results, log=log) if updates: log.info('Encrypted private data before transmission to server') os.makedirs(collected_dir, exist_ok=True) collected_path = os.path.join(collected_dir, str(int(time.time()))) with open(collected_path, 'w') as f: try: f.write(json.dumps(results)) except: os.unlink(collected_path) raise log.debug('Saved collected data to {}', collected_path)
def encrypt_document(getter, doc, log=None, selectors=None): if not getter('secret_keeping:enabled'): return doc, None key_id = getter('secret_keeping:key_id') if selectors is None: selectors = get_selectors(getter) update = {'$unset': {}, '$set': {}} for s in selectors: decrypted_data = get_setting(doc, s.plain_mem, check_defaults=False) if not decrypted_data: continue # This assures that the same decrypted data will always end up with the # same md5 hash by ensuring that the data are always encoded in the # same order. if isinstance(decrypted_data, dict): decrypted_data = orderify(decrypted_data) decrypted_data = json.dumps(decrypted_data).encode('utf-8') with NamedTemporaryFile('w+b') as unencrypted_file, \ NamedTemporaryFile('w+b') as encrypted_file: unencrypted_file.write(decrypted_data) unencrypted_file.flush() try: gpg_command('--encrypt', '--recipient', key_id, '-o', encrypted_file.name, unencrypted_file.name, minimum_version=client_gpg_version) except subprocess.CalledProcessError as e: if log: log.error('Gpg failed to encrypt. Output:\n{}', e.output.decode('utf8')) raise encrypted_file.seek(0) encrypted_data = { 'hash': md5(decrypted_data).hexdigest(), 'data': b64encode(encrypted_file.read()).decode('utf8') } if s.plain_mongo != s.enc_mongo: update['$unset'][s.plain_mongo] = True update['$set'][s.enc_mongo] = encrypted_data set_setting(doc, s.plain_mem, None) set_setting(doc, s.enc_mem, encrypted_data) if update['$set']: if not update['$unset']: update.pop('$unset') return doc, update return doc, None
def pipe_open(): data = json.loads(request.form['data']) uuid = data['pipe_id'] with pipes_lock: if uuid not in pipes: log.error('Attempt to open nonexistent pipe {}', uuid) abort(404) key = data['encryption_key'] iv = data['encryption_iv'] encryptors[uuid]['client'] = {'send': Encryptor(key, iv), 'receive': Encryptor(key, iv)} try: pipe = pipes[uuid] if pipe['client_opened']: raise Exception('Attempt to open already opened pipe') pipe['client_opened'] = True finally: # DictProxy doesn't detect updates to nested dicts. pipes[uuid] = pipe return json.dumps({'status': 'ok'})
def update(): db = get_db() data = json.loads(request.form['data']) hostname = data['hostname'] old_release = data['old_release'] releases = sorted(r for r in os.listdir(releases_dir) if r.endswith('.asc')) response_data = {} if len(releases) == 0: response_data['status'] = 'current' else: current_release_file = releases[-1] current_release_number = \ int(current_release_file[0:current_release_file.index('.')]) if old_release >= current_release_number: response_data['status'] = 'current' log.debug('{} is current ({})', hostname, current_release_number) else: log.info('Sending release {} to {} (currently at {})', current_release_number, hostname, old_release) response_data['status'] = 'out-of-date' response_data['current_release'] = current_release_number response_data['update'] = open( os.path.join(releases_dir, current_release_file)).read() patches = [{ 'id': str(d['_id']), 'files': d['files'] } for d in db.patches.find({'pending_hosts': hostname}, projection=['files'])] if patches: log.info('Sending patches {} ({}) to {}', ', '.join(p['id'] for p in patches), ', '.join(f['path'] for p in patches for f in p['files']), hostname) response_data['patches'] = patches return json.dumps(response_data)
try: subprocess.check_call(('dnf', 'info', 'xguest'), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except (FileNotFoundError, subprocess.CalledProcessError): return None return any(l for l in open('/etc/passwd') if re.match(r'xguest:', l)) # Make sure xinit_checker is last. Just because somebody is running xinit # doesn't mean that they aren't _also_ running a display manager that has a # guest session, so xinit_checker should only be used as a last resort. checkers = (lightdm_checker, xinit_checker, gdm3_checker, xguest_checker) for checker in checkers: results = checker() if results is not None: break if results is None: results = 'unknown' results = ({ 'enabled': results } if results != 'unknown' or find_x_users() else None) results = cached_data('guest_session', results, add_timestamp=True, raise_exception=False) print(json.dumps(results))