summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2007-12-11 14:19:10 -0500
committerSimo Sorce <ssorce@redhat.com>2007-12-11 14:19:10 -0500
commit5215b21ea404f2370bfdd4a2e452577e065a718c (patch)
tree8893bdeebf94979c66febd0d1f84fde9d311ed4e
parent4f0b2154146cc3ed3b32b34713089323d96c1c74 (diff)
parent01131e2a37a9aec197b4e286e0559165d403fe73 (diff)
downloadfreeipa-5215b21ea404f2370bfdd4a2e452577e065a718c.tar.gz
freeipa-5215b21ea404f2370bfdd4a2e452577e065a718c.tar.xz
freeipa-5215b21ea404f2370bfdd4a2e452577e065a718c.zip
merge with upstream
-rw-r--r--ipa-admintools/Makefile8
-rw-r--r--ipa-admintools/ipa-adddelegation52
-rw-r--r--ipa-admintools/ipa-addradiusclient195
-rw-r--r--ipa-admintools/ipa-addradiusprofile194
-rw-r--r--ipa-admintools/ipa-delradiusclient77
-rw-r--r--ipa-admintools/ipa-delradiusprofile85
-rw-r--r--ipa-admintools/ipa-findgroup10
-rw-r--r--ipa-admintools/ipa-findradiusclient104
-rw-r--r--ipa-admintools/ipa-findradiusprofile107
-rw-r--r--ipa-admintools/ipa-finduser11
-rw-r--r--ipa-admintools/ipa-moddelegation56
-rw-r--r--ipa-admintools/ipa-radiusclientmod273
-rw-r--r--ipa-admintools/ipa-radiusprofilemod263
-rw-r--r--ipa-python/ipaadminutil.py75
-rw-r--r--ipa-python/ipaclient.py66
-rw-r--r--ipa-python/ipautil.py474
-rw-r--r--ipa-python/ipavalidate.py1
-rw-r--r--ipa-python/radius_util.py366
-rw-r--r--ipa-python/rpcclient.py141
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/user.py4
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/principallist.kid10
-rw-r--r--ipa-server/ipa-install/Makefile.am6
-rw-r--r--ipa-server/ipa-install/ipa-server-setupssl216
-rw-r--r--ipa-server/ipa-install/share/60radius.ldif38
-rw-r--r--ipa-server/ipa-install/share/bootstrap-template.ldif24
-rw-r--r--ipa-server/ipa-install/share/default-aci.ldif3
-rw-r--r--ipa-server/ipa-install/share/encrypted_attribute.ldif6
-rw-r--r--ipa-server/ipa-install/share/radius.radiusd.conf.template23
-rw-r--r--ipa-server/ipaserver/Makefile.am1
-rw-r--r--ipa-server/ipaserver/certs.py213
-rw-r--r--ipa-server/ipaserver/dsinstance.py39
-rw-r--r--ipa-server/ipaserver/httpinstance.py15
-rw-r--r--ipa-server/ipaserver/radiusinstance.py93
-rw-r--r--ipa-server/xmlrpc-server/funcs.py201
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py24
35 files changed, 3136 insertions, 338 deletions
diff --git a/ipa-admintools/Makefile b/ipa-admintools/Makefile
index 9586e71cf..5c3fdfe8c 100644
--- a/ipa-admintools/Makefile
+++ b/ipa-admintools/Makefile
@@ -23,6 +23,14 @@ install:
install -m 755 ipa-listdelegation $(SBINDIR)
install -m 755 ipa-moddelegation $(SBINDIR)
install -m 755 ipa-getkeytab $(SBINDIR)
+ install -m 755 ipa-addradiusclient $(SBINDIR)
+ install -m 755 ipa-radiusclientmod $(SBINDIR)
+ install -m 755 ipa-delradiusclient $(SBINDIR)
+ install -m 755 ipa-findradiusclient $(SBINDIR)
+ install -m 755 ipa-addradiusprofile $(SBINDIR)
+ install -m 755 ipa-radiusprofilemod $(SBINDIR)
+ install -m 755 ipa-delradiusprofile $(SBINDIR)
+ install -m 755 ipa-findradiusprofile $(SBINDIR)
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
diff --git a/ipa-admintools/ipa-adddelegation b/ipa-admintools/ipa-adddelegation
index 53bd43ce2..62b4b96e8 100644
--- a/ipa-admintools/ipa-adddelegation
+++ b/ipa-admintools/ipa-adddelegation
@@ -23,9 +23,9 @@ from optparse import OptionParser
import ipa
import ipa.user
import ipa.ipaclient as ipaclient
-import ipa.ipavalidate as ipavalidate
import ipa.config
import ipa.aci
+import ipa.ipaadminutil as ipaadminutil
import xmlrpclib
import kerberos
@@ -68,27 +68,53 @@ def main():
client = ipaclient.IPAClient()
source_grp = client.find_groups(options.source)
- if source_grp[0] > 1:
- print "Multiple matches found for %s." % options.source
- return 2
- elif source_grp[0] == 0:
- print "No matches found for %s." % options.source
+ counter = source_grp[0]
+ source_grp = source_grp[1:]
+ groupindex = -1
+ if counter == 0:
+ print "No entries found for %s" % options.source
return 2
+ elif counter == -1:
+ print "These results are truncated."
+ print "Please refine your search and try again."
+ return 3
+
+ if counter > 1:
+ print "\nMultiple entries for the source group found."
+ groupindex = ipaadminutil.select_group(counter, source_grp)
+ if groupindex == "q":
+ return 0
+
+ if groupindex >= 0:
+ source_grp = [source_grp[groupindex]]
target_grp = client.find_groups(options.target)
- if target_grp[0] > 1:
- print "Multiple matches found for %s." % options.target
- return 3
- elif target_grp[0] == 0:
- print "No matches found for %s." % options.target
+ counter = target_grp[0]
+ target_grp = target_grp[1:]
+ groupindex = -1
+ if counter == 0:
+ print "No entries found for %s" % options.target
+ return 2
+ elif counter == -1:
+ print "These results are truncated."
+ print "Please refine your search and try again."
return 3
+ if counter > 1:
+ print "\nMultiple entries for the target group found."
+ groupindex = ipaadminutil.select_group(counter, target_grp)
+ if groupindex == "q":
+ return 0
+
+ if groupindex >= 0:
+ target_grp = [target_grp[groupindex]]
+
attr_list = options.attributes.split(',')
new_aci = ipa.aci.ACI()
new_aci.name = args[1]
- new_aci.source_group = source_grp[1].dn
- new_aci.dest_group = target_grp[1].dn
+ new_aci.source_group = source_grp[0].dn
+ new_aci.dest_group = target_grp[0].dn
new_aci.attrs = attr_list
aci_entry = client.get_aci_entry(['*', 'aci'])
diff --git a/ipa-admintools/ipa-addradiusclient b/ipa-admintools/ipa-addradiusclient
new file mode 100644
index 000000000..1db571a71
--- /dev/null
+++ b/ipa-admintools/ipa-addradiusclient
@@ -0,0 +1,195 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import sys
+import os
+from optparse import OptionParser
+
+import ipa.ipaclient as ipaclient
+import ipa.ipautil as ipautil
+import ipa.config
+import ipa.ipaerror
+import ipa.radius_util as radius_util
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+radius_attrs = radius_util.radius_client_attr_to_ldap_attr.keys()
+radius_attr_to_ldap_attr = radius_util.radius_client_attr_to_ldap_attr
+ldap_attr_to_radius_attr = radius_util.radius_client_ldap_attr_to_radius_attr
+mandatory_radius_attrs = ['Client-IP-Address', 'Secret']
+distinguished_attr = 'Client-IP-Address'
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Valid interative attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ print
+ print "Required attributes are:"
+ print ipautil.format_list(mandatory_radius_attrs, quote='"')
+ sys.exit(0)
+
+def main():
+ pairs = {}
+
+ opt_parser = OptionParser(add_help_option=False)
+
+ opt_parser.add_option("-a", "--Client-IP-Address", dest="ip_addr",
+ help="RADIUS client ip address")
+ opt_parser.add_option("-s", "--Secret", dest="secret",
+ help="RADIUS client ip address")
+ opt_parser.add_option("-n", "--Name", dest="name",
+ help="RADIUS client name")
+ opt_parser.add_option("-t", "--NAS-Type", dest="nastype",
+ help="RADIUS client NAS Type")
+ opt_parser.add_option("-d", "--Description", dest="desc",
+ help="description of the RADIUS client")
+
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+ opt_parser.add_option("-i", "--interactive", dest="interactive", action='store_true', default=False,
+ help="interactive mode, prompts with auto-completion")
+ opt_parser.add_option("-p", "--pair", dest="pairs", action='append',
+ help="specify one or more attribute=value pair(s), value may be optionally quoted, pairs are delimited by whitespace")
+ opt_parser.add_option("-f", "--file", dest="pair_file",
+ help="attribute=value pair(s) are read from file, value may be optionally quoted, pairs are delimited by whitespace. Reads from stdin if file is -")
+ opt_parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
+ help="print information")
+
+ opt_parser.set_usage("Usage: %s [options] %s" % (distinguished_attr, os.path.basename(sys.argv[0])))
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if len(args) < 2:
+ opt_parser.error('missing %s' % (distinguished_attr))
+
+ ip_addr = args[1]
+ pairs[distinguished_attr] = ip_addr
+
+ # Get pairs from a file or stdin
+ if options.pair_file:
+ try:
+ av = ipautil.read_pairs_file(options.pair_file)
+ pairs.update(av)
+ except Exception, e:
+ print "ERROR, could not read pairs (%s)" % (e)
+
+ # Get pairs specified on the command line as a named argument
+ if options.ip_addr: pairs[distinguished_attr] = options.ip_addr
+ if options.secret: pairs['Secret'] = options.secret
+ if options.name: pairs['Name'] = options.name
+ if options.nastype: pairs['NAS-Type'] = options.nastype
+ if options.desc: pairs['Description'] = options.desc
+
+ # Get pairs specified on the command line as a pair argument
+ if options.pairs:
+ for p in options.pairs:
+ av = ipautil.parse_key_value_pairs(p)
+ pairs.update(av)
+
+ # Get pairs interactively
+ if options.interactive:
+ # Prompt first for mandatory attributes which have not been previously specified
+ prompted_mandatory_attrs = []
+ existing_attrs = pairs.keys()
+ for attr in mandatory_radius_attrs:
+ if not attr in existing_attrs:
+ prompted_mandatory_attrs.append(attr)
+
+ c = ipautil.AttributeValueCompleter(radius_attrs, pairs)
+ c.open()
+ av = c.get_pairs("Enter: ", prompted_mandatory_attrs, radius_util.validate)
+ pairs.update(av)
+ c.close()
+
+ # FIXME: validation should be moved to xmlrpc server
+
+ # Data collection done, assure mandatory data has been specified
+
+ if pairs.has_key(distinguished_attr) and pairs[distinguished_attr] != ip_addr:
+ print "ERROR, %s specified on command line (%s) does not match value found in pairs (%s)" % \
+ (distinguished_attr, ip_addr, pairs[distinguished_attr])
+ return 1
+
+ valid = True
+ for attr in mandatory_radius_attrs:
+ if not pairs.has_key(attr):
+ valid = False
+ print "ERROR, %s is mandatory, but has not been specified" % (attr)
+ if not valid:
+ return 1
+
+ # Make sure each attribute is a member of the set of valid attributes
+ valid = True
+ for attr,value in pairs.items():
+ if attr not in radius_attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ return 1
+
+ # Makse sure each value is valid
+ valid = True
+ for attr,value in pairs.items():
+ if not radius_util.validate(attr, value):
+ valid = False
+ if not valid:
+ return 1
+
+ # Dump what we've got so far
+ if options.verbose:
+ print "Pairs:"
+ for attr,value in pairs.items():
+ print "\t%s = %s" % (attr, value)
+
+ radius_entity = radius_util.RadiusClient()
+ for attr,value in pairs.items():
+ radius_entity.setValue(radius_attr_to_ldap_attr[attr], value)
+
+ try:
+ ipa_client = ipaclient.IPAClient()
+ ipa_client.add_radius_client(radius_entity)
+ print "successfully added"
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-addradiusprofile b/ipa-admintools/ipa-addradiusprofile
new file mode 100644
index 000000000..66db52267
--- /dev/null
+++ b/ipa-admintools/ipa-addradiusprofile
@@ -0,0 +1,194 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import sys
+import os
+from optparse import OptionParser
+
+import ipa.ipaclient as ipaclient
+import ipa.ipautil as ipautil
+import ipa.config
+import ipa.ipaerror
+import ipa.radius_util as radius_util
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+radius_attrs = radius_util.radius_profile_attr_to_ldap_attr.keys()
+radius_attr_to_ldap_attr = radius_util.radius_profile_attr_to_ldap_attr
+ldap_attr_to_radius_attr = radius_util.radius_profile_ldap_attr_to_radius_attr
+mandatory_radius_attrs = ['UID']
+distinguished_attr = 'UID'
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Valid interative attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ print
+ print "Required attributes are:"
+ print ipautil.format_list(mandatory_radius_attrs, quote='"')
+ sys.exit(0)
+
+def main():
+ pairs = {}
+
+ opt_parser = OptionParser(add_help_option=False)
+
+ opt_parser.add_option("-u", "--uid", dest="uid",
+ help="RADIUS profile identifier")
+ opt_parser.add_option("-s", "--shared", dest="shared", default=False, action='store_true',
+ help="profile is shared")
+ opt_parser.add_option("-d", "--Description", dest="desc",
+ help="description of the RADIUS client")
+
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+ opt_parser.add_option("-i", "--interactive", dest="interactive", action='store_true', default=False,
+ help="interactive mode, prompts with auto-completion")
+ opt_parser.add_option("-p", "--pair", dest="pairs", action='append',
+ help="specify one or more attribute=value pair(s), value may be optionally quoted, pairs are delimited by whitespace")
+ opt_parser.add_option("-f", "--file", dest="pair_file",
+ help="attribute=value pair(s) are read from file, value may be optionally quoted, pairs are delimited by whitespace. Reads from stdin if file is -")
+ opt_parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
+ help="print information")
+
+ opt_parser.set_usage("Usage: %s [options] %s" % (distinguished_attr, os.path.basename(sys.argv[0])))
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if len(args) < 2:
+ opt_parser.error('missing %s' % (distinguished_attr))
+
+ uid = args[1]
+ user_profile = not options.shared
+ pairs[distinguished_attr] = uid
+
+ # Per user profiles are pre-created (i.e. objectclass radiusprofile is always added for each user)
+ if user_profile:
+ print "ERROR, you cannot add a per-user radius profile, it pre-exists"
+ return 1
+
+ # Get pairs from a file or stdin
+ if options.pair_file:
+ try:
+ av = ipautil.read_pairs_file(options.pair_file)
+ pairs.update(av)
+ except Exception, e:
+ print "ERROR, could not read pairs (%s)" % (e)
+
+ # Get pairs specified on the command line as a named argument
+ if options.uid: pairs['UID'] = options.uid
+ if options.desc: pairs['Description'] = options.desc
+
+ # Get pairs specified on the command line as a pair argument
+ if options.pairs:
+ for p in options.pairs:
+ av = ipautil.parse_key_value_pairs(p)
+ pairs.update(av)
+
+ # Get pairs interactively
+ if options.interactive:
+ # Prompt first for mandatory attributes which have not been previously specified
+ prompted_mandatory_attrs = []
+ existing_attrs = pairs.keys()
+ for attr in mandatory_radius_attrs:
+ if not attr in existing_attrs:
+ prompted_mandatory_attrs.append(attr)
+
+ c = ipautil.AttributeValueCompleter(radius_attrs, pairs)
+ c.open()
+ av = c.get_pairs("Enter: ", prompted_mandatory_attrs, radius_util.validate)
+ pairs.update(av)
+ c.close()
+
+ # FIXME: validation should be moved to xmlrpc server
+
+ # Data collection done, assure mandatory data has been specified
+
+ if pairs.has_key(distinguished_attr) and pairs[distinguished_attr] != uid:
+ print "ERROR, %s specified on command line (%s) does not match value found in pairs (%s)" % \
+ (distinguished_attr, uid, pairs[distinguished_attr])
+ return 1
+
+ valid = True
+ for attr in mandatory_radius_attrs:
+ if not pairs.has_key(attr):
+ valid = False
+ print "ERROR, %s is mandatory, but has not been specified" % (attr)
+ if not valid:
+ return 1
+
+ # Make sure each attribute is a member of the set of valid attributes
+ valid = True
+ for attr,value in pairs.items():
+ if attr not in radius_attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ return 1
+
+ # Makse sure each value is valid
+ valid = True
+ for attr,value in pairs.items():
+ if not radius_util.validate(attr, value):
+ valid = False
+ if not valid:
+ return 1
+
+ # Dump what we've got so far
+ if options.verbose:
+ print "Pairs:"
+ for attr,value in pairs.items():
+ print "\t%s = %s" % (attr, value)
+
+ radius_entity = radius_util.RadiusProfile()
+ for attr,value in pairs.items():
+ radius_entity.setValue(radius_attr_to_ldap_attr[attr], value)
+
+ try:
+ ipa_client = ipaclient.IPAClient()
+ ipa_client.add_radius_profile(radius_entity)
+ print "successfully added"
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-delradiusclient b/ipa-admintools/ipa-delradiusclient
new file mode 100644
index 000000000..dd26e8ac9
--- /dev/null
+++ b/ipa-admintools/ipa-delradiusclient
@@ -0,0 +1,77 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+import sys
+from optparse import OptionParser
+import ipa
+import ipa.ipaclient as ipaclient
+import ipa.ipavalidate as ipavalidate
+import ipa.config
+import ipa.ipaerror
+import ipa.radius_util as radius_util
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ sys.exit(0)
+
+
+def main():
+ opt_parser = OptionParser(add_help_option=False)
+
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+ opt_parser.set_usage("Usage: %s [options] Client-IP-Address" % (os.path.basename(sys.argv[0])))
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if len(args) < 2:
+ opt_parser.error("missing Client-IP-Address")
+
+ ip_addr = args[1]
+
+ try:
+ ipa_client = ipaclient.IPAClient()
+ ipa_client.delete_radius_client(ip_addr)
+ print "successfully deleted"
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-delradiusprofile b/ipa-admintools/ipa-delradiusprofile
new file mode 100644
index 000000000..f77d01747
--- /dev/null
+++ b/ipa-admintools/ipa-delradiusprofile
@@ -0,0 +1,85 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+import sys
+from optparse import OptionParser
+import ipa
+import ipa.ipaclient as ipaclient
+import ipa.ipavalidate as ipavalidate
+import ipa.config
+import ipa.ipaerror
+import ipa.radius_util as radius_util
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ sys.exit(0)
+
+
+def main():
+ opt_parser = OptionParser(add_help_option=False)
+
+ opt_parser.add_option("-s", "--shared", dest="shared", default=False, action='store_true',
+ help="profile is shared")
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+ opt_parser.set_usage("Usage: %s [options] UID" % (os.path.basename(sys.argv[0])))
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if len(args) < 2:
+ opt_parser.error("missing UID")
+
+ uid = args[1]
+ user_profile = not options.shared
+
+ # Per user profiles are pre-created (i.e. objectclass radiusprofile is always added for each user)
+ if user_profile:
+ print "ERROR, you cannot delete a per-user radius profile, it always exists"
+ return 1
+
+ try:
+ ipa_client = ipaclient.IPAClient()
+ ipa_client.delete_radius_profile(uid, user_profile)
+ print "successfully deleted"
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-findgroup b/ipa-admintools/ipa-findgroup
index 73b0bb1bc..b5a5f0766 100644
--- a/ipa-admintools/ipa-findgroup
+++ b/ipa-admintools/ipa-findgroup
@@ -21,6 +21,7 @@
import sys
from optparse import OptionParser
import ipa.ipaclient as ipaclient
+import ipa.ipaadminutil as ipaadminutil
import ipa.config
import errno
@@ -62,6 +63,7 @@ def main():
counter = groups[0]
groups = groups[1:]
+ groupindex = -1
if counter == 0:
print "No entries found for", args[1]
return 2
@@ -69,6 +71,14 @@ def main():
print "These results are truncated."
print "Please refine your search and try again."
+ if counter > 1:
+ groupindex = ipaadminutil.select_group(counter, groups)
+ if groupindex == "q":
+ return 0
+
+ if groupindex >= 0:
+ groups = [groups[groupindex]]
+
for ent in groups:
try:
members = client.group_members(ent.dn, ['dn','cn'])
diff --git a/ipa-admintools/ipa-findradiusclient b/ipa-admintools/ipa-findradiusclient
new file mode 100644
index 000000000..ade4bd397
--- /dev/null
+++ b/ipa-admintools/ipa-findradiusclient
@@ -0,0 +1,104 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+import sys
+from optparse import OptionParser
+import ipa
+from ipa import radius_util
+import ipa.ipaclient as ipaclient
+import ipa.ipavalidate as ipavalidate
+import ipa.config
+import ipa.ipaerror
+import ipa.ipautil
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+attrs = radius_util.radius_client_ldap_attr_to_radius_attr.keys()
+
+#------------------------------------------------------------------------------
+
+def parse_options():
+ return options, args
+
+#------------------------------------------------------------------------------
+
+# FIXME
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Note: Client-IP-Address may contain wildcards, to get all clients use '*'"
+ sys.exit(0)
+
+def main():
+ opt_parser = OptionParser(add_help_option=False)
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ opt_parser.set_usage("Usage: %s [options] Client-IP-Address [Client-IP-Address ...]" % (os.path.basename(sys.argv[0])))
+
+ if len(args) < 2:
+ opt_parser.error("missing Client-IP-Address(es)")
+
+ ip_addrs = args[1:]
+
+ try:
+ ipa_client = ipaclient.IPAClient()
+ radius_clients = ipa_client.find_radius_clients(ip_addrs, sattrs=attrs)
+ counter = radius_clients[0]
+ radius_clients = radius_clients[1:]
+
+ if counter == 0:
+ print "No entries found for", ip_addrs
+ return 2
+
+ for radius_client in radius_clients:
+ client_attrs = radius_client.attrList()
+ client_attrs.sort()
+
+ print "%s:" % radius_client.getValues(radius_util.radius_client_attr_to_ldap_attr['Client-IP-Address'])
+ for attr in client_attrs:
+ value = radius_client.getValues(attr)
+ print "\t%s = %s" % (radius_util.radius_client_ldap_attr_to_radius_attr[attr], value)
+
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-findradiusprofile b/ipa-admintools/ipa-findradiusprofile
new file mode 100644
index 000000000..ba714068a
--- /dev/null
+++ b/ipa-admintools/ipa-findradiusprofile
@@ -0,0 +1,107 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+import sys
+from optparse import OptionParser
+import ipa
+from ipa import radius_util
+import ipa.ipaclient as ipaclient
+import ipa.ipavalidate as ipavalidate
+import ipa.config
+import ipa.ipaerror
+import ipa.ipautil
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+attrs = radius_util.radius_profile_ldap_attr_to_radius_attr.keys()
+
+#------------------------------------------------------------------------------
+
+def parse_options():
+ return options, args
+
+#------------------------------------------------------------------------------
+
+# FIXME
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Note: UID may contain wildcards, to get all profiles use '*'"
+ sys.exit(0)
+
+def main():
+ opt_parser = OptionParser(add_help_option=False)
+ opt_parser.add_option("-s", "--shared", dest="shared", default=False, action='store_true',
+ help="profile is shared")
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ opt_parser.set_usage("Usage: %s [options] UID [UID ...]" % (os.path.basename(sys.argv[0])))
+
+ if len(args) < 2:
+ opt_parser.error("missing UID(es)")
+
+ uids = args[1:]
+ user_profile = not options.shared
+
+ try:
+ ipa_client = ipaclient.IPAClient()
+ radius_profiles = ipa_client.find_radius_profiles(uids, user_profile, sattrs=attrs)
+ counter = radius_profiles[0]
+ radius_profiles = radius_profiles[1:]
+
+ if counter == 0:
+ print "No entries found for", uids
+ return 2
+
+ for radius_profile in radius_profiles:
+ profile_attrs = radius_profile.attrList()
+ profile_attrs.sort()
+
+ print "%s:" % radius_profile.getValues(radius_util.radius_profile_attr_to_ldap_attr['UID'])
+ for attr in profile_attrs:
+ value = radius_profile.getValues(attr)
+ print "\t%s = %s" % (radius_util.radius_profile_ldap_attr_to_radius_attr[attr], value)
+
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-finduser b/ipa-admintools/ipa-finduser
index 2ee19dfe0..9a57087b1 100644
--- a/ipa-admintools/ipa-finduser
+++ b/ipa-admintools/ipa-finduser
@@ -23,6 +23,7 @@ from optparse import OptionParser
import ipa.ipaclient as ipaclient
import ipa.config
import ipa.ipautil as ipautil
+import ipa.ipaadminutil as ipaadminutil
import base64
import errno
@@ -87,6 +88,7 @@ def main():
counter = users[0]
users = users[1:]
+ userindex = 0
if counter == 0:
print "No entries found for", args[1]
return 2
@@ -94,6 +96,15 @@ def main():
print "These results are truncated."
print "Please refine your search and try again."
+ if counter > 1:
+ userindex = ipaadminutil.select_user(counter, users)
+ if userindex == "q":
+ return
+
+
+ if userindex >= 0:
+ users = [users[userindex]]
+
for ent in users:
attr = ent.attrList()
attr.sort()
diff --git a/ipa-admintools/ipa-moddelegation b/ipa-admintools/ipa-moddelegation
index 103c0586d..2be10b9f6 100644
--- a/ipa-admintools/ipa-moddelegation
+++ b/ipa-admintools/ipa-moddelegation
@@ -23,7 +23,7 @@ from optparse import OptionParser
import ipa
import ipa.user
import ipa.ipaclient as ipaclient
-import ipa.ipavalidate as ipavalidate
+import ipa.ipaadminutil as ipaadminutil
import ipa.config
import ipa.aci
@@ -75,21 +75,47 @@ def main():
if options.source:
source_grp = client.find_groups(options.source)
- if source_grp[0] > 1:
- print "Multiple matches found for %s." % options.source
- return 1
- elif source_grp[0] == 0:
- print "No matches found for %s." % options.source
- return 1
+ counter = source_grp[0]
+ source_grp = source_grp[1:]
+ groupindex = -1
+ if counter == 0:
+ print "No entries found for %s" % options.source
+ return 2
+ elif counter == -1:
+ print "These results are truncated."
+ print "Please refine your search and try again."
+ return 3
+
+ if counter > 1:
+ print "\nMultiple entries for the source group found."
+ groupindex = ipaadminutil.select_group(counter, source_grp)
+ if groupindex == "q":
+ return 0
+
+ if groupindex >= 0:
+ source_grp = [source_grp[groupindex]]
if options.target:
target_grp = client.find_groups(options.target)
- if target_grp[0] > 1:
- print "Multiple matches found for %s." % options.target
- return 1
- elif target_grp[0] == 0:
- print "No matches found for %s." % options.target
- return 1
+ counter = target_grp[0]
+ target_grp = target_grp[1:]
+ groupindex = -1
+ if counter == 0:
+ print "No entries found for %s" % options.target
+ return 2
+ elif counter == -1:
+ print "These results are truncated."
+ print "Please refine your search and try again."
+ return 3
+
+ if counter > 1:
+ print "\nMultiple entries for the target group found."
+ groupindex = ipaadminutil.select_group(counter, target_grp)
+ if groupindex == "q":
+ return 0
+
+ if groupindex >= 0:
+ target_grp = [target_grp[groupindex]]
if options.attributes:
attr_list = options.attributes.split(',')
@@ -125,11 +151,11 @@ def main():
new_aci = ipa.aci.ACI()
new_aci.name = args[1]
if options.source:
- new_aci.source_group = source_grp[1].dn
+ new_aci.source_group = source_grp[0].dn
else:
new_aci.source_group = old_aci.source_group
if options.target:
- new_aci.dest_group = target_grp[1].dn
+ new_aci.dest_group = target_grp[0].dn
else:
new_aci.dest_group = old_aci.dest_group
if options.attributes:
diff --git a/ipa-admintools/ipa-radiusclientmod b/ipa-admintools/ipa-radiusclientmod
new file mode 100644
index 000000000..9e973b121
--- /dev/null
+++ b/ipa-admintools/ipa-radiusclientmod
@@ -0,0 +1,273 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import sys
+import os
+from optparse import OptionParser
+from sets import Set
+
+import ipa.ipaclient as ipaclient
+import ipa.ipautil as ipautil
+import ipa.config
+import ipa.ipaerror
+import ipa.radius_util as radius_util
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+radius_attrs = radius_util.radius_client_attr_to_ldap_attr.keys()
+radius_attr_to_ldap_attr = radius_util.radius_client_attr_to_ldap_attr
+ldap_attr_to_radius_attr = radius_util.radius_client_ldap_attr_to_radius_attr
+mandatory_radius_attrs = ['Client-IP-Address', 'Secret']
+distinguished_attr = 'Client-IP-Address'
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Valid interative attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ print
+ print "Required attributes are:"
+ print ipautil.format_list(mandatory_radius_attrs, quote='"')
+ sys.exit(0)
+
+def main():
+ opt_parser = OptionParser(add_help_option=False)
+
+ opt_parser.add_option("-a", "--Client-IP-Address", dest="ip_addr",
+ help="RADIUS client ip address")
+ opt_parser.add_option("-s", "--Secret", dest="secret",
+ help="RADIUS client ip address")
+ opt_parser.add_option("-n", "--Name", dest="name",
+ help="RADIUS client name")
+ opt_parser.add_option("-t", "--NAS-Type", dest="nastype",
+ help="RADIUS client NAS Type")
+ opt_parser.add_option("-d", "--Description", dest="desc",
+ help="description of the RADIUS client")
+
+ opt_parser.add_option("-D", "--delete-attrs", dest="delete_attrs", action='store_true', default=False,
+ help="delete the specified attributes")
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+ opt_parser.add_option("-i", "--interactive", dest="interactive", action='store_true', default=False,
+ help="interactive mode, prompts with auto-completion")
+ opt_parser.add_option("-A", "--attr", dest="attrs", action='append',
+ help="If adding or modifying then this argument specifies one or more attribute=value pair(s), value may be optionally quoted, pairs are seperated by whitespace. If deleting attributes then this argument specifies one or more attribute names seperated by whitespace or commas")
+ opt_parser.add_option("-f", "--file", dest="data_file",
+ help="If adding or modifying then attribute=value pair(s) are read from file, value may be optionally quoted, pairs are delimited by whitespace. If deleting attributes then attributes are read from file, attributes are seperated by whitespace or commas. Reads from stdin if file is -")
+ opt_parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
+ help="print information")
+
+ opt_parser.set_usage("Usage: %s [options] %s" % (distinguished_attr, os.path.basename(sys.argv[0])))
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if len(args) < 2:
+ opt_parser.error('missing %s' % (distinguished_attr))
+
+ ip_addr = args[1]
+
+ # Verify entity previously exists and get current values
+ ipa_client = ipaclient.IPAClient()
+ try:
+ radius_entity = ipa_client.get_radius_client_by_ip_addr(ip_addr)
+ except ipa.ipaerror.exception_for(ipa.ipaerror.LDAP_NOT_FOUND):
+ print "client %s not found" % ip_addr
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % e.message
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+
+ # Deleteing attributes is fundamentally different than adding/modifying an attribute.
+ # When adding/modifying there is always a value the attribute is paired with,
+ # so handle the two cases independently.
+ if options.delete_attrs:
+ attrs = Set()
+ # Get attrs from a file or stdin
+ if options.data_file:
+ try:
+ items = ipautil.read_items_file(options.data_file)
+ attrs.update(items)
+ except Exception, e:
+ print "ERROR, could not read attrs (%s)" % (e)
+
+ # Get attrs specified on the command line as a named argument
+ if options.secret is not None: attrs.add('Secret')
+ if options.name is not None: attrs.add('Name')
+ if options.nastype is not None: attrs.add('NAS-Type')
+ if options.desc is not None: attrs.add('Description')
+
+ # Get attrs specified on the command line as a attr argument
+ if options.attrs:
+ for a in options.attrs:
+ items = ipautil.parse_items(a)
+ attrs.update(items)
+
+ # Get attrs interactively
+ if options.interactive:
+ deletable_attrs = []
+ for radius_attr in radius_attrs:
+ if radius_attr in mandatory_radius_attrs: continue
+ if radius_entity.hasAttr(radius_attr_to_ldap_attr[radius_attr]):
+ deletable_attrs.append(radius_attr)
+
+ if deletable_attrs:
+ c = ipautil.ItemCompleter(deletable_attrs)
+ c.open()
+ items = c.get_items("Enter: ")
+ attrs.update(items)
+ c.close()
+
+ # Data collection done, assure no mandatory attrs are in the delete list
+ valid = True
+ for attr in mandatory_radius_attrs:
+ if attr in attrs:
+ valid = False
+ print "ERROR, %s is mandatory, but is set to be deleted" % (attr)
+ if not valid:
+ return 1
+
+ # Make sure each attribute is a member of the set of valid attributes
+ valid = True
+ for attr in attrs:
+ if attr not in radius_attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ return 1
+
+ # Dump what we've got so far
+ if options.verbose:
+ print "Attributes:"
+ for attr in attrs:
+ print "\t%s" % (attr)
+
+ for attr in attrs:
+ radius_entity.delValue(radius_attr_to_ldap_attr[attr])
+
+ else:
+ pairs = {}
+ pairs[distinguished_attr] = ip_addr
+
+ # Populate the pair list with pre-existing values
+ for attr in radius_attrs:
+ value = radius_entity.getValues(radius_attr_to_ldap_attr[attr])
+ if value is None: continue
+ pairs[attr] = value
+
+ # Get pairs from a file or stdin
+ if options.data_file:
+ try:
+ av = ipautil.read_pairs_file(options.data_file)
+ pairs.update(av)
+ except Exception, e:
+ print "ERROR, could not read pairs (%s)" % (e)
+
+ # Get pairs specified on the command line as a named argument
+ if options.ip_addr is not None: pairs[distinguished_attr] = options.ip_addr
+ if options.secret is not None: pairs['Secret'] = options.secret
+ if options.name is not None: pairs['Name'] = options.name
+ if options.nastype is not None: pairs['NAS-Type'] = options.nastype
+ if options.desc is not None: pairs['Description'] = options.desc
+
+ # Get pairs specified on the command line as a pair argument
+ if options.attrs:
+ for p in options.attrs:
+ av = ipautil.parse_key_value_pairs(p)
+ pairs.update(av)
+
+ # Get pairs interactively
+ if options.interactive:
+ prompted_attrs = radius_attrs[:]
+ prompted_attrs.remove(distinguished_attr)
+ c = ipautil.AttributeValueCompleter(prompted_attrs, pairs)
+ c.open()
+ av = c.get_pairs("Enter: ", validate_callback=radius_util.validate)
+ pairs.update(av)
+ c.close()
+
+ # FIXME: validation should be moved to xmlrpc server
+
+ # Data collection done, assure mandatory data has been specified
+
+ if pairs.has_key(distinguished_attr) and pairs[distinguished_attr] != ip_addr:
+ print "ERROR, %s specified on command line (%s) does not match value found in pairs (%s)" % \
+ (distinguished_attr, ip_addr, pairs[distinguished_attr])
+ return 1
+
+ # Make sure each attribute is a member of the set of valid attributes
+ valid = True
+ for attr,value in pairs.items():
+ if attr not in radius_attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ return 1
+
+ # Makse sure each value is valid
+ valid = True
+ for attr,value in pairs.items():
+ if not radius_util.validate(attr, value):
+ valid = False
+ if not valid:
+ return 1
+
+ # Dump what we've got so far
+ if options.verbose:
+ print "Pairs:"
+ for attr,value in pairs.items():
+ print "\t%s = %s" % (attr, value)
+
+ for attr,value in pairs.items():
+ radius_entity.setValue(radius_attr_to_ldap_attr[attr], value)
+
+ try:
+ ipa_client.update_radius_client(radius_entity)
+ print "successfully modified"
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-admintools/ipa-radiusprofilemod b/ipa-admintools/ipa-radiusprofilemod
new file mode 100644
index 000000000..405abcfcc
--- /dev/null
+++ b/ipa-admintools/ipa-radiusprofilemod
@@ -0,0 +1,263 @@
+#! /usr/bin/python -E
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import sys
+import os
+from optparse import OptionParser
+from sets import Set
+
+import ipa.ipaclient as ipaclient
+import ipa.ipautil as ipautil
+import ipa.config
+import ipa.ipaerror
+import ipa.radius_util as radius_util
+
+import xmlrpclib
+import kerberos
+import ldap
+
+#------------------------------------------------------------------------------
+
+radius_attrs = radius_util.radius_profile_attr_to_ldap_attr.keys()
+radius_attr_to_ldap_attr = radius_util.radius_profile_attr_to_ldap_attr
+ldap_attr_to_radius_attr = radius_util.radius_profile_ldap_attr_to_radius_attr
+mandatory_radius_attrs = ['UID']
+distinguished_attr = 'UID'
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Valid interative attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ print
+ print "Required attributes are:"
+ print ipautil.format_list(mandatory_radius_attrs, quote='"')
+ sys.exit(0)
+
+def main():
+ opt_parser = OptionParser(add_help_option=False)
+
+ opt_parser.add_option("-u", "--uid", dest="uid",
+ help="RADIUS profile identifier")
+ opt_parser.add_option("-s", "--shared", dest="shared", default=False, action='store_true',
+ help="profile is shared")
+ opt_parser.add_option("-d", "--Description", dest="desc",
+ help="description of the RADIUS client")
+
+ opt_parser.add_option("-D", "--delete-attrs", dest="delete_attrs", action='store_true', default=False,
+ help="delete the specified attributes")
+ opt_parser.add_option("-h", "--help", action="callback", callback=help_option_callback,
+ help="detailed help information")
+ opt_parser.add_option("-i", "--interactive", dest="interactive", action='store_true', default=False,
+ help="interactive mode, prompts with auto-completion")
+ opt_parser.add_option("-A", "--attr", dest="attrs", action='append',
+ help="If adding or modifying then this argument specifies one or more attribute=value pair(s), value may be optionally quoted, pairs are seperated by whitespace. If deleting attributes then this argument specifies one or more attribute names seperated by whitespace or commas")
+ opt_parser.add_option("-f", "--file", dest="data_file",
+ help="If adding or modifying then attribute=value pair(s) are read from file, value may be optionally quoted, pairs are delimited by whitespace. If deleting attributes then attributes are read from file, attributes are seperated by whitespace or commas. Reads from stdin if file is -")
+ opt_parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
+ help="print information")
+
+ opt_parser.set_usage("Usage: %s [options] %s" % (distinguished_attr, os.path.basename(sys.argv[0])))
+
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if len(args) < 2:
+ opt_parser.error('missing %s' % (distinguished_attr))
+
+ uid = args[1]
+ user_profile = not options.shared
+
+ # Verify entity previously exists and get current values
+ ipa_client = ipaclient.IPAClient()
+ try:
+ radius_entity = ipa_client.get_radius_profile_by_uid(uid, user_profile)
+ except ipa.ipaerror.exception_for(ipa.ipaerror.LDAP_NOT_FOUND):
+ print "profile %s not found" % uid
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % e.message
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+
+ # Deleteing attributes is fundamentally different than adding/modifying an attribute.
+ # When adding/modifying there is always a value the attribute is paired with,
+ # so handle the two cases independently.
+ if options.delete_attrs:
+ attrs = Set()
+ # Get attrs from a file or stdin
+ if options.data_file:
+ try:
+ items = ipautil.read_items_file(options.data_file)
+ attrs.update(items)
+ except Exception, e:
+ print "ERROR, could not read attrs (%s)" % (e)
+
+ # Get attrs specified on the command line as a named argument
+ if options.desc is not None: attrs.add('Description')
+
+ # Get attrs specified on the command line as a attr argument
+ if options.attrs:
+ for a in options.attrs:
+ items = ipautil.parse_items(a)
+ attrs.update(items)
+
+ # Get attrs interactively
+ if options.interactive:
+ deletable_attrs = []
+ for radius_attr in radius_attrs:
+ if radius_attr in mandatory_radius_attrs: continue
+ if radius_entity.hasAttr(radius_attr_to_ldap_attr[radius_attr]):
+ deletable_attrs.append(radius_attr)
+
+ if deletable_attrs:
+ c = ipautil.ItemCompleter(deletable_attrs)
+ c.open()
+ items = c.get_items("Enter: ")
+ attrs.update(items)
+ c.close()
+
+ # Data collection done, assure no mandatory attrs are in the delete list
+ valid = True
+ for attr in mandatory_radius_attrs:
+ if attr in attrs:
+ valid = False
+ print "ERROR, %s is mandatory, but is set to be deleted" % (attr)
+ if not valid:
+ return 1
+
+ # Make sure each attribute is a member of the set of valid attributes
+ valid = True
+ for attr in attrs:
+ if attr not in radius_attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ return 1
+
+ # Dump what we've got so far
+ if options.verbose:
+ print "Attributes:"
+ for attr in attrs:
+ print "\t%s" % (attr)
+
+ for attr in attrs:
+ radius_entity.delValue(radius_attr_to_ldap_attr[attr])
+
+ else:
+ pairs = {}
+ pairs[distinguished_attr] = uid
+
+ # Populate the pair list with pre-existing values
+ for attr in radius_attrs:
+ value = radius_entity.getValues(radius_attr_to_ldap_attr[attr])
+ if value is None: continue
+ pairs[attr] = value
+
+ # Get pairs from a file or stdin
+ if options.data_file:
+ try:
+ av = ipautil.read_pairs_file(options.data_file)
+ pairs.update(av)
+ except Exception, e:
+ print "ERROR, could not read pairs (%s)" % (e)
+
+ # Get pairs specified on the command line as a named argument
+ if options.desc is not None: pairs['Description'] = options.desc
+
+ # Get pairs specified on the command line as a pair argument
+ if options.attrs:
+ for p in options.attrs:
+ av = ipautil.parse_key_value_pairs(p)
+ pairs.update(av)
+
+ # Get pairs interactively
+ if options.interactive:
+ prompted_attrs = radius_attrs[:]
+ prompted_attrs.remove(distinguished_attr)
+ c = ipautil.AttributeValueCompleter(prompted_attrs, pairs)
+ c.open()
+ av = c.get_pairs("Enter: ", validate_callback=radius_util.validate)
+ pairs.update(av)
+ c.close()
+
+ # FIXME: validation should be moved to xmlrpc server
+
+ # Data collection done, assure mandatory data has been specified
+
+ if pairs.has_key(distinguished_attr) and pairs[distinguished_attr] != uid:
+ print "ERROR, %s specified on command line (%s) does not match value found in pairs (%s)" % \
+ (distinguished_attr, uid, pairs[distinguished_attr])
+ return 1
+
+ # Make sure each attribute is a member of the set of valid attributes
+ valid = True
+ for attr,value in pairs.items():
+ if attr not in radius_attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(radius_attrs, quote='"')
+ return 1
+
+ # Makse sure each value is valid
+ valid = True
+ for attr,value in pairs.items():
+ if not radius_util.validate(attr, value):
+ valid = False
+ if not valid:
+ return 1
+
+ # Dump what we've got so far
+ if options.verbose:
+ print "Pairs:"
+ for attr,value in pairs.items():
+ print "\t%s = %s" % (attr, value)
+
+ for attr,value in pairs.items():
+ radius_entity.setValue(radius_attr_to_ldap_attr[attr], value)
+
+ try:
+ ipa_client.update_radius_profile(radius_entity)
+ print "successfully modified"
+ except xmlrpclib.Fault, f:
+ print f.faultString
+ return 1
+ except kerberos.GSSError, e:
+ print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
+ return 1
+ except xmlrpclib.ProtocolError, e:
+ print "Unable to connect to IPA server: %s" % (e.errmsg)
+ return 1
+ except ipa.ipaerror.IPAError, e:
+ print "%s" % (e.message)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ipa-python/ipaadminutil.py b/ipa-python/ipaadminutil.py
new file mode 100644
index 000000000..0acc75b04
--- /dev/null
+++ b/ipa-python/ipaadminutil.py
@@ -0,0 +1,75 @@
+# Authors: Rob Crittenden <rcritten@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 or later
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import string
+import tempfile
+import logging
+import subprocess
+import os
+
+def select_user(counter, users):
+ i = 1
+ print "%s entries were found. Which one would you like to display?" % counter
+ for ent in users:
+ print "%s: %s (%s)" % (i, ent.getValues('cn'), ent.getValues('uid'))
+ i += 1
+ while True:
+ resp = raw_input("Choose one: (1 - %s), 0 for all, q to quit: " % counter)
+ if resp == "q":
+ return "q"
+ if resp == "0":
+ userindex = -1
+ break;
+ try:
+ userindex = int(resp) - 1
+ if (userindex >= 0 and userindex <= counter):
+ break;
+ break;
+ except:
+ # fall through to the error msg
+ pass
+
+ print "Please enter a number between 1 and %s" % counter
+
+ return userindex
+
+def select_group(counter, groups):
+ i = 1
+ print "%s entries were found. Which one would you like to display?" % counter
+ for ent in groups:
+ print "%s: %s" % (i, ent.getValues('cn'))
+ i += 1
+ while True:
+ resp = raw_input("Choose one: (1 - %s), 0 for all, q to quit: " % counter)
+ if resp == "q":
+ return "q"
+ if resp == "0":
+ groupindex = -1
+ break;
+ try:
+ groupindex = int(resp) - 1
+ if (groupindex >= 0 and groupindex <= counter):
+ break;
+ except:
+ # fall through to the error msg
+ pass
+
+ print "Please enter a number between 1 and %s" % counter
+
+ return groupindex
diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py
index 426f66817..d815afa7b 100644
--- a/ipa-python/ipaclient.py
+++ b/ipa-python/ipaclient.py
@@ -27,6 +27,7 @@ import user
import group
import ipa
import config
+import radius_util
class IPAClient:
@@ -399,3 +400,68 @@ class IPAClient:
def get_keytab(self, princ_name):
return self.transport.get_keytab(princ_name)
+# radius support
+ def get_radius_client_by_ip_addr(self, ip_addr, container=None, sattrs=None):
+ result = self.transport.get_radius_client_by_ip_addr(ip_addr, container, sattrs)
+ return radius_util.RadiusClient(result)
+
+ def add_radius_client(self, client, container=None):
+ client_dict = client.toDict()
+
+ # dn is set on the server-side
+ del client_dict['dn']
+
+ # convert to a regular dict before sending
+ result = self.transport.add_radius_client(client_dict, container)
+ return result
+
+ def update_radius_client(self, client):
+ result = self.transport.update_radius_client(client.origDataDict(), client.toDict())
+ return result
+
+ def delete_radius_client(self, ip_addr, container=None):
+ return self.transport.delete_radius_client(ip_addr, container)
+
+ def find_radius_clients(self, criteria, container=None, sattrs=None, searchlimit=0, timelimit=-1):
+ result = self.transport.find_radius_clients(criteria, container, sattrs, searchlimit, timelimit)
+ counter = result[0]
+
+ users = [counter]
+ for attrs in result[1:]:
+ if attrs is not None:
+ users.append(user.User(attrs))
+
+ return users
+
+ def get_radius_profile_by_uid(self, uid, user_profile=None, sattrs=None):
+ result = self.transport.get_radius_profile_by_uid(uid, user_profile, sattrs)
+ return radius_util.RadiusClient(result)
+
+ def add_radius_profile(self, profile, user_profile=None):
+ profile_dict = profile.toDict()
+
+ # dn is set on the server-side
+ del profile_dict['dn']
+
+ # convert to a regular dict before sending
+ result = self.transport.add_radius_profile(profile_dict, user_profile)
+ return result
+
+ def update_radius_profile(self, profile):
+ result = self.transport.update_radius_profile(profile.origDataDict(), profile.toDict())
+ return result
+
+ def delete_radius_profile(self, ip_addr, user_profile=None):
+ return self.transport.delete_radius_profile(ip_addr, user_profile)
+
+ def find_radius_profiles(self, criteria, user_profile=None, sattrs=None, searchlimit=0, timelimit=-1):
+ result = self.transport.find_radius_profiles(criteria, user_profile, sattrs, searchlimit, timelimit)
+ counter = result[0]
+
+ users = [counter]
+ for attrs in result[1:]:
+ if attrs is not None:
+ users.append(user.User(attrs))
+
+ return users
+
diff --git a/ipa-python/ipautil.py b/ipa-python/ipautil.py
index 2dc9b0c9d..3c2b37f73 100644
--- a/ipa-python/ipautil.py
+++ b/ipa-python/ipautil.py
@@ -25,8 +25,11 @@ import logging
import subprocess
from random import Random
from time import gmtime
-import os
+import os, sys, traceback, readline
import stat
+import shutil
+
+from types import *
import re
import xmlrpclib
@@ -99,6 +102,15 @@ def dir_exists(filename):
except:
return False
+def install_file(fname, dest):
+ if file_exists(dest):
+ os.rename(dest, dest + ".orig")
+ shutil.move(fname, dest)
+
+def backup_file(fname):
+ if file_exists(fname):
+ os.rename(fname, fname + ".orig")
+
class CIDict(dict):
"""
Case-insensitive but case-respecting dictionary.
@@ -347,7 +359,6 @@ def parse_generalized_time(timestr):
except ValueError:
return None
-
def ipa_generate_password():
rndpwd = ''
r = Random()
@@ -357,3 +368,462 @@ def ipa_generate_password():
rndpwd += chr(r.randint(65,90)) #stricter set for testing
return rndpwd
+
+def format_list(items, quote=None, page_width=80):
+ '''Format a list of items formatting them so they wrap to fit the
+ available width. The items will be sorted.
+
+ The items may optionally be quoted. The quote parameter may either be
+ a string, in which case it is added before and after the item. Or the
+ quote parameter may be a pair (either a tuple or list). In this case
+ quote[0] is left hand quote and quote[1] is the right hand quote.
+ '''
+ left_quote = right_quote = ''
+ num_items = len(items)
+ if not num_items: return ""
+
+ if quote is not None:
+ if type(quote) in StringTypes:
+ left_quote = right_quote = quote
+ elif type(quote) is TupleType or type(quote) is ListType:
+ left_quote = quote[0]
+ right_quote = quote[1]
+
+ max_len = max(map(len, items))
+ max_len += len(left_quote) + len(right_quote)
+ num_columns = (page_width + max_len) / (max_len+1)
+ num_rows = (num_items + num_columns - 1) / num_columns
+ items.sort()
+
+ rows = [''] * num_rows
+ i = row = col = 0
+
+ while i < num_items:
+ row = 0
+ if col == 0:
+ separator = ''
+ else:
+ separator = ' '
+
+ while i < num_items and row < num_rows:
+ rows[row] += "%s%*s" % (separator, -max_len, "%s%s%s" % (left_quote, items[i], right_quote))
+ i += 1
+ row += 1
+ col += 1
+ return '\n'.join(rows)
+
+key_value_re = re.compile("(\w+)\s*=\s*(([^\s'\\\"]+)|(?P<quote>['\\\"])((?P=quote)|(.*?[^\\\])(?P=quote)))")
+def parse_key_value_pairs(input):
+ ''' Given a string composed of key=value pairs parse it and return
+ a dict of the key/value pairs. Keys must be a word, a key must be followed
+ by an equal sign (=) and a value. The value may be a single word or may be
+ quoted. Quotes may be either single or double quotes, but must be balanced.
+ Inside the quoted text the same quote used to start the quoted value may be
+ used if it is escaped by preceding it with a backslash (\).
+ White space between the key, the equal sign, and the value is ignored.
+ Values are always strings. Empty values must be specified with an empty
+ quoted string, it's value after parsing will be an empty string.
+
+ Example: The string
+
+ arg0 = '' arg1 = 1 arg2='two' arg3 = "three's a crowd" arg4 = "this is a \" quote"
+
+ will produce
+
+ arg0= arg1=1
+ arg2=two
+ arg3=three's a crowd
+ arg4=this is a " quote
+ '''
+
+ kv_dict = {}
+ for match in key_value_re.finditer(input):
+ key = match.group(1)
+ quote = match.group('quote')
+ if match.group(5):
+ value = match.group(6)
+ if value is None: value = ''
+ value = re.sub('\\\%s' % quote, quote, value)
+ else:
+ value = match.group(2)
+ kv_dict[key] = value
+ return kv_dict
+
+def parse_items(text):
+ '''Given text with items separated by whitespace or comma, return a list of those items'''
+ split_re = re.compile('[ ,\t\n]+')
+ items = split_re.split(text)
+ for item in items[:]:
+ if not item: items.remove(item)
+ return items
+
+def read_pairs_file(filename):
+ comment_re = re.compile('#.*$', re.MULTILINE)
+ if filename == '-':
+ fd = sys.stdin
+ else:
+ fd = open(filename)
+ text = fd.read()
+ text = comment_re.sub('', text) # kill comments
+ pairs = parse_key_value_pairs(text)
+ if fd != sys.stdin: fd.close()
+ return pairs
+
+def read_items_file(filename):
+ comment_re = re.compile('#.*$', re.MULTILINE)
+ if filename == '-':
+ fd = sys.stdin
+ else:
+ fd = open(filename)
+ text = fd.read()
+ text = comment_re.sub('', text) # kill comments
+ items = parse_items(text)
+ if fd != sys.stdin: fd.close()
+ return items
+
+
+class AttributeValueCompleter:
+ '''
+ Gets input from the user in the form "lhs operator rhs"
+ TAB completes partial input.
+ lhs completes to a name in @lhs_names
+ The lhs is fully parsed if a lhs_delim delimiter is seen, then TAB will
+ complete to the operator and a default value.
+ Default values for a lhs value can specified as:
+ - a string, all lhs values will use this default
+ - a dict, the lhs value is looked up in the dict to return the default or None
+ - a function with a single arg, the lhs value, it returns the default or None
+
+ After creating the completer you must open it to set the terminal
+ up, Then get a line of input from the user by calling read_input()
+ which returns two values, the lhs and rhs, which might be None if
+ lhs or rhs was not parsed. After you are done getting input you
+ should close the completer to restore the terminal.
+
+ Example: (note this is essentially what the convenience function get_pairs() does)
+
+ This will allow the user to autocomplete foo & foobar, both have
+ defaults defined in a dict. In addition the foobar attribute must
+ be specified before the prompting loop will exit. Also, this
+ example show how to require that each attrbute entered by the user
+ is valid.
+
+ attrs = ['foo', 'foobar']
+ defaults = {'foo' : 'foo_default', 'foobar' : 'foobar_default'}
+ mandatory_attrs = ['foobar']
+
+ c = AttributeValueCompleter(attrs, defaults)
+ c.open()
+ mandatory_attrs_remaining = mandatory_attrs[:]
+
+ while True:
+ if mandatory_attrs_remaining:
+ attribute, value = c.read_input("Enter: ", mandatory_attrs_remaining[0])
+ try:
+ mandatory_attrs_remaining.remove(attribute)
+ except ValueError:
+ pass
+ else:
+ attribute, value = c.read_input("Enter: ")
+ if attribute is None:
+ # Are we done?
+ if mandatory_attrs_remaining:
+ print "ERROR, you must specify: %s" % (','.join(mandatory_attrs_remaining))
+ continue
+ else:
+ break
+ if attribute not in attrs:
+ print "ERROR: %s is not a valid attribute" % (attribute)
+ else:
+ print "got '%s' = '%s'" % (attribute, value)
+
+ c.close()
+ print "exiting..."
+ '''
+
+ def __init__(self, lhs_names, default_value=None, lhs_regexp=r'^\s*(?P<lhs>[^ =]+)', lhs_delims=' =',
+ operator='=', strip_rhs=True):
+ self.lhs_names = lhs_names
+ self.default_value = default_value
+ # lhs_regexp must have named group 'lhs' which returns the contents of the lhs
+ self.lhs_regexp = lhs_regexp
+ self.lhs_re = re.compile(self.lhs_regexp)
+ self.lhs_delims = lhs_delims
+ self.operator = operator
+ self.strip_rhs = strip_rhs
+ self.pairs = None
+ self._reset()
+
+ def _reset(self):
+ self.lhs = None
+ self.lhs_complete = False
+ self.operator_complete = False
+ self.rhs = None
+
+ def open(self):
+ # Save state
+ self.prev_completer = readline.get_completer()
+ self.prev_completer_delims = readline.get_completer_delims()
+
+ # Set up for ourself
+ readline.parse_and_bind("tab: complete")
+ readline.set_completer(self.complete)
+ readline.set_completer_delims(self.lhs_delims)
+
+ def close(self):
+ # Restore previous state
+ readline.set_completer_delims(self.prev_completer_delims)
+ readline.set_completer(self.prev_completer)
+
+ def parse_input(self):
+ '''We are looking for 3 tokens: <lhs,op,rhs>
+ Extract as much of each token as possible.
+ Set flags indicating if token is fully parsed.
+ '''
+ try:
+ self._reset()
+ buf_len = len(self.line_buffer)
+ pos = 0
+ lhs_match = self.lhs_re.search(self.line_buffer, pos)
+ if not lhs_match: return # no lhs content
+ self.lhs = lhs_match.group('lhs') # get lhs contents
+ pos = lhs_match.end('lhs') # new scanning position
+ if pos == buf_len: return # nothing after lhs, lhs incomplete
+ self.lhs_complete = True # something trails the lhs, lhs is complete
+ operator_beg = self.line_buffer.find(self.operator, pos) # locate operator
+ if operator_beg == -1: return # did not find the operator
+ self.operator_complete = True # operator fully parsed
+ operator_end = operator_beg + len(self.operator)
+ pos = operator_end # step over the operator
+ self.rhs = self.line_buffer[pos:]
+ except Exception, e:
+ traceback.print_exc()
+ print "Exception in %s.parse_input(): %s" % (self.__class__.__name__, e)
+
+ def get_default_value(self):
+ '''default_value can be a string, a dict, or a function.
+ If it's a string it's a global default for all attributes.
+ If it's a dict the default is looked up in the dict index by attribute.
+ If it's a function, the function is called with 1 parameter, the attribute
+ and it should return the default value for the attriubte or None'''
+
+ if not self.lhs_complete: raise ValueError("attribute not parsed")
+
+ # If the user previously provided a value let that override the supplied default
+ if self.pairs is not None:
+ prev_value = self.pairs.get(self.lhs)
+ if prev_value is not None: return prev_value
+
+ # No previous user provided value, query for a default
+ default_value_type = type(self.default_value)
+ if default_value_type is DictType:
+ return self.default_value.get(self.lhs, None)
+ elif default_value_type is FunctionType:
+ return self.default_value(self.lhs)
+ elif default_value_type is StringsType:
+ return self.default_value
+ else:
+ return None
+
+ def get_lhs_completions(self, text):
+ if text:
+ self.completions = [lhs for lhs in self.lhs_names if lhs.startswith(text)]
+ else:
+ self.completions = self.lhs_names
+
+ def complete(self, text, state):
+ self.line_buffer= readline.get_line_buffer()
+ self.parse_input()
+ if not self.lhs_complete:
+ # lhs is not complete, set up to complete the lhs
+ if state == 0:
+ beg = readline.get_begidx()
+ end = readline.get_endidx()
+ self.get_lhs_completions(self.line_buffer[beg:end])
+ if state >= len(self.completions): return None
+ return self.completions[state]
+
+
+ elif not self.operator_complete:
+ # lhs is complete, but the operator is not so we complete
+ # by inserting the operator manually.
+ # Also try to complete the default value at this time.
+ readline.insert_text('%s ' % self.operator)
+ default_value = self.get_default_value()
+ if default_value is not None:
+ readline.insert_text(default_value)
+ readline.redisplay()
+ return None
+ else:
+ # lhs and operator are complete, if the the rhs is blank
+ # (either empty or only only whitespace) then attempt
+ # to complete by inserting the default value, otherwise
+ # there is nothing we can complete to so we're done.
+ if self.rhs.strip():
+ return None
+ default_value = self.get_default_value()
+ if default_value is not None:
+ readline.insert_text(default_value)
+ readline.redisplay()
+ return None
+
+ def pre_input_hook(self):
+ readline.insert_text('%s %s ' % (self.initial_lhs, self.operator))
+ readline.redisplay()
+
+ def read_input(self, prompt, initial_lhs=None):
+ self.initial_lhs = initial_lhs
+ try:
+ self._reset()
+ if initial_lhs is None:
+ readline.set_pre_input_hook(None)
+ else:
+ readline.set_pre_input_hook(self.pre_input_hook)
+ self.line_buffer = raw_input(prompt).strip()
+ self.parse_input()
+ if self.strip_rhs and self.rhs is not None:
+ return self.lhs, self.rhs.strip()
+ else:
+ return self.lhs, self.rhs
+ except EOFError:
+ return None, None
+
+ def get_pairs(self, prompt, mandatory_attrs=None, validate_callback=None, must_match=True, value_required=True):
+ self.pairs = {}
+ if mandatory_attrs:
+ mandatory_attrs_remaining = mandatory_attrs[:]
+ else:
+ mandatory_attrs_remaining = []
+
+ print "Enter name = value"
+ print "Press <ENTER> to accept, a blank line terminates input"
+ print "Pressing <TAB> will auto completes name, assignment, and value"
+ print
+ while True:
+ if mandatory_attrs_remaining:
+ attribute, value = self.read_input(prompt, mandatory_attrs_remaining[0])
+ else:
+ attribute, value = self.read_input(prompt)
+ if attribute is None:
+ # Are we done?
+ if mandatory_attrs_remaining:
+ print "ERROR, you must specify: %s" % (','.join(mandatory_attrs_remaining))
+ continue
+ else:
+ break
+ if value is None:
+ if value_required:
+ print "ERROR: you must specify a value for %s" % attribute
+ continue
+ else:
+ if must_match and attribute not in self.lhs_names:
+ print "ERROR: %s is not a valid name" % (attribute)
+ continue
+ if validate_callback is not None:
+ if not validate_callback(attribute, value):
+ print "ERROR: %s is not valid for %s" % (value, attribute)
+ continue
+ try:
+ mandatory_attrs_remaining.remove(attribute)
+ except ValueError:
+ pass
+
+ self.pairs[attribute] = value
+ return self.pairs
+
+class ItemCompleter:
+ '''
+ Prompts the user for items in a list of items with auto completion.
+ TAB completes partial input.
+ More than one item can be specifed during input, whitespace and/or comma's seperate.
+ Example:
+
+ possible_items = ['foo', 'bar']
+ c = ItemCompleter(possible_items)
+ c.open()
+ # Use read_input() to limit input to a single carriage return (e.g. <ENTER>)
+ #items = c.read_input("Enter: ")
+ # Use get_items to iterate until a blank line is entered.
+ items = c.get_items("Enter: ")
+ c.close()
+ print "items=%s" % (items)
+
+ '''
+
+ def __init__(self, items):
+ self.items = items
+ self.initial_input = None
+ self.item_delims = ' \t,'
+ self.split_re = re.compile('[%s]+' % self.item_delims)
+
+ def open(self):
+ # Save state
+ self.prev_completer = readline.get_completer()
+ self.prev_completer_delims = readline.get_completer_delims()
+
+ # Set up for ourself
+ readline.parse_and_bind("tab: complete")
+ readline.set_completer(self.complete)
+ readline.set_completer_delims(self.item_delims)
+
+ def close(self):
+ # Restore previous state
+ readline.set_completer_delims(self.prev_completer_delims)
+ readline.set_completer(self.prev_completer)
+
+ def get_item_completions(self, text):
+ if text:
+ self.completions = [lhs for lhs in self.items if lhs.startswith(text)]
+ else:
+ self.completions = self.items
+
+ def complete(self, text, state):
+ self.line_buffer= readline.get_line_buffer()
+ if state == 0:
+ beg = readline.get_begidx()
+ end = readline.get_endidx()
+ self.get_item_completions(self.line_buffer[beg:end])
+ if state >= len(self.completions): return None
+ return self.completions[state]
+
+ def pre_input_hook(self):
+ readline.insert_text('%s %s ' % (self.initial_input, self.operator))
+ readline.redisplay()
+
+ def read_input(self, prompt, initial_input=None):
+ items = []
+
+ self.initial_input = initial_input
+ try:
+ if initial_input is None:
+ readline.set_pre_input_hook(None)
+ else:
+ readline.set_pre_input_hook(self.pre_input_hook)
+ self.line_buffer = raw_input(prompt).strip()
+ items = self.split_re.split(self.line_buffer)
+ for item in items[:]:
+ if not item: items.remove(item)
+ return items
+ except EOFError:
+ return items
+
+ def get_items(self, prompt, must_match=True):
+ items = []
+
+ print "Enter name [name ...]"
+ print "Press <ENTER> to accept, blank line or control-D terminates input"
+ print "Pressing <TAB> auto completes name"
+ print
+ while True:
+ new_items = self.read_input(prompt)
+ if not new_items: break
+ for item in new_items:
+ if must_match:
+ if item not in self.items:
+ print "ERROR: %s is not valid" % (item)
+ continue
+ if item in items: continue
+ items.append(item)
+
+ return items
+
diff --git a/ipa-python/ipavalidate.py b/ipa-python/ipavalidate.py
index 1750b8397..36d94e02d 100644
--- a/ipa-python/ipavalidate.py
+++ b/ipa-python/ipavalidate.py
@@ -106,3 +106,4 @@ def Path(text, notEmpty=False):
return 1
return 0
+
diff --git a/ipa-python/radius_util.py b/ipa-python/radius_util.py
new file mode 100644
index 000000000..1f6e7902b
--- /dev/null
+++ b/ipa-python/radius_util.py
@@ -0,0 +1,366 @@
+# Authors: John Dennis <jdennis@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import sys
+import os
+import re
+import ldap
+import getpass
+import ldap.filter
+
+from ipa import ipautil
+from ipa.entity import Entity
+import ipa.ipavalidate as ipavalidate
+
+
+__all__ = [
+ 'RADIUS_PKG_NAME',
+ 'RADIUS_PKG_CONFIG_DIR',
+ 'RADIUS_SERVICE_NAME',
+ 'RADIUS_USER',
+ 'RADIUS_IPA_KEYTAB_FILEPATH',
+ 'RADIUS_LDAP_ATTR_MAP_FILEPATH',
+ 'RADIUSD_CONF_FILEPATH',
+ 'RADIUSD_CONF_TEMPLATE_FILEPATH',
+ 'RADIUSD',
+
+ 'RadiusClient',
+ 'RadiusProfile',
+
+ 'clients_container',
+ 'radius_clients_basedn',
+ 'radius_client_filter',
+ 'radius_client_dn',
+
+ 'profiles_container',
+ 'radius_profiles_basedn',
+ 'radius_profile_filter',
+ 'radius_profile_dn',
+
+ 'radius_client_ldap_attr_to_radius_attr',
+ 'radius_client_attr_to_ldap_attr',
+
+ 'radius_profile_ldap_attr_to_radius_attr',
+ 'radius_profile_attr_to_ldap_attr',
+
+ 'get_secret',
+ 'validate_ip_addr',
+ 'validate_secret',
+ 'validate_name',
+ 'validate_nastype',
+ 'validate_desc',
+ 'validate',
+ ]
+
+#------------------------------------------------------------------------------
+
+RADIUS_PKG_NAME = 'freeradius'
+RADIUS_PKG_CONFIG_DIR = '/etc/raddb'
+
+RADIUS_SERVICE_NAME = 'radius'
+RADIUS_USER = 'radiusd'
+
+RADIUS_IPA_KEYTAB_FILEPATH = os.path.join(RADIUS_PKG_CONFIG_DIR, 'ipa.keytab')
+RADIUS_LDAP_ATTR_MAP_FILEPATH = os.path.join(RADIUS_PKG_CONFIG_DIR, 'ldap.attrmap')
+RADIUSD_CONF_FILEPATH = os.path.join(RADIUS_PKG_CONFIG_DIR, 'radiusd.conf')
+RADIUSD_CONF_TEMPLATE_FILEPATH = os.path.join(ipautil.SHARE_DIR, 'radius.radiusd.conf.template')
+
+RADIUSD = '/usr/sbin/radiusd'
+
+#------------------------------------------------------------------------------
+
+dotted_octet_re = re.compile(r"^(\d+)\.(\d+)\.(\d+)\.(\d+)(/(\d+))?$")
+dns_re = re.compile(r"^[a-zA-Z][a-zA-Z0-9.-]+$")
+# secret, name, nastype all have 31 char max in freeRADIUS, max ip address len is 255
+valid_secret_len = (1,31)
+valid_name_len = (1,31)
+valid_nastype_len = (1,31)
+valid_ip_addr_len = (1,255)
+
+valid_ip_addr_msg = '''\
+IP address must be either a DNS name (letters,digits,dot,hyphen, beginning with
+a letter),or a dotted octet followed by an optional mask (e.g 192.168.1.0/24)'''
+
+valid_desc_msg = "Description must text string"
+
+#------------------------------------------------------------------------------
+
+class RadiusClient(Entity):
+
+ def __init2__(self):
+ pass
+
+class RadiusProfile(Entity):
+
+ def __init2__(self):
+ pass
+
+
+#------------------------------------------------------------------------------
+
+def reverse_map_dict(src_dict):
+ reverse_dict = {}
+
+ for k,v in src_dict.items():
+ if reverse_dict.has_key(v):
+ raise ValueError("reverse_map_dict: collision on (%s) with values (%s),(%s)" % \
+ v, reverse_dict[v], src_dict[k])
+ reverse_dict[v] = k
+ return reverse_dict
+
+#------------------------------------------------------------------------------
+
+radius_client_ldap_attr_to_radius_attr = ipautil.CIDict({
+ 'radiusClientIPAddress' : 'Client-IP-Address',
+ 'radiusClientSecret' : 'Secret',
+ 'radiusClientNASType' : 'NAS-Type',
+ 'radiusClientShortName' : 'Name',
+ 'description' : 'Description',
+ })
+
+radius_client_attr_to_ldap_attr = reverse_map_dict(radius_client_ldap_attr_to_radius_attr)
+
+#------------------------------------------------------------------------------
+
+radius_profile_ldap_attr_to_radius_attr = ipautil.CIDict({
+ 'uid' : 'UID',
+ 'radiusArapFeatures' : 'Arap-Features',
+ 'radiusArapSecurity' : 'Arap-Security',
+ 'radiusArapZoneAccess' : 'Arap-Zone-Access',
+ 'radiusAuthType' : 'Auth-Type',
+ 'radiusCallbackId' : 'Callback-Id',
+ 'radiusCallbackNumber' : 'Callback-Number',
+ 'radiusCalledStationId' : 'Called-Station-Id',
+ 'radiusCallingStationId' : 'Calling-Station-Id',
+ 'radiusClass' : 'Class',
+ 'radiusClientIPAddress' : 'Client-IP-Address',
+ 'radiusExpiration' : 'Expiration',
+ 'radiusFilterId' : 'Filter-Id',
+ 'radiusFramedAppleTalkLink' : 'Framed-AppleTalk-Link',
+ 'radiusFramedAppleTalkNetwork' : 'Framed-AppleTalk-Network',
+ 'radiusFramedAppleTalkZone' : 'Framed-AppleTalk-Zone',
+ 'radiusFramedCompression' : 'Framed-Compression',
+ 'radiusFramedIPAddress' : 'Framed-IP-Address',
+ 'radiusFramedIPNetmask' : 'Framed-IP-Netmask',
+ 'radiusFramedIPXNetwork' : 'Framed-IPX-Network',
+ 'radiusFramedMTU' : 'Framed-MTU',
+ 'radiusFramedProtocol' : 'Framed-Protocol',
+ 'radiusFramedRoute' : 'Framed-Route',
+ 'radiusFramedRouting' : 'Framed-Routing',
+ 'radiusGroupName' : 'Group-Name',
+ 'radiusHint' : 'Hint',
+ 'radiusHuntgroupName' : 'Huntgroup-Name',
+ 'radiusIdleTimeout' : 'Idle-Timeout',
+ 'radiusLoginIPHost' : 'Login-IP-Host',
+ 'radiusLoginLATGroup' : 'Login-LAT-Group',
+ 'radiusLoginLATNode' : 'Login-LAT-Node',
+ 'radiusLoginLATPort' : 'Login-LAT-Port',
+ 'radiusLoginLATService' : 'Login-LAT-Service',
+ 'radiusLoginService' : 'Login-Service',
+ 'radiusLoginTCPPort' : 'Login-TCP-Port',
+ 'radiusLoginTime' : 'Login-Time',
+ 'radiusNASIpAddress' : 'NAS-IP-Address',
+ 'radiusPasswordRetry' : 'Password-Retry',
+ 'radiusPortLimit' : 'Port-Limit',
+ 'radiusProfileDn' : 'Profile-Dn',
+ 'radiusPrompt' : 'Prompt',
+ 'radiusProxyToRealm' : 'Proxy-To-Realm',
+ 'radiusRealm' : 'Realm',
+ 'radiusReplicateToRealm' : 'Replicate-To-Realm',
+ 'radiusReplyMessage' : 'Reply-Message',
+ 'radiusServiceType' : 'Service-Type',
+ 'radiusSessionTimeout' : 'Session-Timeout',
+ 'radiusSimultaneousUse' : 'Simultaneous-Use',
+ 'radiusStripUserName' : 'Strip-User-Name',
+ 'radiusTerminationAction' : 'Termination-Action',
+ 'radiusTunnelAssignmentId' : 'Tunnel-Assignment-Id',
+ 'radiusTunnelClientEndpoint' : 'Tunnel-Client-Endpoint',
+ 'radiusTunnelMediumType' : 'Tunnel-Medium-Type',
+ 'radiusTunnelPassword' : 'Tunnel-Password',
+ 'radiusTunnelPreference' : 'Tunnel-Preference',
+ 'radiusTunnelPrivateGroupId' : 'Tunnel-Private-Group-Id',
+ 'radiusTunnelServerEndpoint' : 'Tunnel-Server-Endpoint',
+ 'radiusTunnelType' : 'Tunnel-Type',
+ 'radiusUserCategory' : 'User-Category',
+ 'radiusVSA' : 'VSA',
+})
+
+radius_profile_attr_to_ldap_attr = reverse_map_dict(radius_profile_ldap_attr_to_radius_attr)
+
+#------------------------------------------------------------------------------
+
+clients_container = 'cn=clients,cn=radius'
+
+def radius_clients_basedn(container, suffix):
+ if container is None: container = clients_container
+ return '%s,%s' % (container, suffix)
+
+def radius_client_filter(ip_addr):
+ return "(&(radiusClientIPAddress=%s)(objectclass=radiusClientProfile))" % \
+ ldap.filter.escape_filter_chars(ip_addr)
+
+def radius_client_dn(client, container, suffix):
+ if container is None: container = clients_container
+ return 'radiusClientIPAddress=%s,%s,%s' % (ldap.dn.escape_dn_chars(client), container, suffix)
+
+# --
+
+profiles_container = 'cn=profiles,cn=radius'
+
+def radius_profiles_basedn(container, suffix):
+ if container is None: container = profiles_container
+ return '%s,%s' % (container, suffix)
+
+def radius_profile_filter(uid):
+ return "(&(uid=%s)(objectclass=radiusprofile))" % \
+ ldap.filter.escape_filter_chars(uid)
+
+def radius_profile_dn(uid, container, suffix):
+ if container is None: container = profiles_container
+ return 'uid=%s,%s,%s' % (ldap.dn.escape_dn_chars(uid), container, suffix)
+
+
+#------------------------------------------------------------------------------
+
+def get_ldap_attr_translations():
+ comment_re = re.compile('#.*$')
+ radius_attr_to_ldap_attr = {}
+ ldap_attr_to_radius_attr = {}
+ try:
+ f = open(LDAP_ATTR_MAP_FILEPATH)
+ for line in f.readlines():
+ line = comment_re.sub('', line).strip()
+ if not line: continue
+ attr_type, radius_attr, ldap_attr = line.split()
+ print 'type="%s" radius="%s" ldap="%s"' % (attr_type, radius_attr, ldap_attr)
+ radius_attr_to_ldap_attr[radius_attr] = {'ldap_attr':ldap_attr, 'attr_type':attr_type}
+ ldap_attr_to_radius_attr[ldap_attr] = {'radius_attr':radius_attr, 'attr_type':attr_type}
+ f.close()
+ except Exception, e:
+ logging.error('cold not read radius ldap attribute map file (%s): %s', LDAP_ATTR_MAP_FILEPATH, e)
+ pass # FIXME
+
+ #for k,v in radius_attr_to_ldap_attr.items():
+ # print '%s --> %s' % (k,v)
+ #for k,v in ldap_attr_to_radius_attr.items():
+ # print '%s --> %s' % (k,v)
+
+def get_secret():
+ valid = False
+ while (not valid):
+ secret = getpass.getpass("Enter Secret: ")
+ confirm = getpass.getpass("Confirm Secret: ")
+ if (secret != confirm):
+ print "Secrets do not match"
+ continue
+ valid = True
+ return secret
+
+#------------------------------------------------------------------------------
+
+def valid_ip_addr(text):
+
+ # is it a dotted octet? If so there should be 4 integers seperated
+ # by a dot and each integer should be between 0 and 255
+ # there may be an optional mask preceded by a slash (e.g. 1.2.3.4/24)
+ match = dotted_octet_re.search(text)
+ if match:
+ # dotted octet notation
+ i = 1
+ while i <= 4:
+ octet = int(match.group(i))
+ if octet > 255: return False
+ i += 1
+ if match.group(5):
+ mask = int(match.group(6))
+ if mask <= 32:
+ return True
+ else:
+ return False
+ return True
+ else:
+ # DNS name, can contain letters, numbers, dot and hypen, must start with a letter
+ if dns_re.search(text): return True
+ return False
+
+def validate_length(value, limits):
+ length = len(value)
+ if length < limits[0] or length > limits[1]:
+ return False
+ return True
+
+def valid_length_msg(name, limits):
+ return "%s length must be at least %d and not more than %d" % (name, limits[0], limits[1])
+
+def err_msg(variable, variable_name=None):
+ if variable_name is None: variable_name = 'value'
+ print "ERROR: %s = %s" % (variable_name, variable)
+
+#------------------------------------------------------------------------------
+
+def validate_ip_addr(ip_addr, variable_name=None):
+ if not validate_length(ip_addr, valid_ip_addr_len):
+ err_msg(ip_addr, variable_name)
+ print valid_length_msg('ip address', valid_ip_addr_len)
+ return False
+ if not valid_ip_addr(ip_addr):
+ err_msg(ip_addr, variable_name)
+ print valid_ip_addr_msg
+ return False
+ return True
+
+def validate_secret(secret, variable_name=None):
+ if not validate_length(secret, valid_secret_len):
+ err_msg(secret, variable_name)
+ print valid_length_msg('secret', valid_secret_len)
+ return False
+ return True
+
+def validate_name(name, variable_name=None):
+ if not validate_length(name, valid_name_len):
+ err_msg(name, variable_name)
+ print valid_length_msg('name', valid_name_len)
+ return False
+ return True
+
+def validate_nastype(nastype, variable_name=None):
+ if not validate_length(nastype, valid_nastype_len):
+ err_msg(nastype, variable_name)
+ print valid_length_msg('NAS Type', valid_nastype_len)
+ return False
+ return True
+
+def validate_desc(desc, variable_name=None):
+ if ipavalidate.plain(desc, notEmpty=True) != 0:
+ print valid_desc_msg
+ return False
+ return True
+
+def validate(attribute, value):
+ if attribute == 'Client-IP-Address':
+ return validate_ip_addr(value, attribute)
+ if attribute == 'Secret':
+ return validate_secret(value, attribute)
+ if attribute == 'NAS-Type':
+ return validate_nastype(value, attribute)
+ if attribute == 'Name':
+ return validate_name(value, attribute)
+ if attribute == 'Description':
+ return validate_desc(value, attribute)
+ return True
diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py
index de32e9beb..5656b99d9 100644
--- a/ipa-python/rpcclient.py
+++ b/ipa-python/rpcclient.py
@@ -732,3 +732,144 @@ class RPCClient:
raise xmlrpclib.Fault(value, msg)
return ipautil.unwrap_binary_data(result)
+
+# radius support
+
+ def get_radius_client_by_ip_addr(self, ip_addr, container, sattrs=None):
+ server = self.setup_server()
+ if container is None: container = "__NONE__"
+ if sattrs is None: sattrs = "__NONE__"
+ try:
+ result = server.get_radius_client_by_ip_addr(ip_addr, container, sattrs)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def add_radius_client(self, client, container=None):
+ server = self.setup_server()
+
+ if container is None: container = "__NONE__"
+
+ try:
+ result = server.add_radius_client(ipautil.wrap_binary_data(client), container)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def update_radius_client(self, oldclient, newclient):
+ server = self.setup_server()
+
+ try:
+ result = server.update_radius_client(ipautil.wrap_binary_data(oldclient),
+ ipautil.wrap_binary_data(newclient))
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+
+ def delete_radius_client(self, ip_addr, container=None):
+ server = self.setup_server()
+ if container is None: container = "__NONE__"
+
+ try:
+ result = server.delete_radius_client(ip_addr, container)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def find_radius_clients(self, criteria, container=None, sattrs=None, searchlimit=0, timelimit=-1):
+ server = self.setup_server()
+ if container is None: container = "__NONE__"
+ try:
+ # None values are not allowed in XML-RPC
+ if sattrs is None:
+ sattrs = "__NONE__"
+ result = server.find_radius_clients(criteria, container, sattrs, searchlimit, timelimit)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def get_radius_profile_by_uid(self, ip_addr, user_profile, sattrs=None):
+ server = self.setup_server()
+ if user_profile is None: user_profile = "__NONE__"
+ if sattrs is None: sattrs = "__NONE__"
+ try:
+ result = server.get_radius_profile_by_uid(ip_addr, user_profile, sattrs)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def add_radius_profile(self, profile, user_profile=None):
+ server = self.setup_server()
+
+ if user_profile is None: user_profile = "__NONE__"
+
+ try:
+ result = server.add_radius_profile(ipautil.wrap_binary_data(profile), user_profile)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def update_radius_profile(self, oldprofile, newprofile):
+ server = self.setup_server()
+
+ try:
+ result = server.update_radius_profile(ipautil.wrap_binary_data(oldprofile),
+ ipautil.wrap_binary_data(newprofile))
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+
+ def delete_radius_profile(self, ip_addr, user_profile=None):
+ server = self.setup_server()
+ if user_profile is None: user_profile = "__NONE__"
+
+ try:
+ result = server.delete_radius_profile(ip_addr, user_profile)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
+ def find_radius_profiles(self, criteria, user_profile=None, sattrs=None, searchlimit=0, timelimit=-1):
+ server = self.setup_server()
+ if user_profile is None: user_profile = "__NONE__"
+ try:
+ # None values are not allowed in XML-RPC
+ if sattrs is None:
+ sattrs = "__NONE__"
+ result = server.find_radius_profiles(criteria, user_profile, sattrs, searchlimit, timelimit)
+ except xmlrpclib.Fault, fault:
+ raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return ipautil.unwrap_binary_data(result)
+
diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py
index b0c4d0aa7..74369a6ae 100644
--- a/ipa-server/ipa-gui/ipagui/forms/user.py
+++ b/ipa-server/ipa-gui/ipagui/forms/user.py
@@ -3,8 +3,8 @@ from turbogears import validators, widgets
from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm
class UserFields(object):
- givenname = widgets.TextField(name="givenname", label="Given Name")
- sn = widgets.TextField(name="sn", label="Family Name")
+ givenname = widgets.TextField(name="givenname", label="First Name")
+ sn = widgets.TextField(name="sn", label="Last Name")
cn = widgets.TextField(name="cn", label="Common Names")
cns = ExpandingForm(name="cns", label="Common Names", fields=[cn])
title = widgets.TextField(name="title", label="Title")
diff --git a/ipa-server/ipa-gui/ipagui/templates/principallist.kid b/ipa-server/ipa-gui/ipagui/templates/principallist.kid
index dcd9dd4b8..d4177d8d7 100644
--- a/ipa-server/ipa-gui/ipagui/templates/principallist.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/principallist.kid
@@ -16,6 +16,14 @@
<script type="text/javascript">
document.getElementById("hostname").focus();
</script>
+ <script type="text/javascript">
+ function confirmDownload() {
+ if (confirm("Are you sure you want to download this principal? It will reset the secret, invalidating any existing keytabs")) {
+ return true;
+ }
+ return false;
+ }
+ </script>
</div>
<div py:if='(principals != None) and (len(principals) > 0)'>
<h2>${len(principals)} results returned:</h2>
@@ -33,7 +41,7 @@
<tbody>
<tr py:for="principal in principals">
<td>
- <a href="${tg.url('/principal/show',principal=principal.krbprincipalname)}"
+ <a href="${tg.url('/principal/show',principal=principal.krbprincipalname)}" onclick="return confirmDownload();"
>${principal.hostname}</a>
</td>
<td>
diff --git a/ipa-server/ipa-install/Makefile.am b/ipa-server/ipa-install/Makefile.am
index 4765cfb54..1b46d354b 100644
--- a/ipa-server/ipa-install/Makefile.am
+++ b/ipa-server/ipa-install/Makefile.am
@@ -10,14 +10,8 @@ sbin_SCRIPTS = \
ipa-replica-prepare \
$(NULL)
-appdir = $(IPA_DATA_DIR)
-app_SCRIPTS = \
- ipa-server-setupssl \
- $(NULL)
-
EXTRA_DIST = \
README \
- $(app_SCRIPTS) \
$(sbin_SCRIPTS) \
$(NULL)
diff --git a/ipa-server/ipa-install/ipa-server-setupssl b/ipa-server/ipa-install/ipa-server-setupssl
deleted file mode 100644
index 1774f214d..000000000
--- a/ipa-server/ipa-install/ipa-server-setupssl
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/bin/bash
-
-if [ "$1" ] ; then
- password=$1
-else
- echo "password required"
- exit 1
-fi
-
-if [ "$2" -a -d "$2" ] ; then
- secdir="$2"
-else
- secdir=/etc/dirsrv/slapd-localhost
-fi
-
-if [ "$3" ] ; then
- myhost=$3
-else
- myhost=`hostname --fqdn`
-fi
-
-
-if [ "$4" ] ; then
- ldapport=$4
-else
- ldapport=389
-fi
-
-me=`whoami`
-if [ "$me" = "root" ] ; then
- isroot=1
-fi
-
-# see if there are already certs and keys
-if [ -f $secdir/cert8.db ] ; then
- # look for CA cert
- if certutil -L -d $secdir -n "CA certificate" 2> /dev/null ; then
- echo "Using existing CA certificate"
- else
- echo "No CA certificate found - will create new one"
- needCA=1
- fi
-
- # look for server cert
- if certutil -L -d $secdir -n "Server-Cert" 2> /dev/null ; then
- echo "Using existing directory Server-Cert"
- else
- echo "No Server Cert found - will create new one"
- needServerCert=1
- fi
-
- prefix="new-"
- prefixarg="-P $prefix"
-else
- needCA=1
- needServerCert=1
-fi
-
-if test -z "$needCA" -a -z "$needServerCert" ; then
- echo "No certs needed - exiting"
- exit 0
-fi
-
-# get our user and group
-if test -n "$isroot" ; then
- uid=`/bin/ls -ald $secdir | awk '{print $3}'`
- gid=`/bin/ls -ald $secdir | awk '{print $4}'`
-fi
-
-# 2. Create a password file for your security token password:
-if [ -f $secdir/pwdfile.txt ] ; then
- echo "Using existing $secdir/pwdfile.txt"
-else
- (ps -ef ; w ) | sha1sum | awk '{print $1}' > $secdir/pwdfile.txt
- if test -n "$isroot" ; then
- chown $uid:$gid $secdir/pwdfile.txt
- fi
- chmod 400 $secdir/pwdfile.txt
-fi
-
-# 3. Create a "noise" file for your encryption mechanism:
-if [ -f $secdir/noise.txt ] ; then
- echo "Using existing $secdir/noise.txt file"
-else
- (w ; ps -ef ; date ) | sha1sum | awk '{print $1}' > $secdir/noise.txt
- if test -n "$isroot" ; then
- chown $uid:$gid $secdir/noise.txt
- fi
- chmod 400 $secdir/noise.txt
-fi
-
-# 4. Create the key3.db and cert8.db databases:
-certutil -N $prefixarg -d $secdir -f $secdir/pwdfile.txt
-if test -n "$isroot" ; then
- chown $uid:$gid $secdir/${prefix}key3.db $secdir/${prefix}cert8.db
-fi
-chmod 600 $secdir/${prefix}key3.db $secdir/${prefix}cert8.db
-
-
-if test -n "$needCA" ; then
-# 5. Generate the encryption key:
- certutil -G $prefixarg -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
-# 6. Generate the self-signed certificate:
- certutil -S $prefixarg -n "CA certificate" -s "cn=CAcert" -x -t "CT,," -m 1000 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
-# export the CA cert for use with other apps
- certutil -L $prefixarg -d $secdir -n "CA certificate" -a > $secdir/cacert.asc
- pk12util -d $secdir $prefixarg -o $secdir/cacert.p12 -n "CA certificate" -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
-fi
-
-if test -n "$needServerCert" ; then
-# 7. Generate the server certificate:
- certutil -S $prefixarg -n "Server-Cert" -s "cn=$myhost,ou=Fedora Directory Server" -c "CA certificate" -t "u,u,u" -m 1001 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
-fi
-
-# create the pin file
-if [ ! -f $secdir/pin.txt ] ; then
- pinfile=$secdir/pin.txt
- echo 'Internal (Software) Token:'`cat $secdir/pwdfile.txt` > $pinfile
- if test -n "$isroot" ; then
- chown $uid:$gid $pinfile
- fi
- chmod 400 $pinfile
-else
- echo Using existing $secdir/pin.txt
-fi
-
-if [ -n "$prefix" ] ; then
- # move the old files out of the way
- mv $secdir/cert8.db $secdir/orig-cert8.db
- mv $secdir/key3.db $secdir/orig-key3.db
- # move in the new files - will be used after server restart
- mv $secdir/${prefix}cert8.db $secdir/cert8.db
- mv $secdir/${prefix}key3.db $secdir/key3.db
-fi
-
-modnssdir=/etc/httpd/alias
-
-# Setup SSL in Apache
-if [ -e $modnssdir ]; then
- mkdir ${modnssdir}.ipa
- mv $modnssdir/cert8.db ${modnssdir}.ipa
- mv $modnssdir/key3.db ${modnssdir}.ipa
-fi
-
-# Create a new database for mod_nss
-echo -e "\n" > $modnssdir/pw.txt
-certutil -N -d $modnssdir -f $modnssdir/pw.txt
-
-# Add the CA we created
-certutil -A -d $modnssdir -n "CA certificate" -t "CT,CT," -a -i $secdir/cacert.asc
-
-# Request a new server cert
-certutil -R -d $modnssdir \
- -s "cn=$myhost,ou=Apache Web Server" \
- -o $modnssdir/tmpcertreq \
- -g 1024 \
- -z $secdir/noise.txt \
- -f $modnssdir/pw.txt
-
-# Have the FDS CA issue the cert
-echo -e "2\n9\nn\n1\n9\nn\n" | \
-certutil -C -d $secdir \
- -c "CA certificate" \
- -i $modnssdir/tmpcertreq \
- -o $modnssdir/tmpcert.der \
- -m 1002 \
- -v 120 \
- -f $secdir/pwdfile.txt \
- -1 \
- -5
-
-# Now add this cert to the Apache database
-certutil -A -d $modnssdir -n "Server-Cert"\
- -t u,u,u \
- -i $modnssdir/tmpcert.der \
- -f $modnsdir/tmpcert.der
-
-rm -f $modnssdir/pw.txt $modnssdir/tmpcertreq $modnssder/tmpcert.der
-
-# enable SSL in the directory server
-
-ldapmodify -x -h localhost -p $ldapport -D "cn=Directory Manager" -w $password <<EOF
-dn: cn=encryption,cn=config
-changetype: modify
-replace: nsSSL3
-nsSSL3: on
--
-replace: nsSSLClientAuth
-nsSSLClientAuth: allowed
--
-add: nsSSL3Ciphers
-nsSSL3Ciphers: -rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,
- +rsa_des_sha,+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,+fortezza,
- +fortezza_rc4_128_sha,+fortezza_null,+tls_rsa_export1024_with_rc4_56_sha,
- +tls_rsa_export1024_with_des_cbc_sha
-
-dn: cn=config
-changetype: modify
-add: nsslapd-security
-nsslapd-security: on
--
-replace: nsslapd-ssl-check-hostname
-nsslapd-ssl-check-hostname: off
-
-dn: cn=RSA,cn=encryption,cn=config
-changetype: add
-objectclass: top
-objectclass: nsEncryptionModule
-cn: RSA
-nsSSLPersonalitySSL: Server-Cert
-nsSSLToken: internal (software)
-nsSSLActivation: on
-
-EOF
-
-
diff --git a/ipa-server/ipa-install/share/60radius.ldif b/ipa-server/ipa-install/share/60radius.ldif
index 1802029ea..93a5ba319 100644
--- a/ipa-server/ipa-install/share/60radius.ldif
+++ b/ipa-server/ipa-install/share/60radius.ldif
@@ -4,6 +4,11 @@
# LDAP v3 version by Jochen Friedrich <jochen@scram.de>
# Updates by Adrian Pavlykevych <pam@polynet.lviv.ua>
# Modified by John Dennis <jdennis@redhat.com> for use with Directory Sever/IPA
+#
+# Note: These OID's do not seem to be registered, the closest I could find
+# was 1.3.6.1.4.1.3317
+# {iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) gnome(3317)}
+#
##############
dn: cn=schema
attributeTypes:
@@ -487,7 +492,7 @@ objectClasses:
NAME 'radiusprofile'
SUP top AUXILIARY
DESC ''
- MUST cn
+ MUST uid
MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $
radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $
radiusCalledStationId $ radiusCallingStationId $ radiusClass $
@@ -521,3 +526,34 @@ objectClasses:
MUST cn
MAY ( uid $ userPassword $ description )
)
+attributeTypes:
+ ( 1.3.6.1.4.1.3317.4.3.1.64
+ NAME 'radiusClientSecret'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+attributeTypes:
+ ( 1.3.6.1.4.1.3317.4.3.1.65
+ NAME 'radiusClientNASType'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+attributeTypes:
+ ( 1.3.6.1.4.1.3317.4.3.1.66
+ NAME 'radiusClientShortName'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+objectClasses:
+ ( 1.3.6.1.4.1.3317.4.3.2.3
+ NAME 'radiusClientProfile'
+ SUP top STRUCTURAL
+ DESC 'A Container Objectclass to be used for describing radius clients'
+ MUST (radiusClientIPAddress $ radiusClientSecret)
+ MAY ( radiusClientNASType $ radiusClientShortName $ description )
+ )
diff --git a/ipa-server/ipa-install/share/bootstrap-template.ldif b/ipa-server/ipa-install/share/bootstrap-template.ldif
index 3f0558d11..9642070c7 100644
--- a/ipa-server/ipa-install/share/bootstrap-template.ldif
+++ b/ipa-server/ipa-install/share/bootstrap-template.ldif
@@ -81,6 +81,30 @@ homeDirectory: /home/admin
loginShell: /bin/bash
gecos: Administrator
+dn: cn=radius,$SUFFIX
+changetype: add
+objectClass: nsContainer
+objectClass: top
+cn: radius
+
+dn: cn=clients,cn=radius,$SUFFIX
+changetype: add
+objectClass: nsContainer
+objectClass: top
+cn: clients
+
+dn: cn=profiles,cn=radius,$SUFFIX
+changetype: add
+objectClass: nsContainer
+objectClass: top
+cn: profiles
+
+dn: uid=ipa_default, cn=profiles,cn=radius,$SUFFIX
+changetype: add
+objectClass: top
+objectClass: radiusprofile
+uid: ipa_default
+
dn: cn=admins,cn=groups,cn=accounts,$SUFFIX
changetype: add
objectClass: top
diff --git a/ipa-server/ipa-install/share/default-aci.ldif b/ipa-server/ipa-install/share/default-aci.ldif
index 6b8afd28b..95743eebb 100644
--- a/ipa-server/ipa-install/share/default-aci.ldif
+++ b/ipa-server/ipa-install/share/default-aci.ldif
@@ -9,9 +9,10 @@ aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaN
aci: (targetattr = "krbPrincipalName || krbUPEnabled || krbPrincipalKey || krbMKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData")(version 3.0; acl "KDC System Account has access to kerberos material"; allow (read, search, compare) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
aci: (targetattr = "krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "KDC System Account can update some fields"; allow (read, search, compare, write) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
aci: (targetattr = "userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword || krbPasswordExpiration || krbPwdHistory || krbLastPwdChange")(version 3.0; acl "Kpasswd access to passowrd hashes for passowrd changes"; allow (read, write) userdn = "ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
-aci: (targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup))")(targetattr != "aci")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
+aci: (targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup)(objectClass=radiusprofile))")(targetattr != "aci")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
aci: (targetfilter = "(objectClass=krbPwdPolicy)")(targetattr = "krbMaxPwdLife || krbMinPwdLife || krbPwdMinDiffChars || krbPwdMinLength || krbPwdHistoryLength")(version 3.0;acl "Admins can write password policies"; allow (read, search, compare, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
aci: (targetattr = "givenName || sn || cn || displayName || initials || loginShell || homePhone || mobile || pager || facsimileTelephoneNumber || telephoneNumber || street || roomNumber || l || st || postalCode || manager || description || carLicense || labeledURI || inetUserHTTPURL || seeAlso")(version 3.0;acl "Self service";allow (write) userdn = "ldap:///self";)
+aci: (target="ldap:///cn=radius,$SUFFIX")(version 3.0; acl "Only radius and admin can access radius service data"; deny (all) userdn!="ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX || ldap:///krbprincipalname=radius/$FQDN@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
dn: cn=ipaConfig,cn=etc,$SUFFIX
changetype: modify
diff --git a/ipa-server/ipa-install/share/encrypted_attribute.ldif b/ipa-server/ipa-install/share/encrypted_attribute.ldif
new file mode 100644
index 000000000..3f5e1b43d
--- /dev/null
+++ b/ipa-server/ipa-install/share/encrypted_attribute.ldif
@@ -0,0 +1,6 @@
+dn: cn=$ENCRYPTED_ATTRIBUTE, cn=encrypted attributes, cn=userRoot, cn=ldbm database, cn=plugins, cn=config
+changetype: add
+objectClass: top
+objectClass: nsAttributeEncryption
+cn: $ENCRYPTED_ATTRIBUTE
+nsEncryptionAlgorithm: AES
diff --git a/ipa-server/ipa-install/share/radius.radiusd.conf.template b/ipa-server/ipa-install/share/radius.radiusd.conf.template
index d03105485..3bc4927dd 100644
--- a/ipa-server/ipa-install/share/radius.radiusd.conf.template
+++ b/ipa-server/ipa-install/share/radius.radiusd.conf.template
@@ -57,9 +57,6 @@ thread pool {
max_requests_per_server = 0
}
modules {
- pap {
- auto_header = yes
- }
chap {
authtype = CHAP
}
@@ -85,13 +82,19 @@ $$INCLUDE $${confdir}/eap.conf
filter = "(uid=%{Stripped-User-Name:-%{User-Name}})"
base_filter = "(objectclass=radiusprofile)"
start_tls = no
- access_attr = "$ACCESS_ATTRIBUTE"
+ profile_attribute = "radiusProfileDn"
+ default_profile = "uid=ipa_default,cn=profiles,cn=radius,cn=services,cn=etc,$SUFFIX
+ # FIXME: we'll want to toggle the access_attr feature on/off,
+ # but it needs a control, so disable it for now.
+ #access_attr = "$ACCESS_ATTRIBUTE"
+ #access_attr_used_for_allow = "$ACCESS_ATTRIBUTE_DEFAULT"
dictionary_mapping = $${raddbdir}/ldap.attrmap
ldap_connections_number = 5
edir_account_policy_check=no
timeout = 4
timelimit = 3
net_timeout = 1
+ clients_basedn = "$CLIENTS_BASEDN"
}
realm IPASS {
format = prefix
@@ -229,6 +232,10 @@ $$INCLUDE $${confdir}/eap.conf
override = no
maximum-timeout = 0
}
+ krb5 {
+ keytab = "$RADIUS_KEYTAB"
+ service_principal = "$RADIUS_PRINCIPAL"
+ }
}
instantiate {
exec
@@ -242,20 +249,18 @@ authorize {
eap
#files
ldap
- pap
}
authenticate {
- Auth-Type PAP {
- pap
- }
Auth-Type CHAP {
chap
}
Auth-Type MS-CHAP {
mschap
}
- unix
eap
+ Auth-Type Kerberos {
+ krb5
+ }
}
preacct {
preprocess
diff --git a/ipa-server/ipaserver/Makefile.am b/ipa-server/ipaserver/Makefile.am
index c2d164b96..f1c094b36 100644
--- a/ipa-server/ipaserver/Makefile.am
+++ b/ipa-server/ipaserver/Makefile.am
@@ -14,6 +14,7 @@ app_PYTHON = \
service.py \
installutils.py \
replication.py \
+ certs.py \
$(NULL)
EXTRA_DIST = \
diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py
new file mode 100644
index 000000000..fb6b01d0e
--- /dev/null
+++ b/ipa-server/ipaserver/certs.py
@@ -0,0 +1,213 @@
+# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 or later
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os, stat, subprocess
+import sha
+
+from ipa import ipautil
+
+class CertDB(object):
+ def __init__(self, dir):
+ self.secdir = dir
+
+ self.noise_fname = self.secdir + "/noise.txt"
+ self.passwd_fname = self.secdir + "/pwdfile.txt"
+ self.certdb_fname = self.secdir + "/cert8.db"
+ self.keydb_fname = self.secdir + "/key3.db"
+ self.secmod_fname = self.secdir + "/secmod.db"
+ self.cacert_fname = self.secdir + "/cacert.asc"
+ self.pk12_fname = self.secdir + "/cacert.p12"
+ self.pin_fname = self.secdir + "/pin.txt"
+ self.certreq_fname = self.secdir + "/tmpcertreq"
+ self.certder_fname = self.secdir + "/tmpcert.der"
+
+ # Making this a starting value that will generate
+ # unique values for the current DB is the
+ # responsibility of the caller for now. In the
+ # future we might automatically determine this
+ # for a given db.
+ self.cur_serial = 1000
+
+ self.cacert_name = "CA certificate"
+ self.valid_months = "120"
+ self.keysize = "1024"
+
+ # We are going to set the owner of all of the cert
+ # files to the owner of the containing directory
+ # instead of that of the process. This works when
+ # this is called by root for a daemon that runs as
+ # a normal user
+ mode = os.stat(self.secdir)
+ self.uid = mode[stat.ST_UID]
+ self.gid = mode[stat.ST_GID]
+
+ def next_serial(self):
+ r = self.cur_serial
+ self.cur_serial += 1
+ return str(r)
+
+ def set_perms(self, fname, write=False):
+ os.chown(fname, self.uid, self.gid)
+ perms = stat.S_IRUSR
+ if write:
+ perms |= stat.S_IWUSR
+ os.chmod(fname, perms)
+
+ def gen_password(self):
+ return sha.sha(ipautil.ipa_generate_password()).hexdigest()
+
+ def run_certutil(self, args, stdin=None):
+ new_args = ["/usr/bin/certutil", "-d", self.secdir]
+ new_args = new_args + args
+ ipautil.run(new_args, stdin)
+
+ def create_noise_file(self):
+ ipautil.backup_file(self.noise_fname)
+ f = open(self.noise_fname, "w")
+ f.write(self.gen_password())
+ self.set_perms(self.noise_fname)
+
+ def create_passwd_file(self, passwd=True):
+ ipautil.backup_file(self.passwd_fname)
+ f = open(self.passwd_fname, "w")
+ if passwd:
+ f.write(self.gen_password())
+ else:
+ f.write("\n")
+ f.close()
+ self.set_perms(self.passwd_fname)
+
+ def create_certdbs(self):
+ ipautil.backup_file(self.certdb_fname)
+ ipautil.backup_file(self.keydb_fname)
+ ipautil.backup_file(self.secmod_fname)
+ self.run_certutil(["-N",
+ "-f", self.passwd_fname])
+ self.set_perms(self.passwd_fname, write=True)
+
+ def create_ca_cert(self):
+ # Generate the encryption key
+ self.run_certutil(["-G", "-z", self.noise_fname, "-f", self.passwd_fname])
+ # Generate the self-signed cert
+ self.run_certutil(["-S", "-n", self.cacert_name,
+ "-s", "cn=CAcert",
+ "-x",
+ "-t", "CT,,",
+ "-m", self.next_serial(),
+ "-v", self.valid_months,
+ "-z", self.noise_fname,
+ "-f", self.passwd_fname])
+
+ # export the CA cert for use with other apps
+ ipautil.backup_file(self.cacert_fname)
+ self.run_certutil(["-L", "-n", "CA certificate",
+ "-a",
+ "-o", self.cacert_fname])
+ self.set_perms(self.cacert_fname)
+ ipautil.backup_file(self.pk12_fname)
+ ipautil.run(["/usr/bin/pk12util", "-d", self.secdir,
+ "-o", self.pk12_fname,
+ "-n", "CA certificate",
+ "-w", self.passwd_fname,
+ "-k", self.passwd_fname])
+ self.set_perms(self.pk12_fname)
+
+ def load_cacert(self, cacert_fname):
+ self.run_certutil(["-A", "-n", self.cacert_name,
+ "-t", "CT,CT,",
+ "-a",
+ "-i", cacert_fname])
+
+ def create_server_cert(self, nickname, name, other_certdb=None):
+ cdb = other_certdb
+ if not cdb:
+ cdb = self
+ self.request_cert(name)
+ cdb.issue_cert(self.certreq_fname, self.certder_fname)
+ self.add_cert(self.certder_fname, nickname)
+ os.unlink(self.certreq_fname)
+ os.unlink(self.certder_fname)
+
+ def request_cert(self, name):
+ self.run_certutil(["-R", "-s", name,
+ "-o", self.certreq_fname,
+ "-g", self.keysize,
+ "-z", self.noise_fname,
+ "-f", self.passwd_fname])
+
+ def issue_cert(self, certreq_fname, cert_fname):
+ p = subprocess.Popen(["/usr/bin/certutil",
+ "-d", self.secdir,
+ "-C", "-c", self.cacert_name,
+ "-i", certreq_fname,
+ "-o", cert_fname,
+ "-m", self.next_serial(),
+ "-v", self.valid_months,
+ "-f", self.passwd_fname,
+ "-1", "-5"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ # Bah - this sucks, but I guess it isn't possible to fully
+ # control this with command line arguments.
+ #
+ # What this is requesting is:
+ # -1 (Create key usage extension)
+ # 2 - Key encipherment
+ # 9 - done
+ # n - not critical
+ #
+ # -5 (Create netscape cert type extension)
+ # 1 - SSL Server
+ # 9 - done
+ # n - not critical
+ p.stdin.write("2\n9\nn\n1\n9\nn\n")
+ p.wait()
+
+
+ def add_cert(self, cert_fname, nickname):
+ self.run_certutil(["-A", "-n", nickname,
+ "-t", "u,u,u",
+ "-i", cert_fname,
+ "-f", cert_fname])
+
+ def create_pin_file(self):
+ ipautil.backup_file(self.pin_fname)
+ f = open(self.pin_fname, "w")
+ f.write("Internal (Software) Token:")
+ pwd = open(self.passwd_fname)
+ f.write(pwd.read())
+ f.close()
+ self.set_perms(self.pin_fname)
+
+ def create_self_signed(self, passwd=True):
+ self.create_noise_file()
+ self.create_passwd_file(passwd)
+ self.create_certdbs()
+ self.create_ca_cert()
+ self.create_pin_file()
+
+ def create_from_cacert(self, cacert_fname, passwd=False):
+ self.create_noise_file()
+ self.create_passwd_file(passwd)
+ self.create_certdbs()
+ self.load_cacert(cacert_fname)
+
+
+
diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py
index 6ba721c3b..5edc3879e 100644
--- a/ipa-server/ipaserver/dsinstance.py
+++ b/ipa-server/ipaserver/dsinstance.py
@@ -29,6 +29,8 @@ from ipa import ipautil
import service
import installutils
+import certs
+import ipaldap, ldap
SERVER_ROOT_64 = "/usr/lib64/dirsrv"
SERVER_ROOT_32 = "/usr/lib/dirsrv"
@@ -290,13 +292,36 @@ class DsInstance(service.Service):
def __enable_ssl(self):
self.step("configuring ssl for ds instance")
dirname = config_dirname(self.realm_name)
- args = ["/usr/share/ipa/ipa-server-setupssl", self.dm_password,
- dirname, self.host_name]
- try:
- ipautil.run(args)
- logging.debug("done configuring ssl for ds instance")
- except ipautil.CalledProcessError, e:
- logging.critical("Failed to configure ssl in ds instance %s" % e)
+ ca = certs.CertDB(dirname)
+ ca.create_self_signed()
+ ca.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name)
+
+ conn = ipaldap.IPAdmin("127.0.0.1")
+ conn.simple_bind_s("cn=directory manager", self.dm_password)
+
+ mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"),
+ (ldap.MOD_REPLACE, "nsSSL3Ciphers",
+ "-rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,\
++rsa_des_sha,+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,+fortezza,\
++fortezza_rc4_128_sha,+fortezza_null,+tls_rsa_export1024_with_rc4_56_sha,\
++tls_rsa_export1024_with_des_cbc_sha")]
+ conn.modify_s("cn=encryption,cn=config", mod)
+
+ mod = [(ldap.MOD_ADD, "nsslapd-security", "on"),
+ (ldap.MOD_REPLACE, "nsslapd-ssl-check-hostname", "off")]
+ conn.modify_s("cn=config", mod)
+
+ entry = ipaldap.Entry("cn=RSA,cn=encryption,cn=config")
+
+ entry.setValues("objectclass", "top", "nsEncryptionModule")
+ entry.setValues("cn", "RSA")
+ entry.setValues("nsSSLPersonalitySSL", "Server-Cert")
+ entry.setValues("nsSSLToken", "internal (software)")
+ entry.setValues("nsSSLActivation", "on")
+
+ conn.addEntry(entry)
+
+ conn.unbind()
def __add_default_layout(self):
self.step("adding default layout")
diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py
index 301035130..1799cca07 100644
--- a/ipa-server/ipaserver/httpinstance.py
+++ b/ipa-server/ipaserver/httpinstance.py
@@ -27,11 +27,14 @@ import sys
import time
import service
+import certs
+import dsinstance
from ipa.ipautil import *
HTTPD_DIR = "/etc/httpd"
SSL_CONF = HTTPD_DIR + "/conf.d/ssl.conf"
NSS_CONF = HTTPD_DIR + "/conf.d/nss.conf"
+NSS_DIR = HTTPD_DIR + "/alias"
selinux_warning = """WARNING: could not set selinux boolean httpd_can_network_connect to true.
The web interface may not function correctly until this boolean is
@@ -64,12 +67,13 @@ class HTTPInstance(service.Service):
self.fqdn = fqdn
self.realm = realm
- self.start_creation(6, "Configuring the web interface")
+ self.start_creation(7, "Configuring the web interface")
self.__disable_mod_ssl()
self.__set_mod_nss_port()
self.__configure_http()
self.__create_http_keytab()
+ self.__setup_ssl()
self.step("restarting httpd")
self.restart()
@@ -143,3 +147,12 @@ class HTTPInstance(service.Service):
self.step("Setting mod_nss port to 443")
if update_file(NSS_CONF, '8443', '443') != 0:
print "Updating %s failed." % NSS_CONF
+
+ def __setup_ssl(self):
+ self.step("Setting up ssl")
+ ds_ca = certs.CertDB(dsinstance.config_dirname(self.realm))
+ ca = certs.CertDB(NSS_DIR)
+ ds_ca.cur_serial = 2000
+ ca.create_from_cacert(ds_ca.cacert_fname)
+ ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca)
+
diff --git a/ipa-server/ipaserver/radiusinstance.py b/ipa-server/ipaserver/radiusinstance.py
index 72cfe6cb6..3b89018f0 100644
--- a/ipa-server/ipaserver/radiusinstance.py
+++ b/ipa-server/ipaserver/radiusinstance.py
@@ -18,6 +18,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
+import sys
import subprocess
import string
import tempfile
@@ -27,6 +28,7 @@ import pwd
import time
import sys
from ipa.ipautil import *
+from ipa import radius_util
import service
@@ -34,28 +36,20 @@ import os
import re
IPA_RADIUS_VERSION = '0.0.0'
-PKG_NAME = 'freeradius'
-PKG_CONFIG_DIR = '/etc/raddb'
-
-RADIUS_SERVICE_NAME = 'radius'
-RADIUS_USER = 'radiusd'
-
-IPA_KEYTAB_FILEPATH = os.path.join(PKG_CONFIG_DIR, 'ipa.keytab')
-LDAP_ATTR_MAP_FILEPATH = os.path.join(PKG_CONFIG_DIR, 'ldap.attrmap')
-RADIUSD_CONF_FILEPATH = os.path.join(PKG_CONFIG_DIR, 'radiusd.conf')
-RADIUSD_CONF_TEMPLATE_FILEPATH = os.path.join(SHARE_DIR, 'radius.radiusd.conf.template')
-
-RADIUSD = '/usr/sbin/radiusd'
# FIXME there should a utility to get the user base dn
from ipaserver.funcs import DefaultUserContainer, DefaultGroupContainer
#-------------------------------------------------------------------------------
+def ldap_mod(fd, dn, pwd):
+ args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name]
+ run(args)
+
def get_radius_version():
version = None
try:
- p = subprocess.Popen([RADIUSD, '-v'], stdout=subprocess.PIPE,
+ p = subprocess.Popen([radius_util.RADIUSD, '-v'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
status = p.returncode
@@ -80,10 +74,11 @@ class RadiusInstance(service.Service):
def create_instance(self, realm_name, host_name, ldap_server):
self.realm = realm_name.upper()
+ self.suffix = realm_to_suffix(self.realm)
self.fqdn = host_name
self.ldap_server = ldap_server
- self.principal = "%s/%s@%s" % (RADIUS_SERVICE_NAME, self.fqdn, self.realm)
- self.basedn = realm_to_suffix(self.realm)
+ self.principal = "%s/%s@%s" % (radius_util.RADIUS_SERVICE_NAME, self.fqdn, self.realm)
+ self.basedn = self.suffix
self.user_basedn = "%s,%s" % (DefaultUserContainer, self.basedn) # FIXME, should be utility to get this
self.radius_version = get_radius_version()
self.start_creation(4, "Configuring radiusd")
@@ -113,31 +108,34 @@ class RadiusInstance(service.Service):
version = 'IPA_RADIUS_VERSION=%s FREE_RADIUS_VERSION=%s' % (IPA_RADIUS_VERSION, self.radius_version)
sub_dict = {'CONFIG_FILE_VERSION_INFO' : version,
'LDAP_SERVER' : self.ldap_server,
- 'RADIUS_KEYTAB' : IPA_KEYTAB_FILEPATH,
+ 'RADIUS_KEYTAB' : radius_util.RADIUS_IPA_KEYTAB_FILEPATH,
'RADIUS_PRINCIPAL' : self.principal,
'RADIUS_USER_BASE_DN' : self.user_basedn,
- 'ACCESS_ATTRIBUTE' : 'dialupAccess'
+ 'ACCESS_ATTRIBUTE' : '',
+ 'ACCESS_ATTRIBUTE_DEFAULT' : 'TRUE',
+ 'CLIENTS_BASEDN' : radius_util.radius_clients_basedn(None, self.suffix),
+ 'SUFFIX' : self.suffix,
}
try:
- radiusd_conf = template_file(RADIUSD_CONF_TEMPLATE_FILEPATH, sub_dict)
- radiusd_fd = open(RADIUSD_CONF_FILEPATH, 'w+')
+ radiusd_conf = template_file(radius_util.RADIUSD_CONF_TEMPLATE_FILEPATH, sub_dict)
+ radiusd_fd = open(radius_util.RADIUSD_CONF_FILEPATH, 'w+')
radiusd_fd.write(radiusd_conf)
radiusd_fd.close()
except Exception, e:
- logging.error("could not create %s: %s", RADIUSD_CONF_FILEPATH, e)
+ logging.error("could not create %s: %s", radius_util.RADIUSD_CONF_FILEPATH, e)
def __create_radius_keytab(self):
- self.step("create radiusd keytab")
+ self.step("creating a keytab for httpd")
try:
- if file_exists(IPA_KEYTAB_FILEPATH):
- os.remove(IPA_KEYTAB_FILEPATH)
+ if file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
+ os.remove(radius_util.RADIUS_IPA_KEYTAB_FILEPATH)
except os.error:
- logging.error("Failed to remove %s", IPA_KEYTAB_FILEPATH)
+ logging.error("Failed to remove %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH)
(kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
kwrite.write("addprinc -randkey %s\n" % (self.principal))
kwrite.flush()
- kwrite.write("ktadd -k %s %s\n" % (IPA_KEYTAB_FILEPATH, self.principal))
+ kwrite.write("ktadd -k %s %s\n" % (radius_util.RADIUS_IPA_KEYTAB_FILEPATH, self.principal))
kwrite.flush()
kwrite.close()
kread.close()
@@ -145,42 +143,29 @@ class RadiusInstance(service.Service):
# give kadmin time to actually write the file before we go on
retry = 0
- while not file_exists(IPA_KEYTAB_FILEPATH):
+ while not file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
time.sleep(1)
retry += 1
if retry > 15:
print "Error timed out waiting for kadmin to finish operations\n"
sys.exit(1)
-
try:
- pent = pwd.getpwnam(RADIUS_USER)
- os.chown(IPA_KEYTAB_FILEPATH, pent.pw_uid, pent.pw_gid)
+ pent = pwd.getpwnam(radius_util.RADIUS_USER)
+ os.chown(radius_util.RADIUS_IPA_KEYTAB_FILEPATH, pent.pw_uid, pent.pw_gid)
except Exception, e:
- logging.error("could not chown on %s to %s: %s", IPA_KEYTAB_FILEPATH, RADIUS_USER, e)
+ logging.error("could not chown on %s to %s: %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH, radius_util.RADIUS_USER, e)
+
+ #FIXME, should use IPAdmin method
+ def __set_ldap_encrypted_attributes(self):
+ ldif_file = 'encrypted_attribute.ldif'
+ self.step("setting ldap encrypted attributes")
+ ldif_txt = template_file(SHARE_DIR + ldif_file, {'ENCRYPTED_ATTRIBUTE':'radiusClientSecret'})
+ ldif_fd = write_tmp_file(ldif_txt)
+ try:
+ ldap_mod(ldif_fd, "cn=Directory Manager", self.dm_password)
+ except subprocess.CalledProcessError, e:
+ logging.critical("Failed to load %s: %s" % (ldif_file, str(e)))
+ ldif_fd.close()
#-------------------------------------------------------------------------------
-# FIXME: this should be in a common area so it can be shared
-def get_ldap_attr_translations():
- comment_re = re.compile('#.*$')
- radius_attr_to_ldap_attr = {}
- ldap_attr_to_radius_attr = {}
- try:
- f = open(LDAP_ATTR_MAP_FILEPATH)
- for line in f.readlines():
- line = comment_re.sub('', line).strip()
- if not line: continue
- attr_type, radius_attr, ldap_attr = line.split()
- print 'type="%s" radius="%s" ldap="%s"' % (attr_type, radius_attr, ldap_attr)
- radius_attr_to_ldap_attr[radius_attr] = {'ldap_attr':ldap_attr, 'attr_type':attr_type}
- ldap_attr_to_radius_attr[ldap_attr] = {'radius_attr':radius_attr, 'attr_type':attr_type}
- f.close()
- except Exception, e:
- logging.error('cold not read radius ldap attribute map file (%s): %s', LDAP_ATTR_MAP_FILEPATH, e)
- pass # FIXME
-
- #for k,v in radius_attr_to_ldap_attr.items():
- # print '%s --> %s' % (k,v)
- #for k,v in ldap_attr_to_radius_attr.items():
- # print '%s --> %s' % (k,v)
-
diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index 8a49488b8..4943da24d 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -31,6 +31,7 @@ import copy
import attrs
from ipa import ipaerror
from urllib import quote,unquote
+from ipa import radius_util
import string
from types import *
@@ -52,11 +53,6 @@ DefaultUserContainer = "cn=users,cn=accounts"
DefaultGroupContainer = "cn=groups,cn=accounts"
DefaultServiceContainer = "cn=services,cn=accounts"
-# FIXME: need to check the ipadebug option in ipa.conf
-#logging.basicConfig(level=logging.DEBUG,
-# format='%(asctime)s %(levelname)s %(message)s',
-# stream=sys.stderr)
-
#
# Apache runs in multi-process mode so each process will have its own
# connection. This could theoretically drive the total number of connections
@@ -557,7 +553,201 @@ class IPAServer:
schema.append(d)
return schema
+# radius support
+
+ # clients
+ def get_radius_client_by_ip_addr(self, ip_addr, container=None, sattrs=None, opts=None):
+ filter = radius_util.radius_client_filter(ip_addr)
+ basedn = radius_util.radius_clients_basedn(container, self.basedn)
+ return self.__get_sub_entry(basedn, filter, sattrs, opts)
+
+ def __radius_client_exists(self, ip_addr, container, opts):
+ filter = radius_util.radius_client_filter(ip_addr)
+ basedn = radius_util.radius_clients_basedn(container, self.basedn)
+
+ try:
+ entry = self.__get_sub_entry(basedn, filter, ['dn','uid'], opts)
+ return True
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return False
+
+ def add_radius_client (self, client, container=None, opts=None):
+ if container is None:
+ container = radius_util.clients_container
+
+ ip_addr = client['radiusClientIPAddress']
+
+ if self.__radius_client_exists(ip_addr, container, opts):
+ raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
+
+ dn = radius_util.radius_client_dn(ip_addr, container, self.basedn)
+ entry = ipaserver.ipaldap.Entry(dn)
+
+ # some required objectclasses
+ entry.setValues('objectClass', 'top', 'radiusClientProfile')
+
+ # fill in our new entry with everything sent by the client
+ for attr in client:
+ entry.setValues(attr, client[attr])
+
+ conn = self.getConnection(opts)
+ try:
+ res = conn.addEntry(entry)
+ finally:
+ self.releaseConnection(conn)
+ return res
+
+ def update_radius_client(self, oldentry, newentry, opts=None):
+ return self.update_entry(oldentry, newentry, opts)
+
+ def delete_radius_client(self, ip_addr, container=None, opts=None):
+ client = self.get_radius_client_by_ip_addr(ip_addr, container, ['dn', 'cn'], opts)
+ if client is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
+
+ conn = self.getConnection(opts)
+ try:
+ res = conn.deleteEntry(client['dn'])
+ finally:
+ self.releaseConnection(conn)
+ return res
+
+ def find_radius_clients(self, ip_attrs, container=None, sattrs=None, searchlimit=0, timelimit=-1, opts=None):
+ def gen_filter(objectclass, attr, values):
+ '''Given ('myclass', 'myattr', [v1, v2]) returns
+ (&(objectclass=myclass)(|(myattr=v1)(myattr=v2)))
+ '''
+ # Don't use __safe_filter, prevents wildcarding
+ #attrs = ''.join(['(%s=%s)' % (attr, self.__safe_filter(val)) for val in values])
+ attrs = ''.join(['(%s=%s)' % (attr, val) for val in values])
+ filter = "(&(objectclass=%s)(|%s))" % (objectclass, attrs)
+ return filter
+
+ basedn = radius_util.radius_clients_basedn(container, self.basedn)
+ filter = gen_filter('radiusClientProfile', 'radiusClientIPAddress', ip_attrs)
+ conn = self.getConnection(opts)
+ try:
+ try:
+ results = conn.getListAsync(basedn, self.scope, filter, sattrs, 0, None, None, timelimit, searchlimit)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ results = [0]
+ finally:
+ self.releaseConnection(conn)
+
+ counter = results[0]
+ results = results[1:]
+ radius_clients = [counter]
+ for radius_client in results:
+ radius_clients.append(self.convert_entry(radius_client))
+
+ return radius_clients
+
+ # profiles
+ def get_radius_profile_by_uid(self, uid, user_profile=True, sattrs=None, opts=None):
+ if user_profile:
+ container = DefaultUserContainer
+ else:
+ container = radius_util.profiles_container
+
+ uid = self.__safe_filter(uid)
+ filter = radius_util.radius_profile_filter(uid)
+ basedn = radius_util.radius_profiles_basedn(container, self.basedn)
+ return self.__get_sub_entry(basedn, filter, sattrs, opts)
+
+ def __radius_profile_exists(self, uid, user_profile, opts):
+ if user_profile:
+ container = DefaultUserContainer
+ else:
+ container = radius_util.profiles_container
+
+ uid = self.__safe_filter(uid)
+ filter = radius_util.radius_profile_filter(uid)
+ basedn = radius_util.radius_profiles_basedn(container, self.basedn)
+
+ try:
+ entry = self.__get_sub_entry(basedn, filter, ['dn','uid'], opts)
+ return True
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return False
+
+ def add_radius_profile (self, profile, user_profile=True, opts=None):
+ uid = profile['uid']
+
+ if self.__radius_profile_exists(uid, user_profile, opts):
+ raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
+
+ if user_profile:
+ container = DefaultUserContainer
+ else:
+ container = radius_util.profiles_container
+
+ dn = radius_util.radius_profile_dn(uid, container, self.basedn)
+ entry = ipaserver.ipaldap.Entry(dn)
+
+ # some required objectclasses
+ entry.setValues('objectClass', 'top', 'radiusprofile')
+
+ # fill in our new entry with everything sent by the profile
+ for attr in profile:
+ entry.setValues(attr, profile[attr])
+
+ conn = self.getConnection(opts)
+ try:
+ res = conn.addEntry(entry)
+ finally:
+ self.releaseConnection(conn)
+ return res
+ def update_radius_profile(self, oldentry, newentry, opts=None):
+ return self.update_entry(oldentry, newentry, opts)
+
+ def delete_radius_profile(self, uid, user_profile, opts=None):
+ profile = self.get_radius_profile_by_uid(uid, user_profile, ['dn', 'cn'], opts)
+ if profile is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
+
+ conn = self.getConnection(opts)
+ try:
+ res = conn.deleteEntry(profile['dn'])
+ finally:
+ self.releaseConnection(conn)
+ return res
+
+ def find_radius_profiles(self, uids, user_profile=True, sattrs=None, searchlimit=0, timelimit=-1, opts=None):
+ def gen_filter(objectclass, attr, values):
+ '''Given ('myclass', 'myattr', [v1, v2]) returns
+ (&(objectclass=myclass)(|(myattr=v1)(myattr=v2)))
+ '''
+ # Don't use __safe_filter, prevents wildcarding
+ #attrs = ''.join(['(%s=%s)' % (attr, self.__safe_filter(val)) for val in values])
+ attrs = ''.join(['(%s=%s)' % (attr, val) for val in values])
+ filter = "(&(objectclass=%s)(|%s))" % (objectclass, attrs)
+ return filter
+
+ if user_profile:
+ container = DefaultUserContainer
+ else:
+ container = radius_util.profiles_container
+
+ filter = gen_filter('radiusprofile', 'uid', uids)
+ basedn="%s,%s" % (container, self.basedn)
+ conn = self.getConnection(opts)
+ try:
+ try:
+ results = conn.getListAsync(basedn, self.scope, filter, sattrs, 0, None, None, timelimit, searchlimit)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ results = [0]
+ finally:
+ self.releaseConnection(conn)
+
+ counter = results[0]
+ results = results[1:]
+ radius_profiles = [counter]
+ for radius_profile in results:
+ radius_profiles.append(self.convert_entry(radius_profile))
+
+ return radius_profiles
+
def set_custom_fields (self, schema, opts=None):
"""Set the list of custom user fields.
@@ -612,6 +802,7 @@ class IPAServer:
"""Returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1."""
+ logging.debug("IPA: find users %s" % criteria)
config = self.get_ipa_config(opts)
if timelimit < 0:
timelimit = float(config.get('ipasearchtimelimit'))
diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py
index 31cfbae69..7752b2586 100644
--- a/ipa-server/xmlrpc-server/ipaxmlrpc.py
+++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py
@@ -32,6 +32,7 @@ import traceback
import pprint
from xmlrpclib import Marshaller,loads,dumps,Fault
from mod_python import apache
+import logging
import ipaserver
import funcs
@@ -148,12 +149,15 @@ class ModXMLRPCRequestHandler(object):
opts['ipadebug'] = pythonopts.get("IPADebug")
if opts['ipadebug'].lower() == "on":
+ logging.basicConfig(level=logging.DEBUG,
+ format='[%(asctime)s] [%(levelname)s] %(message)s',
+ datefmt='%a %b %d %H:%M:%S %Y',
+ stream=sys.stderr)
+
for o in opts:
- sys.stderr.write("IPA: setting option %s: %s\n" % (o, opts[o]))
- sys.stderr.flush()
- for e in req.subprocess_env:
- sys.stderr.write("IPA: environment %s: %s\n" % (e, req.subprocess_env[e]))
- sys.stderr.flush()
+ logging.debug("IPA: setting option %s: %s" % (o, opts[o]))
+# for e in req.subprocess_env:
+# logging.debug("IPA: environment %s: %s" % (e, req.subprocess_env[e]))
# Tack onto the end of the passed-in arguments any options we also
# need
@@ -362,6 +366,16 @@ def handler(req, profiling=False):
h.register_function(f.add_service_principal)
h.register_function(f.find_service_principal)
h.register_function(f.get_keytab)
+ h.register_function(f.get_radius_client_by_ip_addr)
+ h.register_function(f.add_radius_client)
+ h.register_function(f.update_radius_client)
+ h.register_function(f.delete_radius_client)
+ h.register_function(f.find_radius_clients)
+ h.register_function(f.get_radius_profile_by_uid)
+ h.register_function(f.add_radius_profile)
+ h.register_function(f.update_radius_profile)
+ h.register_function(f.delete_radius_profile)
+ h.register_function(f.find_radius_profiles)
h.handle_request(req)
finally:
pass