Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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})
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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'})
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
    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))