예제 #1
0
class baseuser(LDAPObject):
    """
    baseuser object.
    """

    stage_container_dn        = api.env.container_stageuser
    active_container_dn       = api.env.container_user
    delete_container_dn       = api.env.container_deleteuser
    object_class = ['posixaccount']
    object_class_config = 'ipauserobjectclasses'
    possible_objectclasses = [
        'meporiginentry', 'ipauserauthtypeclass', 'ipauser',
        'ipatokenradiusproxyuser', 'ipacertmapobject'
    ]
    disallow_object_classes = ['krbticketpolicyaux']
    permission_filter_objectclasses = ['posixaccount']
    search_attributes_config = 'ipausersearchfields'
    default_attributes = [
        'uid', 'givenname', 'sn', 'homedirectory', 'loginshell',
        'uidnumber', 'gidnumber', 'mail', 'ou',
        'telephonenumber', 'title', 'memberof', 'nsaccountlock',
        'memberofindirect', 'ipauserauthtype', 'userclass',
        'ipatokenradiusconfiglink', 'ipatokenradiususername',
        'krbprincipalexpiration', 'usercertificate;binary',
        'krbprincipalname', 'krbcanonicalname',
        'ipacertmapdata'
    ]
    search_display_attributes = [
        'uid', 'givenname', 'sn', 'homedirectory', 'krbcanonicalname',
        'krbprincipalname', 'loginshell',
        'mail', 'telephonenumber', 'title', 'nsaccountlock',
        'uidnumber', 'gidnumber', 'sshpubkeyfp',
    ]
    uuid_attribute = 'ipauniqueid'
    attribute_members = {
        'manager': ['user'],
        'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
        'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
    }
    allow_rename = True
    bindable = True
    password_attributes = [('userpassword', 'has_password'),
                           ('krbprincipalkey', 'has_keytab')]
    label = _('Users')
    label_singular = _('User')

    takes_params = (
        Str('uid',
            pattern=PATTERN_GROUPUSER_NAME,
            pattern_errmsg='may only include letters, numbers, _, -, . and $',
            maxlength=255,
            cli_name='login',
            label=_('User login'),
            primary_key=True,
            default_from=lambda givenname, sn: givenname[0] + sn,
            normalizer=lambda value: value.lower(),
        ),
        Str('givenname',
            cli_name='first',
            label=_('First name'),
        ),
        Str('sn',
            cli_name='last',
            label=_('Last name'),
        ),
        Str('cn',
            label=_('Full name'),
            default_from=lambda givenname, sn: '%s %s' % (givenname, sn),
            autofill=True,
        ),
        Str('displayname?',
            label=_('Display name'),
            default_from=lambda givenname, sn: '%s %s' % (givenname, sn),
            autofill=True,
        ),
        Str('initials?',
            label=_('Initials'),
            default_from=lambda givenname, sn: '%c%c' % (givenname[0], sn[0]),
            autofill=True,
        ),
        Str('homedirectory?',
            cli_name='homedir',
            label=_('Home directory'),
        ),
        Str('gecos?',
            label=_('GECOS'),
            default_from=lambda givenname, sn: '%s %s' % (givenname, sn),
            autofill=True,
        ),
        Str('loginshell?',
            cli_name='shell',
            label=_('Login shell'),
        ),
        Principal(
            'krbcanonicalname?',
            validate_realm,
            label=_('Principal name'),
            flags={'no_option', 'no_create', 'no_update', 'no_search'},
            normalizer=normalize_user_principal
        ),
        Principal(
            'krbprincipalname*',
            validate_realm,
            cli_name='principal',
            label=_('Principal alias'),
            default_from=lambda uid: kerberos.Principal(
                uid.lower(), realm=api.env.realm),
            autofill=True,
            normalizer=normalize_user_principal,
        ),
        DateTime('krbprincipalexpiration?',
            cli_name='principal_expiration',
            label=_('Kerberos principal expiration'),
        ),
        DateTime('krbpasswordexpiration?',
            cli_name='password_expiration',
            label=_('User password expiration'),
        ),
        Str('mail*',
            cli_name='email',
            label=_('Email address'),
        ),
        Password('userpassword?',
            cli_name='password',
            label=_('Password'),
            doc=_('Prompt to set the user password'),
            # FIXME: This is temporary till bug is fixed causing updates to
            # bomb out via the webUI.
            exclude='webui',
        ),
        Flag('random?',
            doc=_('Generate a random user password'),
            flags=('no_search', 'virtual_attribute'),
            default=False,
        ),
        Str('randompassword?',
            label=_('Random password'),
            flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
        ),
        Int('uidnumber?',
            cli_name='uid',
            label=_('UID'),
            doc=_('User ID Number (system will assign one if not provided)'),
            minvalue=1,
        ),
        Int('gidnumber?',
            label=_('GID'),
            doc=_('Group ID Number'),
            minvalue=1,
        ),
        Str('street?',
            cli_name='street',
            label=_('Street address'),
        ),
        Str('l?',
            cli_name='city',
            label=_('City'),
        ),
        Str('st?',
            cli_name='state',
            label=_('State/Province'),
        ),
        Str('postalcode?',
            label=_('ZIP'),
        ),
        Str('telephonenumber*',
            cli_name='phone',
            label=_('Telephone Number')
        ),
        Str('mobile*',
            label=_('Mobile Telephone Number')
        ),
        Str('pager*',
            label=_('Pager Number')
        ),
        Str('facsimiletelephonenumber*',
            cli_name='fax',
            label=_('Fax Number'),
        ),
        Str('ou?',
            cli_name='orgunit',
            label=_('Org. Unit'),
        ),
        Str('title?',
            label=_('Job Title'),
        ),
        # keep backward compatibility using single value manager option
        Str('manager?',
            label=_('Manager'),
        ),
        Str('carlicense*',
            label=_('Car License'),
        ),
        Str('ipasshpubkey*', validate_sshpubkey,
            cli_name='sshpubkey',
            label=_('SSH public key'),
            normalizer=normalize_sshpubkey,
            flags=['no_search'],
        ),
        Str('sshpubkeyfp*',
            label=_('SSH public key fingerprint'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        StrEnum('ipauserauthtype*',
            cli_name='user_auth_type',
            label=_('User authentication types'),
            doc=_('Types of supported user authentication'),
            values=(u'password', u'radius', u'otp'),
        ),
        Str('userclass*',
            cli_name='class',
            label=_('Class'),
            doc=_('User category (semantics placed on this attribute are for '
                  'local interpretation)'),
        ),
        Str('ipatokenradiusconfiglink?',
            cli_name='radius',
            label=_('RADIUS proxy configuration'),
        ),
        Str('ipatokenradiususername?',
            cli_name='radius_username',
            label=_('RADIUS proxy username'),
        ),
        Str('departmentnumber*',
            label=_('Department Number'),
        ),
        Str('employeenumber?',
            label=_('Employee Number'),
        ),
        Str('employeetype?',
            label=_('Employee Type'),
        ),
        Str('preferredlanguage?',
            label=_('Preferred Language'),
            pattern='^(([a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?(;q\=((0(\.[0-9]{0,3})?)|(1(\.0{0,3})?)))?' \
             + '(\s*,\s*[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?(;q\=((0(\.[0-9]{0,3})?)|(1(\.0{0,3})?)))?)*)|(\*))$',
            pattern_errmsg='must match RFC 2068 - 14.4, e.g., "da, en-gb;q=0.8, en;q=0.7"',
        ),
        Bytes('usercertificate*', validate_certificate,
            cli_name='certificate',
            label=_('Certificate'),
            doc=_('Base-64 encoded user certificate'),
        ),
        Str(
            'ipacertmapdata*',
            cli_name='certmapdata',
            label=_('Certificate mapping data'),
            doc=_('Certificate mapping data'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
    )

    def normalize_and_validate_email(self, email, config=None):
        if not config:
            config = self.backend.get_ipa_config()

        # check if default email domain should be added
        defaultdomain = config.get('ipadefaultemaildomain', [None])[0]
        if email:
            norm_email = []
            if not isinstance(email, (list, tuple)):
                email = [email]
            for m in email:
                if isinstance(m, six.string_types):
                    if '@' not in m and defaultdomain:
                        m = m + u'@' + defaultdomain
                    if not Email(m):
                        raise errors.ValidationError(name='email', error=_('invalid e-mail format: %(email)s') % dict(email=m))
                    norm_email.append(m)
                else:
                    if not Email(m):
                        raise errors.ValidationError(name='email', error=_('invalid e-mail format: %(email)s') % dict(email=m))
                    norm_email.append(m)
            return norm_email

        return email

    def normalize_manager(self, manager, container):
        """
        Given a userid verify the user's existence (in the appropriate containter) and return the dn.
        """
        if not manager:
            return None

        if not isinstance(manager, list):
            manager = [manager]

        try:
            container_dn = DN(container, api.env.basedn)
            for i, mgr in enumerate(manager):
                if isinstance(mgr, DN) and mgr.endswith(container_dn):
                    continue
                entry_attrs = self.backend.find_entry_by_attr(
                        self.primary_key.name, mgr, self.object_class, [''],
                        container_dn
                    )
                manager[i] = entry_attrs.dn
        except errors.NotFound:
            raise errors.NotFound(reason=_('manager %(manager)s not found') % dict(manager=mgr))

        return manager

    def _user_status(self, user, container):
        assert isinstance(user, DN)
        return user.endswith(container)

    def active_user(self, user):
        assert isinstance(user, DN)
        return self._user_status(user, DN(self.active_container_dn, api.env.basedn))

    def stage_user(self, user):
        assert isinstance(user, DN)
        return self._user_status(user, DN(self.stage_container_dn, api.env.basedn))

    def delete_user(self, user):
        assert isinstance(user, DN)
        return self._user_status(user, DN(self.delete_container_dn, api.env.basedn))

    def convert_usercertificate_pre(self, entry_attrs):
        if 'usercertificate' in entry_attrs:
            entry_attrs['usercertificate;binary'] = entry_attrs.pop(
                'usercertificate')

    def convert_usercertificate_post(self, entry_attrs, **options):
        if 'usercertificate;binary' in entry_attrs:
            entry_attrs['usercertificate'] = entry_attrs.pop(
                'usercertificate;binary')

    def convert_attribute_members(self, entry_attrs, *keys, **options):
        super(baseuser, self).convert_attribute_members(
            entry_attrs, *keys, **options)

        if options.get("raw", False):
            return

        # due the backward compatibility, managers have to be returned in
        # 'manager' attribute instead of 'manager_user'
        try:
            entry_attrs['failed_manager'] = entry_attrs.pop('manager')
        except KeyError:
            pass

        try:
            entry_attrs['manager'] = entry_attrs.pop('manager_user')
        except KeyError:
            pass
예제 #2
0
class cert_get_requestdata(Local):
    __doc__ = _('Gather data for a certificate signing request.')

    NO_CLI = True

    takes_options = (
        Principal(
            'principal',
            label=_('Principal'),
            doc=_('Principal for this certificate (e.g.'
                  ' HTTP/test.example.com)'),
        ),
        Str(
            'profile_id?',
            label=_('Profile ID'),
            doc=_('CSR Generation Profile to use'),
        ),
        Bytes(
            'public_key_info',
            label=_('Subject Public Key Info'),
            doc=_('DER-encoded SubjectPublicKeyInfo structure'),
        ),
        Str(
            'out?',
            doc=_('Write CertificationRequestInfo to file'),
        ),
    )

    has_output = (output.Output(
        'result',
        type=dict,
        doc=_('Dictionary mapping variable name to value'),
    ), )

    has_output_params = (Str(
        'request_info',
        label=_('CertificationRequestInfo structure'),
    ))

    def execute(self, *args, **options):
        # Deferred import, ipaclient.csrgen is expensive to load.
        # see https://pagure.io/freeipa/issue/7484
        from ipaclient import csrgen
        from ipaclient import csrgen_ffi

        if 'out' in options:
            util.check_writable_file(options['out'])

        principal = options.get('principal')
        profile_id = options.get('profile_id')
        if profile_id is None:
            profile_id = dogtag.DEFAULT_PROFILE
        public_key_info = options.get('public_key_info')
        public_key_info = base64.b64decode(public_key_info)

        if self.api.env.in_server:
            backend = self.api.Backend.ldap2
        else:
            backend = self.api.Backend.rpcclient
        if not backend.isconnected():
            backend.connect()

        try:
            if principal.is_host:
                principal_obj = api.Command.host_show(principal.hostname,
                                                      all=True)
            elif principal.is_service:
                principal_obj = api.Command.service_show(unicode(principal),
                                                         all=True)
            elif principal.is_user:
                principal_obj = api.Command.user_show(principal.username,
                                                      all=True)
        except errors.NotFound:
            raise errors.NotFound(
                reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        config = api.Command.config_show()['result']

        generator = csrgen.CSRGenerator(csrgen.FileRuleProvider())

        csr_config = generator.csr_config(principal_obj, config, profile_id)
        request_info = base64.b64encode(
            csrgen_ffi.build_requestinfo(csr_config.encode('utf8'),
                                         public_key_info))

        result = {}
        if 'out' in options:
            with open(options['out'], 'wb') as f:
                f.write(request_info)
        else:
            result = dict(request_info=request_info)

        return dict(result=result)
예제 #3
0
class passwd(Command):
    __doc__ = _("Set a user's password.")

    takes_args = (
        Principal(
            'principal',
            validate_realm,
            cli_name='user',
            label=_('User name'),
            primary_key=True,
            autofill=True,
            default_from=lambda: kerberos.Principal(krb_utils.get_principal()),
            normalizer=lambda value: normalize_user_principal(value),
        ),
        Password(
            'password',
            label=_('New Password'),
        ),
        Password(
            'current_password',
            label=_('Current Password'),
            confirm=False,
            default_from=lambda principal: get_current_password(principal),
            autofill=True,
        ),
    )

    takes_options = (Password(
        'otp?',
        label=_('OTP'),
        doc=_('The OTP if the user has a token configured'),
        confirm=False,
    ), )

    has_output = output.simple_value
    msg_summary = _('Changed password for "%(value)s"')

    def execute(self, principal, password, current_password, **options):
        """
        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

        principal = unicode(principal)

        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
            logger.warning('User attempted to change password using magic '
                           'value')
            raise errors.ACIError(info=_('Invalid credentials'))

        if current_password == MAGIC_VALUE:
            ldap.modify_password(entry_attrs.dn, password)
        else:
            otp = options.get('otp')
            ldap.modify_password(entry_attrs.dn, password, current_password,
                                 otp)

        return dict(
            result=True,
            value=principal,
        )
예제 #4
0
파일: cert.py 프로젝트: shanyin/freeipa
class cert_request(Create, BaseCertMethod, VirtualCommand):
    __doc__ = _('Submit a certificate signing request.')

    obj_name = 'certreq'
    attr_name = 'request'

    takes_args = (Str(
        'csr',
        validate_csr,
        label=_('CSR'),
        cli_name='csr_file',
        normalizer=normalize_csr,
        noextrawhitespace=False,
    ), )
    operation = "request certificate"

    takes_options = (
        Principal(
            'principal',
            validate_realm,
            label=_('Principal'),
            doc=_(
                'Principal for this certificate (e.g. HTTP/test.example.com)'),
            normalizer=normalize_principal),
        Flag(
            'add',
            doc=_("automatically add the principal if it doesn't exist "
                  "(service principals only)"),
        ),
    )

    def get_args(self):
        # FIXME: the 'no_create' flag is ignored for positional arguments
        for arg in super(cert_request, self).get_args():
            if arg.name == 'request_id':
                continue
            yield arg

    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),
        )
예제 #5
0
class service(LDAPObject):
    """
    Service object.
    """
    container_dn = api.env.container_service
    object_name = _('service')
    object_name_plural = _('services')
    object_class = [
        'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', 'ipaobject',
        'ipaservice', 'pkiuser'
    ]
    possible_objectclasses = ['ipakrbprincipal', 'ipaallowedoperations']
    permission_filter_objectclasses = ['ipaservice']
    search_attributes = ['krbprincipalname', 'managedby', 'ipakrbauthzdata']
    default_attributes = [
        'krbprincipalname', 'krbcanonicalname', 'usercertificate', 'managedby',
        'ipakrbauthzdata', 'memberof', 'ipaallowedtoperform',
        'krbprincipalauthind']
    uuid_attribute = 'ipauniqueid'
    attribute_members = {
        'managedby': ['host'],
        'memberof': ['role'],
        'ipaallowedtoperform_read_keys': ['user', 'group', 'host', 'hostgroup'],
        'ipaallowedtoperform_write_keys': ['user', 'group', 'host', 'hostgroup'],
    }
    bindable = True
    relationships = {
        'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
        'ipaallowedtoperform_read_keys': ('Allow to retrieve keytab by', 'retrieve_keytab_by_', 'not_retrieve_keytab_by_'),
        'ipaallowedtoperform_write_keys': ('Allow to create keytab by', 'write_keytab_by_', 'not_write_keytab_by'),
    }
    password_attributes = [('krbprincipalkey', 'has_keytab')]
    managed_permissions = {
        'System: Read Services': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass',
                'ipauniqueid', 'managedby', 'memberof', 'usercertificate',
                'krbprincipalname', 'krbcanonicalname', 'krbprincipalaliases',
                'krbprincipalexpiration', 'krbpasswordexpiration',
                'krblastpwdchange', 'ipakrbauthzdata', 'ipakrbprincipalalias',
                'krbobjectreferences', 'krbprincipalauthind',
            },
        },
        'System: Add Services': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///krbprincipalname=*,cn=services,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Add Services";allow (add) groupdn = "ldap:///cn=Add Services,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Service Administrators'},
        },
        'System: Manage Service Keytab': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'krblastpwdchange', 'krbprincipalkey'},
            'replaces': [
                '(targetattr = "krbprincipalkey || krblastpwdchange")(target = "ldap:///krbprincipalname=*,cn=services,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Manage service keytab";allow (write) groupdn = "ldap:///cn=Manage service keytab,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Service Administrators', 'Host Administrators'},
        },
        'System: Manage Service Keytab Permissions': {
            'ipapermright': {'read', 'search', 'compare', 'write'},
            'ipapermdefaultattr': {
                'ipaallowedtoperform;write_keys',
                'ipaallowedtoperform;read_keys', 'objectclass'
            },
            'default_privileges': {'Service Administrators', 'Host Administrators'},
        },
        'System: Modify Services': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'usercertificate', 'krbprincipalauthind'},
            'replaces': [
                '(targetattr = "usercertificate")(target = "ldap:///krbprincipalname=*,cn=services,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Modify Services";allow (write) groupdn = "ldap:///cn=Modify Services,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Service Administrators'},
        },
        'System: Manage Service Principals': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'krbprincipalname', 'krbcanonicalname'},
            'default_privileges': {
                'Service Administrators',
            },
        },
        'System: Remove Services': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///krbprincipalname=*,cn=services,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Remove Services";allow (delete) groupdn = "ldap:///cn=Remove Services,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Service Administrators'},
        },
    }

    label = _('Services')
    label_singular = _('Service')

    takes_params = (
        Principal(
            'krbcanonicalname',
            validate_realm,
            cli_name='canonical_principal',
            label=_('Principal name'),
            doc=_('Service principal'),
            primary_key=True,
            normalizer=normalize_principal,
            require_service=True
        ),
        Principal(
            'krbprincipalname*',
            validate_realm,
            cli_name='principal',
            label=_('Principal alias'),
            doc=_('Service principal alias'),
            normalizer=normalize_principal,
            require_service=True,
            flags={'no_create'}
        ),
        Certificate('usercertificate*',
            cli_name='certificate',
            label=_('Certificate'),
            doc=_('Base-64 encoded service certificate'),
            flags=['no_search',],
        ),
        Str('subject',
            label=_('Subject'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('serial_number',
            label=_('Serial Number'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('serial_number_hex',
            label=_('Serial Number (hex)'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('issuer',
            label=_('Issuer'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('valid_not_before',
            label=_('Not Before'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('valid_not_after',
            label=_('Not After'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('sha1_fingerprint',
            label=_('Fingerprint (SHA1)'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('sha256_fingerprint',
            label=_('Fingerprint (SHA256)'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str('revocation_reason?',
            label=_('Revocation reason'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        StrEnum('ipakrbauthzdata*',
            cli_name='pac_type',
            label=_('PAC type'),
            doc=_("Override default list of supported PAC types."
                  " Use 'NONE' to disable PAC support for this service,"
                  " e.g. this might be necessary for NFS services."),
            values=(u'MS-PAC', u'PAD', u'NONE'),
        ),
        Str('krbprincipalauthind*',
            cli_name='auth_ind',
            label=_('Authentication Indicators'),
            doc=_("Defines a whitelist for Authentication Indicators."
                  " Use 'otp' to allow OTP-based 2FA authentications."
                  " Use 'radius' to allow RADIUS-based 2FA authentications."
                  " Other values may be used for custom configurations."),
        ),
    ) + ticket_flags_params

    def validate_ipakrbauthzdata(self, entry):
        new_value = entry.get('ipakrbauthzdata', [])

        if not new_value:
            return

        if not isinstance(new_value, (list, tuple)):
            new_value = set([new_value])
        else:
            new_value = set(new_value)

        if u'NONE' in new_value and len(new_value) > 1:
            raise errors.ValidationError(name='ipakrbauthzdata',
                error=_('NONE value cannot be combined with other PAC types'))

    def get_dn(self, *keys, **kwargs):
        key = keys[0]
        if isinstance(key, str):
            key = kerberos.Principal(key)

        key = unicode(normalize_principal(key))

        parent_dn = DN(self.container_dn, self.api.env.basedn)
        true_rdn = 'krbprincipalname'

        return self.backend.make_dn_from_attr(
            true_rdn, key, parent_dn
        )

    def get_primary_key_from_dn(self, dn):
        """
        If the entry has krbcanonicalname set return the value of the
        attribute. If the attribute is not found, assume old-style entry which
        should have only single value of krbprincipalname and return it.

        Otherwise return input DN.
        """
        assert isinstance(dn, DN)

        try:
            entry_attrs = self.backend.get_entry(
                dn, [self.primary_key.name]
            )
            try:
                return entry_attrs[self.primary_key.name][0]
            except (KeyError, IndexError):
                return ''
        except errors.NotFound:
            pass

        try:
            return dn['krbprincipalname']
        except KeyError:
            return unicode(dn)

    def populate_krbcanonicalname(self, entry_attrs, options):
        if options.get('raw', False):
            return

        entry_attrs.setdefault(
            'krbcanonicalname', entry_attrs['krbprincipalname'])
예제 #6
0
파일: cert.py 프로젝트: shanyin/freeipa
class BaseCertObject(Object):
    takes_params = (
        Str(
            'cacn?',
            cli_name='ca',
            default=IPA_CA_CN,
            autofill=True,
            label=_('Issuing CA'),
            doc=_('Name of issuing CA'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Bytes(
            'certificate',
            validate_certificate,
            label=_("Certificate"),
            doc=_("Base-64 encoded certificate."),
            normalizer=x509.normalize_certificate,
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNParam(
            'subject',
            label=_('Subject'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_rfc822name*',
            label=_('Subject email address'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNSNameParam(
            'san_dnsname*',
            label=_('Subject DNS name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_x400address*',
            label=_('Subject X.400 address'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNParam(
            'san_directoryname*',
            label=_('Subject directory name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_edipartyname*',
            label=_('Subject EDI Party name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_uri*',
            label=_('Subject URI'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_ipaddress*',
            label=_('Subject IP Address'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_oid*',
            label=_('Subject OID'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Principal(
            'san_other_upn*',
            label=_('Subject UPN'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Principal(
            'san_other_kpn*',
            label=_('Subject Kerberos principal name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_other*',
            label=_('Subject Other Name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNParam(
            'issuer',
            label=_('Issuer'),
            doc=_('Issuer DN'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DateTime(
            'valid_not_before',
            label=_('Not Before'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DateTime(
            'valid_not_after',
            label=_('Not After'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'md5_fingerprint',
            label=_('Fingerprint (MD5)'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'sha1_fingerprint',
            label=_('Fingerprint (SHA1)'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Int(
            'serial_number',
            label=_('Serial number'),
            doc=_(
                'Serial number in decimal or if prefixed with 0x in hexadecimal'
            ),
            normalizer=normalize_serial_number,
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'serial_number_hex',
            label=_('Serial number (hex)'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
    )

    def _parse(self, obj, full=True):
        """Extract certificate-specific data into a result object.

        ``obj``
            Result object containing certificate, into which extracted
            data will be inserted.
        ``full``
            Whether to include all fields, or only the ones we guess
            people want to see most of the time.  Also add
            recognised otherNames to the generic ``san_other``
            attribute when ``True`` in addition to the specialised
            attribute.

        """
        cert = obj.get('certificate')
        if cert is not None:
            cert = x509.load_certificate(cert)
            obj['subject'] = DN(unicode(cert.subject))
            obj['issuer'] = DN(unicode(cert.issuer))
            obj['serial_number'] = cert.serial_number
            if full:
                obj['valid_not_before'] = unicode(cert.valid_not_before_str)
                obj['valid_not_after'] = unicode(cert.valid_not_after_str)
                obj['md5_fingerprint'] = unicode(
                    nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
                obj['sha1_fingerprint'] = unicode(
                    nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

            try:
                ext_san = cert.get_extension(nss.SEC_OID_X509_SUBJECT_ALT_NAME)
                general_names = x509.decode_generalnames(ext_san.value)
            except KeyError:
                general_names = []

            for name_type, _desc, name, der_name in general_names:
                try:
                    self._add_san_attribute(obj, full, name_type, name,
                                            der_name)
                except Exception:
                    # Invalid GeneralName (i.e. not a valid X.509 cert);
                    # don't fail but log something about it
                    root_logger.warning(
                        "Encountered bad GeneralName; skipping", exc_info=True)

        serial_number = obj.get('serial_number')
        if serial_number is not None:
            obj['serial_number_hex'] = u'0x%X' % serial_number

    def _add_san_attribute(self, obj, full, name_type, name, der_name):
        name_type_map = {
            nss.certRFC822Name: 'san_rfc822name',
            nss.certDNSName: 'san_dnsname',
            nss.certX400Address: 'san_x400address',
            nss.certDirectoryName: 'san_directoryname',
            nss.certEDIPartyName: 'san_edipartyname',
            nss.certURI: 'san_uri',
            nss.certIPAddress: 'san_ipaddress',
            nss.certRegisterID: 'san_oid',
            (nss.certOtherName, x509.SAN_UPN): 'san_other_upn',
            (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME): 'san_other_kpn',
        }
        default_attrs = {
            'san_rfc822name',
            'san_dnsname',
            'san_other_upn',
            'san_other_kpn',
        }

        attr_name = name_type_map.get(name_type, 'san_other')

        if full or attr_name in default_attrs:
            if attr_name != 'san_other':
                name_formatted = name
            else:
                # display as "OID : b64(DER)"
                name_formatted = u'{}:{}'.format(name_type[1],
                                                 base64.b64encode(der_name))
            attr_value = self.params[attr_name].type(name_formatted)
            obj.setdefault(attr_name, []).append(attr_value)

        if full and attr_name.startswith('san_other_'):
            # also include known otherName in generic otherName attribute
            name_formatted = u'{}:{}'.format(name_type[1],
                                             base64.b64encode(der_name))
            attr_value = self.params['san_other'].type(name_formatted)
            obj.setdefault('san_other', []).append(attr_value)
예제 #7
0
class BaseCertObject(Object):
    takes_params = (
        Str(
            'cacn?',
            cli_name='ca',
            default=IPA_CA_CN,
            autofill=True,
            label=_('Issuing CA'),
            doc=_('Name of issuing CA'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Bytes(
            'certificate',
            validate_certificate,
            label=_("Certificate"),
            doc=_("Base-64 encoded certificate."),
            normalizer=x509.normalize_certificate,
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNParam(
            'subject',
            label=_('Subject'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_rfc822name*',
            label=_('Subject email address'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNSNameParam(
            'san_dnsname*',
            label=_('Subject DNS name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_x400address*',
            label=_('Subject X.400 address'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNParam(
            'san_directoryname*',
            label=_('Subject directory name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_edipartyname*',
            label=_('Subject EDI Party name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_uri*',
            label=_('Subject URI'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_ipaddress*',
            label=_('Subject IP Address'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_oid*',
            label=_('Subject OID'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Principal(
            'san_other_upn*',
            label=_('Subject UPN'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Principal(
            'san_other_kpn*',
            label=_('Subject Kerberos principal name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'san_other*',
            label=_('Subject Other Name'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DNParam(
            'issuer',
            label=_('Issuer'),
            doc=_('Issuer DN'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DateTime(
            'valid_not_before',
            label=_('Not Before'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        DateTime(
            'valid_not_after',
            label=_('Not After'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'md5_fingerprint',
            label=_('Fingerprint (MD5)'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'sha1_fingerprint',
            label=_('Fingerprint (SHA1)'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Int(
            'serial_number',
            label=_('Serial number'),
            doc=_(
                'Serial number in decimal or if prefixed with 0x in hexadecimal'
            ),
            normalizer=normalize_serial_number,
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'serial_number_hex',
            label=_('Serial number (hex)'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
    )

    def _parse(self, obj, full=True):
        """Extract certificate-specific data into a result object.

        ``obj``
            Result object containing certificate, into which extracted
            data will be inserted.
        ``full``
            Whether to include all fields, or only the ones we guess
            people want to see most of the time.  Also add
            recognised otherNames to the generic ``san_other``
            attribute when ``True`` in addition to the specialised
            attribute.

        """
        if 'certificate' in obj:
            cert = x509.load_certificate(obj['certificate'])
            obj['subject'] = DN(cert.subject)
            obj['issuer'] = DN(cert.issuer)
            obj['serial_number'] = cert.serial
            obj['valid_not_before'] = x509.format_datetime(
                cert.not_valid_before)
            obj['valid_not_after'] = x509.format_datetime(cert.not_valid_after)
            if full:
                obj['md5_fingerprint'] = x509.to_hex_with_colons(
                    cert.fingerprint(hashes.MD5()))
                obj['sha1_fingerprint'] = x509.to_hex_with_colons(
                    cert.fingerprint(hashes.SHA1()))

            general_names = x509.process_othernames(
                x509.get_san_general_names(cert))

            for gn in general_names:
                try:
                    self._add_san_attribute(obj, full, gn)
                except Exception:
                    # Invalid GeneralName (i.e. not a valid X.509 cert);
                    # don't fail but log something about it
                    root_logger.warning(
                        "Encountered bad GeneralName; skipping", exc_info=True)

        serial_number = obj.get('serial_number')
        if serial_number is not None:
            obj['serial_number_hex'] = u'0x%X' % serial_number

    def _add_san_attribute(self, obj, full, gn):
        name_type_map = {
            cryptography.x509.RFC822Name:
            ('san_rfc822name', attrgetter('value')),
            cryptography.x509.DNSName: ('san_dnsname', attrgetter('value')),
            # cryptography.x509.???: 'san_x400address',
            cryptography.x509.DirectoryName:
            ('san_directoryname', lambda x: DN(x.value)),
            # cryptography.x509.???: 'san_edipartyname',
            cryptography.x509.UniformResourceIdentifier: ('san_uri',
                                                          attrgetter('value')),
            cryptography.x509.IPAddress:
            ('san_ipaddress', attrgetter('value')),
            cryptography.x509.RegisteredID:
            ('san_oid', attrgetter('value.dotted_string')),
            cryptography.x509.OtherName: ('san_other', _format_othername),
            x509.UPN: ('san_other_upn', attrgetter('name')),
            x509.KRB5PrincipalName: ('san_other_kpn', attrgetter('name')),
        }
        default_attrs = {
            'san_rfc822name',
            'san_dnsname',
            'san_other_upn',
            'san_other_kpn',
        }

        if type(gn) not in name_type_map:
            return

        attr_name, format_name = name_type_map[type(gn)]

        if full or attr_name in default_attrs:
            attr_value = self.params[attr_name].type(format_name(gn))
            obj.setdefault(attr_name, []).append(attr_value)

        if full and attr_name.startswith('san_other_'):
            # also include known otherName in generic otherName attribute
            attr_value = self.params['san_other'].type(_format_othername(gn))
            obj.setdefault('san_other', []).append(attr_value)
예제 #8
0
       [--user <user>|--service <service>|--shared]
       [--users <users>] [--groups <groups>] [--services <services>]
""") + _("""
 Delete vault members:
   ipa vault-remove-member <name>
       [--user <user>|--service <service>|--shared]
       [--users <users>] [--groups <groups>] [--services <services>]
""")


register = Registry()

vault_options = (
    Principal(
        'service?',
        validate_realm,
        doc=_('Service name of the service vault'),
        normalizer=normalize_principal,
    ),
    Flag(
        'shared?',
        doc=_('Shared vault'),
    ),
    Str(
        'username?',
        cli_name='user',
        doc=_('Username of the user vault'),
    ),
)


class VaultModMember(LDAPModMember):
예제 #9
0
파일: csrgen.py 프로젝트: steve7389/freeipa
class cert_get_requestdata(Local):
    __doc__ = _('Gather data for a certificate signing request.')

    takes_options = (
        Principal(
            'principal',
            label=_('Principal'),
            doc=_('Principal for this certificate (e.g.'
                  ' HTTP/test.example.com)'),
        ),
        Str(
            'profile_id',
            label=_('Profile ID'),
            doc=_('CSR Generation Profile to use'),
        ),
        Str(
            'helper',
            label=_('Name of CSR generation tool'),
            doc=_('Name of tool (e.g. openssl, certutil) that will be used to'
                  ' create CSR'),
        ),
        Str(
            'out?',
            doc=_('Write CSR generation script to file'),
        ),
    )

    has_output = (output.Output(
        'result',
        type=dict,
        doc=_('Dictionary mapping variable name to value'),
    ), )

    has_output_params = (Str(
        'script',
        label=_('Generation script'),
    ))

    def execute(self, *args, **options):
        if 'out' in options:
            util.check_writable_file(options['out'])

        principal = options.get('principal')
        profile_id = options.get('profile_id')
        helper = options.get('helper')

        if self.api.env.in_server:
            backend = self.api.Backend.ldap2
        else:
            backend = self.api.Backend.rpcclient
        if not backend.isconnected():
            backend.connect()

        try:
            if principal.is_host:
                principal_obj = api.Command.host_show(principal.hostname,
                                                      all=True)
            elif principal.is_service:
                principal_obj = api.Command.service_show(unicode(principal),
                                                         all=True)
            elif principal.is_user:
                principal_obj = api.Command.user_show(principal.username,
                                                      all=True)
        except errors.NotFound:
            raise errors.NotFound(
                reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        config = api.Command.config_show()['result']

        generator = CSRGenerator(FileRuleProvider())

        script = generator.csr_script(principal_obj, config, profile_id,
                                      helper)

        result = {}
        if 'out' in options:
            with open(options['out'], 'wb') as f:
                f.write(script)
        else:
            result = dict(script=script)

        return dict(result=result)
예제 #10
0
class GetKeytab(Command):
    __doc__ = _('Retrieve a keytab.')

    name = 'get_keytab'

    takes_args = (
        Principal(
            'krbcanonicalname',
            validate_realm,
            cli_name='canonical_principal',
            label=_('Principal name'),
            doc=_('Kerberos principal'),
            primary_key=True,
            normalizer=normalize_principal
        )
    )

    takes_options = (
        Flag(
            'retrieve',
            doc=_('Retrieve an existing keytab (service principals only)'),
        )
    )

    has_output = (
        output.summary,
        output.Output(
            'result',
            dict,
            _('The keytab response which has a base64 encoded keytab element.')
        ),
    )

    # See https://web.mit.edu/kerberos/krb5-devel/doc/formats/keytab_file_format.html
    # for the keytab file format.
    #
    # NOTE: http://web.mit.edu/kerberos/www/krb5-1.12/doc/formats/keytab_file_format.html
    # has an incorrect size for the principal's count of components and it is
    # missing the entry's second key version.

    # Keytab header:
    #   [0] = 5 (KRB5)
    #   [1] = 2 (Big endian)
    __keytab_big_endian_header = b'\x05\x02'

    # The keytab record lenght is a signed 32 bit integers where 0 indicates
    # the end of records.
    __keytab_end_of_records = b'\x00\x00\x00\x00'

    # A keytab has a header followed by record lengths followed by records or
    # holes.
    __empty_keytab = __keytab_big_endian_header + __keytab_end_of_records

    def execute(self, krbcanonicalname, **options):
        retrieve = options.get('retrieve')

        principal_string = unicode(krbcanonicalname)

        keytab = self.get_keytab(principal_string,  retrieve)

        base64_keytab = base64.b64encode(keytab).decode('ascii')

        action = ('Created', 'Retrieved')[retrieve]
        summary = u'{0} keytab for principal "{1}"'.format(
            action, principal_string)

        return dict(
            summary=summary,
            result=dict(keytab=base64_keytab)
        )

    def get_keytab(self, principal, retrieve):
        action = ('creating', 'retrieving')[retrieve]
        logger.debug(u'%s keytab for principal "%s"', action, principal)

        try:
            # NOTE: This is run from httpd with PrivateTmp=yes so the
            # temporary files are not accessible to other services running on
            # the same VM. This is in addition to the security of the
            # discretionary access controls being locked down (using
            # NamedTemporaryFile creates files that are only accessible by the
            # user).
            with tempfile.NamedTemporaryFile(suffix='.keytab') as temp_keytab:

                # Since this uses a temporary file and ipa-getkkeytab tool can
                # either create a new keytab file or add to an existing keytab
                # file. A valid keytab file needs to be constructed.
                temp_keytab.write(self.__empty_keytab)
                temp_keytab.flush()

                args = ['ipa-getkeytab',
                        '-p', principal,
                        '-k', temp_keytab.name]
                if retrieve:
                    args.append('-r')

                # The ipa-getkeytab utility enforces the authorization policy
                # for retrieving the keytab.
                p = subprocess.Popen(args,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     close_fds=True,
                                     shell=False)
                # There is no stdout output from ipa-getkeytab
                (_, err) = p.communicate()
                p_status = p.wait()

                if p_status != 0:
                    logger.error(
                        u'ipa-getkeytab failed %s keytab for principal "%s" with error code %d and standard error '
                        u'message "%s"',
                        action, principal, p_status, err)
                    raise errors.InternalError()

                temp_keytab.seek(0)
                keytab = temp_keytab.read()

        except errors.InternalError:
            # The details were already logged
            raise
        except Exception as e:
            logger.error(u'failed %s keytab for principal "%s" with error "%s"', action, principal, e)
            raise

        logger.debug(u'%s keytab for principal "%s" succeeded', action, principal)

        return keytab