From 993f76fe6035cf59cceb88f3611fc53680738007 Mon Sep 17 00:00:00 2001 From: "rcritten@redhat.com" Date: Mon, 6 Aug 2007 10:05:53 -0400 Subject: [PATCH] - Abstracted client class to work directly or over RPC - Add mod_auth_kerb and cyrus-sasl-gssapi to Requires - Remove references to admin server in ipa-server-setupssl - Generate a client certificate for the XML-RPC server to connect to LDAP with - Create a keytab for Apache - Create an ldif with a test user - Provide a certmap.conf for doing SSL client authentication - Update tools to use kerberos - Add User class --- ipa-admintools/freeipa-admintools.spec | 11 +- ipa-admintools/freeipa-admintools.spec.in | 11 +- ipa-admintools/ipa-adduser | 12 +- ipa-admintools/ipa-finduser | 30 +- ipa-python/freeipa-python.spec | 9 +- ipa-python/freeipa-python.spec.in | 9 +- ipa-python/ipaclient.py | 87 +++++ ipa-python/krbtransport.py | 55 +++ ipa-python/rpcclient.py | 150 ++++---- ipa-python/user.py | 112 ++++++ ipa-server/freeipa-server.spec | 15 +- ipa-server/freeipa-server.spec.in | 15 +- ipa-server/ipa-install/Makefile | 3 +- ipa-server/ipa-install/ipa-server-install | 3 + ipa-server/ipa-install/ipa-server-setupssl | 68 +--- .../ipa-install/share/bootstrap-template.ldif | 23 ++ .../ipa-install/share/certmap.conf.template | 82 +++++ ipa-server/ipa-install/share/default-aci.ldif | 3 + ipa-server/ipa-install/test/Makefile | 8 + ...st-users.ldif => test-users-template.ldif} | 12 +- ipa-server/ipaserver/dsinstance.py | 22 +- ipa-server/ipaserver/ipaldap.py | 77 +++- ipa-server/ipaserver/krbinstance.py | 18 + ipa-server/xmlrpc-server/funcs.py | 329 ++++++++++-------- ipa-server/xmlrpc-server/ipa.conf | 18 +- ipa-server/xmlrpc-server/ipaxmlrpc.py | 17 +- 26 files changed, 877 insertions(+), 322 deletions(-) create mode 100644 ipa-python/ipaclient.py create mode 100644 ipa-python/krbtransport.py create mode 100644 ipa-python/user.py create mode 100644 ipa-server/ipa-install/share/certmap.conf.template create mode 100644 ipa-server/ipa-install/test/Makefile rename ipa-server/ipa-install/test/{test-users.ldif => test-users-template.ldif} (60%) diff --git a/ipa-admintools/freeipa-admintools.spec b/ipa-admintools/freeipa-admintools.spec index bcd3d9d29..904a3b693 100755 --- a/ipa-admintools/freeipa-admintools.spec +++ b/ipa-admintools/freeipa-admintools.spec @@ -1,6 +1,6 @@ Name: freeipa-admintools Version: 0.1.0 -Release: 1%{?dist} +Release: 3%{?dist} Summary: FreeIPA authentication server Group: System Environment/Base @@ -36,7 +36,12 @@ rm -rf %{buildroot} %changelog +* Mon Aug 5 2007 Rob Crittenden - 0.1.0-3 +- Abstracted client class to work directly or over RPC + +* Wed Aug 1 2007 Rob Crittenden - 0.1.0-2 +- Update tools to do kerberos +- Add User class + * Fri Jul 27 2007 Karl MacMillan - 0.1.0-1 - Initial rpm version - - diff --git a/ipa-admintools/freeipa-admintools.spec.in b/ipa-admintools/freeipa-admintools.spec.in index bcd3d9d29..904a3b693 100755 --- a/ipa-admintools/freeipa-admintools.spec.in +++ b/ipa-admintools/freeipa-admintools.spec.in @@ -1,6 +1,6 @@ Name: freeipa-admintools Version: 0.1.0 -Release: 1%{?dist} +Release: 3%{?dist} Summary: FreeIPA authentication server Group: System Environment/Base @@ -36,7 +36,12 @@ rm -rf %{buildroot} %changelog +* Mon Aug 5 2007 Rob Crittenden - 0.1.0-3 +- Abstracted client class to work directly or over RPC + +* Wed Aug 1 2007 Rob Crittenden - 0.1.0-2 +- Update tools to do kerberos +- Add User class + * Fri Jul 27 2007 Karl MacMillan - 0.1.0-1 - Initial rpm version - - diff --git a/ipa-admintools/ipa-adduser b/ipa-admintools/ipa-adduser index 8c308b084..af922833d 100644 --- a/ipa-admintools/ipa-adduser +++ b/ipa-admintools/ipa-adduser @@ -21,10 +21,11 @@ import sys from optparse import OptionParser import ipa -import ipa.rpcclient +import ipa.ipaclient as ipaclient import ipa.config import xmlrpclib +import kerberos def usage(): print "ipa-adduser [-c|--gecos STRING] [-d|--directory STRING] [-f|--firstname STRING] [-l|--lastname STRING] user" @@ -73,10 +74,15 @@ def main(): user['loginshell'] = "/bin/bash" try: - ipa.rpcclient.add_user(user) - print args[0] + " successfully added" + client = ipaclient.IPAClient() + client.add_user(user) + print args[1] + " 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 return 0 diff --git a/ipa-admintools/ipa-finduser b/ipa-admintools/ipa-finduser index 0892791c4..a54e141e7 100644 --- a/ipa-admintools/ipa-finduser +++ b/ipa-admintools/ipa-finduser @@ -20,13 +20,12 @@ import sys from optparse import OptionParser -import ipa -import ipa.rpcclient +import ipa.ipaclient as ipaclient import ipa.config -import base64 import sys import xmlrpclib +import kerberos def usage(): print "ipa-finduser " @@ -48,16 +47,27 @@ def main(): usage() try: - ent = ipa.rpcclient.get_user(args[1]) - for name, value in ent.items(): - if isinstance(value, str): - print name + ": " + value + client = ipaclient.IPAClient() + ent = client.get_user(args[1]) + attr = ent.attrList() + + print "dn: " + ent.dn + + for a in attr: + value = ent.getValues(a) + if isinstance(value,str): + print a + ": " + value else: - print name + ": " - for x in value: - print "\t" + x + print a + ": " + for l in value: + print "\t" + l + except xmlrpclib.Fault, fault: print fault.faultString + return 1 + except kerberos.GSSError, e: + print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0]) + return 1 return 0 diff --git a/ipa-python/freeipa-python.spec b/ipa-python/freeipa-python.spec index 8339cf74a..e9b1e708d 100755 --- a/ipa-python/freeipa-python.spec +++ b/ipa-python/freeipa-python.spec @@ -1,6 +1,6 @@ Name: freeipa-python Version: 0.1.0 -Release: 1%{?dist} +Release: 3%{?dist} Summary: FreeIPA authentication server Group: System Environment/Base @@ -42,6 +42,13 @@ rm -rf %{buildroot} %changelog +* Mon Aug 5 2007 Rob Crittenden - 0.1.0-3 +- Abstracted client class to work directly or over RPC + +* Wed Aug 1 2007 Rob Crittenden - 0.1.0-2 +- Add User class +- Add kerberos authentication to the XML-RPC request made from tools. + * Fri Jul 27 2007 Karl MacMillan - 0.1.0-1 - Initial rpm version diff --git a/ipa-python/freeipa-python.spec.in b/ipa-python/freeipa-python.spec.in index bec58819d..b0a373085 100755 --- a/ipa-python/freeipa-python.spec.in +++ b/ipa-python/freeipa-python.spec.in @@ -1,6 +1,6 @@ Name: freeipa-python Version: VERSION -Release: 1%{?dist} +Release: 3%{?dist} Summary: FreeIPA authentication server Group: System Environment/Base @@ -42,6 +42,13 @@ rm -rf %{buildroot} %changelog +* Mon Aug 5 2007 Rob Crittenden - 0.1.0-3 +- Abstracted client class to work directly or over RPC + +* Wed Aug 1 2007 Rob Crittenden - 0.1.0-2 +- Add User class +- Add kerberos authentication to the XML-RPC request made from tools. + * Fri Jul 27 2007 Karl MacMillan - 0.1.0-1 - Initial rpm version diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py new file mode 100644 index 000000000..c75b5bc96 --- /dev/null +++ b/ipa-python/ipaclient.py @@ -0,0 +1,87 @@ +#! /usr/bin/python -E +# Authors: Rob Crittenden +# +# 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 +# + +#!/usr/bin/python + +import sys +sys.path.append("/usr/share/ipa") + +from ipaserver import funcs +import ipa.rpcclient as rpcclient +import user +import ipa +import config + +class IPAClient: + + def __init__(self,local=None): + self.local = local + ipa.config.init_config() + if local: + self.transport = funcs.IPAServer() + # client needs to call set_principal(user@REALM) + else: + self.transport = rpcclient.RPCClient() + + def set_principal(self,princ): + if self.local: + self.transport.set_principal(princ) + + def get_user(self,uid): + result = self.transport.get_user(uid) + return user.User(result) + + def add_user(self,user): + + realm = config.config.get_realm() + + # FIXME: This should be dynamic and can include just about anything + # Let us add in some missing attributes + if user.get('homeDirectory') is None: + user['homeDirectory'] ='/home/%s' % user['uid'] + if user.get('gecos') is None: + user['gecos'] = user['uid'] + + # FIXME: This can be removed once the DS plugin is installed + user['uidNumber'] ='501' + + # FIXME: What is the default group for users? + user['gidNumber'] ='501' + user['krbPrincipalName'] = "%s@%s" % (user['uid'], realm) + user['cn'] = "%s %s" % (user['givenName'], user['sn']) + if user.get('gn'): + del user['gn'] + + result = self.transport.add_user(user) + return result + + def get_all_users(self): + result = self.transport.get_all_users() + + all_users = [] + for (attrs) in result: + if attrs is not None: + all_users.append(user.User(attrs)) + + return all_users + + def get_add_schema(self): + result = self.transport.get_add_schema() + return result diff --git a/ipa-python/krbtransport.py b/ipa-python/krbtransport.py new file mode 100644 index 000000000..dbb8ec34e --- /dev/null +++ b/ipa-python/krbtransport.py @@ -0,0 +1,55 @@ +#! /usr/bin/python -E +# Authors: Rob Crittenden +# +# 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 +# + +#!/usr/bin/python + +import httplib +import xmlrpclib +import kerberos +from kerberos import GSSError + +class KerbTransport(xmlrpclib.Transport): + """Handles Kerberos Negotiation authentication to an XML-RPC server.""" + + def get_host_info(self, host): + + host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(self, host) + + # Set the remote host principal + h = host + hostinfo = h.split(':') + service = "HTTP@" + hostinfo[0] + + try: + rc, vc = kerberos.authGSSClientInit(service); + except kerberos.GSSError, e: + raise GSSError(e) + + try: + kerberos.authGSSClientStep(vc, ""); + except kerberos.GSSError, e: + raise GSSError(e) + + extra_headers = [ + ("Authorization", "negotiate %s" % kerberos.authGSSClientResponse(vc) ) + ] + + return host, extra_headers, x509 + diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index 8834a82f0..d4e645e1e 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -20,85 +20,101 @@ #!/usr/bin/python -try: - import krbV -except ImportError: - pass import xmlrpclib import socket import config +from krbtransport import KerbTransport +from kerberos import GSSError +import os +import base64 +import user +import ipa # Some errors to catch # http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto -def server_url(): - return "http://" + config.config.get_server() + "/ipa" +class RPCClient: -def setup_server(): - return xmlrpclib.ServerProxy(server_url()) + def __init__(self): + ipa.config.init_config() -def get_user(username): - """Get a specific user""" - server = setup_server() - try: - result = server.get_user(username) - myuser = result - except xmlrpclib.Fault, fault: - raise xmlrpclib.Fault(fault.faultCode, fault.faultString) - return None - except socket.error, (value, msg): - raise xmlrpclib.Fault(value, msg) - return None + def server_url(self): + return "http://" + config.config.get_server() + "/ipa" - return myuser + def setup_server(self): + return xmlrpclib.ServerProxy(self.server_url(), KerbTransport()) -def add_user(user): - """Add a new user""" - server = setup_server() + def convert_entry(self,ent): + # Convert into a dict. We need to handle multi-valued attributes as well + # so we'll convert those into lists. + user={} + for (k) in ent: + k = k.lower() + if user.get(k) is not None: + if isinstance(user[k],list): + user[k].append(ent[k].strip()) + else: + first = user[k] + user[k] = () + user[k].append(first) + user[k].append(ent[k].strip()) + else: + user[k] = ent[k] + + return user + + def get_user(self,username): + """Get a specific user""" + server = self.setup_server() + try: + result = server.get_user(username) + except xmlrpclib.Fault, fault: + raise xmlrpclib.Fault(fault.faultCode, fault.faultString) + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) - # FIXME: Get the realm from somewhere - realm = config.config.get_realm() - - # FIXME: This should be dynamic and can include just about anything - # Let us add in some missing attributes - if user.get('homeDirectory') is None: - user['homeDirectory'] ='/home/%s' % user['uid'] - if user.get('gecos') is None: - user['gecos'] = user['uid'] - - # FIXME: This can be removed once the DS plugin is installed - user['uidNumber'] ='501' - - # FIXME: What is the default group for users? - user['gidNumber'] ='501' - user['krbPrincipalName'] = "%s@%s" % (user['uid'], realm) - user['cn'] = "%s %s" % (user['givenName'], user['sn']) - - try: - result = server.add_user(user) return result - except xmlrpclib.Fault, fault: - raise xmlrpclib.Fault(fault.faultCode, fault.faultString) - return None - except socket.error, (value, msg): - raise xmlrpclib.Fault(value, msg) - return None + + + def add_user(self,user): + """Add a new user""" + server = self.setup_server() -def get_add_schema(): - """Get the list of attributes we need to ask when adding a new - user. - """ - server = setup_server() + try: + result = server.add_user(user) + except xmlrpclib.Fault, fault: + raise xmlrpclib.Fault(fault.faultCode, fault.faultString) + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) + + return result + + def get_add_schema(self): + """Get the list of attributes we need to ask when adding a new + user. + """ + server = self.setup_server() + + # FIXME: Hardcoded and designed for the TurboGears GUI. Do we want + # this for the CLI as well? + try: + result = server.get_add_schema() + except xmlrpclib.Fault, fault: + raise xmlrpclib.Fault(fault.faultCode, fault.faultString) + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) + + return result - # FIXME: Hardcoded and designed for the TurboGears GUI. Do we want - # this for the CLI as well? - try: - result = server.get_add_schema() - except xmlrpclib.Fault, fault: - raise xmlrpclib.Fault(fault, fault.faultString) - return None - except socket.error, (value, msg): - raise xmlrpclib.Fault(value, msg) - return None - - return result + def get_all_users (self): + """Return a list containing a User object for each existing user.""" + + server = self.setup_server() + try: + result = server.get_all_users() + except xmlrpclib.Fault, fault: + raise xmlrpclib.Fault(fault.faultCode, fault.faultString) + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) + + return result diff --git a/ipa-python/user.py b/ipa-python/user.py new file mode 100644 index 000000000..6162354be --- /dev/null +++ b/ipa-python/user.py @@ -0,0 +1,112 @@ +import ldap +import ldif +import re +import cStringIO + +class User: + """This class represents an IPA user. An LDAP entry consists of a DN + and a list of attributes. Each attribute consists of a name and a list of + values. For the time being I will maintain this. + + In python-ldap, entries are returned as a list of 2-tuples. + Instance variables: + dn - string - the string DN of the entry + data - cidict - case insensitive dict of the attributes and values""" + + def __init__(self,entrydata): + """data is the raw data returned from the python-ldap result method, + which is a search result entry or a reference or None. + If creating a new empty entry, data is the string DN.""" + if entrydata: + if isinstance(entrydata,tuple): + self.dn = entrydata[0] + self.data = ldap.cidict.cidict(entrydata[1]) + elif isinstance(entrydata,str): + self.dn = entrydata + self.data = ldap.cidict.cidict() + elif isinstance(entrydata,dict): + self.dn = entrydata['dn'] + del entrydata['dn'] + self.data = ldap.cidict.cidict(entrydata) + else: + self.dn = '' + self.data = ldap.cidict.cidict() + + def __nonzero__(self): + """This allows us to do tests like if entry: returns false if there is no data, + true otherwise""" + return self.data != None and len(self.data) > 0 + + def hasAttr(self,name): + """Return True if this entry has an attribute named name, False otherwise""" + return self.data and self.data.has_key(name) + + def __getattr__(self,name): + """If name is the name of an LDAP attribute, return the first value for that + attribute - equivalent to getValue - this allows the use of + entry.cn + instead of + entry.getValue('cn') + This also allows us to return None if an attribute is not found rather than + throwing an exception""" + return self.getValue(name) + + def getValues(self,name): + """Get the list (array) of values for the attribute named name""" + return self.data.get(name) + + def getValue(self,name): + """Get the first value for the attribute named name""" + value = self.data.get(name,[None]) + if isinstance(value[0],list) or isinstance(value[0],tuple): + return value[0] + else: + return value + + def setValue(self,name,*value): + """Value passed in may be a single value, several values, or a single sequence. + For example: + ent.setValue('name', 'value') + ent.setValue('name', 'value1', 'value2', ..., 'valueN') + ent.setValue('name', ['value1', 'value2', ..., 'valueN']) + ent.setValue('name', ('value1', 'value2', ..., 'valueN')) + Since *value is a tuple, we may have to extract a list or tuple from that + tuple as in the last two examples above""" + if isinstance(value[0],list) or isinstance(value[0],tuple): + self.data[name] = value[0] + else: + self.data[name] = value + + setValues = setValue + + def toTupleList(self): + """Convert the attrs and values to a list of 2-tuples. The first element + of the tuple is the attribute name. The second element is either a + single value or a list of values.""" + return self.data.items() + + def attrList(self): + """Return a list of all attributes in the entry""" + return self.data.keys() + +# def __str__(self): +# """Convert the Entry to its LDIF representation""" +# return self.__repr__() +# +# # the ldif class base64 encodes some attrs which I would rather see in raw form - to +# # encode specific attrs as base64, add them to the list below +# ldif.safe_string_re = re.compile('^$') +# base64_attrs = ['nsstate', 'krbprincipalkey', 'krbExtraData'] +# +# def __repr__(self): +# """Convert the Entry to its LDIF representation""" +# sio = cStringIO.StringIO() +# # what's all this then? the unparse method will currently only accept +# # a list or a dict, not a class derived from them. self.data is a +# # cidict, so unparse barfs on it. I've filed a bug against python-ldap, +# # but in the meantime, we have to convert to a plain old dict for printing +# # I also don't want to see wrapping, so set the line width really high (1000) +# newdata = {} +# newdata.update(self.data) +# ldif.LDIFWriter(sio,User.base64_attrs,1000).unparse(self.dn,newdata) +# return sio.getvalue() diff --git a/ipa-server/freeipa-server.spec b/ipa-server/freeipa-server.spec index 4801eb7fe..8348e4b9c 100755 --- a/ipa-server/freeipa-server.spec +++ b/ipa-server/freeipa-server.spec @@ -1,6 +1,6 @@ Name: freeipa-server Version: 0.1.0 -Release: 1%{?dist} +Release: 3%{?dist} Summary: FreeIPA authentication server Group: System Environment/Base @@ -10,7 +10,7 @@ Source0: %{name}-%{version}.tgz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch -Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python python-ldap freeipa-python +Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python mod_auth_kerb python-ldap freeipa-python cyrus-sasl-gssapi %define httpd_conf /etc/httpd/conf.d @@ -44,6 +44,17 @@ rm -rf %{buildroot} %changelog +* Mon Aug 5 2007 Rob Crittenden - 0.1.0-3 +- Abstracted client class to work directly or over RPC + +* Wed Aug 1 2007 Rob Crittenden - 0.1.0-2 +- Add mod_auth_kerb and cyrus-sasl-gssapi to Requires +- Remove references to admin server in ipa-server-setupssl +- Generate a client certificate for the XML-RPC server to connect to LDAP with +- Create a keytab for Apache +- Create an ldif with a test user +- Provide a certmap.conf for doing SSL client authentication + * Fri Jul 27 2007 Karl MacMillan - 0.1.0-1 - Initial rpm version diff --git a/ipa-server/freeipa-server.spec.in b/ipa-server/freeipa-server.spec.in index 16aff06b7..4de5207bc 100644 --- a/ipa-server/freeipa-server.spec.in +++ b/ipa-server/freeipa-server.spec.in @@ -1,6 +1,6 @@ Name: freeipa-server Version: VERSION -Release: 1%{?dist} +Release: 3%{?dist} Summary: FreeIPA authentication server Group: System Environment/Base @@ -10,7 +10,7 @@ Source0: %{name}-%{version}.tgz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch -Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python python-ldap freeipa-python +Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python mod_auth_kerb python-ldap freeipa-python cyrus-sasl-gssapi %define httpd_conf /etc/httpd/conf.d @@ -44,6 +44,17 @@ rm -rf %{buildroot} %changelog +* Mon Aug 5 2007 Rob Crittenden - 0.1.0-3 +- Abstracted client class to work directly or over RPC + +* Wed Aug 1 2007 Rob Crittenden - 0.1.0-2 +- Add mod_auth_kerb and cyrus-sasl-gssapi to Requires +- Remove references to admin server in ipa-server-setupssl +- Generate a client certificate for the XML-RPC server to connect to LDAP with +- Create a keytab for Apache +- Create an ldif with a test user +- Provide a certmap.conf for doing SSL client authentication + * Fri Jul 27 2007 Karl MacMillan - 0.1.0-1 - Initial rpm version diff --git a/ipa-server/ipa-install/Makefile b/ipa-server/ipa-install/Makefile index 0d4953976..877ae09cc 100644 --- a/ipa-server/ipa-install/Makefile +++ b/ipa-server/ipa-install/Makefile @@ -6,7 +6,8 @@ install: install -m 755 ipa-server-install $(SBINDIR) install -m 755 ipa-server-setupssl $(SBINDIR) $(MAKE) -C share $@ + $(MAKE) -C test $@ clean: $(MAKE) -C share $@ - rm -f *~ *.pyc \ No newline at end of file + rm -f *~ *.pyc diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install index fbf3fd054..2fa9182bc 100644 --- a/ipa-server/ipa-install/ipa-server-install +++ b/ipa-server/ipa-install/ipa-server-install @@ -119,6 +119,9 @@ def main(): # Restart apache run(["/sbin/service", "httpd", "restart"]) + # Set apache to be on at boot + run(["/sbin/chkconfig", "httpd", "on"]) + # Create the config file fd = open("/etc/ipa/ipa.conf", "w") fd.write("[defaults]\n") diff --git a/ipa-server/ipa-install/ipa-server-setupssl b/ipa-server/ipa-install/ipa-server-setupssl index f75327907..d7eb6f39f 100644 --- a/ipa-server/ipa-install/ipa-server-setupssl +++ b/ipa-server/ipa-install/ipa-server-setupssl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash if [ "$1" ] ; then password=$1 @@ -49,22 +49,14 @@ if [ -f $secdir/cert8.db ] ; then needServerCert=1 fi - # look for admin server cert - if certutil -L -d $secdir -n "server-cert" 2> /dev/null ; then - echo "Using existing admin server-cert" - else - echo "No Admin Server Cert found - will create new one" - needASCert=1 - fi prefix="new-" prefixarg="-P $prefix" else needCA=1 needServerCert=1 - needASCert=1 fi -if test -z "$needCA" -a -z "$needServerCert" -a -z "$needASCert" ; then +if test -z "$needCA" -a -z "$needServerCert" ; then echo "No certs needed - exiting" exit 0 fi @@ -120,17 +112,17 @@ if test -n "$needServerCert" ; then 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 -if test -n "$needASCert" ; then -# Generate the admin server certificate - certutil -S $prefixarg -n "server-cert" -s "cn=$myhost,ou=Fedora Administration Server" -c "CA certificate" -t "u,u,u" -m 1002 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt +# 8. Generate the web service client certificate: + echo -e "0\n2\n9\nn\n0\n9\nn\n" | certutil -S $prefixarg -n webservice -s "uid=webservice, CN=Web Service, OU=Fedora Directory Server" -c "CA certificate" -t u,pu,u -m 1002 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt -1 -5 -# export the admin server certificate/private key for import into its key/cert db - pk12util -d $secdir $prefixarg -o $secdir/adminserver.p12 -n server-cert -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt - if test -n "$isroot" ; then - chown $uid:$gid $secdir/adminserver.p12 - fi - chmod 400 $secdir/adminserver.p12 -fi + pk12util -d $secdir $prefixarg -o $secdir/webservice.p12 -n "webservice" -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt + + openssl pkcs12 -in $secdir/webservice.p12 -clcerts -nokeys -out /usr/share/ipa/cert.pem -passin file:$secdir/pwdfile.txt + openssl pkcs12 -in $secdir/webservice.p12 -nocerts -nodes -out /usr/share/ipa/key.pem -passin file:$secdir/pwdfile.txt + + cp -p $secdir/cacert.asc /usr/share/ipa + chown apache:apache /usr/share/ipa/cert.pem /usr/share/ipa/key.pem /usr/share/ipa/cacert.asc + chmod 600 /usr/share/ipa/cert.pem /usr/share/ipa/key.pem # create the pin file if [ ! -f $secdir/pin.txt ] ; then @@ -153,42 +145,6 @@ if [ -n "$prefix" ] ; then mv $secdir/${prefix}key3.db $secdir/key3.db fi -# create the admin server key/cert db -asprefix=admin-serv- -if [ ! -f ${asprefix}cert8.db ] ; then - certutil -N -d $secdir -P $asprefix -f $secdir/pwdfile.txt - if test -n "$isroot" ; then - chown $uid:$gid $secdir/admin-serv-*.db - fi - chmod 600 $secdir/admin-serv-*.db -fi - -if test -n "$needASCert" ; then -# import the admin server key/cert - pk12util -d $secdir -P $asprefix -n server-cert -i $secdir/adminserver.p12 -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt - -# import the CA cert to the admin server cert db - certutil -A -d $secdir -P $asprefix -n "CA certificate" -t "CT,," -a -i $secdir/cacert.asc -fi - -if [ ! -f $secdir/password.conf ] ; then -# create the admin server password file - echo 'internal:'`cat $secdir/pwdfile.txt` > $secdir/password.conf - if test -n "$isroot" ; then - chown $uid:$gid $secdir/password.conf - fi - chmod 400 $secdir/password.conf -fi - -# tell admin server to use the password file -if [ -f ../admin-serv/config/nss.conf ] ; then - sed -e "s@^NSSPassPhraseDialog .*@NSSPassPhraseDialog file:`pwd`/password.conf@" ../admin-serv/config/nss.conf > /tmp/nss.conf && mv /tmp/nss.conf ../admin-serv/config/nss.conf - if test -n "$isroot" ; then - chown $uid:$gid ../admin-serv/config/nss.conf - fi - chmod 400 ../admin-serv/config/nss.conf -fi - # enable SSL in the directory server ldapmodify -x -h localhost -p $ldapport -D "cn=Directory Manager" -w $password < +# : [] +# : [] +# +# Notes: +# +# 1. Mapping can be defined per issuer of a certificate. If mapping doesn't +# exists for a particular 'issuerDN' then the server uses the default +# mapping. +# +# 2. There must be an entry for =default and issuerDN "default". +# This mapping is the default mapping. +# +# 3. '#' can be used to comment out a line. +# +# 4. DNComps & FilterComps are used to form the base DN and filter resp. for +# performing an LDAP search while mapping the cert to a user entry. +# +# 5. DNComps can be one of the following: +# commented out - take the user's DN from the cert as is +# empty - search the entire LDAP tree (DN == suffix) +# attr names - a comma separated list of attributes to form DN +# +# 6. FilterComps can be one of the following: +# commented out - set the filter to "objectclass=*" +# empty - set the filter to "objectclass=*" +# attr names - a comma separated list of attributes to form the filter +# + +certmap default default +#default:DNComps +#default:FilterComps e, uid +#default:verifycert on +#default:CmapLdapAttr certSubjectDN +#default:library +#default:InitFn +default:DNComps +default:FilterComps uid diff --git a/ipa-server/ipa-install/share/default-aci.ldif b/ipa-server/ipa-install/share/default-aci.ldif index dc729ceb1..7870461b7 100644 --- a/ipa-server/ipa-install/share/default-aci.ldif +++ b/ipa-server/ipa-install/share/default-aci.ldif @@ -6,3 +6,6 @@ aci: (targetattr!="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTP aci: (targetattr="carLicense ||description ||displayName ||facsimileTelephoneNumber ||homePhone ||homePostalAddress ||initials ||jpegPhoto ||labeledURL ||mail ||mobile ||pager ||photo ||postOfficeBox ||postalAddress ||postalCode ||preferredDeliveryMethod ||preferredLanguage ||registeredAddress ||roomNumber | |secretary ||seeAlso ||st ||street ||telephoneNumber ||telexNumber ||title || userCertificate ||userPassword ||userSMIMECertificate ||x500UniqueIdentifier")(version 3.0; acl "Enable self write for common attributes"; allow (write) userdn="ldap:///self";) aci: (targetattr="krbPrincipalKey")(version 3.0; acl "KDC System Account"; allow(read, search,compare)userdn="ldap:///uid=kdc,cn=kerberos,$SUFFIX";) aci: (targetattr="*")(version 3.0; acl "Directory Administrators can manage all entries"; allow(all)groupdn="ldap:///cn=Directory Administrators,$SUFFIX";) +aci: (target="ldap:///uid=*,ou=users,ou=default,$SUFFIX")(targetattr="*")(version 3.0; acl "allowproxy-webservice"; allow (proxy) userdn="ldap:///uid=webservice,ou=special,$SUFFIX";) +aci: (target="ldap:///uid=*,ou=users,ou=default,$SUFFIX")(targetattr="*")(version 3.0; acl "admins can write entries"; allow(add,delete,write)groupdn="ldap:///cn=admin,ou=groups,ou=default,$SUFFIX";) +aci: (targetattr="userPrincipal")(version 3.0; acl "allow webservice to find users by kerberos principal name"; allow (read, search) userdn="ldap:///uid=webservice,ou=special,$SUFFIX";) diff --git a/ipa-server/ipa-install/test/Makefile b/ipa-server/ipa-install/test/Makefile new file mode 100644 index 000000000..696ae771e --- /dev/null +++ b/ipa-server/ipa-install/test/Makefile @@ -0,0 +1,8 @@ +SHAREDIR = $(DESTDIR)/usr/share/ipa + +install: + -mkdir -p $(SHAREDIR) + install -m 644 *.ldif $(SHAREDIR) + +clean: + rm -f *~ diff --git a/ipa-server/ipa-install/test/test-users.ldif b/ipa-server/ipa-install/test/test-users-template.ldif similarity index 60% rename from ipa-server/ipa-install/test/test-users.ldif rename to ipa-server/ipa-install/test/test-users-template.ldif index 424eedb55..0057d9766 100644 --- a/ipa-server/ipa-install/test/test-users.ldif +++ b/ipa-server/ipa-install/test/test-users-template.ldif @@ -1,5 +1,6 @@ # test, users, default, $REALM dn: uid=test,ou=users,ou=default,$SUFFIX +changetype: add uidNumber: 1001 uid: test gecos: test @@ -13,8 +14,17 @@ shadowInactive: -1 shadowLastChange: 13655 shadowFlag: -1 gidNumber: 100 +objectclass: krbPrincipalAux +objectclass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount objectClass: account objectClass: top -cn: test +cn: Test User +sn: User +krbPrincipalName: test@$REALM + +dn: cn=admin,ou=groups,ou=default,$SUFFIX +changetype: modify +add: uniqueMember +uniqueMember: uid=test,ou=users,ou=default,$SUFFIX diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py index 775a2f2b3..face142a6 100644 --- a/ipa-server/ipaserver/dsinstance.py +++ b/ipa-server/ipaserver/dsinstance.py @@ -88,8 +88,10 @@ class DsInstance: self.__create_instance() self.__add_default_schemas() self.__enable_ssl() + self.__certmap_conf() self.restart() self.__add_default_layout() + self.__create_test_users() def config_dirname(self): if not self.serverid: @@ -136,7 +138,7 @@ class DsInstance: args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name] logging.debug("calling setup-ds.pl") else: - args = ["/usr/sbin/ds_newinst.pl", inf_fd.name] + args = ["/usr/bin/ds_newinst.pl", inf_fd.name] logging.debug("calling ds_newinst.pl") run(args) logging.debug("completed creating ds instance") @@ -166,3 +168,21 @@ class DsInstance: "-w", self.admin_password, "-f", inf_fd.name] run(args) logging.debug("done adding default ds layout") + + def __create_test_users(self): + logging.debug("create test users ldif") + txt = template_file(SHARE_DIR + "test-users-template.ldif", self.sub_dict) + user_fd = open(SHARE_DIR+"test-users.ldif", "w") + user_fd.write(txt) + user_fd.close() + logging.debug("done creating test users ldif") + + def __certmap_conf(self): + logging.debug("configuring certmap.conf for ds instance") + dirname = self.config_dirname() + certmap_conf = template_file(SHARE_DIR+"certmap.conf.template", self.sub_dict) + certmap_fd = open(dirname+"certmap.conf", "w+") + certmap_fd.write(certmap_conf) + certmap_fd.close() + + logging.debug("done configuring certmap.conf for ds instance") diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index f440ae4bb..ee0388cab 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -1,6 +1,6 @@ #! /usr/bin/python -E # Authors: Rich Megginson -# Rob Crittenden -# AuthType Kerberos -# AuthName "Kerberos Login" -# KrbMethodNegotiate on -# KrbMethodK5Passwd off -# KrbServiceName HTTP -# KrbAuthRealms GREYOAK.COM -# Krb5KeyTab /etc/httpd/conf/ipa.keytab -# KrbSaveCredentials on -# Require valid-user + AuthType Kerberos + AuthName "Kerberos Login" + KrbMethodNegotiate on + KrbMethodK5Passwd off + KrbServiceName HTTP + KrbAuthRealms GREYOAK.COM + Krb5KeyTab /etc/httpd/conf/ipa.keytab + KrbSaveCredentials on + Require valid-user ErrorDocument 401 /errors/unauthorized.html SetHandler mod_python diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index ad5e30683..7bad9ab5e 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -123,11 +123,16 @@ class ModXMLRPCRequestHandler(object): def register_instance(self,instance): self.register_module(instance) - def _marshaled_dispatch(self, data): + def _marshaled_dispatch(self, data, remoteuser): """Dispatches an XML-RPC method from marshalled (XML) data.""" params, method = loads(data) + opts={} + opts['remoteuser'] = remoteuser + + params = ipaserver.encode_args(params, opts) + # special case # if method == "get_user": # Marshaller._Marshaller__dump = xmlrpclib_dump @@ -239,7 +244,7 @@ class ModXMLRPCRequestHandler(object): req.allow_methods(['POST'],1) raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED - response = self._marshaled_dispatch(req.read()) + response = self._marshaled_dispatch(req.read(), req.user) req.content_type = "text/xml" req.set_content_length(len(response)) @@ -267,10 +272,12 @@ def handler(req, profiling=False): else: opts = req.get_options() try: + f = funcs.IPAServer() h = ModXMLRPCRequestHandler() - h.register_function(funcs.get_user) - h.register_function(funcs.add_user) - h.register_function(funcs.get_add_schema) + h.register_function(f.get_user) + h.register_function(f.add_user) + h.register_function(f.get_add_schema) + h.register_function(f.get_all_users) h.handle_request(req) finally: pass