def validated_read(argname, filename, mode='r', encoding=None): """Read file and catch errors IOError and UnicodeError (for text files) are turned into a ValidationError """ try: with io.open(filename, mode=mode, encoding=encoding) as f: data = f.read() except IOError as exc: raise errors.ValidationError( name=argname, error=_("Cannot read file '%(filename)s': %(exc)s") % { 'filename': filename, 'exc': exc.args[1] }) except UnicodeError as exc: raise errors.ValidationError( name=argname, error=_("Cannot decode file '%(filename)s': %(exc)s") % { 'filename': filename, 'exc': exc }) return data
def test_invalid_preferred_languages(self, user): """ Try to assign various invalid preferred languages to user """ user.ensure_exists() for invalidlanguage in invalidlanguages: command = user.make_update_command( dict(preferredlanguage=invalidlanguage) ) with raises_exact(errors.ValidationError( name='preferredlanguage', error=(u'must match RFC 2068 - 14.4, e.g., ' '"da, en-gb;q=0.8, en;q=0.7"') )): command() user.delete()
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 normalize_mail_attrs(entry_attrs): if 'mail' in entry_attrs: if len(entry_attrs['mail']) > 1: raise errors.ValidationError( name='mail', error=_('Mail attribute has to be single-valued.' 'Use alias/sendalias to specify more addresses.')) else: if 'primarymail' in entry_attrs: entry_attrs['mail'] = [entry_attrs['primarymail']] else: entry_attrs['primarymail'] = entry_attrs['mail'][0] if 'primarymail' in entry_attrs: entry_attrs['mail'] = [entry_attrs['primarymail']]
def check_required_principal(ldap, principal): """ Raise an error if the host of this principal is an IPA master and one of the principals required for proper execution. """ if not principal.is_service: # bypass check if principal is not a service principal, # see https://pagure.io/freeipa/issue/7793 return try: host_is_master(ldap, principal.hostname) except errors.ValidationError: service_types = ['HTTP', 'ldap', 'DNS', 'dogtagldap'] if principal.service_name in service_types: raise errors.ValidationError(name='principal', error=_('This principal is required by the IPA master'))
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, str): 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 check_order_uniqueness(self, *keys, **options): if options.get('sudoorder') is not None: entries = self.methods.find( sudoorder=options['sudoorder'] )['result'] if len(entries) > 0: rule_name = entries[0]['cn'][0] raise errors.ValidationError( name='order', error=self.order_not_unique_msg % { 'order': options['sudoorder'], 'rule': rule_name, } )
def validate_minlength(self, ldap, entry_attrs, add=False, *keys): """ If any of the libpwquality options are used then the minimum length must be >= 6 which is the built-in default of libpwquality. Allowing a lower value to be set will result in a failed policy check and a generic error message. """ def get_val(entry, attr): """Get a single value from a list or a string""" val = entry.get(attr, 0) if isinstance(val, list): val = val[0] return val def has_pwquality_set(entry): for attr in [ 'ipapwdmaxrepeat', 'ipapwdmaxsequence', 'ipapwddictcheck', 'ipapwdusercheck' ]: val = get_val(entry, attr) if val not in ('FALSE', '0', 0, None): return True return False has_pwquality_value = False if not add: if len(keys) > 0: existing_entry = self.api.Command.pwpolicy_show( keys[-1], all=True, )['result'] else: existing_entry = self.api.Command.pwpolicy_show( all=True, )['result'] existing_entry.update(entry_attrs) min_length = int(get_val(existing_entry, 'krbpwdminlength')) has_pwquality_value = has_pwquality_set(existing_entry) else: min_length = int(get_val(entry_attrs, 'krbpwdminlength')) has_pwquality_value = has_pwquality_set(entry_attrs) if min_length and min_length < 6 and has_pwquality_value: raise errors.ValidationError( name='minlength', error=_('Minimum length must be >= 6 if maxrepeat, ' 'maxsequence, dictcheck or usercheck are defined'))
def pre_callback(self, ldap, dn, entry, entry_attrs, *keys, **options): ca_enabled_check(self.api) if not ldap.can_add(dn[1:], 'ipaca'): 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
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: 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
def handle_subordinate_ids(self, ldap, dn, entry_attrs): """Handle ipaSubordinateId object class""" new_subuid = entry_attrs.single_value.get("ipasubuidnumber") new_subgid = entry_attrs.single_value.get("ipasubgidnumber") if new_subuid is None: new_subuid = DNA_MAGIC # enforce subuid == subgid if new_subgid is not None and new_subgid != new_subuid: raise errors.ValidationError( name="ipasubgidnumber", error=_("subgidnumber must be equal to subuidnumber"), ) self.set_subordinate_ids(ldap, dn, entry_attrs, new_subuid) return True
def pre_callback(self, ldap, dn, entry, entry_attrs, *keys, **options): ca_enabled_check() context.profile = options['file'] match = self.PROFILE_ID_PATTERN.search(options['file']) if match is None: # no profileId found, use CLI value as profileId. context.profile = u'profileId=%s\n%s' % (keys[0], context.profile) elif keys[0] != match.group(1): raise errors.ValidationError( name='file', error= _("Profile ID '%(cli_value)s' does not match profile data '%(file_value)s'" ) % { 'cli_value': keys[0], 'file_value': match.group(1) }) return dn
def validate_trusted_domain_sid(self, sid): if not _dcerpc_bindings_installed: raise errors.NotFound(reason=_( 'Cannot perform SID validation without Samba 4 support installed. ' 'Make sure you have installed server-trust-ad sub-package of IPA on the server' )) domain_validator = ipaserver.dcerpc.DomainValidator(self.api) if not domain_validator.is_configured(): raise errors.NotFound(reason=_( 'Cross-realm trusts are not configured. ' 'Make sure you have run ipa-adtrust-install on the IPA server first' )) if not domain_validator.is_trusted_sid_valid(sid): raise errors.ValidationError( name='domain SID', error=_( 'SID is not recognized as a valid SID for a trusted domain' ))
def ensure_last_krbprincipalname(ldap, entry_attrs, *keys): """ ensure that the LDAP entry has at least one value of krbprincipalname and that this value is equal to krbcanonicalname :param ldap: LDAP connection object :param entry_attrs: LDAP entry made prior to update :param options: command options """ entry = ldap.get_entry(entry_attrs.dn, ['krbcanonicalname', 'krbprincipalname']) krbcanonicalname = entry.single_value.get('krbcanonicalname', None) if krbcanonicalname in keys[-1]: raise errors.ValidationError( name='krbprincipalname', error=_('at least one value equal to the canonical ' 'principal name must be present'))
def validate_radiusserver(ugettext, server): split = server.rsplit(':', 1) server = split[0] if len(split) == 2: try: port = int(split[1]) if (port < 0 or port > 65535): raise ValueError() except ValueError: raise ValidationError(name="ipatokenradiusserver", error=_('invalid port number')) if validate_ipaddr(server): return try: validate_hostname(server, check_fqdn=True, allow_underscore=True) except ValueError as e: raise errors.ValidationError(name="ipatokenradiusserver", error=str(e))
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) self.obj.convert_time_on_input(entry_attrs) self.obj.validate_lifetime(entry_attrs, False, *keys) setattr(context, 'cosupdate', False) if options.get('cospriority') is not None: if keys[-1] is None: raise errors.ValidationError( name='priority', error=_('priority cannot be set on global policy')) try: self.api.Command.cosentry_mod( keys[-1], cospriority=options['cospriority']) except errors.EmptyModlist as e: if len(entry_attrs) == 1: # cospriority only was passed raise e else: setattr(context, 'cosupdate', True) return dn
def forward(self, *keys, **options): filename = None if 'certificate_out' in options: filename = options.pop('certificate_out') try: util.check_writable_file(filename) except errors.FileError as e: raise errors.ValidationError(name='certificate-out', error=str(e)) result = super(WithCertOutArgs, self).forward(*keys, **options) if filename: if options.get('chain', False): certs = result['result']['certificate_chain'] else: certs = [base64.b64decode(result['result']['certificate'])] certs = (x509.load_der_x509_certificate(cert) for cert in certs) x509.write_certificate_list(certs, filename) return result
def update_samba_attrs(ldap, dn, entry_attrs, **options): smb_attrs = { 'ipantlogonscript', 'ipantprofilepath', 'ipanthomedirectory', 'ipanthomedirectorydrive' } if 'objectclass' not in entry_attrs: try: oc = ldap.get_entry(dn, ['objectclass'])['objectclass'] except errors.NotFound: # In case the entry really does not exist, # compare against an empty list oc = [] else: oc = entry_attrs['objectclass'] if 'ipantuserattrs' not in (item.lower() for item in oc): for attr in smb_attrs: if options.get(attr, None): raise errors.ValidationError( name=attr, error=_('Object class ipaNTUserAttrs is missing, ' 'user entry cannot have SMB attributes.'))
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
def validate_external_object(ldap, dn, keys, options, value, object_type): trusted_objects = options.get('trusted_objects', []) param = api.Object[object_type].primary_key if object_type == 'group' and value.startswith('%'): value = value[1:] o_id = param(value) try: param.validate(o_id) except errors.ValidationError as e: membertype = object_type try: id = resolve_object_to_anchor(ldap, object_type, o_id, False) if id.startswith(SID_ANCHOR_PREFIX): if object_type == 'group': trusted_objects.append('%' + value) else: trusted_objects.append(value) options['trusted_objects'] = trusted_objects return except errors.NotFound: raise errors.ValidationError(name=membertype, error=e.error)
def forward(self, *args, **options): if 'certificate_out' in options: certificate_out = options.pop('certificate_out') try: util.check_writable_file(certificate_out) except errors.FileError as e: raise errors.ValidationError(name='certificate-out', error=str(e)) else: certificate_out = None result = super(CertRetrieveOverride, self).forward(*args, **options) if certificate_out is not None: if options.get('chain', False): certs = result['result']['certificate_chain'] else: certs = [base64.b64decode(result['result']['certificate'])] certs = (x509.load_der_x509_certificate(cert) for cert in certs) x509.write_certificate_list(certs, certificate_out) return result
def validate_permission_to_privilege(api, permission): ldap = api.Backend.ldap2 ldapfilter = ldap.combine_filters(rules='&', filters=[ '(objectClass=ipaPermissionV2)', '(!(ipaPermBindRuleType=permission))', ldap.make_filter_from_attr('cn', permission, rules='|')]) try: entries, _truncated = ldap.find_entries( filter=ldapfilter, attrs_list=['cn', 'ipapermbindruletype'], base_dn=DN(api.env.container_permission, api.env.basedn), size_limit=1) except errors.NotFound: pass else: entry = entries[0] message = _('cannot add permission "%(perm)s" with bindtype ' '"%(bindtype)s" to a privilege') raise errors.ValidationError( name='permission', error=message % { 'perm': entry.single_value['cn'], 'bindtype': entry.single_value.get( 'ipapermbindruletype', 'permission')})
def validate_lifetime(self, entry_attrs, add=False, *keys): """ Ensure that the maximum lifetime is greater than the minimum. If there is no minimum lifetime set then don't return an error. """ maxlife=entry_attrs.get('krbmaxpwdlife', None) minlife=entry_attrs.get('krbminpwdlife', None) existing_entry = {} if not add: # then read existing entry existing_entry = self.api.Command.pwpolicy_show(keys[-1], all=True, )['result'] if minlife is None and 'krbminpwdlife' in existing_entry: minlife = int(existing_entry['krbminpwdlife'][0]) * 3600 if maxlife is None and 'krbmaxpwdlife' in existing_entry: maxlife = int(existing_entry['krbmaxpwdlife'][0]) * 86400 if maxlife not in (None, 0) and minlife is not None: if minlife > maxlife: raise errors.ValidationError( name='maxlife', error=_('Maximum password life must be greater than minimum.'), )
def _make_aci(ldap, current, aciname, kw): """ Given a name and a set of keywords construct an ACI. """ # Do some quick and dirty validation. checked_args=['type','filter','subtree','targetgroup','attrs','memberof'] valid={} for arg in checked_args: if arg in kw: valid[arg]=kw[arg] is not None else: valid[arg]=False if valid['type'] + valid['filter'] + valid['subtree'] + valid['targetgroup'] > 1: raise errors.ValidationError(name='target', error=_('type, filter, subtree and targetgroup are mutually exclusive')) if 'aciprefix' not in kw: raise errors.ValidationError(name='aciprefix', error=_('ACI prefix is required')) if sum(valid.itervalues()) == 0: raise errors.ValidationError(name='target', error=_('at least one of: type, filter, subtree, targetgroup, attrs or memberof are required')) if valid['filter'] + valid['memberof'] > 1: raise errors.ValidationError(name='target', error=_('filter and memberof are mutually exclusive')) group = 'group' in kw permission = 'permission' in kw selfaci = 'selfaci' in kw and kw['selfaci'] == True if group + permission + selfaci > 1: raise errors.ValidationError(name='target', error=_('group, permission and self are mutually exclusive')) elif group + permission + selfaci == 0: raise errors.ValidationError(name='target', error=_('One of group, permission or self is required')) # Grab the dn of the group we're granting access to. This group may be a # permission or a user group. entry_attrs = [] if permission: # This will raise NotFound if the permission doesn't exist try: entry_attrs = api.Command['permission_show'](kw['permission'])['result'] except errors.NotFound, e: if 'test' in kw and not kw.get('test'): raise e else: entry_attrs = {'dn': DN(('cn', kw['permission']), api.env.container_permission)}
def _add(self, api_instance, master): """ add attribute to the master :param api_instance: API instance :param master: master FQDN :raises: * errors.ValidationError if the associated role is not enabled on the master """ assoc_role_providers = self._get_assoc_role_providers(api_instance) ldap = api_instance.Backend.ldap2 if master not in assoc_role_providers: raise errors.ValidationError( name=master, error=_("must have %(role)s role enabled" % {'role': self.associated_role.name}) ) master_dn = self._get_master_dn(api_instance, master) service_entry = self._get_masters_service_entry(ldap, master_dn) self._add_attribute_to_svc_entry(ldap, service_entry)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): # convert CIDR to separate IP and mask. Validator has already checked syntax if '/' in keys[0]: sl = keys[0].split('/') dn = DN(('cn',sl[0])) + dn[1:] entry_attrs['cn'] = sl[0] entry_attrs['dhcpnetmask'] = sl[1] elif not 'dhcpnetmask' in options: raise errors.ValidationError( name=keys[0], error=_('either subnet name should include size, e.g. 128.6.0.0/16, or --netmask must be specified')) if not 'dhcpoption' in entry_attrs: #broadcast-address 172.17.8.223, routers 172.17.8.193, subnet-mask 255.255.255.224 cidr = entry_attrs['dhcpnetmask'] net = None try: net = ipaddress.IPv4Network(_(str(entry_attrs['cn']) + '/' + str(cidr))) except ValueError: raise self.obj.handle_not_found('invalid address') options = ['broadcast-address ' + str(net.broadcast_address), 'subnet-mask ' + str(net.netmask), 'routers ' + str(next(net.hosts()))] entry_attrs['dhcpoption'] = options # in case user did something weird, get netmask from cidr spec. that's what # the dhcpd ldap code does c = entry_attrs['dhcpnetmask'] net = None try: net = ipaddress.IPv4Network(_(str(entry_attrs['cn']) + '/' + str(c))) except ValueError: raise self.obj.handle_not_found('invalid address') check_dhcp_entry('subnet ' + str(entry_attrs['cn']) + ' netmask ' + str(net.netmask) + '{', '}', entry_attrs); return dn
def execute(self, *keys, **options): parentmap = options.pop('parentmap', None) key = options.pop('key') result = self.api.Command['automountmap_add'](*keys, **options) try: if parentmap != u'auto.master': if key.startswith('/'): raise errors.ValidationError( name='mount', error=_('mount point is relative to parent map, ' 'cannot begin with /')) location = keys[0] map = keys[1] options['automountinformation'] = map # Ensure the referenced map exists self.api.Command['automountmap_show'](location, parentmap) # Add a submount key self.api.Command['automountkey_add']( location, parentmap, automountkey=key, automountinformation='-fstype=autofs ldap:%s' % map) else: # adding to auto.master # Ensure auto.master exists self.api.Command['automountmap_show'](keys[0], parentmap) self.api.Command['automountkey_add']( keys[0], u'auto.master', automountkey=key, automountinformation=keys[1]) except Exception: # The key exists, drop the map self.api.Command['automountmap_del'](*keys) raise return result
def _check_hide_server(self, fqdn): result = self.api.Command.config_show()['result'] err = [] # single value entries if result.get("ca_renewal_master_server") == fqdn: err.append(_("Cannot hide CA renewal master.")) if result.get("dnssec_key_master_server") == fqdn: err.append(_("Cannot hide DNSSec key master.")) # multi value entries, only fail if we are the last one checks = [ ("ca_server_server", "CA"), ("dns_server_server", "DNS"), ("ipa_master_server", "IPA"), ("kra_server_server", "KRA"), ] for key, name in checks: values = result.get(key, []) if values == [fqdn]: # fqdn is the only entry err.append( _("Cannot hide last enabled %(name)s server.") % {'name': name}) if err: raise errors.ValidationError(name=fqdn, error=' '.join(str(e) for e in err))
def pre_callback(self, ldap, dn, *keys, **options): if options.get('permission'): # We can only add permissions with bind rule type set to # "permission" (or old-style permissions) ldapfilter = ldap.combine_filters( rules='&', filters=[ '(objectClass=ipaPermissionV2)', '(!(ipaPermBindRuleType=permission))', ldap.make_filter_from_attr('cn', options['permission'], rules='|'), ]) try: entries, truncated = ldap.find_entries( filter=ldapfilter, attrs_list=['cn', 'ipapermbindruletype'], base_dn=DN(self.api.env.container_permission, self.api.env.basedn), size_limit=1) except errors.NotFound: pass else: entry = entries[0] message = _('cannot add permission "%(perm)s" with bindtype ' '"%(bindtype)s" to a privilege') raise errors.ValidationError( name='permission', error=message % { 'perm': entry.single_value['cn'], 'bindtype': entry.single_value.get('ipapermbindruletype', 'permission') }) return dn
def validate_auth_indicator(entry): new_value = entry.get('krbprincipalauthind', None) if not new_value: return # The following services are considered internal IPA services # and shouldn't be allowed to have auth indicators. # https://pagure.io/freeipa/issue/8206 pkey = api.Object['service'].get_primary_key_from_dn(entry.dn) if pkey == str(entry.dn): # krbcanonicalname may not be set yet if this is a host entry, # try krbprincipalname if 'krbprincipalname' in entry: pkey = entry['krbprincipalname'] principal = kerberos.Principal(pkey) server = api.Command.server_find(principal.hostname)['result'] if server: prefixes = ("host", "cifs", "ldap", "HTTP") else: prefixes = ("cifs", ) if principal.service_name in prefixes: raise errors.ValidationError( name='krbprincipalauthind', error=_('authentication indicators not allowed ' 'in service "%s"' % principal.service_name))