Add --zonemgr/--admin-mail validator

Do at least a basic validation of DNS zone manager mail address.

Do not require '@' to be in the mail address as the SOA record
stores this value without it and people may be used to configure
it that way. '@' is always removed by the installer/dns plugin before
the DNS zone is created.

https://fedorahosted.org/freeipa/ticket/1966
This commit is contained in:
Martin Kosek
2011-10-24 18:35:48 +02:00
parent 9bdbdbc0f3
commit b26d0dcc04
6 changed files with 62 additions and 16 deletions

View File

@@ -854,7 +854,7 @@ args: 1,19,3
arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, required=True) arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, required=True)
option: Str('name_from_ip', _validate_ipnet, attribute=True, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False) option: Str('name_from_ip', _validate_ipnet, attribute=True, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
option: Str('idnssoamname', attribute=True, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=True) option: Str('idnssoamname', attribute=True, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=True)
option: Str('idnssoarname', attribute=True, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=True) option: Str('idnssoarname', _rname_validator, attribute=True, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=True)
option: Int('idnssoaserial', attribute=True, autofill=True, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False) option: Int('idnssoaserial', attribute=True, autofill=True, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False)
option: Int('idnssoarefresh', attribute=True, autofill=True, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False) option: Int('idnssoarefresh', attribute=True, autofill=True, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
option: Int('idnssoaretry', attribute=True, autofill=True, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False) option: Int('idnssoaretry', attribute=True, autofill=True, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
@@ -899,7 +899,7 @@ arg: Str('criteria?', noextrawhitespace=False)
option: Str('idnsname', attribute=True, autofill=False, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=False) option: Str('idnsname', attribute=True, autofill=False, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=False)
option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, query=True, required=False) option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, query=True, required=False) option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
option: Str('idnssoarname', attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, query=True, required=False) option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, query=True, required=False)
option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, query=True, required=False) option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, query=True, required=False)
option: Int('idnssoarefresh', attribute=True, autofill=False, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False) option: Int('idnssoarefresh', attribute=True, autofill=False, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
option: Int('idnssoaretry', attribute=True, autofill=False, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False) option: Int('idnssoaretry', attribute=True, autofill=False, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
@@ -925,7 +925,7 @@ args: 1,18,3
arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=True) arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=True)
option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False) option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=False) option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=False)
option: Str('idnssoarname', attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=False) option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=False)
option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False) option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False)
option: Int('idnssoarefresh', attribute=True, autofill=False, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False) option: Int('idnssoarefresh', attribute=True, autofill=False, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
option: Int('idnssoaretry', attribute=True, autofill=False, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False) option: Int('idnssoaretry', attribute=True, autofill=False, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)

View File

@@ -48,7 +48,8 @@ def parse_options():
parser.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use") parser.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use")
parser.add_option("--no-reverse", dest="no_reverse", action="store_true", parser.add_option("--no-reverse", dest="no_reverse", action="store_true",
default=False, help="Do not create reverse DNS zone") default=False, help="Do not create reverse DNS zone")
parser.add_option("--zonemgr", dest="zonemgr", parser.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback,
type="string",
help="DNS zone manager e-mail address. Defaults to root") help="DNS zone manager e-mail address. Defaults to root")
parser.add_option("--zone-notif", dest="zone_notif", parser.add_option("--zone-notif", dest="zone_notif",
action="store_true", default=False, action="store_true", default=False,

View File

@@ -58,7 +58,6 @@ from ipaserver.plugins.ldap2 import ldap2
from ipapython import sysrestore from ipapython import sysrestore
from ipapython.ipautil import * from ipapython.ipautil import *
from ipalib import api, errors, util from ipalib import api, errors, util
from ipalib.parameters import IA5Str
from ipapython.config import IPAOptionParser from ipapython.config import IPAOptionParser
from ipalib.dn import DN from ipalib.dn import DN
from ipalib.x509 import load_certificate_from_file, load_certificate_chain_from_file from ipalib.x509 import load_certificate_from_file, load_certificate_chain_from_file
@@ -76,16 +75,6 @@ VALID_SUBJECT_ATTRS = ['cn', 'st', 'o', 'ou', 'dnqualifier', 'c',
'incorporationlocality', 'incorporationstate', 'incorporationlocality', 'incorporationstate',
'incorporationcountry', 'businesscategory'] 'incorporationcountry', 'businesscategory']
def zonemgr_callback(option, opt_str, value, parser):
"""
Make sure the zonemgr is an IA5String.
"""
name = opt_str.replace('--','')
v = unicode(value, 'utf-8')
ia = IA5Str(name)
ia._convert_scalar(v)
parser.values.zonemgr = value
def subject_callback(option, opt_str, value, parser): def subject_callback(option, opt_str, value, parser):
""" """
Make sure the certificate subject base is a valid DN Make sure the certificate subject base is a valid DN
@@ -195,7 +184,7 @@ def parse_options():
dns_group.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use") dns_group.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use")
dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true", dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true",
default=False, help="Do not create reverse DNS zone") default=False, help="Do not create reverse DNS zone")
dns_group.add_option("--zonemgr", action="callback", callback=zonemgr_callback, dns_group.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback,
type="string", type="string",
help="DNS zone manager e-mail address. Defaults to root") help="DNS zone manager e-mail address. Defaults to root")
dns_group.add_option("--zone-notif", dest="zone_notif", dns_group.add_option("--zone-notif", dest="zone_notif",

View File

@@ -26,6 +26,7 @@ from ipalib import Command
from ipalib import Flag, Int, List, Str, StrEnum from ipalib import Flag, Int, List, Str, StrEnum
from ipalib.plugins.baseldap import * from ipalib.plugins.baseldap import *
from ipalib import _, ngettext from ipalib import _, ngettext
from ipalib.util import validate_zonemgr
from ipapython import dnsclient from ipapython import dnsclient
from ipapython.ipautil import valid_ip from ipapython.ipautil import valid_ip
from ldap import explode_dn from ldap import explode_dn
@@ -136,6 +137,13 @@ _record_attributes = [str('%srecord' % t.lower()) for t in _record_types]
# supported DNS classes, IN = internet, rest is almost never used # supported DNS classes, IN = internet, rest is almost never used
_record_classes = (u'IN', u'CS', u'CH', u'HS') _record_classes = (u'IN', u'CS', u'CH', u'HS')
def _rname_validator(ugettext, zonemgr):
try:
validate_zonemgr(zonemgr)
except ValueError, e:
return unicode(e)
return None
# normalizer for admin email # normalizer for admin email
def _rname_normalizer(value): def _rname_normalizer(value):
value = value.replace('@', '.') value = value.replace('@', '.')
@@ -323,6 +331,7 @@ class dnszone(LDAPObject):
doc=_('Authoritative nameserver domain name'), doc=_('Authoritative nameserver domain name'),
), ),
Str('idnssoarname', Str('idnssoarname',
_rname_validator,
cli_name='admin_email', cli_name='admin_email',
label=_('Administrator e-mail address'), label=_('Administrator e-mail address'),
doc=_('Administrator e-mail address'), doc=_('Administrator e-mail address'),

View File

@@ -203,3 +203,33 @@ def check_writable_file(filename):
fp.close() fp.close()
except (IOError, OSError), e: except (IOError, OSError), e:
raise errors.FileError(reason=str(e)) raise errors.FileError(reason=str(e))
def validate_zonemgr(zonemgr):
""" See RFC 1033, 1035 """
regex_domain = re.compile(r'^[a-z0-9][a-z0-9-]*$', re.IGNORECASE)
regex_name = re.compile(r'^[a-z0-9][a-z0-9-_]*$', re.IGNORECASE)
if len(zonemgr) > 255:
raise ValueError(_('cannot be longer that 255 characters'))
if zonemgr.count('@') == 1:
name, dot, domain = zonemgr.partition('@')
elif zonemgr.count('@') > 1:
raise ValueError(_('too many \'@\' characters'))
else:
# address in SOA format already (without @)
name, dot, domain = zonemgr.partition('.')
if domain.endswith('.'):
domain = domain[:-1]
if '.' not in domain:
raise ValueError(_('address domain is not fully qualified ' \
'("example.com" instead of just "example")'))
if not regex_name.match(name):
raise ValueError(_('mail account may only include letters, numbers, -, and _'))
if not all(regex_domain.match(part) for part in domain.split(".")):
raise ValueError(_('domain name may only include letters, numbers, and -'))

View File

@@ -32,6 +32,8 @@ from ipaserver.install.installutils import resolve_host
from ipapython import sysrestore from ipapython import sysrestore
from ipapython import ipautil from ipapython import ipautil
from ipalib.constants import DNS_ZONE_REFRESH from ipalib.constants import DNS_ZONE_REFRESH
from ipalib.parameters import IA5Str
from ipalib.util import validate_zonemgr
import ipalib import ipalib
from ipalib import api, util, errors from ipalib import api, util, errors
@@ -286,6 +288,21 @@ def get_rr(zone, name, type):
return [] return []
def zonemgr_callback(option, opt_str, value, parser):
"""
Properly validate and convert --zonemgr Option to IA5String
"""
# validate the value first
try:
validate_zonemgr(value)
except ValueError, e:
parser.error("invalid zonemgr: " + unicode(e))
name = opt_str.replace('--','')
v = unicode(value, 'utf-8')
ia = IA5Str(name)
ia._convert_scalar(v)
parser.values.zonemgr = value
class DnsBackup(object): class DnsBackup(object):
def __init__(self, service): def __init__(self, service):