summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipa-admintools/ipa-addradiusclient195
-rw-r--r--ipa-admintools/ipa-findradiusclient15
-rw-r--r--ipa-admintools/ipa-radiusclientmod92
-rw-r--r--ipa-python/ipaclient.py16
-rw-r--r--ipa-python/ipautil.py330
-rw-r--r--ipa-python/radius_client.py39
-rw-r--r--ipa-python/radius_util.py231
-rw-r--r--ipa-python/rpcclient.py24
-rw-r--r--ipa-server/ipa-install/share/60radius.ldif12
-rw-r--r--ipa-server/ipa-install/share/bootstrap-template.ldif4
-rw-r--r--ipa-server/ipaserver/radiusinstance.py67
-rw-r--r--ipa-server/xmlrpc-server/funcs.py155
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py5
13 files changed, 950 insertions, 235 deletions
diff --git a/ipa-admintools/ipa-addradiusclient b/ipa-admintools/ipa-addradiusclient
index 55926214..b5d829ac 100644
--- a/ipa-admintools/ipa-addradiusclient
+++ b/ipa-admintools/ipa-addradiusclient
@@ -19,13 +19,16 @@
#
import sys
+import os
from optparse import OptionParser
-import ipa
+import copy
+
from ipa.radius_client import *
import ipa.ipaclient as ipaclient
-import ipa.ipavalidate as ipavalidate
+import ipa.ipautil as ipautil
import ipa.config
import ipa.ipaerror
+import ipa.radius_util as radius_util
import xmlrpclib
import kerberos
@@ -33,97 +36,127 @@ import ldap
#------------------------------------------------------------------------------
-def parse_options():
- parser = OptionParser()
- parser.add_option("--usage", action="store_true",
- help="Program usage")
- parser.add_option("-a", "--address", dest="ip_addr",
- help="RADIUS client IP address (required)")
- parser.add_option("-s", "--secret", dest="secret",
- help="RADIUS client secret (required)")
- parser.add_option("-n", "--name", dest="name",
+attrs = radius_util.client_name_to_ldap_attr.keys()
+mandatory_attrs = ['Client-IP-Address', 'Secret']
+
+#------------------------------------------------------------------------------
+
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Valid interative attributes are:"
+ print ipautil.format_list(attrs, quote='"')
+ print
+ print "Required attributes are:"
+ print ipautil.format_list(mandatory_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")
- parser.add_option("-t", "--type", dest="nastype",
+ opt_parser.add_option("-t", "--NAS-Type", dest="nastype",
help="RADIUS client NAS Type")
- parser.add_option("-d", "--description", dest="desc",
+ opt_parser.add_option("-d", "--Description", dest="desc",
help="description of the RADIUS client")
- args = ipa.config.init_config(sys.argv)
- options, args = parser.parse_args(args)
+ 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")
- return options, args
+ #opt_parser.set_usage("Usage: %s [options] %s" % (os.path.basename(sys.argv[0]), ' '.join(mandatory_attrs)))
-#------------------------------------------------------------------------------
-
-def main():
- ip_addr = None
- secret = None
- name = None
- nastype = None
- desc = None
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ # Get pairs from a file or stdin
+ if options.pair_file:
+ try:
+ av = radius_util.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['Client-IP-Address'] = 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:
+ # Remove any mandatory attriubtes which have been previously specified
+ interactive_mandatory_attrs = copy.copy(mandatory_attrs)
+ for attr in pairs.keys():
+ try:
+ interactive_mandatory_attrs.remove(attr)
+ except ValueError:
+ pass
+ c = ipautil.AttributeValueCompleter(attrs, pairs)
+ c.open()
+ av = c.get_pairs("Enter: ", interactive_mandatory_attrs, validate)
+ pairs.update(av)
+ c.close()
+
+ # Data collection done, assure mandatory data has been specified
+ valid = True
+ for attr in mandatory_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
- radius_client = ipa.radius_client.RadiusClient()
- options, args = parse_options()
-
- # client address is required
- if options.ip_addr:
- ip_addr = options.ip_addr
- if not validate_ip_addr(ip_addr): return 1
- else:
- valid = False
- while not valid:
- ip_addr = raw_input("Client IP: ")
- if validate_ip_addr(ip_addr): valid = True
-
- # client secret is required
- if options.secret:
- secret = options.secret
- if not validate_secret(secret): return 1
- else:
- valid = False
- while not valid:
- secret = get_secret()
- if validate_secret(secret): valid = True
-
- # client name is optional
- if options.name:
- name = options.name
- if not validate_name(name): return 1
-
- # client NAS Type is optional
- if options.nastype:
- nastype = options.nastype
- if not validate_nastype(nastype): return 1
-
- # client description is optional
- if options.desc:
- desc = options.desc
- if not validate_desc(desc): return 1
-
-
- #print "ip_addr=%s secret=%s name=%s nastype=%s desc=%s" % (ip_addr, secret, name, nastype, desc)
-
- if ip_addr is not None:
- radius_client.setValue('radiusClientNASIpAddress', ip_addr)
- else:
- print "client IP Address is required"
+ # 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 attrs:
+ valid = False
+ print "ERROR, %s is not a valid attribute" % (attr)
+ if not valid:
+ print "Valid attributes are:"
+ print ipautil.format_list(attrs, quote='"')
return 1
- if secret is not None:
- radius_client.setValue('radiusClientSecret', secret)
- else:
- print "client secret is required"
+ # Makse sure each value is valid
+ valid = True
+ for attr,value in pairs.items():
+ if not validate(attr, value):
+ valid = False
+ if not valid:
return 1
- if name is not None:
- radius_client.setValue('radiusClientShortName', name)
+ # 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_client = ipa.radius_client.RadiusClient()
+ for attr,value in pairs.items():
+ radius_client.setValue(radius_util.client_name_to_ldap_attr[attr], value)
- if nastype is not None:
- radius_client.setValue('radiusClientNASType', nastype)
-
- if desc is not None:
- radius_client.setValue('description', desc)
-
try:
ipa_client = ipaclient.IPAClient()
ipa_client.add_radius_client(radius_client)
diff --git a/ipa-admintools/ipa-findradiusclient b/ipa-admintools/ipa-findradiusclient
index 63d51007..a922c6ea 100644
--- a/ipa-admintools/ipa-findradiusclient
+++ b/ipa-admintools/ipa-findradiusclient
@@ -22,6 +22,7 @@ import sys
from optparse import OptionParser
import ipa
from ipa.radius_client import *
+from ipa import radius_util
import ipa.ipaclient as ipaclient
import ipa.ipavalidate as ipavalidate
import ipa.config
@@ -45,21 +46,13 @@ def parse_options():
#------------------------------------------------------------------------------
-attr_to_name = ipa.ipautil.CIDict({
- 'radiusClientNASIpAddress' : 'IP Address',
- 'radiusClientSecret' : 'Secret',
- 'radiusClientNASType' : 'NAS Type',
- 'radiusClientShortName' : 'Name',
- 'description' : 'Description',
- })
-
# FIXME
def usage():
print "ipa-findradiusclients ip_addr [ip_addr ...]"
sys.exit(1)
def main():
- attrs=['radiusClientNASIpAddress', 'radiusClientSecret', 'radiusClientNASType', 'radiusClientShortName', 'description']
+ attrs=['radiusClientIPAddress', 'radiusClientSecret', 'radiusClientNASType', 'radiusClientShortName', 'description']
options, args = parse_options()
@@ -82,10 +75,10 @@ def main():
attrs = radius_client.attrList()
attrs.sort()
- print "%s:" % radius_client.getValues('radiusClientNASIpAddress')
+ print "%s:" % radius_client.getValues('radiusClientIPAddress')
for attr in attrs:
value = radius_client.getValues(attr)
- print "\t%s = %s" % (attr_to_name[attr], value)
+ print "\t%s = %s" % (radius_util.client_ldap_attr_to_name[attr], value)
except xmlrpclib.Fault, f:
print f.faultString
diff --git a/ipa-admintools/ipa-radiusclientmod b/ipa-admintools/ipa-radiusclientmod
index 3f40b7b7..9f5d8d75 100644
--- a/ipa-admintools/ipa-radiusclientmod
+++ b/ipa-admintools/ipa-radiusclientmod
@@ -19,13 +19,14 @@
#
import sys
+import os
from optparse import OptionParser
-import ipa
from ipa.radius_client import *
import ipa.ipaclient as ipaclient
-import ipa.ipavalidate as ipavalidate
+import ipa.ipautil as ipautil
import ipa.config
import ipa.ipaerror
+import ipa.radius_util as radius_util
import xmlrpclib
import kerberos
@@ -33,49 +34,76 @@ import ldap
#------------------------------------------------------------------------------
-def parse_options():
- parser = OptionParser()
- parser.add_option("--usage", action="store_true",
- help="Program usage")
- parser.add_option("-s", "--secret", dest="secret",
- help="RADIUS client secret (required)")
- parser.add_option("-n", "--name", dest="name",
- help="RADIUS client name")
- parser.add_option("-t", "--type", dest="nastype",
- help="RADIUS client NAS Type")
- parser.add_option("-d", "--description", dest="desc",
- help="description of the RADIUS client")
+attrs = radius_util.client_name_to_ldap_attr.keys()
+mandatory_attrs = ['Client-IP-Address']
- args = ipa.config.init_config(sys.argv)
- options, args = parser.parse_args(args)
+#------------------------------------------------------------------------------
- return options, args
+def help_option_callback(option, opt_str, value, parser, *args, **kwargs):
+ parser.print_help()
+ print
+ print "Valid interative attributes are:"
+ print ipautil.format_list(attrs, quote='"')
+ print
+ print "Required attributes are:"
+ print ipautil.format_list(mandatory_attrs, quote='"')
+ sys.exit(0)
#------------------------------------------------------------------------------
-# FIXME
-def usage():
- print "ipa-radiusclientmod ip_addr"
- sys.exit(1)
-
def main():
- ip_addr = None
- secret = None
- name = None
- nastype = None
- desc = None
+ 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.add_option("-i", "--interactive", dest="interactive", action='store_true', default=False,
+ help="interactive mode, prompts with auto-completion")
+ opt_parser.add_option("-n", "--name", dest="name",
+ help="RADIUS client name")
+ opt_parser.add_option("-t", "--type", dest="nastype",
+ help="RADIUS client NAS Type")
+ opt_parser.add_option("-d", "--description", dest="desc",
+ help="description of the RADIUS client")
- options, args = parse_options()
+ #FIXME interactive vs. non-interactive usage
+ opt_parser.set_usage("Usage: %s [options] %s" % (os.path.basename(sys.argv[0]), ' '.join(mandatory_attrs)))
+ #FIXME, map options name to our name?
+ #FIXME if mandatory is on command line remove it from mandatory passed to completer
- if len(args) != 2:
- usage()
+ args = ipa.config.init_config(sys.argv)
+ options, args = opt_parser.parse_args(args)
+
+ if options.interactive:
+ c = ipautil.AttributeValueCompleter(attrs)
+ c.open()
+ pairs = c.get_pairs("Enter: ", mandatory_attrs, validate)
+ c.close()
+ else:
+ pairs = {}
+
+ if False and len(args) != 2:
+ print "wrong number of arguments"
+ opt_parser.print_help()
+ sys.exit(1)
+
+ pairs['Client-IP-Address'] = args[1]
+ pairs['Secret'] = args[2]
+ if options.name: pairs['Name'] = options.name
+ if options.nastype: pairs['NAS-Type'] = options.nastype
+ if options.desc: pairs['Description'] = options.desc
+
+ for name,value in pairs.items():
+ if not validate(name, value): return 1
ip_addr = args[1]
+
radius_client = ipa.radius_client.RadiusClient()
ipa_client = ipaclient.IPAClient()
try:
- radius_client = ipa_client.get_radius_client_by_ip_addr(ip_addr)
+ #radius_client = ipa_client.get_radius_client_by_ip_addr(ip_addr)
+ dn = radius_util.radius_client_dn(ip_addr, 'dc=ipatest,dc=jrd')
+ print dn
+ radius_client = ipa_client.get_entry_by_dn(dn)
pass
except ipa.ipaerror.exception_for(ipa.ipaerror.LDAP_NOT_FOUND):
print "client %s not found" % ip_addr
@@ -87,6 +115,8 @@ def main():
print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
return 1
+ sys.exit(0)
+
if options.secret:
secret = options.secret
if not validate_secret(secret): return 1
diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py
index f487bec2..e9b0002f 100644
--- a/ipa-python/ipaclient.py
+++ b/ipa-python/ipaclient.py
@@ -334,29 +334,29 @@ class IPAClient:
return entries
# radius support
- def get_radius_client_by_ip_addr(self,ip_addr,sattrs=None):
- result = self.transport.get_radius_client_by_ip_addr(ip_addr,sattrs)
+ 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_client.RadiusClient(result)
- def add_radius_client(self,client):
+ 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)
+ 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):
- return self.transport.delete_radius_client(ip_addr)
+ def delete_radius_client(self, ip_addr, container=None):
+ return self.transport.delete_radius_client(ip_addr, container)
- def find_radius_clients(self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
- result = self.transport.find_radius_clients(criteria, sattrs, searchlimit, timelimit)
+ 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]
diff --git a/ipa-python/ipautil.py b/ipa-python/ipautil.py
index e7f59419..407406de 100644
--- a/ipa-python/ipautil.py
+++ b/ipa-python/ipautil.py
@@ -25,6 +25,10 @@ import logging
import subprocess
import os
import stat
+import copy
+import readline
+import traceback
+from types import *
from string import lower
import re
@@ -331,3 +335,329 @@ def parse_generalized_time(timestr):
except ValueError:
return None
+
+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 text
+
+ 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("([^\s=]+)\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
+
+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 = copy.copy(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._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 _debug(self):
+ print >> output_fd, "lhs='%s' lhs_complete=%s operator='%s' operator_complete=%s rhs='%s'" % \
+ (self.lhs, self.lhs_complete, self.operator, self.operator_complete, self.rhs)
+
+
+ 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")
+ 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):
+ pairs = {}
+ if mandatory_attrs:
+ mandatory_attrs_remaining = copy.copy(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
+
+ pairs[attribute] = value
+ return pairs
diff --git a/ipa-python/radius_client.py b/ipa-python/radius_client.py
index 2709c4d9..907e0210 100644
--- a/ipa-python/radius_client.py
+++ b/ipa-python/radius_client.py
@@ -21,6 +21,7 @@ import getpass
import re
from ipa.entity import Entity
+import ipa.ipavalidate as ipavalidate
__all__ = ['RadiusClient',
'get_secret',
@@ -29,6 +30,7 @@ __all__ = ['RadiusClient',
'validate_name',
'validate_nastype',
'validate_desc',
+ 'validate',
]
#------------------------------------------------------------------------------
@@ -41,7 +43,10 @@ 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_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"
#------------------------------------------------------------------------------
@@ -101,38 +106,60 @@ def validate_length(value, limits):
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):
+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):
+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):
+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):
+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):
+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/radius_util.py b/ipa-python/radius_util.py
new file mode 100644
index 00000000..482b9f19
--- /dev/null
+++ b/ipa-python/radius_util.py
@@ -0,0 +1,231 @@
+# 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 ldap.filter
+
+from ipa import ipautil
+
+
+__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',
+
+ 'clients_container',
+ 'radius_clients_basedn',
+ 'radius_client_filter',
+ 'radius_client_dn',
+
+ 'profiles_container',
+ 'radius_profiles_basedn',
+ 'radius_profile_filter',
+ 'radius_profile_dn',
+
+ 'client_ldap_attr_to_name',
+ 'client_name_to_ldap_attr',
+
+ 'read_pairs_file',
+]
+
+#------------------------------------------------------------------------------
+
+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'
+
+#------------------------------------------------------------------------------
+
+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
+
+#------------------------------------------------------------------------------
+
+client_ldap_attr_to_name = ipautil.CIDict({
+ 'radiusClientIPAddress' : 'Client-IP-Address',
+ 'radiusClientSecret' : 'Secret',
+ 'radiusClientNASType' : 'NAS-Type',
+ 'radiusClientShortName' : 'Name',
+ 'description' : 'Description',
+ })
+
+client_name_to_ldap_attr = reverse_map_dict(client_ldap_attr_to_name)
+
+#------------------------------------------------------------------------------
+
+profile_ldap_attr_to_name = {
+ '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',
+}
+
+profile_name_to_ldap_attr = reverse_map_dict(profile_ldap_attr_to_name)
+
+#------------------------------------------------------------------------------
+
+clients_container = 'cn=clients,cn=radius,cn=services,cn=etc'
+
+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,cn=services,cn=etc'
+
+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)
+
+
+#------------------------------------------------------------------------------
+
+comment_re = re.compile('#.*$', re.MULTILINE)
+def read_pairs_file(filename):
+ if filename == '-':
+ fd = sys.stdin
+ else:
+ fd = open(filename)
+ data = fd.read()
+ data = comment_re.sub('', data) # kill comments
+ pairs = ipautil.parse_key_value_pairs(data)
+ if fd != sys.stdin: fd.close()
+ return pairs
+
+
+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-python/rpcclient.py b/ipa-python/rpcclient.py
index e756058a..531bf72b 100644
--- a/ipa-python/rpcclient.py
+++ b/ipa-python/rpcclient.py
@@ -594,12 +594,12 @@ class RPCClient:
# radius support
- def get_radius_client_by_ip_addr(self,ip_addr,sattrs=None):
+ def get_radius_client_by_ip_addr(self,ip_addr, container, sattrs=None):
server = self.setup_server()
- if sattrs is None:
- sattrs = "__NONE__"
+ if container is None: container = "__NONE__"
+ if sattrs is None: sattrs = "__NONE__"
try:
- result = server.get_radius_client_by_ip_addr(ip_addr, sattrs)
+ 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):
@@ -607,11 +607,13 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
- def add_radius_client(self,client):
+ 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))
+ 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):
@@ -633,11 +635,12 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
- def delete_radius_client(self,ip_addr):
+ 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)
+ 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):
@@ -645,13 +648,14 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
- def find_radius_clients(self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
+ 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, sattrs, searchlimit, timelimit)
+ 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):
diff --git a/ipa-server/ipa-install/share/60radius.ldif b/ipa-server/ipa-install/share/60radius.ldif
index 47692352..3562312a 100644
--- a/ipa-server/ipa-install/share/60radius.ldif
+++ b/ipa-server/ipa-install/share/60radius.ldif
@@ -492,7 +492,7 @@ objectClasses:
NAME 'radiusprofile'
SUP top AUXILIARY
DESC ''
- MUST cn
+ MUST uid
MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $
radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $
radiusCalledStationId $ radiusCallingStationId $ radiusClass $
@@ -527,14 +527,6 @@ objectClasses:
MAY ( uid $ userPassword $ description )
)
attributeTypes:
- ( 1.3.6.1.4.1.3317.4.3.1.63
- NAME 'radiusClientNASIpAddress'
- 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.64
NAME 'radiusClientSecret'
DESC ''
@@ -564,6 +556,6 @@ objectClasses:
NAME 'radiusClientProfile'
SUP top STRUCTURAL
DESC 'A Container Objectclass to be used for describing radius clients'
- MUST (radiusClientNASIpAddress $ radiusClientSecret)
+ 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 fcc2506d..df59bc0e 100644
--- a/ipa-server/ipa-install/share/bootstrap-template.ldif
+++ b/ipa-server/ipa-install/share/bootstrap-template.ldif
@@ -92,11 +92,11 @@ objectClass: nsContainer
objectClass: top
cn: profiles
-dn: cn=ipa_default, cn=profiles,cn=radius,cn=services,cn=etc,$SUFFIX
+dn: uid=ipa_default, cn=profiles,cn=radius,cn=services,cn=etc,$SUFFIX
changetype: add
objectClass: top
objectClass: radiusprofile
-cn: ipa_default
+uid: ipa_default
dn: cn=admins,cn=groups,cn=accounts,$SUFFIX
changetype: add
diff --git a/ipa-server/ipaserver/radiusinstance.py b/ipa-server/ipaserver/radiusinstance.py
index 8317da03..0c94c713 100644
--- a/ipa-server/ipaserver/radiusinstance.py
+++ b/ipa-server/ipaserver/radiusinstance.py
@@ -26,6 +26,7 @@ import logging
import pwd
import time
from ipa.ipautil import *
+from ipa import radius_util
import service
@@ -33,18 +34,6 @@ 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
@@ -58,7 +47,7 @@ def ldap_mod(fd, dn, pwd):
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
@@ -86,7 +75,7 @@ class RadiusInstance(service.Service):
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.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()
@@ -117,34 +106,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' : '',
'ACCESS_ATTRIBUTE_DEFAULT' : 'TRUE',
- 'CLIENTS_BASEDN' : 'cn=clients,cn=radius,cn=services,cn=etc,%s' % self.suffix,
+ '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")
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()
@@ -152,7 +141,7 @@ 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:
@@ -160,10 +149,10 @@ class RadiusInstance(service.Service):
os.exit()
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):
@@ -179,27 +168,3 @@ class RadiusInstance(service.Service):
#-------------------------------------------------------------------------------
-# 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 26fdba48..aa557f79 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -30,6 +30,7 @@ import xmlrpclib
import copy
import attrs
from ipa import ipaerror
+from ipa import radius_util
import string
from types import *
@@ -458,41 +459,40 @@ class IPAServer:
# radius support
- # FIXME, why not just use get_entry_by_dn?
- def get_radius_client_by_ip_addr(self, ip_addr, sattrs=None, opts=None):
- ip_addr = self.__safe_filter(ip_addr)
- basedn = 'cn=clients,cn=radius,cn=services,cn=etc,%s' % self.basedn # FIXME, should not be hardcoded
- filter = "(&(radiusClientNASIpAddress=%s)(objectclass=radiusClientProfile))" % ip_addr
+ # 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 __is_radius_client_unique(self, ip_addr, opts):
- """Return 1 if the radius client is unique in the tree, 0 otherwise."""
- ip_addr = self.__safe_filter(ip_addr)
- basedn = 'cn=clients,cn=radius,cn=services,cn=etc,%s' % self.basedn # FIXME, should not be hardcoded
- filter = "(&(radiusClientNASIpAddress=%s)(objectclass=radiusClientProfile))" % ip_addr
+ 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 0
+ return True
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
- return 1
+ return False
- def add_radius_client (self, client, opts=None):
- client_container = 'cn=clients,cn=radius,cn=services,cn=etc' # FIXME, should not be hardcoded
- if self.__is_radius_client_unique(client['radiusClientNASIpAddress'], opts) == 0:
- raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
+ def add_radius_client (self, client, container=None, opts=None):
+ if container is None:
+ container = radius_util.clients_container
- dn="radiusClientNASIpAddress=%s,%s,%s" % (ldap.dn.escape_dn_chars(client['radiusClientNASIpAddress']),
- client_container,self.basedn)
+ 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 u in client:
- entry.setValues(u, client[u])
+ for attr in client:
+ entry.setValues(attr, client[attr])
conn = self.getConnection(opts)
try:
@@ -504,8 +504,8 @@ class IPAServer:
def update_radius_client(self, oldentry, newentry, opts=None):
return self.update_entry(oldentry, newentry, opts)
- def delete_radius_client(self, ip_addr, opts=None):
- client = self.get_radius_client_by_ip_addr(ip_addr, ['dn', 'cn'], 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)
@@ -516,7 +516,7 @@ class IPAServer:
self.releaseConnection(conn)
return res
- def find_radius_clients(self, ip_attrs, sattrs=None, searchlimit=0, timelimit=-1, opts=None):
+ 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)))
@@ -527,8 +527,8 @@ class IPAServer:
filter = "(&(objectclass=%s)(|%s))" % (objectclass, attrs)
return filter
- basedn = 'cn=clients,cn=radius,cn=services,cn=etc,%s' % self.basedn # FIXME, should not be hardcoded
- filter = gen_filter('radiusClientProfile', 'radiusClientNASIpAddress', ip_attrs)
+ basedn = radius_util.radius_clients_basedn(container, self.basedn)
+ filter = gen_filter('radiusClientProfile', 'radiusClientIPAddress', ip_attrs)
conn = self.getConnection(opts)
try:
try:
@@ -546,6 +546,111 @@ class IPAServer:
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, uid, user_profile=True, opts=None):
+ if self.__radius_profile_exists(profile['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', 'radiusClientProfile')
+
+ # 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
+
+ uid = self.__safe_filter(uid)
+ filter = gen_filter('radiusClientProfile' '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 get_add_schema (self):
"""Get the list of fields to be used when adding users in the GUI."""
diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py
index d5bdb6b2..ef48f4aa 100644
--- a/ipa-server/xmlrpc-server/ipaxmlrpc.py
+++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py
@@ -356,6 +356,11 @@ def handler(req, profiling=False):
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