Exemple #1
0
    def call_read_write_update_option(self, name, auth, jump_server=None,
                                      **kwargs):
        """Update secrets for an existing named authority.

        Args:
            name (str): The name of the authority to lookup.
            auth (str): Authentification method.

        Raises:
            Exception: If named authority does not exists.

        Outputs:
            stdout: Confirm message with human-readable authority details.
        """

        Log.debug("Incoming update request for named authority {n}", n=name)
        if not self.get_db().exists(name):
            Log.debug("Named authority is not found...")
            error = "Cannot update entry: \"{name}\" not found in " \
                    "keychain (missing key)"
            Log.fatal(error, name=name)
        if jump_server is not None:
            Log.debug("Update requests to change jump server...")
            jump_auth = self.build_authority_from_signature(jump_server)
            self.get_db().update_jump_auth(name, jump_auth)
            Log.debug("New jump set to authority: {a}", a=str(jump_auth))
        passkey = Passkey.resolve(auth)
        self.get_db().update_passkey(name, passkey)
        Log.debug("New passkey set ... ")
        Display.show_update(self.get_db().fetch_auth(name))
Exemple #2
0
    def build_random_name(self):
        """Generate a random name with 16 characters.

        Returns:
            str: Random 16 characters.
        """

        name = uuid4().hex[:16]
        Log.debug("Generating new random name: {n}", n=name)
        return name
Exemple #3
0
    def initialize(cls, secrets):
        """Register keychain to manager's database.

        Args:
            secrets (object): Dict-like object storage.
        """

        cls.__secrets = Keychain(secrets)
        cls.__database = Database(cls.__secrets)
        Log.debug("Manager initialized...")
Exemple #4
0
    def call_read_only_recall_option(self, signature, **kwargs):
        """Lookup wrapper without explicit authority.

        Finds authority by signature and calls "lookup" for passkey.

        Args:
            key (str): Possible authority signature to match or name.

        Raises:
            Exception: If named authority does not exists.

        Outputs:
            stdout: Human-readable passkey with authority details and hostname.
        """

        Log.debug("Searching secret passkey for key {s}", s=signature)
        if len(signature) < self.MIN_NAME_LEN:
            Log.debug("Trying key as authority signature...")
            for name, auth, _, _ in self.get_db().query_all():
                if auth.signature() == signature:
                    Log.debug("Got authority with signature {s}", s=signature)
                    signature = name
                    break
        Log.debug("Running a lookup for named authority: {n}", n=signature)
        self.call_read_only_lookup_option(signature)
Exemple #5
0
    def call_secret_read_stdout_dump_option(self, name, signature, **kwargs):
        """Vulnerable passkey dump to stdout.

        Args:
            name      (str): The name of the authority to lookup.
            signature (str): The signature for an authority to find its name.

        Outputs:
            stdout: Base64 encoded passkey.

        Raises:
            Exception: If unsupported passkey is found.
        """

        def print_passkey(auth_name):
            _, _, secret = self.get_db().lookup(auth_name)
            return "{}\n{}".format(*Passkey.copy(secret))

        passkey = ""  # dummy passkey
        Log.debug("Incoming secret dump request...")
        if len(name) > 0:
            Log.debug("Got named authority {n}...", n=name)
            passkey = print_passkey(name)
        elif len(signature) > 0:
            for each, name in self.get_db().query_auth():
                if each.signature() != signature:
                    continue
                Log.debug("Records matched authority, got passkey...")
                passkey = print_passkey(name)
                break  # exit on first match
        Log.debug("Printing passkey..." if passkey else "Nothing to print...")
        Display.show_dump(passkey)
Exemple #6
0
    def call_read_write_forget_option(self, signature, **kwargs):
        """Remove wrapper without explicit authority.

        Finds authority by signature and calls "remove" for passkey.

        Args:
            key (str): Possible authority signature to match or name.

        Raises:
            Exception: If named authority does not exists.

        Outputs:
            stdout: Human-readable passkey with authority details and hostname.
        """

        Log.debug("Testing if key {s} exists", s=signature)
        if len(signature) < self.MIN_NAME_LEN:
            Log.debug("Trying key as authority signature...")
            for name, auth, _, _ in self.get_db().query_all():
                if auth.signature() == signature:
                    Log.debug("Got authority with signature {s}", s=signature)
                    signature = name
                    break
        Log.debug("Running cleanup after named authority: {n}", n=signature)
        self.call_read_write_remove_option(signature)
Exemple #7
0
def unlocker(secrets, args=()):
    """Manage unlocker's keychain.

    Can add, edit, delete and lookup keys.
    All keys stored are compressed and encoded.
    """

    # register secrets on keychain
    Manager.initialize(secrets)
    Log.debug("Preparing to boot...")

    # initialize manager and parse arguments
    mng = Manager(*args)
    mng.call()
    Log.debug("Preparing to exit...")
Exemple #8
0
def main():
    """Main callable function.

    Configure logging capabilities, read input from shell or stdin (can exit
    fastly) and run unlocker manager.
    """

    # configure log
    Log.configure()
    Log.debug("Running in debug mode...")

    # read input
    args = read_input()

    # run unlocker with input args
    unlocker(args=args)
Exemple #9
0
    def call_read_write_migrate_option(self, *args, **kwargs):
        """Migration wrapper.

        Determine if action is to import or to export and launch process.

        Raises:
            Exception: If something goes terrible wrong.

        Inputs:
            stdin: Compressed import-ready secrets.

        Outputs:
            stdout: Compressed exported stored secrets.
        """

        Log.debug("Incoming migrate request...")
        Migrate.discover(self)
        Log.debug("Migrated data...")
Exemple #10
0
def read_input():
    """Read input from stdin or shell.

    Some shell arguments can trigger exit before reaching end of function.
    """

    # check if data is piped to unlocked and dump passkey
    if StreamData.read():
        Log.debug("Reading stdin: {data}", data=StreamData.buf_in)
        return StreamData.OPTION, StreamData.parse()

    # initialize shell
    shell = ShellParser()
    opts, args = shell.get_args()
    Log.debug("Running '{o}' with arguments: {a}", o=opts, a=args)

    # return shell arguments
    return opts, vars(args)
Exemple #11
0
    def call(self):
        """Call dispatcher.

        Handlers option if is identified and supported.

        Raises:
            Exception: If unsupported option is provided.
        """

        if self.option not in self.supported_options:
            Log.fatal("Unsupported option {o}", o=self.option)
        access = self.supported_options[self.option]
        debug_message = "Calling option {opt} with access level {acc}"
        Log.debug(debug_message, opt=self.option, acc=access)
        method = "call_{}_{}_option".format(access, self.option)
        if not hasattr(self, method):
            error = "Unsupported method {option} or access rights {access}"
            Log.fatal(error, option=self.option, access=access)
        return getattr(self, method)(**self.args)
Exemple #12
0
    def call_debug_write_purge_option(self, keys):
        """Permanently remove keys from keychain in debug mode.

        Can corrupt keychain! (Very unsafe)
        """

        Log.debug("Incoming debug purge request...")
        for key in keys:
            Log.debug("Attempt to remove {k} ...", k=key)
            removed_key = self.get_secrets().remove(key)
            if removed_key is not None:
                Log.debug("Removed {k} ...", k=key)
        Log.debug("Closing...")
Exemple #13
0
    def call_read_only_list_option(self, *args, **kwargs):
        """List handler.

        Outputs:
            stdout: Pager with table-like view of all hostnames.
        """

        Log.debug("Incoming list request...")
        known_hosts = [e for e in self.get_db().query_all()]
        counter = -1
        indexes = {}
        sorted_hosts = []
        Log.debug("Found {n} hosts to list...", n=len(known_hosts))
        while len(known_hosts) > 0:
            if counter >= self.MAX_ITER_LIST:
                Log.fatal("Max. iteration over list display reached...")
            name, auth, host, jump = known_hosts[0]
            counter += 1
            if auth.signature() not in indexes:
                indexes.update({auth.signature(): counter})
            if jump is None:
                sorted_hosts.append(known_hosts.pop(0))
                continue
            jump_index = indexes.get(jump.signature(), -1)
            if jump_index > -1:
                if jump_index == 0:
                    jump_index += 1
                sorted_hosts.insert(jump_index, known_hosts.pop(0))
                continue
            known_hosts.insert(len(known_hosts), known_hosts.pop(0))
        Log.debug("Sorted all known hosts and now preparing to print out...")
        Display.show_list_view(sorted_hosts, **self.args)
Exemple #14
0
def deploy_script(script_name):
    """Install script on current machine.

    Args:
        script_name (str): The name of script to be deployed.

    Raises:
        Exception: If script is missing or cannot be accessed.

    Return:
        bool: True if successfully deployed, otherwise False.
    """

    content = read_helper_script("data/shell/{}".format(script_name))
    if not content:
        Log.fatal("Helper script is missing")
    script = "{}\n{}".format(SCRIPTS_SHEBANG, content)
    try:
        script_name, _ = script_name.split(".", 1)
    except Exception:
        Log.debug("Cannot find file extension for {s}", s=script_name)
    return make_helper_script(script_name, script)
Exemple #15
0
    def call_debug_read_dump_option(self, keys):
        """Dump all keys from keychain in debug mode.
        """

        Log.debug("Incoming debug dump request...")
        for key in keys:
            for k in self.get_secrets().lookup(key):
                key_type = self.get_db().which(k)
                Log.debug(" [{t}] {k}", k=self.get_db().shift(k), t=key_type)
        Log.debug("Closing...")
Exemple #16
0
def make_helper_script(file_script, script_content):
    """Create executable helper script.

    Args:
        file_script        (str): Script to make.
        script_content (unicode): Script content.

    Raises:
        Exception: If application cannot create directory or file.

    Return:
        bool: True if script was successfully created, otherwise False.
    """

    if is_root():
        scripts_dir = SYSTEM_SCRIPTS_DIR
    else:
        scripts_dir = path.join(expanduser("~"), SCRIPTS_DIR)
    Log.debug("Scripts directory located at {path}", path=scripts_dir)
    try:
        if not path.exists(scripts_dir):
            Log.debug("Scripts directory does not exist. Creating...")
            makedirs(scripts_dir)
            Log.debug("Scripts directory successfully created!")
        else:
            Log.debug("Found scripts directory...")
    except Exception as e:
        Log.fatal("Failed to create scripts directory: {err}", err=str(e))
    try:
        filepath = "{}/{}".format(scripts_dir, file_script)
        if path.exists(filepath):
            Log.fatal("A file with this name already exists: {x}", x=filepath)
        with open(filepath, "wb") as fd:
            fd.write(script_content)
        mod = stat(filepath)
        chmod(filepath, mod.st_mode | S_IEXEC)
        return True
    except Exception as e:
        Log.fatal("Cannot create helper script because: {e}", e=str(e))
    return False
Exemple #17
0
    def get_secret_dir(cls):
        """Used to store keys credentials and configuration files.

        Raises:
            Exception: If application cannot create directory.

        Return:
            unicode: Application secret directory.
        """

        secret_dir = path.join(expanduser("~"), cls.UNLOCKER_DIR)
        Log.debug("Secret directory located at {path}", path=secret_dir)
        try:
            if not path.exists(secret_dir):
                Log.debug("Secret directory does not exist. Creating...")
                makedirs(secret_dir)
                Log.debug("Secret directory successfully created!")
            else:
                Log.debug("Found secret directory...")
        except Exception as e:
            Log.fatal("Failed to create secret directory: {err}", err=str(e))
        return unicode(secret_dir)
Exemple #18
0
    def call_read_write_append_option(self, name, host, port, user, auth,
                                      scheme, jump_server, **kwargs):
        """Append secrets to a named authority.

        Args:
            name        (str): The name of the authority to lookup.
            host        (str): Hostname to attach to authority.
            port        (int): Port number to attach to authority.
            user        (str): Username to attach to authority.
            auth        (str): Authentification method.
            scheme      (str): Scheme of the connection.
            jump_server (str): Name or signature of another authority.

        Raises:
            Exception: If named authority already exists.

        Outputs:
            stdout: Confirm message with human-readable authority details.
        """

        Log.debug("Incoming append request...")
        if name is None:
            name = self.build_random_name()
            Log.warn("Named authority is known as: {n}", n=name)
        elif len(name) < self.MIN_NAME_LEN:
            error = "Possible lookup collision: entry name is too short " \
                    "(name must be at least {size} characters)"
            Log.fatal(error, size=self.MIN_NAME_LEN)
        elif len(name) > self.MAX_NAME_LEN:
            error = "Possible bad naming: entry name is too long " \
                    "(name must be at most {size} characters)"
            Log.fatal(error, size=self.MAX_NAME_LEN)
        elif self.get_db().exists(name):
            error = "Another entry with the same name exists: {name} " \
                    "(name must be unique)"
            Log.fatal(error, name=name)
        data = {
            "name": name,
            "host": host,
            "auth": self.build_authority_from_args(user, host, port, scheme),
        }
        if jump_server is not None:
            data.update({
                "jump_auth": self.build_authority_from_signature(jump_server)
            })
        Log.debug("Preparing to add {args}", args=data)
        self.get_db().add(passkey=Passkey.resolve(auth), **data)
        Log.debug("New named authority is saved...")
        Display.show_append(data.get("auth"))
Exemple #19
0
    def call_read_only_lookup_option(self, name, **kwargs):
        """Lookup secrets for a named authority.

        Args:
            name (str): The name of the authority to lookup.

        Raises:
            Exception: If named authority does not exists.

        Outputs:
            stdout: Human-readable passkey with authority details and hostname.
        """

        Log.debug("Incoming lookup request for named authority {n}", n=name)
        if not self.get_db().exists(name):
            Log.debug("Nothing to lookup...")
            error = "Cannot lookup entry: \"{name}\" not found in " \
                    "keychain (missing key)"
            Log.fatal(error, name=name)
        Log.debug("Fetching data for named authority...")
        auth, host, secret = self.get_db().lookup(name)
        passtype, passkey = Passkey.copy(secret, True)
        Log.debug("Printing as safe as possible {n}'s secrets...", n=name)
        Display.show_lookup(auth, host, passtype, passkey)
Exemple #20
0
 def __init__(self, holder):
     self.keychain = holder
     Log.debug("Keychain initialized...")
Exemple #21
0
 def __init__(self, storage):
     if not isinstance(storage, Keychain):
         Log.fatal("Unexpected database storage {t}", t=type(storage))
     self.storage = storage
     Log.debug("Database initialized...")
     Log.debug("Storage status: {k}", k=str(storage))
Exemple #22
0
    def call_read_write_remove_option(self, name, **kwargs):
        """Lookup and remove secrets for a named authority.

        Args:
            name (str): The name of the authority to lookup.

        Raises:
            Exception: If named authority does not exists.

        Outputs:
            stdout: Human-readable passkey with authority details and hostname.
        """

        Log.debug("Incoming remove request for named authority {n}", n=name)
        if not self.get_db().exists(name):
            Log.debug("Nothing to remove...")
            error = "Cannot remove entry: \"{name}\" not found in " \
                    "keychain (missing key)"
            Log.fatal(error, name=name)
        Log.debug("Fetching data for named authority...")
        auth, host, secret = self.get_db().lookup(name)
        dependents = []
        for jump, dep_name in self.get_db().query_jump():
            if jump.signature() == auth.signature():
                debug_message = "Found another authority {a} " \
                                "depending on this... "
                Log.debug(debug_message, a=str(jump))
                dependents.append(dep_name)
        if len(dependents) > 0:
            error = "Not removing entry because {n} other servers bounce of " \
                    "\"{name}\": remove all before trying again (safe mode)"
            Log.fatal(error, n=len(dependents), name=name)
        passtype, passkey = Passkey.copy(secret, True)
        Log.debug("Removing named authority...")
        self.get_db().remove(name)  # remove all keys for named auth
        Log.debug("Permanently removed named authority {n}", n=name)
        Display.show_remove(auth, host, passtype, passkey)