Exemplo n.º 1
0
    def check_access(self, operation=None):
        """
        Perform an LDAP query to determine authorization.

        This should be executed before any actual work is done.
        """
        if self.operation is None and operation is None:
            raise errors.ACIError(info=_('operation not defined'))

        if operation is None:
            operation = self.operation

        ldap = self.api.Backend.ldap2
        self.log.debug("IPA: virtual verify %s" % operation)

        operationdn = DN(('cn', operation), self.api.env.container_virtual, self.api.env.basedn)

        try:
            if not ldap.can_write(operationdn, "objectclass"):
                raise errors.ACIError(
                    info=_('not allowed to perform this command'))
        except errors.NotFound:
            raise errors.ACIError(info=_('No such virtual command'))

        return True
Exemplo n.º 2
0
 def post_callback(self, ldap, dn, entry, *keys, **options):
     default_entry = None
     rights = None
     for attrname in self.obj.default_attributes:
         if attrname not in entry:
             if keys[-1] is not None:
                 # User entry doesn't override the attribute.
                 # Check if this is caused by insufficient read rights
                 if rights is None:
                     rights = baseldap.get_effective_rights(
                         ldap, dn, self.obj.default_attributes)
                 if 'r' not in rights.get(attrname.lower(), ''):
                     raise errors.ACIError(
                         info=_('Ticket policy for %s could not be read') %
                             keys[-1])
                 # Fallback to the default
                 if default_entry is None:
                     try:
                         default_dn = self.obj.get_dn(None)
                         default_entry = ldap.get_entry(default_dn)
                     except errors.NotFound:
                         default_entry = {}
                 if attrname in default_entry:
                     entry[attrname] = default_entry[attrname]
         if attrname not in entry:
             raise errors.ACIError(
                 info=_('Default ticket policy could not be read'))
     return dn
Exemplo n.º 3
0
    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
        assert isinstance(dn, DN)
        # Allow an existing OTP to be reset but don't allow a OTP to be
        # added to an enrolled host.
        if options.get('userpassword') or options.get('random'):
            entry = {}
            self.obj.get_password_attributes(ldap, dn, entry)
            if not entry['has_password'] and entry['has_keytab']:
                raise errors.ValidationError(name='password', error=_('Password cannot be set on enrolled host.'))

        # Once a principal name is set it cannot be changed
        if 'cn' in entry_attrs:
            raise errors.ACIError(info=_('cn is immutable'))
        if 'locality' in entry_attrs:
            entry_attrs['l'] = entry_attrs['locality']
            del entry_attrs['locality']
        if 'krbprincipalname' in entry_attrs:
            (dn, entry_attrs_old) = ldap.get_entry(
                dn, ['objectclass', 'krbprincipalname']
            )
            if 'krbprincipalname' in entry_attrs_old:
                msg = 'Principal name already set, it is unchangeable.'
                raise errors.ACIError(info=msg)
            obj_classes = entry_attrs_old['objectclass']
            if 'krbprincipalaux' not in obj_classes:
                obj_classes.append('krbprincipalaux')
                entry_attrs['objectclass'] = obj_classes
        cert = x509.normalize_certificate(entry_attrs.get('usercertificate'))
        if cert:
            x509.verify_cert_subject(ldap, keys[-1], cert)
            (dn, entry_attrs_old) = ldap.get_entry(dn, ['usercertificate'])
            if 'usercertificate' in entry_attrs_old:
                oldcert = x509.normalize_certificate(entry_attrs_old.get('usercertificate')[0])
                try:
                    serial = unicode(x509.get_serial_number(oldcert, x509.DER))
                    try:
                        result = api.Command['cert_show'](unicode(serial))['result']
                        if 'revocation_reason' not in result:
                            try:
                                api.Command['cert_revoke'](unicode(serial), revocation_reason=4)
                            except errors.NotImplementedError:
                                # some CA's might not implement revoke
                                pass
                    except errors.NotImplementedError:
                        # some CA's might not implement revoke
                        pass
                except NSPRError, nsprerr:
                    if nsprerr.errno == -8183:
                        # If we can't decode the cert them proceed with
                        # modifying the host.
                        self.log.info("Problem decoding certificate %s" % nsprerr.args[1])
                    else:
                        raise nsprerr

            entry_attrs['usercertificate'] = cert
Exemplo n.º 4
0
    def pre_callback(self, ldap, dn, *keys, **options):
        ca_enabled_check(self.api)

        # ensure operator has permission to delete CA
        # before contacting Dogtag
        if not ldap.can_delete(dn):
            raise errors.ACIError(info=_(
                "Insufficient privilege to delete a CA."))

        if keys[0] == IPA_CA_CN:
            raise errors.ProtectedEntryError(
                label=_("CA"),
                key=keys[0],
                reason=_("IPA CA cannot be deleted"))

        ca_id = self.api.Command.ca_show(keys[0])['result']['ipacaid'][0]
        with self.api.Backend.ra_lightweight_ca as ca_api:
            data = ca_api.read_ca(ca_id)
            if data['enabled']:
                raise errors.ProtectedEntryError(
                    label=_("CA"),
                    key=keys[0],
                    reason=_("Must be disabled first"))
            ca_api.delete_ca(ca_id)

        return dn
Exemplo n.º 5
0
    def __kinit_as_trusted_account(self, info, password):
        """
        Initializes ccache with trusted domain account credentials.

        Applies session code defaults for ccache directory and naming prefix.
        Session code uses krbccache_prefix+<pid>, we use
        krbccache_prefix+<TD>+<domain netbios name> so there is no clash

        Returns tuple (ccache name, principal) where (None, None) signifes an error
        on ccache initialization
        """
        ccache_name = os.path.join(
            krbccache_dir, "%sTD%s" % (krbccache_prefix, info['name'][0]))
        principal = '%s$@%s' % (self.flatname, info['dns_domain'].upper())
        (stdout, stderr,
         returncode) = ipautil.run(['/usr/bin/kinit', principal],
                                   env={'KRB5CCNAME': ccache_name},
                                   stdin=password,
                                   raiseonerr=False)
        if returncode == 0:
            return (ccache_name, principal)
        else:
            if returncode == 1:
                raise errors.ACIError(info=_(
                    "KDC for %(domain)s denied trust account for IPA domain with a message '%(message)s'"
                ) % dict(domain=info['dns_domain'], message=stderr.strip()))
            return (None, None)
Exemplo n.º 6
0
    def execute(self, principal, password, current_password):
        """
        Execute the passwd operation.

        The dn should not be passed as a keyword argument as it is constructed
        by this method.

        Returns the entry

        :param principal: The login name or principal of the user
        :param password: the new password
        :param current_password: the existing password, if applicable
        """
        ldap = self.api.Backend.ldap2

        (dn, entry_attrs) = ldap.find_entry_by_attr(
            'krbprincipalname', principal, 'posixaccount', [''],
            DN(api.env.container_user, api.env.basedn))

        if principal == getattr(context, 'principal') and \
            current_password == MAGIC_VALUE:
            # No cheating
            self.log.warn(
                'User attempted to change password using magic value')
            raise errors.ACIError(info=_('Invalid credentials'))

        if current_password == MAGIC_VALUE:
            ldap.modify_password(dn, password)
        else:
            ldap.modify_password(dn, password, current_password)

        return dict(
            result=True,
            value=principal,
        )
Exemplo n.º 7
0
    def pre_callback(self, ldap, dn, entry, entry_attrs, *keys, **options):
        ca_enabled_check()
        if not ldap.can_add(dn[1:]):
            raise errors.ACIError(
                info=_("Insufficient 'add' privilege for entry '%s'.") % dn)

        # check for name collision before creating CA in Dogtag
        try:
            api.Object.ca.get_dn_if_exists(keys[-1])
            self.obj.handle_duplicate_entry(*keys)
        except errors.NotFound:
            pass

        # check for subject collision before creating CA in Dogtag
        result = api.Command.ca_find(ipacasubjectdn=options['ipacasubjectdn'])
        if result['count'] > 0:
            raise errors.DuplicateEntry(
                message=_("Subject DN is already used by CA '%s'") %
                result['result'][0]['cn'][0])

        # Create the CA in Dogtag.
        with self.api.Backend.ra_lightweight_ca as ca_api:
            resp = ca_api.create_ca(options['ipacasubjectdn'])
        entry['ipacaid'] = [resp['id']]
        entry['ipacaissuerdn'] = [resp['issuerDN']]

        # In the event that the issued certificate's subject DN
        # differs from what was requested, record the actual DN.
        #
        entry['ipacasubjectdn'] = [resp['dn']]
        return dn
Exemplo n.º 8
0
    def has_upg(self):
        """Returns True/False whether User-Private Groups are enabled.

        This is determined based on whether the UPG Definition's originfilter
        contains "(objectclass=disable)".

        If the UPG Definition or its originfilter is not readable,
        an ACI error is raised.
        """

        upg_dn = DN(('cn', 'UPG Definition'), ('cn', 'Definitions'),
                    ('cn', 'Managed Entries'), ('cn', 'etc'),
                    self.api.env.basedn)

        try:
            with self.error_handler():
                upg_entries = self.conn.search_s(str(upg_dn),
                                                 _ldap.SCOPE_BASE,
                                                 attrlist=['*'])
                upg_entries = self._convert_result(upg_entries)
        except errors.NotFound:
            upg_entries = None
        if not upg_entries or 'originfilter' not in upg_entries[0]:
            raise errors.ACIError(
                info=_('Could not read UPG Definition originfilter. '
                       'Check your permissions.'))
        org_filter = upg_entries[0].single_value['originfilter']
        return '(objectclass=disable)' not in org_filter
Exemplo n.º 9
0
def caacl_check(principal_type, principal, ca, profile_id):
    principal_type_map = {USER: '******', HOST: 'host', SERVICE: 'service'}
    if not acl_evaluate(principal_type_map[principal_type], principal, ca,
                        profile_id):
        raise errors.ACIError(
            info=_("Principal '%(principal)s' "
                   "is not permitted to use CA '%(ca)s' "
                   "with profile '%(profile_id)s' for certificate issuance.") %
            dict(principal=unicode(principal), ca=ca, profile_id=profile_id))
Exemplo n.º 10
0
    def execute(self, hostname, **kw):
        """
        Execute the machine join operation.

        Returns the entry as it will be created in LDAP.

        :param hostname: The name of the host joined
        :param kw: Keyword arguments for the other attributes.
        """
        assert 'cn' not in kw
        ldap = self.api.Backend.ldap2

        # realm parameter is not supported by host_{add,mod}
        kw.pop('realm', None)

        try:
            # First see if the host exists
            show_kw = {'fqdn': hostname, 'all': True}
            attrs_list = api.Command['host_show'](**show_kw)['result']
            dn = attrs_list['dn']

            # No error raised so far means that host entry exists
            logger.info(
                'Host entry for %s already exists, '
                'joining may fail on the client side '
                'if not forced', hostname)

            # If no principal name is set yet we need to try to add
            # one.
            if 'krbprincipalname' not in attrs_list:
                service = "host/%s@%s" % (hostname, api.env.realm)
                api.Command['host_mod'](hostname,
                                        **kw,
                                        krbprincipalname=service)
                logger.info('No principal set, setting to %s', service)

            # It exists, can we write the password attributes?
            allowed = ldap.can_write(dn, 'krblastpwdchange')
            if not allowed:
                raise errors.ACIError(info=_(
                    "Insufficient 'write' privilege "
                    "to the 'krbLastPwdChange' attribute of entry '%s'.") % dn)

            # Reload the attrs_list and dn so that we return update values
            attrs_list = api.Command['host_show'](**show_kw)['result']
            dn = attrs_list['dn']

        except errors.NotFound:
            attrs_list = api.Command['host_add'](hostname, **kw,
                                                 force=True)['result']
            dn = attrs_list['dn']

        config = api.Command['config_show']()['result']
        attrs_list['ipacertificatesubjectbase'] =\
            config['ipacertificatesubjectbase']

        return dn, attrs_list
Exemplo n.º 11
0
    def _enable_sid(self, ldap, options):
        # the user must have the Replication Administrators privilege
        privilege = 'Replication Administrators'
        if not principal_has_privilege(self.api, context.principal, privilege):
            raise errors.ACIError(
                info=_("not allowed to enable SID generation"))

        # NetBIOS name is either taken from options or generated
        try:
            netbios_name, reset_netbios_name = set_and_check_netbios_name(
                options.get('netbios_name', None), True, self.api)
        except ScriptError:
            raise errors.ValidationError(
                name="NetBIOS name",
                error=_('Up to 15 characters and only uppercase ASCII letters'
                        ', digits and dashes are allowed. Empty string is '
                        'not allowed.'))

        _ret = 0
        _stdout = ''
        _stderr = ''

        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

        method_options = []
        if options.get('add_sids', False):
            method_options.extend(["--add-sids"])
        method_options.extend(["--netbios-name", netbios_name])
        if reset_netbios_name:
            method_options.append("--reset-netbios-name")
        # Dbus definition expects up to 10 arguments
        method_options.extend([''] * (10 - len(method_options)))

        try:
            bus = dbus.SystemBus()
            obj = bus.get_object('org.freeipa.server',
                                 '/',
                                 follow_name_owner_changes=True)
            server = dbus.Interface(obj, 'org.freeipa.server')
            _ret, _stdout, _stderr = server.config_enable_sid(*method_options)
        except dbus.DBusException as e:
            logger.error(
                'Failed to call org.freeipa.server.config_enable_sid.'
                'DBus exception is %s', str(e))
            raise errors.ExecutionError(message=_('Failed to call DBus'))

        # The oddjob restarts dirsrv, we need to re-establish the conn
        if self.api.Backend.ldap2.isconnected():
            self.api.Backend.ldap2.disconnect()
        self.api.Backend.ldap2.connect(ccache=context.ccache_name)

        if _ret != 0:
            logger.error("Helper config_enable_sid return code is %d", _ret)
            raise errors.ExecutionError(
                message=_('Configuration of SID failed. '
                          'See details in the error log'))
Exemplo n.º 12
0
def check_operation_access(api, operation):
    """
    Check access of bound principal to given operation.

    :return: ``True``
    :raises: ``ACIError`` on access denied or ``NotFound`` for
             unknown virtual operation

    """
    operationdn = DN(('cn', operation), api.env.container_virtual,
                     api.env.basedn)

    try:
        if not api.Backend.ldap2.can_write(operationdn, "objectclass"):
            raise errors.ACIError(
                info=_('not allowed to perform operation: %s') % operation)
    except errors.NotFound:
        raise errors.ACIError(info=_('No such virtual command'))

    return True
Exemplo n.º 13
0
 def pre_callback(self, ldap, dn, *keys, **options):
     assert isinstance(dn, DN)
     if not options.get('force') and not self.obj.check_system(
             ldap, dn, *keys):
         raise errors.ACIError(
             info=_('A SYSTEM permission may not be removed'))
     # remove permission even when the underlying ACI is missing
     try:
         self.api.Command.aci_del(keys[-1], aciprefix=ACI_PREFIX)
     except errors.NotFound:
         pass
     return dn
Exemplo n.º 14
0
 def post_callback(self, ldap, dn, entry, *keys, **options):
     default_entry = None
     rights = None
     for attrname in self.obj.default_attributes:
         if attrname not in entry:
             if keys[-1] is not None:
                 # User entry doesn't override the attribute.
                 # Check if this is caused by insufficient read rights
                 if rights is None:
                     rights = baseldap.get_effective_rights(
                         ldap, dn, self.obj.default_attributes)
                 if 'r' not in rights.get(attrname.lower(), ''):
                     raise errors.ACIError(
                         info=_('Ticket policy for %s could not be read') %
                         keys[-1])
                 # Fallback to the default
                 if default_entry is None:
                     try:
                         default_dn = self.obj.get_dn(None)
                         default_entry = ldap.get_entry(default_dn)
                     except errors.NotFound:
                         default_entry = {}
                 if attrname in default_entry:
                     entry[attrname] = default_entry[attrname]
                 elif attrname in _option_based_attrs:
                     # If default entry contains option-based default attrs,
                     # copy the options explicitly
                     attrs = [(a, a.split(';')[0]) for a in default_entry]
                     for a in attrs:
                         if a[1] == attrname and a[0] not in entry:
                             entry[a[0]] = default_entry[a[0]]
         if attrname not in entry and attrname not in _option_based_attrs:
             raise errors.ACIError(
                 info=_('Default ticket policy could not be read'))
     # Rename authentication indicator-specific policy elements from LDAP
     rename_authind_options_from_ldap(entry, options)
     return dn
Exemplo n.º 15
0
    def execute(self, *keys, **options):
        # the server must be the local host
        if keys[-2] != api.env.host:
            raise errors.ValidationError(name='cn',
                                         error=_("must be \"%s\"") %
                                         api.env.host)

        # the server entry must exist
        try:
            self.obj.get_dn_if_exists(*keys[:-1])
        except errors.NotFound:
            raise self.obj.handle_not_found(keys[-2])

        # the user must have the Replication Administrators privilege
        privilege = u'Replication Administrators'
        privilege_dn = self.api.Object.privilege.get_dn(privilege)
        ldap = self.obj.backend
        filter = ldap.make_filter(
            {
                'krbprincipalname': context.principal,  # pylint: disable=no-member
                'memberof': privilege_dn
            },
            rules=ldap.MATCH_ALL)
        try:
            ldap.find_entries(base_dn=self.api.env.basedn, filter=filter)
        except errors.NotFound:
            raise errors.ACIError(
                info=_("not allowed to perform server connection check"))

        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

        bus = dbus.SystemBus()
        obj = bus.get_object('org.freeipa.server',
                             '/',
                             follow_name_owner_changes=True)
        server = dbus.Interface(obj, 'org.freeipa.server')

        ret, stdout, _stderr = server.conncheck(keys[-1])

        result = dict(
            result=(ret == 0),
            value=keys[-2],
        )

        for line in stdout.splitlines():
            messages.add_message(options['version'], result,
                                 messages.ExternalCommandOutput(line=line))

        return result
Exemplo n.º 16
0
    def pre_callback(self, ldap, dn, entry, entry_attrs, *keys, **options):
        ca_enabled_check(self.api)
        if not ldap.can_add(dn[1:]):
            raise errors.ACIError(
                info=_("Insufficient 'add' privilege for entry '%s'.") % dn)

        # check that DN only includes standard naming attributes
        dn_attrs = {
            ava.attr.lower()
            for rdn in options['ipacasubjectdn']
            for ava in rdn
        }
        x509_attrs = {
            attr.lower()
            for attr in six.viewvalues(ATTR_NAME_BY_OID)
        }
        unknown_attrs = dn_attrs - x509_attrs
        if len(unknown_attrs) > 0:
            raise errors.ValidationError(
                name=_("Subject DN"),
                error=_("Unrecognized attributes: %(attrs)s")
                    % dict(attrs=", ".join(unknown_attrs))
            )

        # check for name collision before creating CA in Dogtag
        try:
            api.Object.ca.get_dn_if_exists(keys[-1])
            self.obj.handle_duplicate_entry(*keys)
        except errors.NotFound:
            pass

        # check for subject collision before creating CA in Dogtag
        result = api.Command.ca_find(ipacasubjectdn=options['ipacasubjectdn'])
        if result['count'] > 0:
            raise errors.DuplicateEntry(message=_(
                "Subject DN is already used by CA '%s'"
                ) % result['result'][0]['cn'][0])

        # Create the CA in Dogtag.
        with self.api.Backend.ra_lightweight_ca as ca_api:
            resp = ca_api.create_ca(options['ipacasubjectdn'])
        entry['ipacaid'] = [resp['id']]
        entry['ipacaissuerdn'] = [resp['issuerDN']]

        # In the event that the issued certificate's subject DN
        # differs from what was requested, record the actual DN.
        #
        entry['ipacasubjectdn'] = [resp['dn']]
        return dn
Exemplo n.º 17
0
    def check_access(self, operation=None):
        """
        Perform an LDAP query to determine authorization.

        This should be executed before any actual work is done.
        """
        if self.operation is None and operation is None:
            raise errors.ACIError(info=_('operation not defined'))

        if operation is None:
            operation = self.operation

        logger.debug("IPA: virtual verify %s", operation)

        return check_operation_access(self.api, operation)
Exemplo n.º 18
0
    def pre_callback(self, ldap, dn, *keys, **options):
        assert isinstance(dn, DN)
        try:
            entry = ldap.get_entry(dn, attrs_list=["member"])
        except errors.NotFound:
            raise self.obj.handle_not_found(*keys)

        members = entry.get("member", [])
        if members:
            raise errors.ACIError(
                info=_(
                    "Not allowed to delete User Agreement with linked groups"
                )
            )

        return dn
Exemplo n.º 19
0
    def execute(self, cn, **options):
        ca_enabled_check(self.api)

        ca_obj = self.api.Command.ca_show(cn)['result']

        # ensure operator has permission to modify CAs
        if not self.api.Backend.ldap2.can_write(ca_obj['dn'], 'description'):
            raise errors.ACIError(
                info=_("Insufficient privilege to modify a CA."))

        with self.api.Backend.ra_lightweight_ca as ca_api:
            self.perform_action(ca_api, ca_obj['ipacaid'][0])

        return dict(
            result=True,
            value=pkey_to_value(cn, options),
        )
Exemplo n.º 20
0
    def execute(self, hostname, **kw):
        """
        Execute the machine join operation.

        Returns the entry as it will be created in LDAP.

        :param hostname: The name of the host joined
        :param kw: Keyword arguments for the other attributes.
        """
        assert 'cn' not in kw
        ldap = self.api.Backend.ldap2

        host = None
        try:
            # First see if the host exists
            kw = {'fqdn': hostname, 'all': True}
            attrs_list = api.Command['host_show'](**kw)['result']
            dn = attrs_list['dn']

            # If no principal name is set yet we need to try to add
            # one.
            if 'krbprincipalname' not in attrs_list:
                service = "host/%s@%s" % (hostname, api.env.realm)
                api.Command['host_mod'](hostname, krbprincipalname=service)

            # It exists, can we write the password attributes?
            allowed = ldap.can_write(dn, 'krblastpwdchange')
            if not allowed:
                raise errors.ACIError(info=_(
                    "Insufficient 'write' privilege to the 'krbLastPwdChange' attribute of entry '%s'."
                ) % dn)

            kw = {'fqdn': hostname, 'all': True}
            attrs_list = api.Command['host_show'](**kw)['result']
            dn = attrs_list['dn']
        except errors.NotFound:
            attrs_list = api.Command['host_add'](hostname,
                                                 force=True)['result']
            dn = attrs_list['dn']

        config = api.Command['config_show']()['result']
        attrs_list['ipacertificatesubjectbase'] = config[
            'ipacertificatesubjectbase']

        return (dn, attrs_list)
Exemplo n.º 21
0
    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
        ca_enabled_check(self.api)
        # Once a profile id is set it cannot be changed
        if 'cn' in entry_attrs:
            raise errors.ProtectedEntryError(label='certprofile', key=keys[0],
                reason=_('Certificate profiles cannot be renamed'))
        if 'file' in options:
            # ensure operator has permission to update a certprofile
            if not ldap.can_write(dn, 'ipacertprofilestoreissued'):
                raise errors.ACIError(info=_(
                    "Insufficient privilege to modify a certificate profile."))

            with self.api.Backend.ra_certprofile as profile_api:
                profile_api.disable_profile(keys[0])
                try:
                    profile_api.update_profile(keys[0], options['file'])
                finally:
                    profile_api.enable_profile(keys[0])

        return dn
Exemplo n.º 22
0
    def execute(self, *keys, **options):
        # the server must be the local host
        if keys[-2] != api.env.host:
            raise errors.ValidationError(name='cn',
                                         error=_("must be \"%s\"") %
                                         api.env.host)

        # the server entry must exist
        try:
            self.obj.get_dn_if_exists(*keys[:-1])
        except errors.NotFound:
            raise self.obj.handle_not_found(keys[-2])

        # the user must have the Replication Administrators privilege
        privilege = u'Replication Administrators'
        if not principal_has_privilege(self.api, context.principal, privilege):
            raise errors.ACIError(
                info=_("not allowed to perform server connection check"))

        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

        bus = dbus.SystemBus()
        obj = bus.get_object('org.freeipa.server',
                             '/',
                             follow_name_owner_changes=True)
        server = dbus.Interface(obj, 'org.freeipa.server')

        ret, stdout, _stderr = server.conncheck(keys[-1])

        result = dict(
            result=(ret == 0),
            value=keys[-2],
        )

        for line in stdout.splitlines():
            messages.add_message(options['version'], result,
                                 messages.ExternalCommandOutput(line=line))

        return result
Exemplo n.º 23
0
    def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
        """Remove users from linked groups
        """
        user_uids = [
            user_dn["uid"] for user_dn in found["memberuser"]["user"]
        ]
        if not user_uids:
            # no users found
            return dn
        # check that current user has write access to modify member user
        # attribute of the agreement.
        # Note: This will fail the entire operation, not just individual
        # removals.
        if not ldap.can_write(dn, "memberuser"):
            raise errors.ACIError(
                info=(
                    "Insufficient 'write' privilege to the 'memberuser' "
                    "attribute of entry '{}'."
                ).format(dn)
            )
        # get group primary keys for agreement without loading all users
        group_obj = self.api.Object.group
        group_container_dn = DN(group_obj.container_dn, self.api.env.basedn)
        try:
            entry = ldap.get_entry(dn, ["member"])
        except errors.NotFound:
            raise self.obj.handle_not_found(*keys)
        group_names = [
            group_obj.get_primary_key_from_dn(m)
            for m in entry["member"]
            if m.endswith(group_container_dn)
        ]
        # remove users group groups
        for group_name in group_names:
            self.api.Command.group_remove_member(group_name, user=user_uids)

        return dn
Exemplo n.º 24
0
    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
                     **options):
        assert isinstance(dn, DN)
        if not self.obj.check_system(ldap, dn, *keys):
            raise errors.ACIError(
                info=_('A SYSTEM permission may not be modified'))

        # check if permission is in LDAP
        try:
            (dn, attrs) = ldap.get_entry(dn,
                                         attrs_list,
                                         normalize=self.obj.normalize_dn)
        except errors.NotFound:
            self.obj.handle_not_found(*keys)

        # when renaming permission, check if the target permission does not
        # exists already. Then, make changes to underlying ACI
        if 'rename' in options:
            if options['rename']:
                try:
                    try:
                        new_dn = EditableDN(dn)
                        new_dn[0][
                            'cn']  # assure the first RDN has cn as it's type
                    except (IndexError, KeyError), e:
                        raise ValueError(
                            "expected dn starting with 'cn=' but got '%s'" %
                            dn)
                    new_dn[0].value = options['rename']
                    (new_dn,
                     attrs) = ldap.get_entry(new_dn,
                                             attrs_list,
                                             normalize=self.obj.normalize_dn)
                    raise errors.DuplicateEntry()
                except errors.NotFound:
                    pass  # permission may be renamed, continue
Exemplo n.º 25
0
    def execute(self, *keys, **options):
        """
        This requires updating both the user and the group. We first need to
        verify that both the user and group can be updated, then we go
        about our work. We don't want a situation where only the user or
        group can be modified and we're left in a bad state.
        """
        ldap = self.obj.backend

        group_dn = self.obj.get_dn(*keys, **options)
        user_dn = self.api.Object['user'].get_dn(*keys)

        try:
            user_attrs = ldap.get_entry(user_dn)
        except errors.NotFound:
            raise self.obj.handle_not_found(*keys)
        is_managed = self.obj.has_objectclass(user_attrs['objectclass'],
                                              'mepmanagedentry')
        if (not ldap.can_write(user_dn, "objectclass") or
                not ldap.can_write(user_dn, "mepManagedEntry") and is_managed):
            raise errors.ACIError(info=_('not allowed to modify user entries'))

        group_attrs = ldap.get_entry(group_dn)
        is_managed = self.obj.has_objectclass(group_attrs['objectclass'],
                                              'mepmanagedby')
        if (not ldap.can_write(group_dn, "objectclass") or
                not ldap.can_write(group_dn, "mepManagedBy") and is_managed):
            raise errors.ACIError(
                info=_('not allowed to modify group entries'))

        objectclasses = user_attrs['objectclass']
        try:
            i = objectclasses.index('mepOriginEntry')
            del objectclasses[i]
            user_attrs['mepManagedEntry'] = None
            ldap.update_entry(user_attrs)
        except ValueError:
            # Somehow the user isn't managed, let it pass for now. We'll
            # let the group throw "Not managed".
            pass

        group_attrs = ldap.get_entry(group_dn)
        objectclasses = group_attrs['objectclass']
        try:
            i = objectclasses.index('mepManagedEntry')
        except ValueError:
            # this should never happen
            raise errors.NotFound(reason=_('Not a managed group'))
        del objectclasses[i]

        # Make sure the resulting group has the default group objectclasses
        config = ldap.get_ipa_config()
        def_objectclass = config.get(self.obj.object_class_config,
                                     objectclasses)
        objectclasses = list(set(def_objectclass + objectclasses))

        group_attrs['mepManagedBy'] = None
        group_attrs['objectclass'] = objectclasses
        ldap.update_entry(group_attrs)

        return dict(
            result=True,
            value=pkey_to_value(keys[0], options),
        )
Exemplo n.º 26
0
    def execute(self, csr, **kw):
        ca_enabled_check()

        ldap = self.api.Backend.ldap2
        add = kw.get('add')
        request_type = kw.get('request_type')
        profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)
        ca = '.'  # top-level CA hardcoded until subca plugin implemented
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        principal_string = kw.get('principal')
        principal = split_any_principal(principal_string)
        servicename, principal_name, realm = principal
        if servicename is None:
            principal_type = USER
        elif servicename == 'host':
            principal_type = HOST
        else:
            principal_type = SERVICE

        bind_principal = split_any_principal(getattr(context, 'principal'))
        bind_service, bind_name, bind_realm = bind_principal

        if bind_service is None:
            bind_principal_type = USER
        elif bind_service == 'host':
            bind_principal_type = HOST
        else:
            bind_principal_type = SERVICE

        if bind_principal != principal and bind_principal_type != HOST:
            # Can the bound principal request certs for another principal?
            self.check_access()

        try:
            self.check_access("request certificate ignore caacl")
            bypass_caacl = True
        except errors.ACIError:
            bypass_caacl = False

        if not bypass_caacl:
            caacl_check(principal_type, principal_string, ca, profile_id)

        try:
            subject = pkcs10.get_subject(csr)
            extensions = pkcs10.get_extensions(csr)
            subjectaltname = pkcs10.get_subjectaltname(csr) or ()
        except (NSPRError, PyAsn1Error, ValueError) as e:
            raise errors.CertificateOperationError(
                error=_("Failure decoding Certificate Signing Request: %s") %
                e)

        # self-service and host principals may bypass SAN permission check
        if bind_principal != principal and bind_principal_type != HOST:
            if '2.5.29.17' in extensions:
                self.check_access('request certificate with subjectaltname')

        dn = None
        principal_obj = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if principal_type == SERVICE:
                principal_obj = api.Command['service_show'](principal_string,
                                                            all=True)
            elif principal_type == HOST:
                principal_obj = api.Command['host_show'](principal_name,
                                                         all=True)
            elif principal_type == USER:
                principal_obj = api.Command['user_show'](principal_name,
                                                         all=True)
        except errors.NotFound as e:
            if principal_type == SERVICE and add:
                principal_obj = api.Command['service_add'](principal_string,
                                                           force=True)
            else:
                raise errors.NotFound(
                    reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        dn = principal_obj['dn']

        # Ensure that the DN in the CSR matches the principal
        cn = subject.common_name  #pylint: disable=E1101
        if not cn:
            raise errors.ValidationError(
                name='csr',
                error=_("No Common Name was found in subject of request."))

        if principal_type in (SERVICE, HOST):
            if cn.lower() != principal_name.lower():
                raise errors.ACIError(info=_(
                    "hostname in subject of request '%(cn)s' "
                    "does not match principal hostname '%(hostname)s'") %
                                      dict(cn=cn, hostname=principal_name))
        elif principal_type == USER:
            # check user name
            if cn != principal_name:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN commonName does not match user's login"))

            # check email address
            mail = subject.email_address  #pylint: disable=E1101
            if mail is not None and mail not in principal_obj.get('mail', []):
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN emailAddress does not match "
                            "any of user's email addresses"))

        # We got this far so the principal entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        for name_type, name in subjectaltname:
            if name_type == pkcs10.SAN_DNSNAME:
                name = unicode(name)
                alt_principal_obj = None
                alt_principal_string = None
                try:
                    if principal_type == HOST:
                        alt_principal_string = 'host/%s@%s' % (name, realm)
                        alt_principal_obj = api.Command['host_show'](name,
                                                                     all=True)
                    elif principal_type == SERVICE:
                        alt_principal_string = '%s/%s@%s' % (servicename, name,
                                                             realm)
                        alt_principal_obj = api.Command['service_show'](
                            alt_principal_string, all=True)
                    elif principal_type == USER:
                        raise errors.ValidationError(
                            name='csr',
                            error=_("subject alt name type %s is forbidden "
                                    "for user principals") % name_type)
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'The service principal for '
                        'subject alt name %s in certificate request does not '
                        'exist') % name)
                if alt_principal_obj is not None:
                    altdn = alt_principal_obj['result']['dn']
                    if not ldap.can_write(altdn, "usercertificate"):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)
                if alt_principal_string is not None and not bypass_caacl:
                    caacl_check(principal_type, alt_principal_string, ca,
                                profile_id)
            elif name_type in (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME,
                               pkcs10.SAN_OTHERNAME_UPN):
                if split_any_principal(name) != principal:
                    raise errors.ACIError(
                        info=_("Principal '%s' in subject alt name does not "
                               "match requested principal") % name)
            elif name_type == pkcs10.SAN_RFC822NAME:
                if principal_type == USER:
                    if name not in principal_obj.get('mail', []):
                        raise errors.ValidationError(
                            name='csr',
                            error=_("RFC822Name does not match "
                                    "any of user's email addresses"))
                else:
                    raise errors.ValidationError(
                        name='csr',
                        error=_("subject alt name type %s is forbidden "
                                "for non-user principals") % name_type)
            else:
                raise errors.ACIError(
                    info=_("Subject alt name type %s is forbidden") %
                    name_type)

        # Request the certificate
        result = self.Backend.ra.request_certificate(csr,
                                                     profile_id,
                                                     request_type=request_type)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the principal's entry
        # (unless the profile tells us not to)
        profile = api.Command['certprofile_show'](profile_id)
        store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE'
        if store and 'certificate' in result:
            cert = str(result.get('certificate'))
            kwargs = dict(addattr=u'usercertificate={}'.format(cert))
            if principal_type == SERVICE:
                api.Command['service_mod'](principal_string, **kwargs)
            elif principal_type == HOST:
                api.Command['host_mod'](principal_name, **kwargs)
            elif principal_type == USER:
                api.Command['user_mod'](principal_name, **kwargs)

        return dict(result=result)
Exemplo n.º 27
0
    def execute(self, csr, all=False, raw=False, **kw):
        ca_enabled_check()

        ldap = self.api.Backend.ldap2
        add = kw.get('add')
        request_type = kw.get('request_type')
        profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)

        # Check that requested authority exists (done before CA ACL
        # enforcement so that user gets better error message if
        # referencing nonexistant CA) and look up authority ID.
        #
        ca = kw['cacn']
        ca_obj = api.Command.ca_show(ca)['result']
        ca_id = ca_obj['ipacaid'][0]
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        principal = kw.get('principal')
        principal_string = unicode(principal)

        if principal.is_user:
            principal_type = USER
        elif principal.is_host:
            principal_type = HOST
        else:
            principal_type = SERVICE

        bind_principal = kerberos.Principal(getattr(context, 'principal'))
        bind_principal_string = unicode(bind_principal)

        if bind_principal.is_user:
            bind_principal_type = USER
        elif bind_principal.is_host:
            bind_principal_type = HOST
        else:
            bind_principal_type = SERVICE

        if (bind_principal_string != principal_string
                and bind_principal_type != HOST):
            # Can the bound principal request certs for another principal?
            self.check_access()

        try:
            self.check_access("request certificate ignore caacl")
            bypass_caacl = True
        except errors.ACIError:
            bypass_caacl = False

        if not bypass_caacl:
            caacl_check(principal_type, principal, ca, profile_id)

        try:
            subject = pkcs10.get_subject(csr)
            extensions = pkcs10.get_extensions(csr)
            subjectaltname = pkcs10.get_subjectaltname(csr) or ()
        except (NSPRError, PyAsn1Error, ValueError) as e:
            raise errors.CertificateOperationError(
                error=_("Failure decoding Certificate Signing Request: %s") %
                e)

        # self-service and host principals may bypass SAN permission check
        if (bind_principal_string != principal_string
                and bind_principal_type != HOST):
            if '2.5.29.17' in extensions:
                self.check_access('request certificate with subjectaltname')

        dn = None
        principal_obj = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if principal_type == SERVICE:
                principal_obj = api.Command['service_show'](principal_string,
                                                            all=True)
            elif principal_type == HOST:
                principal_obj = api.Command['host_show'](principal.hostname,
                                                         all=True)
            elif principal_type == USER:
                principal_obj = api.Command['user_show'](principal.username,
                                                         all=True)
        except errors.NotFound as e:
            if add:
                if principal_type == SERVICE:
                    principal_obj = api.Command['service_add'](
                        principal_string, force=True)
                else:
                    princtype_str = PRINCIPAL_TYPE_STRING_MAP[principal_type]
                    raise errors.OperationNotSupportedForPrincipalType(
                        operation=_("'add' option"),
                        principal_type=princtype_str)
            else:
                raise errors.NotFound(
                    reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        dn = principal_obj['dn']

        # Ensure that the DN in the CSR matches the principal
        cn = subject.common_name  #pylint: disable=E1101
        if not cn:
            raise errors.ValidationError(
                name='csr',
                error=_("No Common Name was found in subject of request."))

        if principal_type in (SERVICE, HOST):
            if cn.lower() != principal.hostname.lower():
                raise errors.ACIError(info=_(
                    "hostname in subject of request '%(cn)s' "
                    "does not match principal hostname '%(hostname)s'") %
                                      dict(cn=cn, hostname=principal.hostname))
        elif principal_type == USER:
            # check user name
            if cn != principal.username:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN commonName does not match user's login"))

            # check email address
            mail = subject.email_address  #pylint: disable=E1101
            if mail is not None and mail not in principal_obj.get('mail', []):
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN emailAddress does not match "
                            "any of user's email addresses"))

        # We got this far so the principal entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        for name_type, desc, name, _der_name in subjectaltname:
            if name_type == nss.certDNSName:
                name = unicode(name)
                alt_principal = None
                alt_principal_obj = None
                try:
                    if principal_type == HOST:
                        alt_principal = kerberos.Principal((u'host', name),
                                                           principal.realm)
                        alt_principal_obj = api.Command['host_show'](name,
                                                                     all=True)
                    elif principal_type == SERVICE:
                        alt_principal = kerberos.Principal(
                            (principal.service_name, name), principal.realm)
                        alt_principal_obj = api.Command['service_show'](
                            alt_principal, all=True)
                    elif principal_type == USER:
                        raise errors.ValidationError(
                            name='csr',
                            error=_("subject alt name type %s is forbidden "
                                    "for user principals") % desc)
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'The service principal for '
                        'subject alt name %s in certificate request does not '
                        'exist') % name)
                if alt_principal_obj is not None:
                    altdn = alt_principal_obj['result']['dn']
                    if not ldap.can_write(altdn, "usercertificate"):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)
                if alt_principal is not None and not bypass_caacl:
                    caacl_check(principal_type, alt_principal, ca, profile_id)
            elif name_type in [
                (nss.certOtherName, x509.SAN_UPN),
                (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME),
            ]:
                if name != principal_string:
                    raise errors.ACIError(
                        info=_("Principal '%s' in subject alt name does not "
                               "match requested principal") % name)
            elif name_type == nss.certRFC822Name:
                if principal_type == USER:
                    if name not in principal_obj.get('mail', []):
                        raise errors.ValidationError(
                            name='csr',
                            error=_("RFC822Name does not match "
                                    "any of user's email addresses"))
                else:
                    raise errors.ValidationError(
                        name='csr',
                        error=_("subject alt name type %s is forbidden "
                                "for non-user principals") % desc)
            else:
                raise errors.ACIError(
                    info=_("Subject alt name type %s is forbidden") % desc)

        # Request the certificate
        try:
            result = self.Backend.ra.request_certificate(
                csr, profile_id, ca_id, request_type=request_type)
        except errors.HTTPRequestError as e:
            if e.status == 409:  # pylint: disable=no-member
                raise errors.CertificateOperationError(
                    error=_("CA '%s' is disabled") % ca)
            else:
                raise e

        if not raw:
            self.obj._parse(result, all)
            result['request_id'] = int(result['request_id'])
            result['cacn'] = ca_obj['cn'][0]

        # Success? Then add it to the principal's entry
        # (unless the profile tells us not to)
        profile = api.Command['certprofile_show'](profile_id)
        store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE'
        if store and 'certificate' in result:
            cert = str(result.get('certificate'))
            kwargs = dict(addattr=u'usercertificate={}'.format(cert))
            if principal_type == SERVICE:
                api.Command['service_mod'](principal_string, **kwargs)
            elif principal_type == HOST:
                api.Command['host_mod'](principal.hostname, **kwargs)
            elif principal_type == USER:
                api.Command['user_mod'](principal.username, **kwargs)

        return dict(
            result=result,
            value=pkey_to_value(int(result['request_id']), kw),
        )
Exemplo n.º 28
0
    from ldap.controls import RequestControl as LDAPControl  #pylint: disable=F0401
except ImportError:
    from ldap.controls import LDAPControl as LDAPControl  #pylint: disable=F0401
import ldap as _ldap
from ipaserver.ipaldap import IPAdmin
from ipalib.session import krbccache_dir, krbccache_prefix
from ipapython import dnsclient

__doc__ = _("""
Classes to manage trust joins using DCE-RPC calls

The code in this module relies heavily on samba4-python package
and Samba4 python bindings.
""")

access_denied_error = errors.ACIError(
    info=_('CIFS server denied your credentials'))
dcerpc_error_codes = {
    -1073741823:
    errors.RemoteRetrieveError(
        reason=_('communication with CIFS server was unsuccessful')),
    -1073741790:
    access_denied_error,
    -1073741715:
    access_denied_error,
    -1073741614:
    access_denied_error,
    -1073741603:
    errors.ValidationError(name=_('AD domain controller'),
                           error=_('unsupported functional level')),
}
Exemplo n.º 29
0
def promote_check(installer):
    options = installer
    installer._enrollment_performed = False
    installer._top_dir = tempfile.mkdtemp("ipa")

    # check selinux status, http and DS ports, NTP conflicting services
    common_check(options.no_ntp)

    client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
    if not client_fstore.has_files():
        ensure_enrolled(installer)
    else:
        if (options.domain_name or options.server or options.realm_name
                or options.host_name or options.password or options.keytab):
            print("IPA client is already configured on this system, ignoring "
                  "the --domain, --server, --realm, --hostname, --password "
                  "and --keytab options.")

        # The NTP configuration can not be touched on pre-installed client:
        if options.no_ntp or options.ntp_servers or options.ntp_pool:
            raise ScriptError(
                "NTP configuration cannot be updated during promotion")

    sstore = sysrestore.StateFile(paths.SYSRESTORE)

    fstore = sysrestore.FileStore(paths.SYSRESTORE)

    env = Env()
    env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
    env._finalize_core(**dict(constants.DEFAULT_CONFIG))

    # pylint: disable=no-member
    xmlrpc_uri = 'https://{}/ipa/xml'.format(ipautil.format_netloc(env.host))
    api.bootstrap(in_server=True,
                  context='installer',
                  confdir=paths.ETC_IPA,
                  ldap_uri=installutils.realm_to_ldapi_uri(env.realm),
                  xmlrpc_uri=xmlrpc_uri)
    # pylint: enable=no-member
    api.finalize()

    config = ReplicaConfig()
    config.realm_name = api.env.realm
    config.host_name = api.env.host
    config.domain_name = api.env.domain
    config.master_host_name = api.env.server
    config.ca_host_name = api.env.ca_host
    config.kra_host_name = config.ca_host_name
    config.ca_ds_port = 389
    config.setup_ca = options.setup_ca
    config.setup_kra = options.setup_kra
    config.dir = installer._top_dir
    config.basedn = api.env.basedn

    http_pkcs12_file = None
    http_pkcs12_info = None
    http_ca_cert = None
    dirsrv_pkcs12_file = None
    dirsrv_pkcs12_info = None
    dirsrv_ca_cert = None
    pkinit_pkcs12_file = None
    pkinit_pkcs12_info = None
    pkinit_ca_cert = None

    if options.http_cert_files:
        if options.http_pin is None:
            options.http_pin = installutils.read_password(
                "Enter Apache Server private key unlock",
                confirm=False,
                validate=False,
                retry=False)
            if options.http_pin is None:
                raise ScriptError(
                    "Apache Server private key unlock password required")
        http_pkcs12_file, http_pin, http_ca_cert = load_pkcs12(
            cert_files=options.http_cert_files,
            key_password=options.http_pin,
            key_nickname=options.http_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        http_pkcs12_info = (http_pkcs12_file.name, http_pin)

    if options.dirsrv_cert_files:
        if options.dirsrv_pin is None:
            options.dirsrv_pin = installutils.read_password(
                "Enter Directory Server private key unlock",
                confirm=False,
                validate=False,
                retry=False)
            if options.dirsrv_pin is None:
                raise ScriptError(
                    "Directory Server private key unlock password required")
        dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = load_pkcs12(
            cert_files=options.dirsrv_cert_files,
            key_password=options.dirsrv_pin,
            key_nickname=options.dirsrv_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin)

    if options.pkinit_cert_files:
        if options.pkinit_pin is None:
            options.pkinit_pin = installutils.read_password(
                "Enter Kerberos KDC private key unlock",
                confirm=False,
                validate=False,
                retry=False)
            if options.pkinit_pin is None:
                raise ScriptError(
                    "Kerberos KDC private key unlock password required")
        pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
            cert_files=options.pkinit_cert_files,
            key_password=options.pkinit_pin,
            key_nickname=options.pkinit_cert_name,
            ca_cert_files=options.ca_cert_files,
            realm_name=config.realm_name)
        pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)

    if (options.http_cert_files and options.dirsrv_cert_files
            and http_ca_cert != dirsrv_ca_cert):
        raise RuntimeError("Apache Server SSL certificate and Directory "
                           "Server SSL certificate are not signed by the same"
                           " CA certificate")

    if (options.http_cert_files and options.pkinit_cert_files
            and http_ca_cert != pkinit_ca_cert):
        raise RuntimeError("Apache Server SSL certificate and PKINIT KDC "
                           "certificate are not signed by the same CA "
                           "certificate")

    installutils.verify_fqdn(config.host_name, options.no_host_dns)
    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)

    ccache = os.environ['KRB5CCNAME']
    kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
                 paths.KRB5_KEYTAB, ccache)

    cafile = paths.IPA_CA_CRT
    if not os.path.isfile(cafile):
        raise RuntimeError("CA cert file is not available! Please reinstall"
                           "the client and try again.")

    ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
    xmlrpc_uri = 'https://{}/ipa/xml'.format(
        ipautil.format_netloc(config.master_host_name))
    remote_api = create_api(mode=None)
    remote_api.bootstrap(in_server=True,
                         context='installer',
                         confdir=paths.ETC_IPA,
                         ldap_uri=ldapuri,
                         xmlrpc_uri=xmlrpc_uri)
    remote_api.finalize()
    installer._remote_api = remote_api

    with rpc_client(remote_api) as client:
        check_remote_version(client, parse_version(api.env.version))
        check_remote_fips_mode(client, api.env.fips_mode)

    conn = remote_api.Backend.ldap2
    replman = None
    try:
        # Try out authentication
        conn.connect(ccache=ccache)
        replman = ReplicationManager(config.realm_name,
                                     config.master_host_name, None)

        promotion_check_ipa_domain(conn, remote_api.env.basedn)

        # Make sure that domain fulfills minimal domain level
        # requirement
        domain_level = current_domain_level(remote_api)
        check_domain_level_is_supported(domain_level)
        if domain_level < constants.MIN_DOMAIN_LEVEL:
            raise RuntimeError(
                "Cannot promote this client to a replica. The domain level "
                "must be raised to {mindomainlevel} before the replica can be "
                "installed".format(mindomainlevel=constants.MIN_DOMAIN_LEVEL))

        # Check authorization
        result = remote_api.Command['hostgroup_find'](
            cn=u'ipaservers', host=[unicode(api.env.host)])['result']
        add_to_ipaservers = not result

        if add_to_ipaservers:
            if options.password and not options.admin_password:
                raise errors.ACIError(info="Not authorized")

            if installer._ccache is None:
                del os.environ['KRB5CCNAME']
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

            try:
                installutils.check_creds(options, config.realm_name)
                installer._ccache = os.environ.get('KRB5CCNAME')
            finally:
                os.environ['KRB5CCNAME'] = ccache

            conn.disconnect()
            conn.connect(ccache=installer._ccache)

            try:
                result = remote_api.Command['hostgroup_show'](
                    u'ipaservers', all=True, rights=True)['result']

                if 'w' not in result['attributelevelrights']['member']:
                    raise errors.ACIError(info="Not authorized")
            finally:
                conn.disconnect()
                conn.connect(ccache=ccache)

        # Check that we don't already have a replication agreement
        if replman.get_replication_agreement(config.host_name):
            msg = ("A replication agreement for this host already exists. "
                   "It needs to be removed.\n"
                   "Run this command:\n"
                   "    %% ipa-replica-manage del {host} --force".format(
                       host=config.host_name))
            raise ScriptError(msg, rval=3)

        # Detect if the other master can handle replication managers
        # cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX
        dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
                ('cn', 'etc'), ipautil.realm_to_suffix(config.realm_name))
        try:
            conn.get_entry(dn)
        except errors.NotFound:
            msg = ("The Replication Managers group is not available in "
                   "the domain. Replica promotion requires the use of "
                   "Replication Managers to be able to replicate data. "
                   "Upgrade the peer master or use the ipa-replica-prepare "
                   "command on the master and use a prep file to install "
                   "this replica.")
            logger.error("%s", msg)
            raise ScriptError(rval=3)

        dns_masters = remote_api.Object['dnsrecord'].get_dns_masters()
        if dns_masters:
            if not options.no_host_dns:
                logger.debug('Check forward/reverse DNS resolution')
                resolution_ok = (
                    check_dns_resolution(config.master_host_name, dns_masters)
                    and check_dns_resolution(config.host_name, dns_masters))
                if not resolution_ok and installer.interactive:
                    if not ipautil.user_input("Continue?", False):
                        raise ScriptError(rval=0)
        else:
            logger.debug('No IPA DNS servers, '
                         'skipping forward/reverse resolution check')

        entry_attrs = conn.get_ipa_config()
        subject_base = entry_attrs.get('ipacertificatesubjectbase', [None])[0]
        if subject_base is not None:
            config.subject_base = DN(subject_base)

        # Find if any server has a CA
        ca_host = service.find_providing_server('CA', conn,
                                                config.ca_host_name)
        if ca_host is not None:
            config.ca_host_name = ca_host
            ca_enabled = True
            if options.dirsrv_cert_files:
                logger.error("Certificates could not be provided when "
                             "CA is present on some master.")
                raise ScriptError(rval=3)
        else:
            if options.setup_ca:
                logger.error("The remote master does not have a CA "
                             "installed, can't set up CA")
                raise ScriptError(rval=3)
            ca_enabled = False
            if not options.dirsrv_cert_files:
                logger.error("Cannot issue certificates: a CA is not "
                             "installed. Use the --http-cert-file, "
                             "--dirsrv-cert-file options to provide "
                             "custom certificates.")
                raise ScriptError(rval=3)

        kra_host = service.find_providing_server('KRA', conn,
                                                 config.kra_host_name)
        if kra_host is not None:
            config.kra_host_name = kra_host
            kra_enabled = True
        else:
            if options.setup_kra:
                logger.error("There is no KRA server in the domain, "
                             "can't setup a KRA clone")
                raise ScriptError(rval=3)
            kra_enabled = False

        if ca_enabled:
            options.realm_name = config.realm_name
            options.host_name = config.host_name
            ca.install_check(False, config, options)

        if kra_enabled:
            try:
                kra.install_check(remote_api, config, options)
            except RuntimeError as e:
                raise ScriptError(e)

        if options.setup_dns:
            dns.install_check(False, remote_api, True, options,
                              config.host_name)
            config.ips = dns.ip_addresses
        else:
            config.ips = installutils.get_server_ip_address(
                config.host_name, not installer.interactive, False,
                options.ip_addresses)

            # check addresses here, dns module is doing own check
            no_matching_interface_for_ip_address_warning(config.ips)

        if options.setup_adtrust:
            adtrust.install_check(False, options, remote_api)

    except errors.ACIError:
        logger.debug("%s", traceback.format_exc())
        raise ScriptError("\nInsufficient privileges to promote the server."
                          "\nPossible issues:"
                          "\n- A user has insufficient privileges"
                          "\n- This client has insufficient privileges "
                          "to become an IPA replica")
    except errors.LDAPError:
        logger.debug("%s", traceback.format_exc())
        raise ScriptError("\nUnable to connect to LDAP server %s" %
                          config.master_host_name)
    finally:
        if replman and replman.conn:
            replman.conn.unbind()
        if conn.isconnected():
            conn.disconnect()

    # check connection
    if not options.skip_conncheck:
        if add_to_ipaservers:
            # use user's credentials when the server host is not ipaservers
            if installer._ccache is None:
                del os.environ['KRB5CCNAME']
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

        try:
            replica_conn_check(config.master_host_name,
                               config.host_name,
                               config.realm_name,
                               options.setup_ca,
                               389,
                               options.admin_password,
                               principal=options.principal,
                               ca_cert_file=cafile)
        finally:
            if add_to_ipaservers:
                os.environ['KRB5CCNAME'] = ccache

    installer._ca_enabled = ca_enabled
    installer._kra_enabled = kra_enabled
    installer._ca_file = cafile
    installer._fstore = fstore
    installer._sstore = sstore
    installer._config = config
    installer._add_to_ipaservers = add_to_ipaservers
    installer._dirsrv_pkcs12_file = dirsrv_pkcs12_file
    installer._dirsrv_pkcs12_info = dirsrv_pkcs12_info
    installer._http_pkcs12_file = http_pkcs12_file
    installer._http_pkcs12_info = http_pkcs12_info
    installer._pkinit_pkcs12_file = pkinit_pkcs12_file
    installer._pkinit_pkcs12_info = pkinit_pkcs12_info
Exemplo n.º 30
0
    def execute(self, csr, all=False, raw=False, **kw):
        ca_enabled_check()

        ldap = self.api.Backend.ldap2
        add = kw.get('add')
        request_type = kw.get('request_type')
        profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)

        # Check that requested authority exists (done before CA ACL
        # enforcement so that user gets better error message if
        # referencing nonexistant CA) and look up authority ID.
        #
        ca = kw['cacn']
        ca_obj = api.Command.ca_show(ca)['result']
        ca_id = ca_obj['ipacaid'][0]
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        principal = kw.get('principal')
        principal_string = unicode(principal)

        if principal.is_user:
            principal_type = USER
        elif principal.is_host:
            principal_type = HOST
        else:
            principal_type = SERVICE

        bind_principal = kerberos.Principal(getattr(context, 'principal'))
        bind_principal_string = unicode(bind_principal)

        if bind_principal.is_user:
            bind_principal_type = USER
        elif bind_principal.is_host:
            bind_principal_type = HOST
        else:
            bind_principal_type = SERVICE

        if (bind_principal_string != principal_string
                and bind_principal_type != HOST):
            # Can the bound principal request certs for another principal?
            self.check_access()

        try:
            self.check_access("request certificate ignore caacl")
            bypass_caacl = True
        except errors.ACIError:
            bypass_caacl = False

        if not bypass_caacl:
            caacl_check(principal_type, principal, ca, profile_id)

        try:
            csr_obj = pkcs10.load_certificate_request(csr)
        except ValueError as e:
            raise errors.CertificateOperationError(
                error=_("Failure decoding Certificate Signing Request: %s") %
                e)

        try:
            ext_san = csr_obj.extensions.get_extension_for_oid(
                cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
        except cryptography.x509.extensions.ExtensionNotFound:
            ext_san = None

        # self-service and host principals may bypass SAN permission check
        if (bind_principal_string != principal_string
                and bind_principal_type != HOST):
            if ext_san is not None:
                self.check_access('request certificate with subjectaltname')

        dn = None
        principal_obj = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if principal_type == SERVICE:
                principal_obj = api.Command['service_show'](principal_string,
                                                            all=True)
            elif principal_type == HOST:
                principal_obj = api.Command['host_show'](principal.hostname,
                                                         all=True)
            elif principal_type == USER:
                principal_obj = api.Command['user_show'](principal.username,
                                                         all=True)
        except errors.NotFound as e:
            if add:
                if principal_type == SERVICE:
                    principal_obj = api.Command['service_add'](
                        principal_string, force=True)
                else:
                    princtype_str = PRINCIPAL_TYPE_STRING_MAP[principal_type]
                    raise errors.OperationNotSupportedForPrincipalType(
                        operation=_("'add' option"),
                        principal_type=princtype_str)
            else:
                raise errors.NotFound(
                    reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        dn = principal_obj['dn']

        # Ensure that the DN in the CSR matches the principal
        #
        # We only look at the "most specific" CN value
        cns = csr_obj.subject.get_attributes_for_oid(
            cryptography.x509.oid.NameOID.COMMON_NAME)
        if len(cns) == 0:
            raise errors.ValidationError(
                name='csr',
                error=_("No Common Name was found in subject of request."))
        cn = cns[-1].value  # "most specific" is end of list

        if principal_type in (SERVICE, HOST):
            if cn.lower() != principal.hostname.lower():
                raise errors.ACIError(info=_(
                    "hostname in subject of request '%(cn)s' "
                    "does not match principal hostname '%(hostname)s'") %
                                      dict(cn=cn, hostname=principal.hostname))
        elif principal_type == USER:
            # check user name
            if cn != principal.username:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN commonName does not match user's login"))

            # check email address
            #
            # fail if any email addr from DN does not appear in ldap entry
            email_addrs = csr_obj.subject.get_attributes_for_oid(
                cryptography.x509.oid.NameOID.EMAIL_ADDRESS)
            if len(set(email_addrs) - set(principal_obj.get('mail', []))) > 0:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN emailAddress does not match "
                            "any of user's email addresses"))

        # We got this far so the principal entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        generalnames = []
        if ext_san is not None:
            generalnames = x509.process_othernames(ext_san.value)
        for gn in generalnames:
            if isinstance(gn, cryptography.x509.general_name.DNSName):
                name = gn.value
                alt_principal = None
                alt_principal_obj = None
                try:
                    if principal_type == HOST:
                        alt_principal = kerberos.Principal((u'host', name),
                                                           principal.realm)
                        alt_principal_obj = api.Command['host_show'](name,
                                                                     all=True)
                    elif principal_type == SERVICE:
                        alt_principal = kerberos.Principal(
                            (principal.service_name, name), principal.realm)
                        alt_principal_obj = api.Command['service_show'](
                            alt_principal, all=True)
                    elif principal_type == USER:
                        raise errors.ValidationError(
                            name='csr',
                            error=_("subject alt name type %s is forbidden "
                                    "for user principals") % "DNSName")
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'The service principal for '
                        'subject alt name %s in certificate request does not '
                        'exist') % name)
                if alt_principal_obj is not None:
                    altdn = alt_principal_obj['result']['dn']
                    if not ldap.can_write(altdn, "usercertificate"):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)
                if alt_principal is not None and not bypass_caacl:
                    caacl_check(principal_type, alt_principal, ca, profile_id)
            elif isinstance(gn, (x509.KRB5PrincipalName, x509.UPN)):
                if gn.name != principal_string:
                    raise errors.ACIError(
                        info=_("Principal '%s' in subject alt name does not "
                               "match requested principal") % gn.name)
            elif isinstance(gn, cryptography.x509.general_name.RFC822Name):
                if principal_type == USER:
                    if gn.value not in principal_obj.get('mail', []):
                        raise errors.ValidationError(
                            name='csr',
                            error=_("RFC822Name does not match "
                                    "any of user's email addresses"))
                else:
                    raise errors.ValidationError(
                        name='csr',
                        error=_("subject alt name type %s is forbidden "
                                "for non-user principals") % "RFC822Name")
            else:
                raise errors.ACIError(
                    info=_("Subject alt name type %s is forbidden") %
                    type(gn).__name__)

        # Request the certificate
        try:
            # re-serialise to PEM, in case the user-supplied data has
            # extraneous material that will cause Dogtag to freak out
            csr_pem = csr_obj.public_bytes(serialization.Encoding.PEM)
            result = self.Backend.ra.request_certificate(
                csr_pem, profile_id, ca_id, request_type=request_type)
        except errors.HTTPRequestError as e:
            if e.status == 409:  # pylint: disable=no-member
                raise errors.CertificateOperationError(
                    error=_("CA '%s' is disabled") % ca)
            else:
                raise e

        if not raw:
            self.obj._parse(result, all)
            result['request_id'] = int(result['request_id'])
            result['cacn'] = ca_obj['cn'][0]

        # Success? Then add it to the principal's entry
        # (unless the profile tells us not to)
        profile = api.Command['certprofile_show'](profile_id)
        store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE'
        if store and 'certificate' in result:
            cert = str(result.get('certificate'))
            kwargs = dict(addattr=u'usercertificate={}'.format(cert))
            if principal_type == SERVICE:
                api.Command['service_mod'](principal_string, **kwargs)
            elif principal_type == HOST:
                api.Command['host_mod'](principal.hostname, **kwargs)
            elif principal_type == USER:
                api.Command['user_mod'](principal.username, **kwargs)

        return dict(
            result=result,
            value=pkey_to_value(int(result['request_id']), kw),
        )