Beispiel #1
0
    def __spawn_instance(self):
        """
        Create and configure a new KRA instance using pkispawn.
        Creates a configuration file with IPA-specific
        parameters and passes it to the base class to call pkispawn
        """

        # Create an empty and secured file
        (cfg_fd, cfg_file) = tempfile.mkstemp()
        os.close(cfg_fd)
        pent = pwd.getpwnam(self.service_user)
        os.chown(cfg_file, pent.pw_uid, pent.pw_gid)
        self.tmp_agent_db = tempfile.mkdtemp(prefix="tmp-",
                                             dir=paths.VAR_LIB_IPA)
        tmp_agent_pwd = ipautil.ipa_generate_password()

        # Create a temporary file for the admin PKCS #12 file
        (admin_p12_fd, admin_p12_file) = tempfile.mkstemp()
        os.close(admin_p12_fd)

        # Create KRA configuration
        config = RawConfigParser()
        config.optionxform = str
        config.add_section("KRA")

        # Security Domain Authentication
        config.set("KRA", "pki_security_domain_https_port", "443")
        config.set("KRA", "pki_security_domain_password", self.admin_password)
        config.set("KRA", "pki_security_domain_user", self.admin_user)

        # issuing ca
        config.set("KRA", "pki_issuing_ca_uri",
                   "https://%s" % ipautil.format_netloc(self.fqdn, 443))

        # Server
        config.set("KRA", "pki_enable_proxy", "True")
        config.set("KRA", "pki_restart_configured_instance", "False")
        config.set("KRA", "pki_backup_keys", "True")
        config.set("KRA", "pki_backup_password", self.admin_password)

        # Client security database
        config.set("KRA", "pki_client_database_dir", self.tmp_agent_db)
        config.set("KRA", "pki_client_database_password", tmp_agent_pwd)
        config.set("KRA", "pki_client_database_purge", "True")
        config.set("KRA", "pki_client_pkcs12_password", self.admin_password)

        # Administrator
        config.set("KRA", "pki_admin_name", self.admin_user)
        config.set("KRA", "pki_admin_uid", self.admin_user)
        config.set("KRA", "pki_admin_email", "root@localhost")
        config.set("KRA", "pki_admin_password", self.admin_password)
        config.set("KRA", "pki_admin_nickname", "ipa-ca-agent")
        config.set("KRA", "pki_admin_subject_dn",
                   str(DN(('cn', 'ipa-ca-agent'), self.subject_base)))
        config.set("KRA", "pki_import_admin_cert", "False")
        config.set("KRA", "pki_client_admin_cert_p12", admin_p12_file)

        # Directory server
        config.set("KRA", "pki_ds_ldap_port", "389")
        config.set("KRA", "pki_ds_password", self.dm_password)
        config.set("KRA", "pki_ds_base_dn", six.text_type(self.basedn))
        config.set("KRA", "pki_ds_database", "ipaca")
        config.set("KRA", "pki_ds_create_new_db", "False")

        self._use_ldaps_during_spawn(config)

        # Certificate subject DNs
        config.set("KRA", "pki_subsystem_subject_dn",
                   str(DN(('cn', 'CA Subsystem'), self.subject_base)))
        config.set("KRA", "pki_sslserver_subject_dn",
                   str(DN(('cn', self.fqdn), self.subject_base)))
        config.set("KRA", "pki_audit_signing_subject_dn",
                   str(DN(('cn', 'KRA Audit'), self.subject_base)))
        config.set(
            "KRA", "pki_transport_subject_dn",
            str(DN(('cn', 'KRA Transport Certificate'), self.subject_base)))
        config.set(
            "KRA", "pki_storage_subject_dn",
            str(DN(('cn', 'KRA Storage Certificate'), self.subject_base)))

        # Certificate nicknames
        # Note that both the server certs and subsystem certs reuse
        # the ca certs.
        config.set("KRA", "pki_subsystem_nickname",
                   "subsystemCert cert-pki-ca")
        config.set("KRA", "pki_sslserver_nickname", "Server-Cert cert-pki-ca")
        config.set("KRA", "pki_audit_signing_nickname",
                   "auditSigningCert cert-pki-kra")
        config.set("KRA", "pki_transport_nickname",
                   "transportCert cert-pki-kra")
        config.set("KRA", "pki_storage_nickname", "storageCert cert-pki-kra")

        # Shared db settings
        # Needed because CA and KRA share the same database
        # We will use the dbuser created for the CA
        config.set("KRA", "pki_share_db", "True")
        config.set(
            "KRA", "pki_share_dbuser_dn",
            str(DN(('uid', 'pkidbuser'), ('ou', 'people'), ('o', 'ipaca'))))

        if not (os.path.isdir(paths.PKI_TOMCAT_ALIAS_DIR)
                and os.path.isfile(paths.PKI_TOMCAT_PASSWORD_CONF)):
            # generate pin which we know can be used for FIPS NSS database
            pki_pin = ipautil.ipa_generate_password()
            config.set("KRA", "pki_pin", pki_pin)
        else:
            pki_pin = None

        _p12_tmpfile_handle, p12_tmpfile_name = tempfile.mkstemp(dir=paths.TMP)

        if self.clone:
            krafile = self.pkcs12_info[0]
            shutil.copy(krafile, p12_tmpfile_name)
            pent = pwd.getpwnam(self.service_user)
            os.chown(p12_tmpfile_name, pent.pw_uid, pent.pw_gid)

            # Security domain registration
            config.set("KRA", "pki_security_domain_hostname", self.fqdn)
            config.set("KRA", "pki_security_domain_https_port", "443")
            config.set("KRA", "pki_security_domain_user", self.admin_user)
            config.set("KRA", "pki_security_domain_password",
                       self.admin_password)

            # Clone
            config.set("KRA", "pki_clone", "True")
            config.set("KRA", "pki_clone_pkcs12_path", p12_tmpfile_name)
            config.set("KRA", "pki_clone_pkcs12_password", self.dm_password)
            config.set("KRA", "pki_clone_setup_replication", "False")
            config.set(
                "KRA", "pki_clone_uri",
                "https://%s" % ipautil.format_netloc(self.master_host, 443))
        else:
            # the admin cert file is needed for the first instance of KRA
            cert = self.get_admin_cert()
            # First make sure that the directory exists
            parentdir = os.path.dirname(paths.ADMIN_CERT_PATH)
            if not os.path.exists(parentdir):
                os.makedirs(parentdir)
            with open(paths.ADMIN_CERT_PATH, "wb") as admin_path:
                admin_path.write(
                    base64.b64encode(cert.public_bytes(x509.Encoding.DER)))

        # Generate configuration file
        with open(cfg_file, "w") as f:
            config.write(f)

        try:
            DogtagInstance.spawn_instance(self,
                                          cfg_file,
                                          nolog_list=(self.dm_password,
                                                      self.admin_password,
                                                      pki_pin, tmp_agent_pwd))
        finally:
            os.remove(p12_tmpfile_name)
            os.remove(cfg_file)
            os.remove(admin_p12_file)

        shutil.move(paths.KRA_BACKUP_KEYS_P12, paths.KRACERT_P12)
        logger.debug("completed creating KRA instance")
Beispiel #2
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),
            basedn=dict(required=True),
            principal=dict(required=False),
            subject_base=dict(required=True),
            ca_enabled=dict(required=True, type='bool'),
            mkhomedir=dict(required=False, type='bool'),
            on_master=dict(required=False, type='bool'),
        ),
        supports_check_mode = True,
    )

    module._ansible_debug = True
    servers = module.params.get('servers')
    realm = module.params.get('realm')
    hostname = module.params.get('hostname')
    basedn = module.params.get('basedn')
    domain = module.params.get('domain')
    principal = module.params.get('principal')
    subject_base = module.params.get('subject_base')
    ca_enabled = module.params.get('ca_enabled')
    mkhomedir = module.params.get('mkhomedir')
    on_master = module.params.get('on_master')

    fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
    statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)

    ###########################################################################

    os.environ['KRB5CCNAME'] = CCACHE_FILE
    
    class Object(object):
        pass
    options = Object()
    options.dns_updates = False
    options.all_ip_addresses = False
    options.ip_addresses = None
    options.request_cert = False
    options.hostname = hostname
    options.preserve_sssd = False
    options.on_master = False
    options.conf_ssh = True
    options.conf_sshd = True
    options.conf_sudo = True
    options.primary = False
    options.permit = False
    options.krb5_offline_passwords = False
    options.create_sshfp = True

    ##########################################################################

    # Create IPA NSS database
    try:
        create_ipa_nssdb()
    except ipautil.CalledProcessError as e:
        module.fail_json(msg="Failed to create IPA NSS database: %s" % e)

    # Get CA certificates from the certificate store
    try:
        ca_certs = get_certs_from_ldap(servers[0], basedn, realm,
                                       ca_enabled)
    except errors.NoCertificateError:
        if ca_enabled:
            ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
        else:
            ca_subject = None
        ca_certs = certstore.make_compat_ca_certs(ca_certs, realm,
                                                  ca_subject)
    ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u))
                      for (c, n, t, u) in ca_certs]

    if hasattr(paths, "KDC_CA_BUNDLE_PEM"):
        x509.write_certificate_list(
            [c for c, n, t, u in ca_certs if t is not False],
            paths.KDC_CA_BUNDLE_PEM)
    if hasattr(paths, "CA_BUNDLE_PEM"):
        x509.write_certificate_list(
            [c for c, n, t, u in ca_certs if t is not False],
            paths.CA_BUNDLE_PEM)

    # Add the CA certificates to the IPA NSS database
    module.debug("Adding CA certificates to the IPA NSS database.")
    ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
    for cert, nickname, trust_flags in ca_certs_trust:
        try:
            ipa_db.add_cert(cert, nickname, trust_flags)
        except CalledProcessError as e:
            module.fail_json(msg="Failed to add %s to the IPA NSS database." % nickname)

    # Add the CA certificates to the platform-dependant systemwide CA store
    tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)

    if not on_master:
        client_dns(servers[0], hostname, options)
        configure_certmonger(fstore, subject_base, realm, hostname,
                             options, ca_enabled)

    if hasattr(paths, "SSH_CONFIG_DIR"):
        ssh_config_dir = paths.SSH_CONFIG_DIR
    else:
        ssh_config_dir = services.knownservices.sshd.get_config_dir()
    update_ssh_keys(hostname, ssh_config_dir, options.create_sshfp)

    try:
        os.remove(CCACHE_FILE)
    except Exception:
        pass

    ##########################################################################

    # Name Server Caching Daemon. Disable for SSSD, use otherwise
    # (if installed)
    nscd = services.knownservices.nscd
    if nscd.is_installed():
        save_state(nscd, statestore)

        try:
            nscd_service_action = 'stop'
            nscd.stop()
        except Exception:
            module.warn("Failed to %s the %s daemon" %
                        (nscd_service_action, nscd.service_name))

        try:
            nscd.disable()
        except Exception:
            module.warn("Failed to disable %s daemon. Disable it manually." %
                        nscd.service_name)

    nslcd = services.knownservices.nslcd
    if nslcd.is_installed():
        save_state(nslcd, statestore)

    retcode, conf = (0, None)

    ##########################################################################

    # Modify nsswitch/pam stack
    tasks.modify_nsswitch_pam_stack(sssd=True,
                                    mkhomedir=mkhomedir,
                                    statestore=statestore)

    module.log("SSSD enabled")

    argspec = inspect.getargspec(services.service)
    if len(argspec.args) > 1:
        sssd = services.service('sssd', api)
    else:
        sssd = services.service('sssd')
    try:
        sssd.restart()
    except CalledProcessError:
        module.warn("SSSD service restart was unsuccessful.")

    try:
        sssd.enable()
    except CalledProcessError as e:
        module.warn(
            "Failed to enable automatic startup of the SSSD daemon: "
            "%s", e)

    if configure_openldap_conf(fstore, basedn, servers):
        module.log("Configured /etc/openldap/ldap.conf")
    else:
        module.log("Failed to configure /etc/openldap/ldap.conf")

    # Check that nss is working properly
    if not on_master:
        user = principal
        if user is None or user == "":
            user = "******" % domain
            module.log("Principal is not set when enrolling with OTP"
                       "; using principal '%s' for 'getent passwd'" % user)
        elif '@' not in user:
            user = "******" % (user, domain)
        n = 0
        found = False
        # Loop for up to 10 seconds to see if nss is working properly.
        # It can sometimes take a few seconds to connect to the remote
        # provider.
        # Particulary, SSSD might take longer than 6-8 seconds.
        while n < 10 and not found:
            try:
                ipautil.run(["getent", "passwd", user])
                found = True
            except Exception as e:
                time.sleep(1)
                n = n + 1

        if not found:
            module.fail_json(msg="Unable to find '%s' user with 'getent "
                             "passwd %s'!" % (user.split("@")[0], user))
            if conf:
                module.log("Recognized configuration: %s" % conf)
            else:
                module.fail_json(msg=
                                 "Unable to reliably detect "
                                 "configuration. Check NSS setup manually.")

            try:
                hardcode_ldap_server(servers)
            except Exception as e:
                module.fail_json(msg="Adding hardcoded server name to "
                                 "/etc/ldap.conf failed: %s" % str(e))

    ##########################################################################

    module.exit_json(changed=True,
                     ca_enabled_ra=ca_enabled)
Beispiel #3
0
    def __call__(self, environ, start_response):
        logger.info('WSGI change_password.__call__:')

        # Get the user and password parameters from the request
        content_type = environ.get('CONTENT_TYPE', '').lower()
        if not content_type.startswith('application/x-www-form-urlencoded'):
            return self.bad_request(
                environ, start_response,
                "Content-Type must be application/x-www-form-urlencoded")

        method = environ.get('REQUEST_METHOD', '').upper()
        if method == 'POST':
            query_string = read_input(environ)
        else:
            return self.bad_request(environ, start_response,
                                    "HTTP request method must be POST")

        try:
            query_dict = parse_qs(query_string)
        except Exception as e:
            return self.bad_request(environ, start_response,
                                    "cannot parse query data")

        data = {}
        for field in ('user', 'old_password', 'new_password', 'otp'):
            value = query_dict.get(field, None)
            if value is not None:
                if len(value) == 1:
                    data[field] = value[0]
                else:
                    return self.bad_request(
                        environ, start_response,
                        "more than one %s parameter" % field)
            elif field != 'otp':  # otp is optional
                return self.bad_request(environ, start_response,
                                        "no %s specified" % field)

        # start building the response
        logger.info("WSGI change_password: start password change of user '%s'",
                    data['user'])
        status = HTTP_STATUS_SUCCESS
        response_headers = [('Content-Type', 'text/html; charset=utf-8')]
        title = 'Password change rejected'
        result = 'error'
        policy_error = None

        bind_dn = DN((self.api.Object.user.primary_key.name, data['user']),
                     self.api.env.container_user, self.api.env.basedn)

        try:
            pw = data['old_password']
            if data.get('otp'):
                pw = data['old_password'] + data['otp']
            conn = ldap2(self.api)
            conn.connect(bind_dn=bind_dn, bind_pw=pw)
        except (NotFound, ACIError):
            result = 'invalid-password'
            message = 'The old password or username is not correct.'
        except Exception as e:
            message = "Could not connect to LDAP server."
            logger.error(
                "change_password: cannot authenticate '%s' to LDAP "
                "server: %s", data['user'], str(e))
        else:
            try:
                conn.modify_password(bind_dn,
                                     data['new_password'],
                                     data['old_password'],
                                     skip_bind=True)
            except ExecutionError as e:
                result = 'policy-error'
                policy_error = escape(str(e))
                message = "Password change was rejected: %s" % escape(str(e))
            except Exception as e:
                message = "Could not change the password"
                logger.error(
                    "change_password: cannot change password of "
                    "'%s': %s", data['user'], str(e))
            else:
                result = 'ok'
                title = "Password change successful"
                message = "Password was changed."
            finally:
                if conn.isconnected():
                    conn.disconnect()

        logger.info('%s: %s', status, message)

        response_headers.append(('X-IPA-Pwchange-Result', result))
        if policy_error:
            response_headers.append(
                ('X-IPA-Pwchange-Policy-Error', policy_error))

        start_response(status, response_headers)
        output = _success_template % dict(title=str(title),
                                          message=str(message))
        return [output.encode('utf-8')]
Beispiel #4
0
def _group_dn(group):
    return DN(('cn', group), OU_GROUPS_DN)
Beispiel #5
0
 def get_delete_dn(self, *keys, **options):
     active_dn = self.get_dn(*keys, **options)
     return DN(active_dn[0], self.delete_container_dn, api.env.basedn)
Beispiel #6
0
    def __enable_ssl(self):
        dirname = config_dirname(self.serverid)
        dsdb = certs.CertDB(
            self.realm,
            nssdir=dirname,
            subject_base=self.subject_base,
            ca_subject=self.ca_subject,
        )
        if self.pkcs12_info:
            if self.ca_is_configured:
                trust_flags = IPA_CA_TRUST_FLAGS
            else:
                trust_flags = EXTERNAL_CA_TRUST_FLAGS
            dsdb.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1],
                                    ca_file=self.ca_file,
                                    trust_flags=trust_flags)
            server_certs = dsdb.find_server_certs()
            if len(server_certs) == 0:
                raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0])

            # We only handle one server cert
            self.nickname = server_certs[0][0]
            self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)

            if self.ca_is_configured:
                dsdb.track_server_cert(
                    self.nickname, self.principal, dsdb.passwd_fname,
                    'restart_dirsrv %s' % self.serverid)

            self.add_cert_to_service()
        else:
            dsdb.create_from_cacert()
            if self.master_fqdn is None:
                ca_args = [
                    paths.CERTMONGER_DOGTAG_SUBMIT,
                    '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
                    '--certfile', paths.RA_AGENT_PEM,
                    '--keyfile', paths.RA_AGENT_KEY,
                    '--cafile', paths.IPA_CA_CRT,
                    '--agent-submit'
                ]
                helper = " ".join(ca_args)
                prev_helper = certmonger.modify_ca_helper('IPA', helper)
            else:
                prev_helper = None
            try:
                cmd = 'restart_dirsrv %s' % self.serverid
                certmonger.request_and_wait_for_cert(
                    certpath=dirname,
                    nickname=self.nickname,
                    principal=self.principal,
                    passwd_fname=dsdb.passwd_fname,
                    subject=str(DN(('CN', self.fqdn), self.subject_base)),
                    ca='IPA',
                    profile=dogtag.DEFAULT_PROFILE,
                    dns=[self.fqdn],
                    post_command=cmd)
            finally:
                if prev_helper is not None:
                    certmonger.modify_ca_helper('IPA', prev_helper)

            # restart_dirsrv in the request above restarts DS, reconnect ldap2
            api.Backend.ldap2.disconnect()
            api.Backend.ldap2.connect()

            self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)

            if prev_helper is not None:
                self.add_cert_to_service()

        dsdb.create_pin_file()

        self.cacert_name = dsdb.cacert_name

        ldap_uri = ipaldap.get_ldap_uri(self.fqdn)
        conn = ipaldap.LDAPClient(ldap_uri)
        conn.simple_bind(bind_dn=ipaldap.DIRMAN_DN,
                         bind_password=self.dm_password)

        mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"),
               (ldap.MOD_REPLACE, "nsSSL3Ciphers", "default"),
               (ldap.MOD_REPLACE, "allowWeakCipher", "off")]
        conn.modify_s(DN(('cn', 'encryption'), ('cn', 'config')), mod)

        mod = [(ldap.MOD_ADD, "nsslapd-security", "on")]
        conn.modify_s(DN(('cn', 'config')), mod)

        entry = conn.make_entry(
            DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')),
            objectclass=["top", "nsEncryptionModule"],
            cn=["RSA"],
            nsSSLPersonalitySSL=[self.nickname],
            nsSSLToken=["internal (software)"],
            nsSSLActivation=["on"],
        )
        conn.add_entry(entry)

        conn.unbind()

        # check for open secure port 636 from now on
        self.open_ports.append(636)
Beispiel #7
0
from ipaplatform.paths import paths
from ipaplatform.tasks import tasks
from ipapython import directivesetter
from ipapython import ipaldap
from ipapython import ipautil
from ipapython.dn import DN
from ipaserver.install import service
from ipaserver.install import sysupgrade
from ipaserver.install import replication
from ipaserver.install.installutils import stopped_service

logger = logging.getLogger(__name__)

INTERNAL_TOKEN = "internal"

OU_GROUPS_DN = DN(('ou', 'groups'), ('o', 'ipaca'))


def _person_dn(uid):
    return DN(('uid', uid), ('ou', 'people'), ('o', 'ipaca'))


def _group_dn(group):
    return DN(('cn', group), OU_GROUPS_DN)


def get_security_domain():
    """
    Get the security domain from the REST interface on the local Dogtag CA
    This function will succeed if the local dogtag CA is up.
    """
Beispiel #8
0
    def execute(self, **options):
        ldap = self.api.Backend.ldap2

        base_dn = DN(self.api.env.container_ranges, self.api.env.basedn)
        search_filter = ("(&(objectClass=ipaIDrange)(!(ipaRangeType=*)))")
        root_logger.debug("update_idrange_type: search for ID ranges with no "
                          "type set")

        while True:
            # Run the search in loop to avoid issues when LDAP limits are hit
            # during update

            try:
                (entries, truncated) = ldap.find_entries(search_filter,
                    ['objectclass'], base_dn, time_limit=0, size_limit=0)

            except errors.NotFound:
                root_logger.debug("update_idrange_type: no ID range without "
                                  "type set found")
                return False, []

            except errors.ExecutionError, e:
                root_logger.error("update_idrange_type: cannot retrieve list "
                                  "of ranges with no type set: %s", e)
                return False, []

            if not entries:
                # No entry was returned, rather break than continue cycling
                root_logger.debug("update_idrange_type: no ID range was "
                                  "returned")
                return False, []

            root_logger.debug("update_idrange_type: found %d "
                              "idranges to update, truncated: %s",
                              len(entries), truncated)

            error = False

            # Set the range type
            for entry in entries:
                objectclasses = [o.lower() for o
                                           in entry.get('objectclass', [])]

                if 'ipatrustedaddomainrange' in objectclasses:
                    # NOTICE: assumes every AD range does not use POSIX
                    #         attributes
                    entry['ipaRangeType'] = ['ipa-ad-trust']
                elif 'ipadomainidrange' in objectclasses:
                    entry['ipaRangeType'] = ['ipa-local']
                else:
                    entry['ipaRangeType'] = ['unknown']
                    root_logger.error("update_idrange_type: could not detect "
                                      "range type for entry: %s" % str(entry.dn))
                    root_logger.error("update_idrange_type: ID range type set "
                                      "to 'unknown' for entry: %s" % str(entry.dn))

                try:
                    ldap.update_entry(entry)
                except (errors.EmptyModlist, errors.NotFound):
                    pass
                except errors.ExecutionError, e:
                    root_logger.debug("update_idrange_type: cannot "
                                      "update idrange type: %s", e)
                    error = True
Beispiel #9
0
 def __init__(self, cn, **kwargs):
     super(IdpTracker, self).__init__(default_version=None)
     self.cn = cn
     self.dn = DN(('cn', cn), api.env.container_idp, api.env.basedn)
     self.kwargs = kwargs
Beispiel #10
0
class test_hostgroup(Declarative):

    cleanup_commands = [
        ('hostgroup_del', [hostgroup1], {}),
        ('host_del', [fqdn1], {}),
    ]

    tests=[

        dict(
            desc='Try to retrieve non-existent %r' % hostgroup1,
            command=('hostgroup_show', [hostgroup1], {}),
            expected=errors.NotFound(
                reason=u'%s: host group not found' % hostgroup1),
        ),


        dict(
            desc='Try to update non-existent %r' % hostgroup1,
            command=('hostgroup_mod', [hostgroup1],
                dict(description=u'Updated hostgroup 1')
            ),
            expected=errors.NotFound(
                reason=u'%s: host group not found' % hostgroup1),
        ),


        dict(
            desc='Try to delete non-existent %r' % hostgroup1,
            command=('hostgroup_del', [hostgroup1], {}),
            expected=errors.NotFound(
                reason=u'%s: host group not found' % hostgroup1),
        ),


        dict(
            desc='Test an invalid hostgroup name %r' % invalidhostgroup1,
            command=('hostgroup_add', [invalidhostgroup1], dict(description=u'Test')),
            expected=errors.ValidationError(name='hostgroup_name',
                error=u'may only include letters, numbers, _, -, and .'),
        ),


        dict(
            desc='Create %r' % hostgroup1,
            command=('hostgroup_add', [hostgroup1],
                dict(description=u'Test hostgroup 1')
            ),
            expected=dict(
                value=hostgroup1,
                summary=u'Added hostgroup "testhostgroup1"',
                result=dict(
                    dn=dn1,
                    cn=[hostgroup1],
                    objectclass=objectclasses.hostgroup,
                    description=[u'Test hostgroup 1'],
                    ipauniqueid=[fuzzy_uuid],
                    mepmanagedentry=[DN(('cn',hostgroup1),('cn','ng'),('cn','alt'),
                                        api.env.basedn)],
                ),
            ),
        ),


        dict(
            desc='Try to create duplicate %r' % hostgroup1,
            command=('hostgroup_add', [hostgroup1],
                dict(description=u'Test hostgroup 1')
            ),
            expected=errors.DuplicateEntry(message=
                u'host group with name "%s" already exists' % hostgroup1),
        ),


        dict(
            desc='Create host %r' % fqdn1,
            command=('host_add', [fqdn1],
                dict(
                    description=u'Test host 1',
                    l=u'Undisclosed location 1',
                    force=True,
                ),
            ),
            expected=dict(
                value=fqdn1,
                summary=u'Added host "%s"' % fqdn1,
                result=dict(
                    dn=host_dn1,
                    fqdn=[fqdn1],
                    description=[u'Test host 1'],
                    l=[u'Undisclosed location 1'],
                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
                    objectclass=objectclasses.host,
                    ipauniqueid=[fuzzy_uuid],
                    managedby_host=[fqdn1],
                    has_keytab=False,
                    has_password=False,
                ),
            ),
        ),


        dict(
            desc=u'Add host %r to %r' % (fqdn1, hostgroup1),
            command=(
                'hostgroup_add_member', [hostgroup1], dict(host=fqdn1)
            ),
            expected=dict(
                completed=1,
                failed=dict(
                    member=dict(
                        host=tuple(),
                        hostgroup=tuple(),
                    ),
                ),
                result={
                    'dn': dn1,
                    'cn': [hostgroup1],
                    'description': [u'Test hostgroup 1'],
                    'member_host': [fqdn1],
                },
            ),
        ),


        dict(
            desc='Retrieve %r' % hostgroup1,
            command=('hostgroup_show', [hostgroup1], {}),
            expected=dict(
                value=hostgroup1,
                summary=None,
                result={
                    'dn': dn1,
                    'member_host': [u'testhost1.%s' % api.env.domain],
                    'cn': [hostgroup1],
                    'description': [u'Test hostgroup 1'],
                },
            ),
        ),


        dict(
            desc='Search for %r' % hostgroup1,
            command=('hostgroup_find', [], dict(cn=hostgroup1)),
            expected=dict(
                count=1,
                truncated=False,
                summary=u'1 hostgroup matched',
                result=[
                    {
                        'dn': dn1,
                        'member_host': [u'testhost1.%s' % api.env.domain],
                        'cn': [hostgroup1],
                        'description': [u'Test hostgroup 1'],
                    },
                ],
            ),
        ),


        dict(
            desc='Update %r' % hostgroup1,
            command=('hostgroup_mod', [hostgroup1],
                dict(description=u'Updated hostgroup 1')
            ),
            expected=dict(
                value=hostgroup1,
                summary=u'Modified hostgroup "testhostgroup1"',
                result=dict(
                    cn=[hostgroup1],
                    description=[u'Updated hostgroup 1'],
                    member_host=[u'testhost1.%s' % api.env.domain],
                ),
            ),
        ),


        dict(
            desc='Retrieve %r to verify update' % hostgroup1,
            command=('hostgroup_show', [hostgroup1], {}),
            expected=dict(
                value=hostgroup1,
                summary=None,
                result={
                    'dn': dn1,
                    'member_host': [u'testhost1.%s' % api.env.domain],
                    'cn': [hostgroup1],
                    'description': [u'Updated hostgroup 1'],
                },
            ),
        ),


        dict(
            desc='Remove host %r from %r' % (fqdn1, hostgroup1),
            command=('hostgroup_remove_member', [hostgroup1],
                dict(host=fqdn1)
            ),
            expected=dict(
                failed=dict(
                    member=dict(
                        host=tuple(),
                        hostgroup=tuple(),
                    ),
                ),
                completed=1,
                result={
                    'dn': dn1,
                    'cn': [hostgroup1],
                    'description': [u'Updated hostgroup 1'],
                },
            ),
        ),


        dict(
            desc='Delete %r' % hostgroup1,
            command=('hostgroup_del', [hostgroup1], {}),
            expected=dict(
                value=[hostgroup1],
                summary=u'Deleted hostgroup "testhostgroup1"',
                result=dict(failed=[]),
            ),
        ),


        dict(
            desc='Create  hostgroup with name containing only one letter: %r' % hostgroup_single,
            command=('hostgroup_add', [hostgroup_single],
                dict(description=u'Test hostgroup with single letter in name')
            ),
            expected=dict(
                value=hostgroup_single,
                summary=u'Added hostgroup "a"',
                result=dict(
                    dn=dn_single,
                    cn=[hostgroup_single],
                    objectclass=objectclasses.hostgroup,
                    description=[u'Test hostgroup with single letter in name'],
                    ipauniqueid=[fuzzy_uuid],
                    mepmanagedentry=[DN(('cn',hostgroup_single),('cn','ng'),('cn','alt'),
                                        api.env.basedn)],
                ),
            ),
        ),


        dict(
            desc='Delete %r' % hostgroup_single,
            command=('hostgroup_del', [hostgroup_single], {}),
            expected=dict(
                value=[hostgroup_single],
                summary=u'Deleted hostgroup "a"',
                result=dict(failed=[]),
            ),
        ),


        dict(
            desc='Delete host %r' % fqdn1,
            command=('host_del', [fqdn1], {}),
            expected=dict(
                value=[fqdn1],
                summary=u'Deleted host "%s"' % fqdn1,
                result=dict(failed=[]),
            ),
        )

    ]
Beispiel #11
0
    def _finalize_core(self, **defaults):
        """
        Complete initialization of standard IPA environment.

        This method will perform the following steps:

            1. Call `Env._bootstrap()` if it hasn't already been called.

            2. Merge-in variables from the configuration file ``self.conf``
               (if it exists) by calling `Env._merge_from_file()`.

            3. Merge-in variables from the defaults configuration file
               ``self.conf_default`` (if it exists) by calling
               `Env._merge_from_file()`.

            4. Intelligently fill-in the *in_server* , *logdir*, *log*, and
               *jsonrpc_uri* variables if they haven't already been set.

            5. Merge-in the variables in ``defaults`` by calling `Env._merge()`.
               In normal circumstances ``defaults`` will simply be those
               specified in `constants.DEFAULT_CONFIG`.

        After this method is called, all the environment variables used by all
        the built-in plugins will be available.  As such, this method should be
        called *before* any plugins are loaded.

        After this method has finished, the `Env` instance is still writable
        so that 3rd-party plugins can set variables they may require as the
        plugins are registered.

        Also see `Env._finalize()`, the final method in the bootstrap sequence.

        :param defaults: Internal defaults for all built-in variables.
        """
        self.__doing('_finalize_core')
        self.__do_if_not_done('_bootstrap')

        # Merge in context config file and then default config file:
        mode = self.__d.get('mode')  # pylint: disable=no-member
        if mode != 'dummy':
            self._merge_from_file(self.conf)
            self._merge_from_file(self.conf_default)

        # Determine if in_server:
        if 'in_server' not in self:
            self.in_server = (self.context == 'server')

        # Set logdir:
        if 'logdir' not in self:
            if self.in_tree or not self.in_server:
                self.logdir = self._join('dot_ipa', 'log')
            else:
                self.logdir = path.join('/', 'var', 'log', 'ipa')

        # Set log file:
        if 'log' not in self:
            self.log = self._join('logdir', '%s.log' % self.context)

        # Workaround for ipa-server-install --uninstall. When no config file
        # is available, we set realm, domain, and basedn to RFC 2606 reserved
        # suffix to suppress attribute errors during uninstallation.
        if (self.in_server and self.context == 'installer' and
                not getattr(self, 'config_loaded', False)):
            if 'realm' not in self:
                self.realm = 'UNCONFIGURED.INVALID'
            if 'domain' not in self:
                self.domain = self.realm.lower()

        if 'basedn' not in self and 'domain' in self:
            self.basedn = DN(*(('dc', dc) for dc in self.domain.split('.')))

        # Derive xmlrpc_uri from server
        # (Note that this is done before deriving jsonrpc_uri from xmlrpc_uri
        # and server from jsonrpc_uri so that when only server or xmlrpc_uri
        # is specified, all 3 keys have a value.)
        if 'xmlrpc_uri' not in self and 'server' in self:
            # pylint: disable=no-member, access-member-before-definition
            self.xmlrpc_uri = 'https://{}/ipa/xml'.format(self.server)

        # Derive ldap_uri from server
        if 'ldap_uri' not in self and 'server' in self:
            # pylint: disable=no-member, access-member-before-definition
            self.ldap_uri = 'ldap://{}'.format(self.server)

        # Derive jsonrpc_uri from xmlrpc_uri
        if 'jsonrpc_uri' not in self:
            if 'xmlrpc_uri' in self:
                xmlrpc_uri = self.xmlrpc_uri
            else:
                xmlrpc_uri = defaults.get('xmlrpc_uri')
            if xmlrpc_uri:
                (scheme, netloc, uripath, params, query, fragment
                        ) = urlparse(xmlrpc_uri)
                uripath = uripath.replace('/xml', '/json', 1)
                self.jsonrpc_uri = urlunparse((
                        scheme, netloc, uripath, params, query, fragment))

        if 'server' not in self:
            if 'jsonrpc_uri' in self:
                jsonrpc_uri = self.jsonrpc_uri
            else:
                jsonrpc_uri = defaults.get('jsonrpc_uri')
            if jsonrpc_uri:
                parsed = urlparse(jsonrpc_uri)
                self.server = parsed.netloc

        self._merge(**defaults)

        # set the best known TLS version if min/max versions are not set
        if 'tls_version_min' not in self:
            self.tls_version_min = TLS_VERSIONS[-1]
        elif self.tls_version_min not in TLS_VERSIONS:
            raise errors.EnvironmentError(
                "Unknown TLS version '{ver}' set in tls_version_min."
                .format(ver=self.tls_version_min))

        if 'tls_version_max' not in self:
            self.tls_version_max = TLS_VERSIONS[-1]
        elif self.tls_version_max not in TLS_VERSIONS:
            raise errors.EnvironmentError(
                "Unknown TLS version '{ver}' set in tls_version_max."
                .format(ver=self.tls_version_max))

        if self.tls_version_max < self.tls_version_min:
            raise errors.EnvironmentError(
                "tls_version_min is set to a higher TLS version than "
                "tls_version_max.")
Beispiel #12
0
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Test the `ipalib.plugins.hostgroup` module.
"""

from ipalib import api, errors
from ipatests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid
from ipatests.test_xmlrpc import objectclasses
from ipapython.dn import DN

hostgroup1 = u'testhostgroup1'
dn1 = DN(('cn',hostgroup1),('cn','hostgroups'),('cn','accounts'),
         api.env.basedn)

hostgroup_single = u'a'
dn_single = DN(('cn',hostgroup_single),('cn','hostgroups'),('cn','accounts'),
         api.env.basedn)

fqdn1 = u'testhost1.%s' % api.env.domain
host_dn1 = DN(('fqdn',fqdn1),('cn','computers'),('cn','accounts'),
              api.env.basedn)

invalidhostgroup1 = u'@invalid'


class test_hostgroup(Declarative):

    cleanup_commands = [
Beispiel #13
0
    def __setup_ssl(self):
        key_passwd_file = paths.HTTPD_PASSWD_FILE_FMT.format(host=api.env.host)
        with open(key_passwd_file, 'wb') as f:
            os.fchmod(f.fileno(), 0o600)
            pkey_passwd = ipautil.ipa_generate_password().encode('utf-8')
            f.write(pkey_passwd)

        if self.pkcs12_info:
            p12_certs, p12_priv_keys = certs.pkcs12_to_certkeys(
                *self.pkcs12_info)
            keys_dict = {
                k.public_key().public_numbers(): k
                for k in p12_priv_keys
            }
            certs_keys = [(c, keys_dict.get(c.public_key().public_numbers()))
                          for c in p12_certs]
            server_certs_keys = [(c, k) for c, k in certs_keys
                                 if k is not None]

            if not server_certs_keys:
                raise RuntimeError(
                    "Could not find a suitable server cert in import in %s" %
                    self.pkcs12_info[0])

            # We only handle one server cert
            self.cert = server_certs_keys[0][0]
            x509.write_certificate(self.cert, paths.HTTPD_CERT_FILE)
            x509.write_pem_private_key(server_certs_keys[0][1],
                                       paths.HTTPD_KEY_FILE,
                                       passwd=pkey_passwd)

            if self.ca_is_configured:
                self.start_tracking_certificates()

            self.add_cert_to_service()

        else:
            if not self.promote:
                ca_args = [
                    paths.CERTMONGER_DOGTAG_SUBMIT, '--ee-url',
                    'https://%s:8443/ca/ee/ca' % self.fqdn, '--certfile',
                    paths.RA_AGENT_PEM, '--keyfile', paths.RA_AGENT_KEY,
                    '--cafile', paths.IPA_CA_CRT, '--agent-submit'
                ]
                helper = " ".join(ca_args)
                prev_helper = certmonger.modify_ca_helper('IPA', helper)
            else:
                prev_helper = None
            try:
                certmonger.request_and_wait_for_cert(
                    certpath=(paths.HTTPD_CERT_FILE, paths.HTTPD_KEY_FILE),
                    principal=self.principal,
                    subject=str(DN(('CN', self.fqdn), self.subject_base)),
                    ca='IPA',
                    profile=dogtag.DEFAULT_PROFILE,
                    dns=[self.fqdn],
                    post_command='restart_httpd',
                    storage='FILE',
                    passwd_fname=key_passwd_file)
            finally:
                if prev_helper is not None:
                    certmonger.modify_ca_helper('IPA', prev_helper)
            self.cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)

            if prev_helper is not None:
                self.add_cert_to_service()

            with open(paths.HTTPD_KEY_FILE, 'rb') as f:
                priv_key = x509.load_pem_private_key(
                    f.read(), pkey_passwd, backend=x509.default_backend())

            # Verify we have a valid server cert
            if (priv_key.public_key().public_numbers() !=
                    self.cert.public_key().public_numbers()):
                raise RuntimeError(
                    "The public key of the issued HTTPD service certificate "
                    "does not match its private key.")

        sysupgrade.set_upgrade_state('ssl.conf', 'migrated_to_mod_ssl', True)
Beispiel #14
0
class idp(LDAPObject):
    """
    Identity Provider object.
    """
    container_dn = api.env.container_idp
    object_name = _('Identity Provider server')
    object_name_plural = _('Identity Provider servers')
    object_class = ['ipaidp']
    default_attributes = [
        'cn',
        'ipaidpauthendpoint',
        'ipaidpdevauthendpoint',
        'ipaidpuserinfoendpoint',
        'ipaidpkeysendpoint',
        'ipaidptokenendpoint',
        'ipaidpissuerurl',
        'ipaidpclientid',
        'ipaidpscope',
        'ipaidpsub',
    ]
    search_attributes = [
        'cn',
        'ipaidpauthendpoint',
        'ipaidpdevauthendpoint',
        'ipaidptokenendpoint',
        'ipaidpuserinfoendpoint',
        'ipaidpkeysendpoint',
        'ipaidpscope',
        'ipaidpsub',
    ]
    allow_rename = True
    label = _('Identity Provider servers')
    label_singular = _('Identity Provider server')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Identity Provider server name'),
            primary_key=True,
        ),
        Str(
            'ipaidpauthendpoint?',
            validate_uri,
            cli_name='auth_uri',
            label=_('Authorization URI'),
            doc=_('OAuth 2.0 authorization endpoint'),
        ),
        Str(
            'ipaidpdevauthendpoint?',
            validate_uri,
            cli_name='dev_auth_uri',
            label=_('Device authorization URI'),
            doc=_('Device authorization endpoint'),
        ),
        Str(
            'ipaidptokenendpoint?',
            validate_uri,
            cli_name='token_uri',
            label=_('Token URI'),
            doc=_('Token endpoint'),
        ),
        Str(
            'ipaidpuserinfoendpoint?',
            validate_uri,
            cli_name='userinfo_uri',
            label=_('User info URI'),
            doc=_('User information endpoint'),
        ),
        Str(
            'ipaidpkeysendpoint?',
            validate_uri,
            cli_name='keys_uri',
            label=_('JWKS URI'),
            doc=_('JWKS endpoint'),
        ),
        Str(
            'ipaidpissuerurl?',
            cli_name='issuer_url',
            label=_('OIDC URL'),
            doc=_('The Identity Provider OIDC URL'),
        ),
        Str(
            'ipaidpclientid',
            cli_name='client_id',
            label=_('Client identifier'),
            doc=_('OAuth 2.0 client identifier'),
        ),
        Password(
            'ipaidpclientsecret?',
            cli_name='secret',
            label=_('Secret'),
            doc=_('OAuth 2.0 client secret'),
            confirm=True,
            flags={'no_display'},
        ),
        Str(
            'ipaidpscope?',
            cli_name='scope',
            label=_('Scope'),
            doc=_('OAuth 2.0 scope. Multiple scopes separated by space'),
        ),
        Str(
            'ipaidpsub?',
            cli_name='idp_user_id',
            label=_('External IdP user identifier attribute'),
            doc=_('Attribute for user identity in OAuth 2.0 userinfo'),
        ),
    )

    permission_filter_objectclasses = ['ipaidp']
    managed_permissions = {
        'System: Add External IdP server': {
            'ipapermright': {'add'},
            'ipapermlocation': DN(container_dn, api.env.basedn),
            'ipapermtargetfilter': {'(objectclass=ipaidp)'},
            'default_privileges': {'External IdP server Administrators'}
        },
        'System: Read External IdP server': {
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn',
                'objectclass',
                'ipaidpauthendpoint',
                'ipaidpdevauthendpoint',
                'ipaidpuserinfoendpoint',
                'ipaidptokenendpoint',
                'ipaidpkeysendpoint',
                'ipaidpissuerurl',
                'ipaidpclientid',
                'ipaidpscope',
                'ipaidpsub',
            },
            'ipapermlocation': DN(container_dn, api.env.basedn),
            'ipapermtargetfilter': {'(objectclass=ipaidp)'},
            'default_privileges': {'External IdP server Administrators'}
        },
        'System: Modify External IdP server': {
            'ipapermright': {'write'},
            'ipapermlocation': DN(container_dn, api.env.basedn),
            'ipapermdefaultattr': {
                'cn',
                'objectclass',
                'ipaidpauthendpoint',
                'ipaidpdevauthendpoint',
                'ipaidpuserinfoendpoint',
                'ipaidptokenendpoint',
                'ipaidpkeysendpoint',
                'ipaidpissuerurl',
                'ipaidpclientid',
                'ipaidpscope',
                'ipaidpclientsecret',
                'ipaidpsub',
            },
            'default_privileges': {'External IdP server Administrators'}
        },
        'System: Delete External IdP server': {
            'ipapermright': {'delete'},
            'ipapermlocation': DN(container_dn, api.env.basedn),
            'ipapermtargetfilter': {'(objectclass=ipaidp)'},
            'default_privileges': {'External IdP server Administrators'}
        },
        'System: Read External IdP server client secret': {
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermlocation': DN(container_dn, api.env.basedn),
            'ipapermdefaultattr': {
                'cn',
                'objectclass',
                'ipaidpauthendpoint',
                'ipaidpdevauthendpoint',
                'ipaidpuserinfoendpoint',
                'ipaidptokenendpoint',
                'ipaidpissuerurl',
                'ipaidpkeysendpoint',
                'ipaidpclientid',
                'ipaidpscope',
                'ipaidpclientsecret',
                'ipaidpsub',
            },
            'ipapermtargetfilter': {'(objectclass=ipaidp)'},
        }
    }
Beispiel #15
0
    def setup_admin(self):
        self.admin_user = "******" % self.fqdn
        self.admin_password = ipautil.ipa_generate_password()
        self.admin_dn = DN(('uid', self.admin_user), self.ipaca_people)
        # remove user if left-over exists
        try:
            api.Backend.ldap2.delete_entry(self.admin_dn)
        except errors.NotFound:
            pass

        # add user
        entry = api.Backend.ldap2.make_entry(
            self.admin_dn,
            objectclass=[
                "top", "person", "organizationalPerson", "inetOrgPerson",
                "cmsuser"
            ],
            uid=[self.admin_user],
            cn=[self.admin_user],
            sn=[self.admin_user],
            usertype=['adminType'],
            mail=['root@localhost'],
            userPassword=[self.admin_password],
            userstate=['1'])
        api.Backend.ldap2.add_entry(entry)

        wait_groups = []
        for group in self.admin_groups:
            group_dn = DN(('cn', group), self.ipaca_groups)
            mod = [(ldap.MOD_ADD, 'uniqueMember', [self.admin_dn])]
            try:
                api.Backend.ldap2.modify_s(group_dn, mod)
            except ldap.TYPE_OR_VALUE_EXISTS:
                # already there
                return None
            else:
                wait_groups.append(group_dn)

        # Now wait until the other server gets replicated this data
        master_conn = ipaldap.LDAPClient.from_hostname_secure(self.master_host)
        logger.debug("Waiting %s seconds for %s to appear on %s",
                     api.env.replication_wait_timeout, self.admin_dn,
                     master_conn)
        deadline = time.time() + api.env.replication_wait_timeout
        while time.time() < deadline:
            time.sleep(1)
            try:
                master_conn.simple_bind(self.admin_dn, self.admin_password)
            except errors.ACIError:
                # user not replicated yet
                pass
            else:
                logger.debug("Successfully logged in as %s", self.admin_dn)
                break
        else:
            logger.error("Unable to log in as %s on %s", self.admin_dn,
                         master_conn)
            logger.info("[hint] tune with replication_wait_timeout")
            raise errors.NotFound(reason="{} did not replicate to {}".format(
                self.admin_dn, master_conn))

        # wait for group membership
        for group_dn in wait_groups:
            replication.wait_for_entry(
                master_conn,
                group_dn,
                timeout=api.env.replication_wait_timeout,
                attr='uniqueMember',
                attrvalue=self.admin_dn)
Beispiel #16
0
class update_referint(Updater):
    """
    Update referential integrity configuration to new style
    http://directory.fedoraproject.org/docs/389ds/design/ri-plugin-configuration.html

    old attr              -> new attr
    nsslapd-pluginArg0    -> referint-update-delay
    nsslapd-pluginArg1    -> referint-logfile
    nsslapd-pluginArg2    -> referint-logchanges
    nsslapd-pluginArg3..N -> referint-membership-attr [3..N]

    Old and new style cannot be mixed, all nslapd-pluginArg* attrs have to be removed
    """

    referint_dn = DN(('cn', 'referential integrity postoperation'),
                     ('cn', 'plugins'), ('cn', 'config'))

    def execute(self, **options):

        root_logger.debug(
            "Upgrading referential integrity plugin configuration")
        ldap = self.api.Backend.ldap2
        try:
            entry = ldap.get_entry(self.referint_dn)
        except errors.NotFound:
            root_logger.error("Referential integrity configuration not found")
            return False, []

        referint_membership_attrs = []

        root_logger.debug("Initial value: %s", repr(entry))

        # nsslapd-pluginArg0    -> referint-update-delay
        update_delay = entry.get('nsslapd-pluginArg0')
        if update_delay:
            root_logger.debug("add: referint-update-delay: %s", update_delay)
            entry['referint-update-delay'] = update_delay
            entry['nsslapd-pluginArg0'] = None
        else:
            root_logger.info("Plugin already uses new style, skipping")
            return False, []

        # nsslapd-pluginArg1    -> referint-logfile
        logfile = entry.get('nsslapd-pluginArg1')
        if logfile:
            root_logger.debug("add: referint-logfile: %s", logfile)
            entry['referint-logfile'] = logfile
            entry['nsslapd-pluginArg1'] = None

        # nsslapd-pluginArg2    -> referint-logchanges
        logchanges = entry.get('nsslapd-pluginArg2')
        if logchanges:
            root_logger.debug("add: referint-logchanges: %s", logchanges)
            entry['referint-logchanges'] = logchanges
            entry['nsslapd-pluginArg2'] = None

        # nsslapd-pluginArg3..N -> referint-membership-attr [3..N]
        for key in entry.keys():
            if key.lower().startswith('nsslapd-pluginarg'):
                arg_val = entry.single_value[key]
                if arg_val:
                    referint_membership_attrs.append(arg_val)
                entry[key] = None

        if referint_membership_attrs:
            # entry['referint-membership-attr'] is None, plugin doesn't allow
            # mixing old and new style
            entry['referint-membership-attr'] = referint_membership_attrs

        root_logger.debug("Final value: %s", repr(entry))
        try:
            ldap.update_entry(entry)
        except errors.EmptyModlist:
            root_logger.debug("No modifications required")
            return False, []

        return False, []
Beispiel #17
0
class DogtagInstance(service.Service):
    """
    This is the base class for a Dogtag 10+ instance, which uses a
    shared tomcat instance and DS to host the relevant subsystems.

    It contains functions that will be common to installations of the
    CA, KRA, and eventually TKS and TPS.
    """

    # Mapping of nicknames for tracking requests, and the profile to
    # use for that certificate.  'configure_renewal()' reads this
    # dict.  The profile MUST be specified.
    tracking_reqs = dict()

    # HSM state is shared between CA and KRA
    hsm_sstore = 'pki_hsm'

    # override token for specific nicknames
    token_names = dict()

    def get_token_name(self, nickname):
        """Look up token name for nickname."""
        return self.token_names.get(nickname, self.token_name)

    ipaca_groups = DN(('ou', 'groups'), ('o', 'ipaca'))
    ipaca_people = DN(('ou', 'people'), ('o', 'ipaca'))
    groups_aci = (
        b'(targetfilter="(objectClass=groupOfUniqueNames)")'
        b'(targetattr="cn || description || objectclass || uniquemember")'
        b'(version 3.0; acl "Allow users from o=ipaca to read groups"; '
        b'allow (read, search, compare) '
        b'userdn="ldap:///uid=*,ou=people,o=ipaca";)')

    def __init__(self,
                 realm,
                 subsystem,
                 service_desc,
                 host_name=None,
                 nss_db=paths.PKI_TOMCAT_ALIAS_DIR,
                 service_prefix=None,
                 config=None):
        """Initializer"""

        super(DogtagInstance, self).__init__('pki-tomcatd',
                                             service_desc=service_desc,
                                             realm_name=realm,
                                             service_user=constants.PKI_USER,
                                             service_prefix=service_prefix)

        self.admin_password = None
        self.fqdn = host_name
        self.pkcs12_info = None
        self.clone = False

        self.basedn = None
        self.admin_user = "******"
        self.admin_dn = DN(('uid', self.admin_user), self.ipaca_people)
        self.admin_groups = None
        self.tmp_agent_db = None
        self.subsystem = subsystem
        # replication parameters
        self.master_host = None
        self.master_replication_port = 389
        self.nss_db = nss_db
        self.config = config  # Path to CS.cfg

        # filled out by configure_instance
        self.pki_config_override = None
        self.ca_subject = None
        self.subject_base = None

    def is_installed(self):
        """
        Determine if subsystem instance has been installed.

        Returns True/False
        """
        return os.path.exists(
            os.path.join(paths.VAR_LIB_PKI_TOMCAT_DIR, self.subsystem.lower()))

    def spawn_instance(self, cfg_file, nolog_list=()):
        """
        Create and configure a new Dogtag instance using pkispawn.
        Passes in a configuration file with IPA-specific
        parameters.
        """
        subsystem = self.subsystem
        args = [paths.PKISPAWN, "-s", subsystem, "-f", cfg_file]

        with open(cfg_file) as f:
            logger.debug('Contents of pkispawn configuration file (%s):\n%s',
                         cfg_file, ipautil.nolog_replace(f.read(), nolog_list))

        try:
            ipautil.run(args, nolog=nolog_list)
        except ipautil.CalledProcessError as e:
            self.handle_setup_error(e)

    def clean_pkispawn_files(self):
        if self.tmp_agent_db is not None:
            logger.debug("Removing %s", self.tmp_agent_db)
            shutil.rmtree(self.tmp_agent_db, ignore_errors=True)

        client_dir = os.path.join('/root/.dogtag/pki-tomcat/',
                                  self.subsystem.lower())
        logger.debug("Removing %s", client_dir)
        shutil.rmtree(client_dir, ignore_errors=True)

    def restart_instance(self):
        self.restart('pki-tomcat')

    def start_instance(self):
        self.start('pki-tomcat')

    def stop_instance(self):
        try:
            self.stop('pki-tomcat')
        except Exception:
            logger.debug("%s", traceback.format_exc())
            logger.critical("Failed to stop the Dogtag instance."
                            "See the installation log for details.")

    def enable_client_auth_to_db(self):
        """
        Enable client auth connection to the internal db.
        """
        sub_system_nickname = "subsystemCert cert-pki-ca"
        if self.token_name != INTERNAL_TOKEN:
            # TODO: Dogtag 10.6.9 does not like "internal" prefix.
            sub_system_nickname = '{}:{}'.format(self.token_name,
                                                 sub_system_nickname)

        with stopped_service('pki-tomcatd', 'pki-tomcat'):
            directivesetter.set_directive(
                self.config,
                'authz.instance.DirAclAuthz.ldap.ldapauth.authtype',
                'SslClientAuth',
                quotes=False,
                separator='=')
            directivesetter.set_directive(
                self.config,
                'authz.instance.DirAclAuthz.ldap.ldapauth.clientCertNickname',
                sub_system_nickname,
                quotes=False,
                separator='=')
            directivesetter.set_directive(
                self.config,
                'authz.instance.DirAclAuthz.ldap.ldapconn.port',
                '636',
                quotes=False,
                separator='=')
            directivesetter.set_directive(
                self.config,
                'authz.instance.DirAclAuthz.ldap.ldapconn.secureConn',
                'true',
                quotes=False,
                separator='=')

            directivesetter.set_directive(self.config,
                                          'internaldb.ldapauth.authtype',
                                          'SslClientAuth',
                                          quotes=False,
                                          separator='=')

            directivesetter.set_directive(
                self.config,
                'internaldb.ldapauth.clientCertNickname',
                sub_system_nickname,
                quotes=False,
                separator='=')
            directivesetter.set_directive(self.config,
                                          'internaldb.ldapconn.port',
                                          '636',
                                          quotes=False,
                                          separator='=')
            directivesetter.set_directive(self.config,
                                          'internaldb.ldapconn.secureConn',
                                          'true',
                                          quotes=False,
                                          separator='=')
            # Remove internaldb password as is not needed anymore
            directivesetter.set_directive(paths.PKI_TOMCAT_PASSWORD_CONF,
                                          'internaldb',
                                          None,
                                          separator='=')

    def uninstall(self):
        if self.is_installed():
            self.print_msg("Unconfiguring %s" % self.subsystem)

        try:
            ipautil.run(
                [paths.PKIDESTROY, "-i", 'pki-tomcat', "-s", self.subsystem])
        except ipautil.CalledProcessError as e:
            logger.critical("failed to uninstall %s instance %s",
                            self.subsystem, e)

    def http_proxy(self):
        """ Update the http proxy file  """
        template_filename = (os.path.join(paths.USR_SHARE_IPA_DIR,
                                          "ipa-pki-proxy.conf.template"))
        sub_dict = dict(
            DOGTAG_PORT=8009,
            CLONE='' if self.clone else '#',
            FQDN=self.fqdn,
        )
        template = ipautil.template_file(template_filename, sub_dict)
        with open(paths.HTTPD_IPA_PKI_PROXY_CONF, "w") as fd:
            fd.write(template)
            os.fchmod(fd.fileno(), 0o644)

    def configure_certmonger_renewal_helpers(self):
        """
        Create a new CA type for certmonger that will retrieve updated
        certificates from the dogtag master server.
        """
        cmonger = services.knownservices.certmonger
        cmonger.enable()
        if not services.knownservices.dbus.is_running():
            # some platforms protect dbus with RefuseManualStart=True
            services.knownservices.dbus.start()
        cmonger.start()

        bus = dbus.SystemBus()
        obj = bus.get_object('org.fedorahosted.certmonger',
                             '/org/fedorahosted/certmonger')
        iface = dbus.Interface(obj, 'org.fedorahosted.certmonger')
        for suffix, args in [
            ('', ''),
            ('-reuse', ' --reuse-existing'),
            ('-selfsigned', ' --force-self-signed'),
        ]:
            name = RENEWAL_CA_NAME + suffix
            path = iface.find_ca_by_nickname(name)
            if not path:
                command = paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT + args
                iface.add_known_ca(
                    name,
                    command,
                    dbus.Array([], dbus.Signature('s')),
                    # Give dogtag extra time to generate cert
                    timeout=CA_DBUS_TIMEOUT)

    def __get_pin(self, token_name=INTERNAL_TOKEN):
        try:
            return certmonger.get_pin(token_name)
        except IOError as e:
            logger.debug('Unable to determine PIN for the Dogtag instance: %s',
                         e)
            raise RuntimeError(e)

    def configure_renewal(self):
        """ Configure certmonger to renew system certs """

        for nickname in self.tracking_reqs:
            token_name = self.get_token_name(nickname)
            pin = self.__get_pin(token_name)
            try:
                certmonger.start_tracking(
                    certpath=self.nss_db,
                    ca=RENEWAL_CA_NAME,
                    nickname=nickname,
                    token_name=token_name,
                    pin=pin,
                    pre_command='stop_pkicad',
                    post_command='renew_ca_cert "%s"' % nickname,
                    profile=self.tracking_reqs[nickname],
                )
            except RuntimeError as e:
                logger.error(
                    "certmonger failed to start tracking certificate: %s", e)

    def stop_tracking_certificates(self, stop_certmonger=True):
        """Stop tracking our certificates. Called on uninstall.
        """
        logger.debug(
            "Configuring certmonger to stop tracking system certificates "
            "for %s", self.subsystem)

        cmonger = services.knownservices.certmonger
        if not services.knownservices.dbus.is_running():
            # some platforms protect dbus with RefuseManualStart=True
            services.knownservices.dbus.start()
        cmonger.start()

        for nickname in self.tracking_reqs:
            try:
                certmonger.stop_tracking(self.nss_db, nickname=nickname)
            except RuntimeError as e:
                logger.error(
                    "certmonger failed to stop tracking certificate: %s", e)

        if stop_certmonger:
            cmonger.stop()

    def update_cert_cs_cfg(self, directive, cert):
        """
        When renewing a Dogtag subsystem certificate the configuration file
        needs to get the new certificate as well.

        ``directive`` is the directive to update in CS.cfg
        cert is IPACertificate.
        cs_cfg is the path to the CS.cfg file
        """

        with stopped_service('pki-tomcatd', 'pki-tomcat'):
            directivesetter.set_directive(
                self.config,
                directive,
                # the cert must be only the base64 string without headers
                (base64.b64encode(cert.public_bytes(
                    x509.Encoding.DER)).decode('ascii')),
                quotes=False,
                separator='=')

    def get_admin_cert(self):
        """
        Get the certificate for the admin user by checking the ldap entry
        for the user.  There should be only one certificate per user.
        """
        logger.debug('Trying to find the certificate for the admin user')
        conn = None

        try:
            conn = ipaldap.LDAPClient.from_realm(self.realm)
            conn.external_bind()

            entry_attrs = conn.get_entry(self.admin_dn, ['usercertificate'])
            admin_cert = entry_attrs.get('usercertificate')[0]

            # TODO(edewata) Add check to warn if there is more than one cert.
        finally:
            if conn is not None:
                conn.unbind()

        return admin_cert

    def handle_setup_error(self, e):
        logger.critical("Failed to configure %s instance: %s", self.subsystem,
                        e)
        logger.critical("See the installation logs and the following "
                        "files/directories for more information:")
        logger.critical("  %s", paths.TOMCAT_TOPLEVEL_DIR)

        raise RuntimeError("%s configuration failed." % self.subsystem)

    def add_ipaca_aci(self):
        """Add ACI to allow ipaca users to read their own group information

        Dogtag users aren't allowed to enumerate their own groups. The
        setup_admin() method needs the permission to wait, until all group
        information has been replicated.
        """
        dn = self.ipaca_groups
        mod = [(ldap.MOD_ADD, 'aci', [self.groups_aci])]
        try:
            api.Backend.ldap2.modify_s(dn, mod)
        except ldap.TYPE_OR_VALUE_EXISTS:
            logger.debug("%s already has ACI to read group information", dn)
        else:
            logger.debug("Added ACI to read groups to %s", dn)

    def setup_admin(self):
        self.admin_user = "******" % self.fqdn
        self.admin_password = ipautil.ipa_generate_password()
        self.admin_dn = DN(('uid', self.admin_user), self.ipaca_people)
        # remove user if left-over exists
        try:
            api.Backend.ldap2.delete_entry(self.admin_dn)
        except errors.NotFound:
            pass

        # add user
        entry = api.Backend.ldap2.make_entry(
            self.admin_dn,
            objectclass=[
                "top", "person", "organizationalPerson", "inetOrgPerson",
                "cmsuser"
            ],
            uid=[self.admin_user],
            cn=[self.admin_user],
            sn=[self.admin_user],
            usertype=['adminType'],
            mail=['root@localhost'],
            userPassword=[self.admin_password],
            userstate=['1'])
        api.Backend.ldap2.add_entry(entry)

        wait_groups = []
        for group in self.admin_groups:
            group_dn = DN(('cn', group), self.ipaca_groups)
            mod = [(ldap.MOD_ADD, 'uniqueMember', [self.admin_dn])]
            try:
                api.Backend.ldap2.modify_s(group_dn, mod)
            except ldap.TYPE_OR_VALUE_EXISTS:
                # already there
                return None
            else:
                wait_groups.append(group_dn)

        # Now wait until the other server gets replicated this data
        master_conn = ipaldap.LDAPClient.from_hostname_secure(self.master_host)
        logger.debug("Waiting %s seconds for %s to appear on %s",
                     api.env.replication_wait_timeout, self.admin_dn,
                     master_conn)
        deadline = time.time() + api.env.replication_wait_timeout
        while time.time() < deadline:
            time.sleep(1)
            try:
                master_conn.simple_bind(self.admin_dn, self.admin_password)
            except errors.ACIError:
                # user not replicated yet
                pass
            else:
                logger.debug("Successfully logged in as %s", self.admin_dn)
                break
        else:
            logger.error("Unable to log in as %s on %s", self.admin_dn,
                         master_conn)
            logger.info("[hint] tune with replication_wait_timeout")
            raise errors.NotFound(reason="{} did not replicate to {}".format(
                self.admin_dn, master_conn))

        # wait for group membership
        for group_dn in wait_groups:
            replication.wait_for_entry(
                master_conn,
                group_dn,
                timeout=api.env.replication_wait_timeout,
                attr='uniqueMember',
                attrvalue=self.admin_dn)

    def __remove_admin_from_group(self, group):
        dn = DN(('cn', group), self.ipaca_groups)
        mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)]
        try:
            api.Backend.ldap2.modify_s(dn, mod)
        except ldap.NO_SUCH_ATTRIBUTE:
            # already removed
            pass

    def teardown_admin(self):
        for group in self.admin_groups:
            self.__remove_admin_from_group(group)
        api.Backend.ldap2.delete_entry(self.admin_dn)

    def backup_config(self):
        """
        Create a backup copy of CS.cfg
        """
        config = self.config
        bak = config + '.ipabkp'
        if services.knownservices['pki_tomcatd'].is_running('pki-tomcat'):
            raise RuntimeError(
                "Dogtag must be stopped when creating backup of %s" % config)
        shutil.copy(config, bak)
        # shutil.copy() doesn't copy owner
        s = os.stat(config)
        os.chown(bak, s.st_uid, s.st_gid)

    def reindex_task(self, force=False):
        """Reindex ipaca entries

        pkispawn sometimes does not run its indextasks. This leads to slow
        unindexed filters on attributes such as description, which is used
        to log in with a certificate. Explicitly reindex attribute that
        should have been reindexed by CA's indextasks.ldif.

        See https://pagure.io/dogtagpki/issue/3083
        """
        state_name = 'reindex_task'
        if not force and sysupgrade.get_upgrade_state('dogtag', state_name):
            return

        cn = "indextask_ipaca_{}".format(int(time.time()))
        dn = DN(('cn', cn), ('cn', 'index'), ('cn', 'tasks'), ('cn', 'config'))
        entry = api.Backend.ldap2.make_entry(
            dn,
            objectClass=['top', 'extensibleObject'],
            cn=[cn],
            nsInstance=['ipaca'],  # Dogtag PKI database
            nsIndexAttribute=[
                # from pki/base/ca/shared/conf/indextasks.ldif
                'archivedBy',
                'certstatus',
                'clientId',
                'dataType',
                'dateOfCreate',
                'description',
                'duration',
                'extension',
                'issuedby',
                'issuername',
                'metaInfo',
                'notafter',
                'notbefore',
                'ownername',
                'publicKeyData',
                'requestid',
                'requestowner',
                'requestsourceid',
                'requeststate',
                'requesttype',
                'revInfo',
                'revokedOn',
                'revokedby',
                'serialno',
                'status',
                'subjectname',
            ],
            ttl=[10],
        )
        logger.debug('Creating ipaca reindex task %s', dn)
        api.Backend.ldap2.add_entry(entry)
        logger.debug('Waiting for task...')
        exitcode = replication.wait_for_task(api.Backend.ldap2, dn)
        logger.debug('Task %s has finished with exit code %i', dn, exitcode)
        sysupgrade.set_upgrade_state('dogtag', state_name, True)

    def set_hsm_state(self, config):
        section_name = self.subsystem.upper()
        assert section_name == 'CA'
        if config.getboolean(section_name, 'pki_hsm_enable', fallback=False):
            enable = True
            token_name = config.get(section_name, 'pki_token_name')
        else:
            enable = False
            token_name = INTERNAL_TOKEN
        self.sstore.backup_state(self.hsm_sstore, "enabled", enable)
        self.sstore.backup_state(self.hsm_sstore, "token_name", token_name)

    def restore_hsm_state(self):
        return (
            self.sstore.restore_state(self.hsm_sstore, "enabled"),
            self.sstore.restore_state(self.hsm_sstore, "token_name"),
        )

    @property
    def hsm_enabled(self):
        """Is HSM support enabled?"""
        return self.sstore.get_state(self.hsm_sstore, "enabled")

    @property
    def token_name(self):
        """HSM token name"""
        return self.sstore.get_state(self.hsm_sstore, "token_name")

    def _configure_clone(self, subsystem_config, security_domain_hostname,
                         clone_pkcs12_path):
        subsystem_config.update(
            # Security domain registration
            pki_security_domain_hostname=security_domain_hostname,
            pki_security_domain_https_port=443,
            pki_security_domain_user=self.admin_user,
            pki_security_domain_password=self.admin_password,
            # Clone
            pki_clone=True,
            pki_clone_pkcs12_path=clone_pkcs12_path,
            pki_clone_pkcs12_password=self.dm_password,
            pki_clone_replication_security="TLS",
            pki_clone_replication_master_port=self.master_replication_port,
            pki_clone_replication_clone_port=389,
            pki_clone_replicate_schema=False,
            pki_clone_uri="https://%s" %
            ipautil.format_netloc(self.master_host, 443),
        )

    def _create_spawn_config(self, subsystem_config):
        loader = PKIIniLoader(subsystem=self.subsystem,
                              fqdn=self.fqdn,
                              domain=api.env.domain,
                              subject_base=self.subject_base,
                              ca_subject=self.ca_subject,
                              admin_user=self.admin_user,
                              admin_password=self.admin_password,
                              dm_password=self.dm_password,
                              pki_config_override=self.pki_config_override)
        return loader.create_spawn_config(subsystem_config)
Beispiel #18
0
    def _remove_server_principal_references(self, master):
        """
        This method removes information about the replica in parts
        of the shared tree that expose it, so clients stop trying to
        use this replica.
        """
        conn = self.Backend.ldap2
        env = self.api.env

        master_principal = "{}@{}".format(master, env.realm).encode('utf-8')

        # remove replica memberPrincipal from s4u2proxy configuration
        s4u2proxy_subtree = DN(env.container_s4u2proxy, env.basedn)
        dn1 = DN(('cn', 'ipa-http-delegation'), s4u2proxy_subtree)
        member_principal1 = b"HTTP/%s" % master_principal

        dn2 = DN(('cn', 'ipa-ldap-delegation-targets'), s4u2proxy_subtree)
        member_principal2 = b"ldap/%s" % master_principal

        dn3 = DN(('cn', 'ipa-cifs-delegation-targets'), s4u2proxy_subtree)
        member_principal3 = b"cifs/%s" % master_principal

        for (dn, member_principal) in ((dn1, member_principal1),
                                       (dn2, member_principal2),
                                       (dn3, member_principal3)):
            try:
                mod = [(ldap.MOD_DELETE, 'memberPrincipal', member_principal)]
                conn.conn.modify_s(str(dn), mod)
            except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE):
                logger.debug(
                    "Replica (%s) memberPrincipal (%s) not found in %s",
                    master, member_principal.decode('utf-8'), dn)
            except Exception as e:
                self.add_message(
                    messages.ServerRemovalWarning(
                        message=_("Failed to clean memberPrincipal "
                                  "%(principal)s from s4u2proxy entry %(dn)s: "
                                  "%(err)s") %
                        dict(principal=(member_principal.decode('utf-8')),
                             dn=dn,
                             err=e)))

        try:
            etc_basedn = DN(('cn', 'etc'), env.basedn)
            filter = '(dnaHostname=%s)' % master
            entries = conn.get_entries(etc_basedn,
                                       ldap.SCOPE_SUBTREE,
                                       filter=filter)
            if len(entries) != 0:
                for entry in entries:
                    conn.delete_entry(entry)
        except errors.NotFound:
            pass
        except Exception as e:
            self.add_message(
                messages.ServerRemovalWarning(
                    message=_("Failed to clean up DNA hostname entries for "
                              "%(master)s: %(err)s") %
                    dict(master=master, err=e)))

        try:
            dn = DN(('cn', 'default'), ('ou', 'profile'), env.basedn)
            ret = conn.get_entry(dn)
            srvlist = ret.single_value.get('defaultServerList', '')
            srvlist = srvlist.split()
            if master in srvlist:
                srvlist.remove(master)
                attr = ' '.join(srvlist)
                ret['defaultServerList'] = attr
                conn.update_entry(ret)
        except (errors.NotFound, errors.MidairCollision, errors.EmptyModlist):
            pass
        except Exception as e:
            self.add_message(
                messages.ServerRemovalWarning(
                    message=_("Failed to remove server %(master)s from server "
                              "list: %(err)s") % dict(master=master, err=e)))
Beispiel #19
0
   ipa deskprofilerule-find --hbacrule="design department access"

 Remove a rule:
   ipa deskprofilerule-del "finance"

 Remove a profile:
   ipa deskprofile-del "Visual Design"

""")

register = Registry()

notboth_err = _('HBAC rule and local members cannot both be set')

PLUGIN_CONFIG = (
    ('container_deskprofile', DN(('cn', 'desktop-profile'))),
    ('container_deskprofilerule', DN(('cn', 'rules'),
                                     ('cn', 'desktop-profile'))),
)


@register()
class deskprofile(LDAPObject):
    """
    Desktop profile object.
    """
    container_dn = None
    object_name = _('FleetCommander Desktop Profile')
    object_name_plural = _('FleetCommander Desktop Profiles')
    object_class = ['ipaassociation', 'ipadeskprofile']
    permission_filter_objectclasses = ['ipadeskprofile']
Beispiel #20
0
    def execute(self, *args, **options):
        # First receive all needed information:
        # 1. HBAC rules (whether enabled or disabled)
        # 2. Required options are (user, target host, service)
        # 3. Options: rules to test (--rules, --enabled, --disabled), request for detail output
        rules = []

        # Use all enabled IPA rules by default
        all_enabled = True
        all_disabled = False

        # We need a local copy of test rules in order find incorrect ones
        testrules = {}
        if 'rules' in options:
            testrules = list(options['rules'])
            # When explicit rules are provided, disable assumptions
            all_enabled = False
            all_disabled = False

        sizelimit = None
        if 'sizelimit' in options:
            sizelimit = int(options['sizelimit'])

        # Check if --disabled is specified, include all disabled IPA rules
        if options['disabled']:
            all_disabled = True
            all_enabled = False

        # Finally, if enabled is specified implicitly, override above decisions
        if options['enabled']:
            all_enabled = True

        hbacset = []
        if len(testrules) == 0:
            hbacset = self.api.Command.hbacrule_find(
                sizelimit=sizelimit)['result']
        else:
            for rule in testrules:
                try:
                    hbacset.append(
                        self.api.Command.hbacrule_show(rule)['result'])
                except:
                    pass

        # We have some rules, import them
        # --enabled will import all enabled rules (default)
        # --disabled will import all disabled rules
        # --rules will implicitly add the rules from a rule list
        for rule in hbacset:
            ipa_rule = convert_to_ipa_rule(rule)
            if ipa_rule.name in testrules:
                ipa_rule.enabled = True
                rules.append(ipa_rule)
                testrules.remove(ipa_rule.name)
            elif all_enabled and ipa_rule.enabled:
                # Option --enabled forces to include all enabled IPA rules into test
                rules.append(ipa_rule)
            elif all_disabled and not ipa_rule.enabled:
                # Option --disabled forces to include all disabled IPA rules into test
                ipa_rule.enabled = True
                rules.append(ipa_rule)

        # Check if there are unresolved rules left
        if len(testrules) > 0:
            # Error, unresolved rules are left in --rules
            return {
                'summary': unicode(_(u'Unresolved rules in --rules')),
                'error': testrules,
                'matched': None,
                'notmatched': None,
                'warning': None,
                'value': False
            }

        # Rules are converted to pyhbac format, build request and then test it
        request = pyhbac.HbacRequest()

        if options['user'] != u'all':
            # check first if this is not a trusted domain user
            if _dcerpc_bindings_installed:
                is_valid_sid = ipaserver.dcerpc.is_sid_valid(options['user'])
            else:
                is_valid_sid = False
            components = util.normalize_name(options['user'])
            if is_valid_sid or 'domain' in components or 'flatname' in components:
                # this is a trusted domain user
                if not _dcerpc_bindings_installed:
                    raise errors.NotFound(reason=_(
                        'Cannot perform external member validation without '
                        'Samba 4 support installed. Make sure you have installed '
                        'server-trust-ad sub-package of IPA on the server'))
                domain_validator = ipaserver.dcerpc.DomainValidator(self.api)
                if not domain_validator.is_configured():
                    raise errors.NotFound(reason=_(
                        'Cannot search in trusted domains without own domain configured. '
                        'Make sure you have run ipa-adtrust-install on the IPA server first'
                    ))
                user_sid, group_sids = domain_validator.get_trusted_domain_user_and_groups(
                    options['user'])
                request.user.name = user_sid

                # Now search for all external groups that have this user or
                # any of its groups in its external members. Found entires
                # memberOf links will be then used to gather all groups where
                # this group is assigned, including the nested ones
                filter_sids = "(&(objectclass=ipaexternalgroup)(|(ipaExternalMember=%s)))" \
                        % ")(ipaExternalMember=".join(group_sids + [user_sid])

                ldap = self.api.Backend.ldap2
                group_container = DN(api.env.container_group, api.env.basedn)
                try:
                    entries, truncated = ldap.find_entries(
                        filter_sids, ['memberof'], group_container)
                except errors.NotFound:
                    request.user.groups = []
                else:
                    groups = []
                    for entry in entries:
                        memberof_dns = entry.get('memberof', [])
                        for memberof_dn in memberof_dns:
                            if memberof_dn.endswith(group_container):
                                groups.append(memberof_dn[0][0].value)
                    request.user.groups = sorted(set(groups))
            else:
                # try searching for a local user
                try:
                    request.user.name = options['user']
                    search_result = self.api.Command.user_show(
                        request.user.name)['result']
                    groups = search_result['memberof_group']
                    if 'memberofindirect_group' in search_result:
                        groups += search_result['memberofindirect_group']
                    request.user.groups = sorted(set(groups))
                except:
                    pass

        if options['service'] != u'all':
            try:
                request.service.name = options['service']
                service_result = self.api.Command.hbacsvc_show(
                    request.service.name)['result']
                if 'memberof_hbacsvcgroup' in service_result:
                    request.service.groups = service_result[
                        'memberof_hbacsvcgroup']
            except:
                pass

        if options['targethost'] != u'all':
            try:
                request.targethost.name = self.canonicalize(
                    options['targethost'])
                tgthost_result = self.api.Command.host_show(
                    request.targethost.name)['result']
                groups = tgthost_result['memberof_hostgroup']
                if 'memberofindirect_hostgroup' in tgthost_result:
                    groups += tgthost_result['memberofindirect_hostgroup']
                request.targethost.groups = sorted(set(groups))
            except:
                pass

        matched_rules = []
        notmatched_rules = []
        error_rules = []
        warning_rules = []

        result = {
            'warning': None,
            'matched': None,
            'notmatched': None,
            'error': None
        }
        if not options['nodetail']:
            # Validate runs rules one-by-one and reports failed ones
            for ipa_rule in rules:
                try:
                    res = request.evaluate([ipa_rule])
                    if res == pyhbac.HBAC_EVAL_ALLOW:
                        matched_rules.append(ipa_rule.name)
                    if res == pyhbac.HBAC_EVAL_DENY:
                        notmatched_rules.append(ipa_rule.name)
                except pyhbac.HbacError as e:
                    code, rule_name = e.args
                    if code == pyhbac.HBAC_EVAL_ERROR:
                        error_rules.append(rule_name)
                        self.log.info('Native IPA HBAC rule "%s" parsing error: %s' % \
                                      (rule_name, pyhbac.hbac_result_string(code)))
                except (TypeError, IOError) as info:
                    self.log.error('Native IPA HBAC module error: %s' % info)

            access_granted = len(matched_rules) > 0
        else:
            res = request.evaluate(rules)
            access_granted = (res == pyhbac.HBAC_EVAL_ALLOW)

        result['summary'] = _('Access granted: %s') % (access_granted)

        if len(matched_rules) > 0:
            result['matched'] = matched_rules
        if len(notmatched_rules) > 0:
            result['notmatched'] = notmatched_rules
        if len(error_rules) > 0:
            result['error'] = error_rules
        if len(warning_rules) > 0:
            result['warning'] = warning_rules

        result['value'] = access_granted
        return result
Beispiel #21
0
def _person_dn(uid):
    return DN(('uid', uid), ('ou', 'people'), ('o', 'ipaca'))
Beispiel #22
0
class test_LDAPEntry(object):
    """
    Test the LDAPEntry class
    """
    cn1 = [u'test1']
    cn2 = [u'test2']
    dn1 = DN(('cn', cn1[0]))
    dn2 = DN(('cn', cn2[0]))

    def setup(self):
        self.ldapuri = api.env.ldap_uri
        self.conn = ldap2(api)
        self.conn.connect(autobind=AUTOBIND_DISABLED)

        self.entry = self.conn.make_entry(self.dn1, cn=self.cn1)

    def teardown(self):
        if self.conn and self.conn.isconnected():
            self.conn.disconnect()

    def test_entry(self):
        e = self.entry
        assert e.dn is self.dn1
        assert u'cn' in e
        assert u'cn' in e.keys()
        assert 'CN' in e
        if six.PY2:
            assert 'CN' not in e.keys()
        else:
            assert 'CN' in e.keys()
        assert 'commonName' in e
        if six.PY2:
            assert 'commonName' not in e.keys()
        else:
            assert 'commonName' in e.keys()
        assert e['CN'] is self.cn1
        assert e['CN'] is e[u'cn']

        e.dn = self.dn2
        assert e.dn is self.dn2

    def test_set_attr(self):
        e = self.entry
        e['commonName'] = self.cn2
        assert u'cn' in e
        assert u'cn' in e.keys()
        assert 'CN' in e
        if six.PY2:
            assert 'CN' not in e.keys()
        else:
            assert 'CN' in e.keys()
        assert 'commonName' in e
        if six.PY2:
            assert 'commonName' not in e.keys()
        else:
            assert 'commonName' in e.keys()
        assert e['CN'] is self.cn2
        assert e['CN'] is e[u'cn']

    def test_del_attr(self):
        e = self.entry
        del e['CN']
        assert 'CN' not in e
        assert 'CN' not in e.keys()
        assert u'cn' not in e
        assert u'cn' not in e.keys()
        assert 'commonName' not in e
        assert 'commonName' not in e.keys()

    def test_popitem(self):
        e = self.entry
        assert e.popitem() == ('cn', self.cn1)
        assert list(e) == []

    def test_setdefault(self):
        e = self.entry
        assert e.setdefault('cn', self.cn2) == self.cn1
        assert e['cn'] == self.cn1
        assert e.setdefault('xyz', self.cn2) == self.cn2
        assert e['xyz'] == self.cn2

    def test_update(self):
        e = self.entry
        e.update({'cn': self.cn2}, xyz=self.cn2)
        assert e['cn'] == self.cn2
        assert e['xyz'] == self.cn2

    def test_pop(self):
        e = self.entry
        assert e.pop('cn') == self.cn1
        assert 'cn' not in e
        assert e.pop('cn', 'default') is 'default'
        with assert_raises(KeyError):
            e.pop('cn')

    def test_clear(self):
        e = self.entry
        e.clear()
        assert not e
        assert 'cn' not in e

    @pytest.mark.skipif(sys.version_info >= (3, 0), reason="Python 2 only")
    def test_has_key(self):
        e = self.entry
        assert not e.has_key('xyz')
        assert e.has_key('cn')
        assert e.has_key('COMMONNAME')

    def test_in(self):
        e = self.entry
        assert 'xyz' not in e
        assert 'cn' in e
        assert 'COMMONNAME' in e

    def test_get(self):
        e = self.entry
        assert e.get('cn') == self.cn1
        assert e.get('commonname') == self.cn1
        assert e.get('COMMONNAME', 'default') == self.cn1
        assert e.get('bad key', 'default') == 'default'

    def test_single_value(self):
        e = self.entry
        assert e.single_value['cn'] == self.cn1[0]
        assert e.single_value['commonname'] == self.cn1[0]
        assert e.single_value.get('COMMONNAME', 'default') == self.cn1[0]
        assert e.single_value.get('bad key', 'default') == 'default'

    def test_sync(self):
        e = self.entry

        nice = e['test'] = [1, 2, 3]
        assert e['test'] is nice

        raw = e.raw['test']
        assert raw == [b'1', b'2', b'3']

        nice.remove(1)
        assert e.raw['test'] is raw
        assert raw == [b'2', b'3']

        raw.append(b'4')
        assert e['test'] is nice
        assert nice == [2, 3, u'4']

        nice.remove(2)
        raw.append(b'5')
        assert nice == [3, u'4']
        assert raw == [b'2', b'3', b'4', b'5']
        assert e['test'] is nice
        assert e.raw['test'] is raw
        assert nice == [3, u'4', u'5']
        assert raw == [b'3', b'4', b'5']

        nice.insert(0, 2)
        raw.remove(b'4')
        assert nice == [2, 3, u'4', u'5']
        assert raw == [b'3', b'5']
        assert e.raw['test'] is raw
        assert e['test'] is nice
        assert nice == [2, 3, u'5']
        assert raw == [b'3', b'5', b'2']

        raw = [b'a', b'b']
        e.raw['test'] = raw
        assert e['test'] is not nice
        assert e['test'] == [u'a', u'b']

        nice = 'not list'
        e['test'] = nice
        assert e['test'] is nice
        assert e.raw['test'] == [b'not list']

        e.raw['test'].append(b'second')
        assert e['test'] == ['not list', u'second']
Beispiel #23
0
class user(baseuser):
    """
    User object.
    """

    container_dn = baseuser.active_container_dn
    label = _('Users')
    label_singular = _('User')
    object_name = _('user')
    object_name_plural = _('users')
    managed_permissions = {
        'System: Read User Standard Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'anonymous',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass', 'cn', 'sn', 'description', 'title', 'uid',
                'displayname', 'givenname', 'initials', 'manager', 'gecos',
                'gidnumber', 'homedirectory', 'loginshell', 'uidnumber',
                'ipantsecurityidentifier'
            },
        },
        'System: Read User Addressbook Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'seealso',
                'telephonenumber',
                'facsimiletelephonenumber',
                'l',
                'ou',
                'st',
                'postalcode',
                'street',
                'destinationindicator',
                'internationalisdnnumber',
                'physicaldeliveryofficename',
                'postaladdress',
                'postofficebox',
                'preferreddeliverymethod',
                'registeredaddress',
                'teletexterminalidentifier',
                'telexnumber',
                'x121address',
                'carlicense',
                'departmentnumber',
                'employeenumber',
                'employeetype',
                'preferredlanguage',
                'mail',
                'mobile',
                'pager',
                'audio',
                'businesscategory',
                'homephone',
                'homepostaladdress',
                'jpegphoto',
                'labeleduri',
                'o',
                'photo',
                'roomnumber',
                'secretary',
                'usercertificate',
                'usersmimecertificate',
                'x500uniqueidentifier',
                'inetuserhttpurl',
                'inetuserstatus',
                'ipacertmapdata',
            },
            'fixup_function': fix_addressbook_permission_bindrule,
        },
        'System: Read User IPA Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'ipauniqueid',
                'ipasshpubkey',
                'ipauserauthtype',
                'userclass',
            },
            'fixup_function': fix_addressbook_permission_bindrule,
        },
        'System: Read User Kerberos Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'krbprincipalname',
                'krbcanonicalname',
                'krbprincipalaliases',
                'krbprincipalexpiration',
                'krbpasswordexpiration',
                'krblastpwdchange',
                'nsaccountlock',
                'krbprincipaltype',
            },
        },
        'System: Read User Kerberos Login Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'krblastsuccessfulauth',
                'krblastfailedauth',
                'krblastpwdchange',
                'krblastadminunlock',
                'krbloginfailedcount',
                'krbpwdpolicyreference',
                'krbticketpolicyreference',
                'krbupenabled',
            },
            'default_privileges': {'User Administrators'},
        },
        'System: Read User Membership': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'memberof',
            },
        },
        'System: Read UPG Definition': {
            # Required for adding users
            'replaces_global_anonymous_aci': True,
            'non_object': True,
            'ipapermlocation': UPG_DEFINITION_DN,
            'ipapermtarget': UPG_DEFINITION_DN,
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {'*'},
            'default_privileges': {'User Administrators'},
        },
        'System: Add Users': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Add Users";allow (add) groupdn = "ldap:///cn=Add Users,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Add User to default group': {
            'non_object':
            True,
            'ipapermright': {'write'},
            'ipapermlocation':
            DN(api.env.container_group, api.env.basedn),
            'ipapermtarget':
            DN('cn=ipausers', api.env.container_group, api.env.basedn),
            'ipapermdefaultattr': {'member'},
            'replaces': [
                '(targetattr = "member")(target = "ldap:///cn=ipausers,cn=groups,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Add user to default group";allow (write) groupdn = "ldap:///cn=Add user to default group,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Change User password': {
            'ipapermright': {'write'},
            'ipapermtargetfilter': [
                '(objectclass=posixaccount)',
                '(!(memberOf=%s))' %
                DN('cn=admins', api.env.container_group, api.env.basedn),
            ],
            'ipapermdefaultattr': {
                'krbprincipalkey', 'passwordhistory', 'sambalmpassword',
                'sambantpassword', 'userpassword', 'krbpasswordexpiration'
            },
            'replaces': [
                '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)',
                '(targetfilter = "(!(memberOf=cn=admins,cn=groups,cn=accounts,$SUFFIX))")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)',
                '(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Windows PassSync service can write passwords"; allow (write) userdn="ldap:///uid=passsync,cn=sysaccounts,cn=etc,$SUFFIX";)',
            ],
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
                'PassSync Service',
            },
        },
        'System: Manage User SSH Public Keys': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'ipasshpubkey'},
            'replaces': [
                '(targetattr = "ipasshpubkey")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Manage User SSH Public Keys";allow (write) groupdn = "ldap:///cn=Manage User SSH Public Keys,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Manage User Certificates': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'usercertificate'},
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
            },
        },
        'System: Manage User Principals': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'krbprincipalname', 'krbcanonicalname'},
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
            },
        },
        'System: Modify Users': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'businesscategory', 'carlicense', 'cn', 'departmentnumber',
                'description', 'displayname', 'employeetype', 'employeenumber',
                'facsimiletelephonenumber', 'gecos', 'givenname',
                'homedirectory', 'homephone', 'inetuserhttpurl', 'initials',
                'l', 'labeleduri', 'loginshell', 'manager', 'mail',
                'mepmanagedentry', 'mobile', 'objectclass', 'ou', 'pager',
                'postalcode', 'roomnumber', 'secretary', 'seealso', 'sn', 'st',
                'street', 'telephonenumber', 'title', 'userclass',
                'preferredlanguage'
            },
            'replaces': [
                '(targetattr = "givenname || sn || cn || displayname || title || initials || loginshell || gecos || homephone || mobile || pager || facsimiletelephonenumber || telephonenumber || street || roomnumber || l || st || postalcode || manager || secretary || description || carlicense || labeleduri || inetuserhttpurl || seealso || employeetype || businesscategory || ou || mepmanagedentry || objectclass")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Modify Users";allow (write) groupdn = "ldap:///cn=Modify Users,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
            },
        },
        'System: Remove Users': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Remove Users";allow (delete) groupdn = "ldap:///cn=Remove Users,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Unlock User': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'krblastadminunlock',
                'krbloginfailedcount',
                'nsaccountlock',
            },
            'replaces': [
                '(targetattr = "krbLastAdminUnlock || krbLoginFailedCount")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Unlock user accounts";allow (write) groupdn = "ldap:///cn=Unlock user accounts,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Read User Compat Tree': {
            'non_object': True,
            'ipapermbindruletype': 'anonymous',
            'ipapermlocation': api.env.basedn,
            'ipapermtarget': DN('cn=users', 'cn=compat', api.env.basedn),
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass',
                'uid',
                'cn',
                'gecos',
                'gidnumber',
                'uidnumber',
                'homedirectory',
                'loginshell',
            },
        },
        'System: Read User Views Compat Tree': {
            'non_object':
            True,
            'ipapermbindruletype':
            'anonymous',
            'ipapermlocation':
            api.env.basedn,
            'ipapermtarget':
            DN('cn=users', 'cn=*', 'cn=views', 'cn=compat', api.env.basedn),
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass',
                'uid',
                'cn',
                'gecos',
                'gidnumber',
                'uidnumber',
                'homedirectory',
                'loginshell',
            },
        },
        'System: Read User NT Attributes': {
            'ipapermbindruletype': 'permission',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'ntuserdomainid',
                'ntuniqueid',
                'ntuseracctexpires',
                'ntusercodepage',
                'ntuserdeleteaccount',
                'ntuserlastlogoff',
                'ntuserlastlogon',
            },
            'default_privileges': {'PassSync Service'},
        },
        'System: Manage User Certificate Mappings': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'ipacertmapdata', 'objectclass'},
            'default_privileges':
            {'Certificate Identity Mapping Administrators'},
        },
    }

    takes_params = baseuser.takes_params + (
        Bool(
            'nsaccountlock?',
            cli_name=('disabled'),
            default=False,
            label=_('Account disabled'),
        ),
        Bool(
            'preserved?',
            label=_('Preserved user'),
            default=False,
            flags=['virtual_attribute', 'no_create', 'no_update'],
        ),
    )

    def get_delete_dn(self, *keys, **options):
        active_dn = self.get_dn(*keys, **options)
        return DN(active_dn[0], self.delete_container_dn, api.env.basedn)

    def get_either_dn(self, *keys, **options):
        '''
        Returns the DN of a user
        The user can be active (active container) or delete (delete container)
        If the user does not exist, returns the Active user DN
        '''
        ldap = self.backend
        # Check that this value is a Active user
        try:
            active_dn = self.get_dn(*keys, **options)
            ldap.get_entry(active_dn, ['dn'])

            # The Active user exists
            dn = active_dn
        except errors.NotFound:
            # Check that this value is a Delete user
            delete_dn = self.get_delete_dn(*keys, **options)
            try:
                ldap.get_entry(delete_dn, ['dn'])

                # The Delete user exists
                dn = delete_dn
            except errors.NotFound:
                # The user is neither Active/Delete -> returns that Active DN
                dn = active_dn

        return dn

    def _normalize_manager(self, manager):
        """
        Given a userid verify the user's existence and return the dn.
        """
        return super(user, self).normalize_manager(manager,
                                                   self.active_container_dn)

    def get_preserved_attribute(self, entry, options):
        if options.get('raw', False):
            return
        delete_container_dn = DN(self.delete_container_dn, api.env.basedn)
        if entry.dn.endswith(delete_container_dn):
            entry['preserved'] = True
        elif options.get('all', False):
            entry['preserved'] = False
Beispiel #24
0
 def setup(self):
     self.conn = None
     self.ldapuri = api.env.ldap_uri
     self.dn = DN(
         ('krbprincipalname', 'ldap/%s@%s' % (api.env.host, api.env.realm)),
         ('cn', 'services'), ('cn', 'accounts'), api.env.basedn)
Beispiel #25
0
    def _preserve_user(self, pkey, delete_container, **options):
        assert isinstance(delete_container, DN)

        dn = self.obj.get_either_dn(pkey, **options)
        delete_dn = DN(dn[0], delete_container)
        ldap = self.obj.backend
        logger.debug("preserve move %s -> %s", dn, delete_dn)

        if dn.endswith(delete_container):
            raise errors.ExecutionError(
                _('%s: user is already preserved' % pkey))
        # Check that this value is a Active user
        try:
            original_entry_attrs = self._exc_wrapper(pkey, options,
                                                     ldap.get_entry)(dn,
                                                                     ['dn'])
        except errors.NotFound:
            raise self.obj.handle_not_found(pkey)

        for callback in self.get_callbacks('pre'):
            dn = callback(self, ldap, dn, pkey, **options)
            assert isinstance(dn, DN)

        # start to move the entry to Delete container
        self._exc_wrapper(pkey, options, ldap.move_entry)(dn,
                                                          delete_dn,
                                                          del_old=True)

        # Then clear the credential attributes
        attrs_to_clear = [
            'krbPrincipalKey', 'krbLastPwdChange', 'krbPasswordExpiration',
            'userPassword'
        ]

        entry_attrs = self._exc_wrapper(pkey, options,
                                        ldap.get_entry)(delete_dn,
                                                        attrs_to_clear)

        clearedCredential = False
        for attr in attrs_to_clear:
            if attr.lower() in entry_attrs:
                del entry_attrs[attr]
                clearedCredential = True
        if clearedCredential:
            self._exc_wrapper(pkey, options, ldap.update_entry)(entry_attrs)

        # Then restore some original entry attributes
        attrs_to_restore = [
            'secretary', 'managedby', 'manager', 'ipauniqueid', 'uidnumber',
            'gidnumber', 'passwordHistory'
        ]

        entry_attrs = self._exc_wrapper(pkey, options,
                                        ldap.get_entry)(delete_dn,
                                                        attrs_to_restore)

        restoreAttr = False
        for attr in attrs_to_restore:
            if ((attr.lower() in original_entry_attrs)
                    and not (attr.lower() in entry_attrs)):
                restoreAttr = True
                entry_attrs[attr.lower()] = original_entry_attrs[attr.lower()]
        if restoreAttr:
            self._exc_wrapper(pkey, options, ldap.update_entry)(entry_attrs)
Beispiel #26
0
    def write_ca_certificates_dir(self, directory, ca_certs):
        # pylint: disable=ipa-forbidden-import
        from ipalib import x509  # FixMe: break import cycle
        # pylint: enable=ipa-forbidden-import

        path = Path(directory)
        try:
            path.mkdir(mode=0o755, exist_ok=True)
        except Exception:
            logger.error("Could not create %s", path)
            raise

        for cert, nickname, trusted, _ext_key_usage in ca_certs:
            if not trusted:
                continue

            # I'm not handling errors here because they have already
            # been checked by the time we get here
            subject = DN(cert.subject)
            issuer = DN(cert.issuer)

            # Construct the certificate filename using the Subject DN so that
            # the user can see which CA a particular file is for, and include
            # the serial number to disambiguate clashes where a subordinate CA
            # had a new certificate issued.
            #
            # Strictly speaking, certificates are uniquely idenified by (Issuer
            # DN, Serial Number). Do we care about the possibility of a clash
            # where a subordinate CA had two certificates issued by different
            # CAs who used the same serial number?)
            filename = f'{subject.ldap_text()} {cert.serial_number}.crt'

            # pylint: disable=old-division
            cert_path = path / filename
            # pylint: enable=old-division
            try:
                f = open(cert_path, 'w')
            except Exception:
                logger.error("Could not create %s", cert_path)
                raise

            with f:
                try:
                    os.fchmod(f.fileno(), 0o644)
                except Exception:
                    logger.error("Could not set mode of %s", cert_path)
                    raise

                try:
                    f.write(f"""\
This file was created by IPA. Do not edit.

Description: {nickname}
Subject: {subject.ldap_text()}
Issuer: {issuer.ldap_text()}
Serial Number (dec): {cert.serial_number}
Serial Number (hex): {cert.serial_number:#x}

""")
                    pem = cert.public_bytes(x509.Encoding.PEM).decode('ascii')
                    f.write(pem)
                except Exception:
                    logger.error("Could not write to %s", cert_path)
                    raise

        return True
Beispiel #27
0
# This is a tuple instead of a dict so that it is immutable.
# To create a dict with this config, just "d = dict(DEFAULT_CONFIG)".
DEFAULT_CONFIG = (
    ('api_version', API_VERSION),
    ('version', VERSION),

    # Domain, realm, basedn:
    # Following values do not have any reasonable default.
    # Do not initialize them so the code which depends on them blows up early
    # and does not do crazy stuff with default values instead of real ones.
    # ('domain', 'example.com'),
    # ('realm', 'EXAMPLE.COM'),
    # ('basedn', DN(('dc', 'example'), ('dc', 'com'))),

    # LDAP containers:
    ('container_accounts', DN(('cn', 'accounts'))),
    ('container_user', DN(('cn', 'users'), ('cn', 'accounts'))),
    ('container_deleteuser', DN(('cn', 'deleted users'), ('cn', 'accounts'), ('cn', 'provisioning'))),
    ('container_stageuser',  DN(('cn', 'staged users'),  ('cn', 'accounts'), ('cn', 'provisioning'))),
    ('container_group', DN(('cn', 'groups'), ('cn', 'accounts'))),
    ('container_service', DN(('cn', 'services'), ('cn', 'accounts'))),
    ('container_host', DN(('cn', 'computers'), ('cn', 'accounts'))),
    ('container_hostgroup', DN(('cn', 'hostgroups'), ('cn', 'accounts'))),
    ('container_rolegroup', DN(('cn', 'roles'), ('cn', 'accounts'))),
    ('container_permission', DN(('cn', 'permissions'), ('cn', 'pbac'))),
    ('container_privilege', DN(('cn', 'privileges'), ('cn', 'pbac'))),
    ('container_automount', DN(('cn', 'automount'))),
    ('container_policies', DN(('cn', 'policies'))),
    ('container_configs', DN(('cn', 'configs'), ('cn', 'policies'))),
    ('container_roles', DN(('cn', 'roles'), ('cn', 'policies'))),
    ('container_applications', DN(('cn', 'applications'), ('cn', 'configs'), ('cn', 'policies'))),
Beispiel #28
0
    def __setup_ssl(self):
        db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR,
                          subject_base=self.subject_base, user="******",
                          group=constants.HTTPD_GROUP,
                          create=True)
        self.disable_system_trust()
        self.create_password_conf()

        if self.pkcs12_info:
            if self.ca_is_configured:
                trust_flags = IPA_CA_TRUST_FLAGS
            else:
                trust_flags = EXTERNAL_CA_TRUST_FLAGS
            db.init_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1],
                                ca_file=self.ca_file,
                                trust_flags=trust_flags)
            server_certs = db.find_server_certs()
            if len(server_certs) == 0:
                raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0])

            # We only handle one server cert
            nickname = server_certs[0][0]
            if nickname == 'ipaCert':
                nickname = server_certs[1][0]
            self.dercert = db.get_cert_from_db(nickname, pem=False)

            if self.ca_is_configured:
                db.track_server_cert(nickname, self.principal, db.passwd_fname, 'restart_httpd')

            self.__set_mod_nss_nickname(nickname)
            self.add_cert_to_service()

        else:
            if not self.promote:
                ca_args = [
                    paths.CERTMONGER_DOGTAG_SUBMIT,
                    '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
                    '--certfile', paths.RA_AGENT_PEM,
                    '--keyfile', paths.RA_AGENT_KEY,
                    '--cafile', paths.IPA_CA_CRT,
                    '--agent-submit'
                ]
                helper = " ".join(ca_args)
                prev_helper = certmonger.modify_ca_helper('IPA', helper)
            else:
                prev_helper = None
            try:
                certmonger.request_and_wait_for_cert(
                    certpath=db.secdir,
                    nickname=self.cert_nickname,
                    principal=self.principal,
                    passwd_fname=db.passwd_fname,
                    subject=str(DN(('CN', self.fqdn), self.subject_base)),
                    ca='IPA',
                    profile=dogtag.DEFAULT_PROFILE,
                    dns=[self.fqdn],
                    post_command='restart_httpd')
            finally:
                if prev_helper is not None:
                    certmonger.modify_ca_helper('IPA', prev_helper)

            self.dercert = db.get_cert_from_db(self.cert_nickname, pem=False)

            if prev_helper is not None:
                self.add_cert_to_service()

            # Verify we have a valid server cert
            server_certs = db.find_server_certs()
            if not server_certs:
                raise RuntimeError("Could not find a suitable server cert.")

        # store the CA cert nickname so that we can publish it later on
        self.cacert_nickname = db.cacert_name
Beispiel #29
0
    def __call__(self, environ, start_response):
        # Make sure this is a form request.
        content_type = environ.get('CONTENT_TYPE', '').lower()
        if not content_type.startswith('application/x-www-form-urlencoded'):
            return self.bad_request(
                environ, start_response,
                "Content-Type must be application/x-www-form-urlencoded")

        # Make sure this is a POST request.
        method = environ.get('REQUEST_METHOD', '').upper()
        if method == 'POST':
            query_string = read_input(environ)
        else:
            return self.bad_request(environ, start_response,
                                    "HTTP request method must be POST")

        # Parse the query string to a dictionary.
        try:
            query_dict = parse_qs(query_string)
        except Exception as e:
            return self.bad_request(environ, start_response,
                                    "cannot parse query data")
        data = {}
        for field in ('user', 'password', 'first_code', 'second_code',
                      'token'):
            value = query_dict.get(field, None)
            if value is not None:
                if len(value) == 1:
                    data[field] = value[0]
                else:
                    return self.bad_request(
                        environ, start_response,
                        "more than one %s parameter" % field)
            elif field != 'token':
                return self.bad_request(environ, start_response,
                                        "no %s specified" % field)

        # Create the request control.
        sr = self.OTPSyncRequest()
        sr.setComponentByName('firstCode', data['first_code'])
        sr.setComponentByName('secondCode', data['second_code'])
        if 'token' in data:
            try:
                token_dn = DN(data['token'])
            except ValueError:
                token_dn = DN(
                    (self.api.Object.otptoken.primary_key.name, data['token']),
                    self.api.env.container_otp, self.api.env.basedn)

            sr.setComponentByName('tokenDN', str(token_dn))
        rc = ldap.controls.RequestControl(sr.OID, True, encoder.encode(sr))

        # Resolve the user DN
        bind_dn = DN((self.api.Object.user.primary_key.name, data['user']),
                     self.api.env.container_user, self.api.env.basedn)

        # Start building the response.
        status = HTTP_STATUS_SUCCESS
        response_headers = [('Content-Type', 'text/html; charset=utf-8')]
        title = 'Token sync rejected'

        # Perform the synchronization.
        conn = ldap2(self.api)
        try:
            conn.connect(bind_dn=bind_dn,
                         bind_pw=data['password'],
                         serverctrls=[
                             rc,
                         ])
            result = 'ok'
            title = "Token sync successful"
            message = "Token was synchronized."
        except (NotFound, ACIError):
            result = 'invalid-credentials'
            message = 'The username, password or token codes are not correct.'
        except Exception as e:
            result = 'error'
            message = "Could not connect to LDAP server."
            logger.error(
                "token_sync: cannot authenticate '%s' to LDAP "
                "server: %s", data['user'], str(e))
        finally:
            if conn.isconnected():
                conn.disconnect()

        # Report status and return.
        response_headers.append(('X-IPA-TokenSync-Result', result))
        start_response(status, response_headers)
        output = _success_template % dict(title=str(title),
                                          message=str(message))
        return [output.encode('utf-8')]
Beispiel #30
0
def realm_to_suffix(realm_name):
    'Convert a kerberos realm to a IPA suffix.'
    s = realm_name.split(".")
    suffix_dn = DN(*[('dc', x.lower()) for x in s])
    return suffix_dn