Beispiel #1
0
def change_principal(principal, password=None, client=None, path=None,
                     canonicalize=False, enterprise=False, keytab=None):
    """Temporarily change the kerberos principal

    Most of the test cases run with the admin ipa user which is granted
    all access and exceptions from rules on some occasions.

    When the test needs to test for an application of some kind
    of a restriction it needs to authenticate as a different principal
    with required set of rights to the operation.

    The context manager changes the principal identity in two ways:

    * using password
    * using keytab

    If the context manager is to be used with a keytab, the keytab
    option must be its absolute path.

    The context manager can be used to authenticate with enterprise
    principals and aliases when given respective options.
    """

    if path:
        ccache_name = path
    else:
        ccache_name = os.path.join('/tmp', str(uuid.uuid4()))

    if client is None:
        client = api


    client.Backend.rpcclient.disconnect()

    try:
        if keytab:
            kinit_keytab(principal, keytab, ccache_name)
        else:
            kinit_password(principal, password, ccache_name,
                           canonicalize=canonicalize,
                           enterprise=enterprise)
        client.Backend.rpcclient.connect(ccache=ccache_name)

        try:
            yield
        finally:
            client.Backend.rpcclient.disconnect()
    finally:
        # If we generated a ccache name, try to remove it, but don't fail
        if not path:
            try:
                os.remove(ccache_name)
            except OSError:
                pass
        client.Backend.rpcclient.connect()
def temp_kinit(principal, password):
    """
    kinit with password using a temporary ccache
    """
    if not password:
        raise RuntimeError("The password is not set")
    if not principal:
        principal = "admin"

    ccache_dir = tempfile.mkdtemp(prefix='krbcc')
    ccache_name = os.path.join(ccache_dir, 'ccache')

    try:
        kinit_password(principal, password, ccache_name)
    except RuntimeError as e:
        raise RuntimeError("Kerberos authentication failed: {}".format(e))

    return ccache_dir, ccache_name
Beispiel #3
0
    def kinit(self, principal, password, ccache_name):
        # get anonymous ccache as an armor for FAST to enable OTP auth
        armor_path = os.path.join(paths.IPA_CCACHES,
                                  "armor_{}".format(os.getpid()))

        self.debug('Obtaining armor in ccache %s', armor_path)

        try:
            kinit_armor(
                armor_path,
                pkinit_anchors=[paths.KDC_CERT, paths.KDC_CA_BUNDLE_PEM],
            )
        except RuntimeError as e:
            self.error("Failed to obtain armor cache")
            # We try to continue w/o armor, 2FA will be impacted
            armor_path = None

        try:
            kinit_password(
                unicode(principal),
                password,
                ccache_name,
                armor_ccache_name=armor_path,
                enterprise=True,
                lifetime=self.api.env.kinit_lifetime)

            if armor_path:
                self.debug('Cleanup the armor ccache')
                ipautil.run([paths.KDESTROY, '-A', '-c', armor_path],
                            env={'KRB5CCNAME': armor_path}, raiseonerr=False)
        except RuntimeError as e:
            if ('kinit: Cannot read password while '
                    'getting initial credentials') in str(e):
                raise PasswordExpired(principal=principal, message=unicode(e))
            elif ('kinit: Client\'s entry in database'
                  ' has expired while getting initial credentials') in str(e):
                raise KrbPrincipalExpired(principal=principal,
                                          message=unicode(e))
            elif ('kinit: Clients credentials have been revoked '
                  'while getting initial credentials') in str(e):
                raise UserLocked(principal=principal,
                                 message=unicode(e))
            raise InvalidSessionPassword(principal=principal,
                                         message=unicode(e))
Beispiel #4
0
    def kinit(self, user, realm, password, ccache_name):
        # get http service ccache as an armor for FAST to enable OTP authentication
        armor_principal = str(
            krb5_format_service_principal_name('HTTP', self.api.env.host,
                                               realm))
        keytab = paths.IPA_KEYTAB
        armor_name = "%sA_%s" % (krbccache_prefix, user)
        armor_path = os.path.join(krbccache_dir, armor_name)

        self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s',
                   armor_principal, keytab, armor_path)

        try:
            kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path)
        except gssapi.exceptions.GSSError as e:
            raise CCacheError(message=unicode(e))

        # Format the user as a kerberos principal
        principal = krb5_format_principal_name(user, realm)

        try:
            kinit_password(principal,
                           password,
                           ccache_name,
                           armor_ccache_name=armor_path)

            self.debug('Cleanup the armor ccache')
            ipautil.run([paths.KDESTROY, '-A', '-c', armor_path],
                        env={'KRB5CCNAME': armor_path},
                        raiseonerr=False)
        except RuntimeError as e:
            if ('kinit: Cannot read password while '
                    'getting initial credentials') in str(e):
                raise PasswordExpired(principal=principal, message=unicode(e))
            elif ('kinit: Client\'s entry in database'
                  ' has expired while getting initial credentials') in str(e):
                raise KrbPrincipalExpired(principal=principal,
                                          message=unicode(e))
            elif ('kinit: Clients credentials have been revoked '
                  'while getting initial credentials') in str(e):
                raise UserLocked(principal=principal, message=unicode(e))
            raise InvalidSessionPassword(principal=principal,
                                         message=unicode(e))
Beispiel #5
0
    def kinit(self, user, realm, password, ccache_name):
        # get http service ccache as an armor for FAST to enable OTP authentication
        armor_principal = str(krb5_format_service_principal_name(
            'HTTP', self.api.env.host, realm))
        keytab = paths.IPA_KEYTAB
        armor_name = "%sA_%s" % (krbccache_prefix, user)
        armor_path = os.path.join(krbccache_dir, armor_name)

        self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s',
                   armor_principal, keytab, armor_path)

        try:
            kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path)
        except gssapi.exceptions.GSSError as e:
            raise CCacheError(message=unicode(e))

        # Format the user as a kerberos principal
        principal = krb5_format_principal_name(user, realm)

        try:
            kinit_password(principal, password, ccache_name,
                           armor_ccache_name=armor_path)

            self.debug('Cleanup the armor ccache')
            ipautil.run(
                [paths.KDESTROY, '-A', '-c', armor_path],
                env={'KRB5CCNAME': armor_path},
                raiseonerr=False)
        except RuntimeError as e:
            if ('kinit: Cannot read password while '
                    'getting initial credentials') in str(e):
                raise PasswordExpired(principal=principal, message=unicode(e))
            elif ('kinit: Client\'s entry in database'
                  ' has expired while getting initial credentials') in str(e):
                raise KrbPrincipalExpired(principal=principal,
                                          message=unicode(e))
            elif ('kinit: Clients credentials have been revoked '
                  'while getting initial credentials') in str(e):
                raise UserLocked(principal=principal,
                                 message=unicode(e))
            raise InvalidSessionPassword(principal=principal,
                                         message=unicode(e))
Beispiel #6
0
def check_creds(options, realm_name):

    # Check if ccache is available
    default_cred = None
    try:
        logger.debug('KRB5CCNAME set to %s',
                     os.environ.get('KRB5CCNAME', None))
        # get default creds, will raise if none found
        default_cred = gssapi.creds.Credentials()
        principal = str(default_cred.name)
    except gssapi.raw.misc.GSSError as e:
        logger.debug('Failed to find default ccache: %s', e)
        principal = None

    # Check if the principal matches the requested one (if any)
    if principal is not None and options.principal is not None:
        op = options.principal
        if op.find('@') == -1:
            op = '%s@%s' % (op, realm_name)
        if principal != op:
            logger.debug('Specified principal %s does not match '
                         'available credentials (%s)',
                         options.principal, principal)
            principal = None

    if principal is None:
        (ccache_fd, ccache_name) = tempfile.mkstemp()
        os.close(ccache_fd)
        options.created_ccache_file = ccache_name

        if options.principal is not None:
            principal = options.principal
        else:
            principal = 'admin'
        stdin = None
        if principal.find('@') == -1:
            principal = '%s@%s' % (principal, realm_name)
        if options.admin_password is not None:
            stdin = options.admin_password
        else:
            if not options.unattended:
                try:
                    stdin = getpass.getpass("Password for %s: " % principal)
                except EOFError:
                    stdin = None
                if not stdin:
                    logger.error(
                        "Password must be provided for %s.", principal)
                    raise ScriptError("Missing password for %s" % principal)
            else:
                if sys.stdin.isatty():
                    logger.error("Password must be provided in "
                                 "non-interactive mode.")
                    logger.info("This can be done via "
                                "echo password | ipa-client-install "
                                "... or with the -w option.")
                    raise ScriptError("Missing password for %s" % principal)
                else:
                    stdin = sys.stdin.readline()

            # set options.admin_password for future use
            options.admin_password = stdin

        try:
            kinit_password(principal, stdin, ccache_name)
        except RuntimeError as e:
            logger.error("Kerberos authentication failed: %s", e)
            raise ScriptError("Invalid credentials: %s" % e)

        os.environ['KRB5CCNAME'] = ccache_name
Beispiel #7
0
def change_principal(principal,
                     password=None,
                     client=None,
                     path=None,
                     canonicalize=False,
                     enterprise=False,
                     keytab=None):
    """Temporarily change the kerberos principal

    Most of the test cases run with the admin ipa user which is granted
    all access and exceptions from rules on some occasions.

    When the test needs to test for an application of some kind
    of a restriction it needs to authenticate as a different principal
    with required set of rights to the operation.

    The context manager changes the principal identity in two ways:

    * using password
    * using keytab

    If the context manager is to be used with a keytab, the keytab
    option must be its absolute path.

    The context manager can be used to authenticate with enterprise
    principals and aliases when given respective options.
    """

    if path:
        ccache_name = path
    else:
        ccache_name = os.path.join('/tmp', str(uuid.uuid4()))

    if client is None:
        client = api

    client.Backend.rpcclient.disconnect()

    try:
        if keytab:
            kinit_keytab(principal, keytab, ccache_name)
        else:
            kinit_password(principal,
                           password,
                           ccache_name,
                           canonicalize=canonicalize,
                           enterprise=enterprise)
        client.Backend.rpcclient.connect(ccache=ccache_name)

        try:
            yield
        finally:
            client.Backend.rpcclient.disconnect()
    finally:
        # If we generated a ccache name, try to remove it, but don't fail
        if not path:
            try:
                os.remove(ccache_name)
            except OSError:
                pass
        client.Backend.rpcclient.connect()
Beispiel #8
0
def main():
    module = AnsibleModule(
        argument_spec = dict(
            servers=dict(required=True, type='list'),
            domain=dict(required=True),
            realm=dict(required=True),
            hostname=dict(required=True),
            kdc=dict(required=True),
            basedn=dict(required=True),            
            principal=dict(required=False),
            password=dict(required=False, no_log=True),
            keytab=dict(required=False),
            ca_cert_file=dict(required=False),
            force_join=dict(required=False, type='bool'),
            kinit_attempts=dict(required=False, type='int', default=5),
            debug=dict(required=False, type='bool'),
        ),
        supports_check_mode = True,
    )

    module._ansible_debug = True
    servers = module.params.get('servers')
    domain = module.params.get('domain')
    realm = module.params.get('realm')
    hostname = module.params.get('hostname')
    basedn = module.params.get('basedn')
    kdc = module.params.get('kdc')
    force_join = module.params.get('force_join')
    principal = module.params.get('principal')
    password = module.params.get('password')
    keytab = module.params.get('keytab')
    ca_cert_file = module.params.get('ca_cert_file')
    kinit_attempts = module.params.get('kinit_attempts')
    debug = module.params.get('debug')

    if password is not None and password != "" and \
       keytab is not None and keytab != "":
        module.fail_json(msg="Password and keytab cannot be used together")

    client_domain = hostname[hostname.find(".")+1:]
    nolog = tuple()
    env = {'PATH': SECURE_PATH}
    fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
    host_principal = 'host/%s@%s' % (hostname, realm)
    sssd = True

    options.ca_cert_file = ca_cert_file
    options.unattended = True
    options.principal = principal if principal != "" else None
    options.force = False
    options.password = password

    ccache_dir = None
    changed = False
    already_joined = False
    try:
        (krb_fd, krb_name) = tempfile.mkstemp()
        os.close(krb_fd)
        configure_krb5_conf(
            cli_realm=realm,
            cli_domain=domain,
            cli_server=servers,
            cli_kdc=kdc,
            dnsok=False,
            filename=krb_name,
            client_domain=client_domain,
            client_hostname=hostname,
            configure_sssd=sssd,
            force=False)
        env['KRB5_CONFIG'] = krb_name
        ccache_dir = tempfile.mkdtemp(prefix='krbcc')
        ccache_name = os.path.join(ccache_dir, 'ccache')
        join_args = [paths.SBIN_IPA_JOIN,
                     "-s", servers[0],
                     "-b", str(realm_to_suffix(realm)),
                     "-h", hostname]
        if debug:
            join_args.append("-d")
            env['XMLRPC_TRACE_CURL'] = 'yes'
        if force_join:
            join_args.append("-f")
        if principal:
            if principal.find('@') == -1:
                principal = '%s@%s' % (principal, realm)
            try:
                kinit_password(principal, password, ccache_name,
                               config=krb_name)
            except RuntimeError as e:
                module.fail_json(
                    msg="Kerberos authentication failed: {}".format(e))
        elif keytab:
            join_args.append("-f")
            if os.path.exists(keytab):
                try:
                    kinit_keytab(host_principal,
                                 keytab,
                                 ccache_name,
                                 config=krb_name,
                                 attempts=kinit_attempts)
                except gssapi.exceptions.GSSError as e:
                    module.fail_json(
                        msg="Kerberos authentication failed: {}".format(e))
            else:
                module.fail_json(
                    msg="Keytab file could not be found: {}".format(keytab))

        elif password:
            join_args.append("-w")
            join_args.append(password)
            nolog = (password,)

        env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = ccache_name
        # Get the CA certificate
        try:
            os.environ['KRB5_CONFIG'] = env['KRB5_CONFIG']
            if NUM_VERSION < 40100:
                get_ca_cert(fstore, options, servers[0], basedn)
            else:
                get_ca_certs(fstore, options, servers[0], basedn, realm)
            del os.environ['KRB5_CONFIG']
        except errors.FileError as e:
            module.fail_json(msg='%s' % e)
        except Exception as e:
            module.fail_json(msg="Cannot obtain CA certificate\n%s" % e)

        # Now join the domain
        result = run(
            join_args, raiseonerr=False, env=env, nolog=nolog,
            capture_error=True)
        stderr = result.error_output

        if result.returncode != 0:
            if result.returncode == 13:
                already_joined = True
                module.log("Host is already joined")
            else:
                if principal:
                    run(["kdestroy"], raiseonerr=False, env=env)
                module.fail_json(msg="Joining realm failed: %s" % stderr)
        else:
            changed = True
            module.log("Enrolled in IPA realm %s" % realm)

        # Fail for missing krb5.keytab on already joined host
        if already_joined and not os.path.exists(paths.KRB5_KEYTAB):
            module.fail_json(msg="krb5.keytab missing! Retry with ipaclient_force_join=yes to generate a new one.")

        if principal:
            run(["kdestroy"], raiseonerr=False, env=env)

        # Obtain the TGT. We do it with the temporary krb5.conf, sot
        # tha only the KDC we're installing under is contacted.
        # Other KDCs might not have replicated the principal yet.
        # Once we have the TGT, it's usable on any server.
        try:
            kinit_keytab(host_principal, paths.KRB5_KEYTAB,
                         paths.IPA_DNS_CCACHE,
                         config=krb_name,
                         attempts=kinit_attempts)
            env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
        except gssapi.exceptions.GSSError as e:
            # failure to get ticket makes it impossible to login and
            # bind from sssd to LDAP, abort installation
            module.fail_json(msg="Failed to obtain host TGT: %s" % e)

    finally:
        try:
            os.remove(krb_name)
        except OSError:
            module.fail_json(msg="Could not remove %s" % krb_name)
        if ccache_dir is not None:
            try:
                os.rmdir(ccache_dir)
            except OSError:
                pass
        if os.path.exists(krb_name + ".ipabkp"):
            try:
                os.remove(krb_name + ".ipabkp")
            except OSError:
                module.fail_json(msg="Could not remove %s.ipabkp" % krb_name)

    module.exit_json(changed=changed,
                     already_joined=already_joined)
Beispiel #9
0
def check_creds(options, realm_name):

    # Check if ccache is available
    default_cred = None
    try:
        logger.debug('KRB5CCNAME set to %s',
                     os.environ.get('KRB5CCNAME', None))
        # get default creds, will raise if none found
        default_cred = gssapi.creds.Credentials()
        principal = str(default_cred.name)
    except gssapi.raw.misc.GSSError as e:
        logger.debug('Failed to find default ccache: %s', e)
        principal = None

    # Check if the principal matches the requested one (if any)
    if principal is not None and options.principal is not None:
        op = options.principal
        if op.find('@') == -1:
            op = '%s@%s' % (op, realm_name)
        if principal != op:
            logger.debug('Specified principal %s does not match '
                         'available credentials (%s)',
                         options.principal, principal)
            principal = None

    if principal is None:
        (ccache_fd, ccache_name) = tempfile.mkstemp()
        os.close(ccache_fd)
        options.created_ccache_file = ccache_name

        if options.principal is not None:
            principal = options.principal
        else:
            principal = 'admin'
        stdin = None
        if principal.find('@') == -1:
            principal = '%s@%s' % (principal, realm_name)
        if options.admin_password is not None:
            stdin = options.admin_password
        else:
            if not options.unattended:
                try:
                    stdin = getpass.getpass("Password for %s: " % principal)
                except EOFError:
                    stdin = None
                if not stdin:
                    logger.error(
                        "Password must be provided for %s.", principal)
                    raise ScriptError("Missing password for %s" % principal)
            else:
                if sys.stdin.isatty():
                    logger.error("Password must be provided in "
                                 "non-interactive mode.")
                    logger.info("This can be done via "
                                "echo password | ipa-client-install "
                                "... or with the -w option.")
                    raise ScriptError("Missing password for %s" % principal)
                else:
                    stdin = sys.stdin.readline()

            # set options.admin_password for future use
            options.admin_password = stdin

        try:
            kinit_password(principal, stdin, ccache_name)
        except RuntimeError as e:
            logger.error("Kerberos authentication failed: %s", e)
            raise ScriptError("Invalid credentials: %s" % e)

        os.environ['KRB5CCNAME'] = ccache_name