summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrcritten@redhat.com <rcritten@redhat.com>2007-08-06 10:05:53 -0400
committerrcritten@redhat.com <rcritten@redhat.com>2007-08-06 10:05:53 -0400
commit993f76fe6035cf59cceb88f3611fc53680738007 (patch)
tree17bb5afed002709bd322f5fe7e99e473adc1d018
parent66ab69d0b23da46b21dbb4bf165011f318ec2da8 (diff)
downloadfreeipa-993f76fe6035cf59cceb88f3611fc53680738007.tar.gz
freeipa-993f76fe6035cf59cceb88f3611fc53680738007.tar.xz
freeipa-993f76fe6035cf59cceb88f3611fc53680738007.zip
- 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
-rwxr-xr-xipa-admintools/freeipa-admintools.spec11
-rwxr-xr-xipa-admintools/freeipa-admintools.spec.in11
-rw-r--r--ipa-admintools/ipa-adduser12
-rw-r--r--ipa-admintools/ipa-finduser30
-rwxr-xr-xipa-python/freeipa-python.spec9
-rwxr-xr-xipa-python/freeipa-python.spec.in9
-rw-r--r--ipa-python/ipaclient.py87
-rw-r--r--ipa-python/krbtransport.py55
-rw-r--r--ipa-python/rpcclient.py148
-rw-r--r--ipa-python/user.py112
-rwxr-xr-xipa-server/freeipa-server.spec15
-rw-r--r--ipa-server/freeipa-server.spec.in15
-rw-r--r--ipa-server/ipa-install/Makefile3
-rw-r--r--ipa-server/ipa-install/ipa-server-install3
-rw-r--r--ipa-server/ipa-install/ipa-server-setupssl68
-rw-r--r--ipa-server/ipa-install/share/bootstrap-template.ldif23
-rw-r--r--ipa-server/ipa-install/share/certmap.conf.template82
-rw-r--r--ipa-server/ipa-install/share/default-aci.ldif3
-rw-r--r--ipa-server/ipa-install/test/Makefile8
-rw-r--r--ipa-server/ipa-install/test/test-users-template.ldif (renamed from ipa-server/ipa-install/test/test-users.ldif)12
-rw-r--r--ipa-server/ipaserver/dsinstance.py22
-rw-r--r--ipa-server/ipaserver/ipaldap.py77
-rw-r--r--ipa-server/ipaserver/krbinstance.py18
-rw-r--r--ipa-server/xmlrpc-server/funcs.py339
-rw-r--r--ipa-server/xmlrpc-server/ipa.conf18
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py17
26 files changed, 881 insertions, 326 deletions
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
-* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
-- Initial rpm version
+* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
+- Abstracted client class to work directly or over RPC
+* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
+- Update tools to do kerberos
+- Add User class
+* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 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
-* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
-- Initial rpm version
+* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
+- Abstracted client class to work directly or over RPC
+* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
+- Update tools to do kerberos
+- Add User class
+* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 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 <uid>"
@@ -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 <rcritten@redhat.com> - 0.1.0-3
+- Abstracted client class to work directly or over RPC
+
+* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
+- Add User class
+- Add kerberos authentication to the XML-RPC request made from tools.
+
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 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 <rcritten@redhat.com> - 0.1.0-3
+- Abstracted client class to work directly or over RPC
+
+* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
+- Add User class
+- Add kerberos authentication to the XML-RPC request made from tools.
+
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 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 <rcritten@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 or later
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+#!/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 <rcritten@redhat.com>
+#
+# Copyright (C) 2007 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 or later
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+#!/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()
-
- # 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'
+ 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: 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'])
+ return result
+
+
+ def add_user(self,user):
+ """Add a new user"""
+ server = self.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)
- 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 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
-def get_add_schema():
- """Get the list of attributes we need to ask when adding a new
- user.
- """
- server = setup_server()
+ def get_all_users (self):
+ """Return a list containing a User object for each existing user."""
- # 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
+ 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 <rcritten@redhat.com> - 0.1.0-3
+- Abstracted client class to work directly or over RPC
+
+* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 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 <kmacmill@localhost.localdomain> - 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 <rcritten@redhat.com> - 0.1.0-3
+- Abstracted client class to work directly or over RPC
+
+* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 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 <kmacmill@localhost.localdomain> - 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 <<EOF
diff --git a/ipa-server/ipa-install/share/bootstrap-template.ldif b/ipa-server/ipa-install/share/bootstrap-template.ldif
index d83f715bb..444a29d44 100644
--- a/ipa-server/ipa-install/share/bootstrap-template.ldif
+++ b/ipa-server/ipa-install/share/bootstrap-template.ldif
@@ -31,3 +31,26 @@ ou: groups
#objectClass: top
#ou: computers
+dn: ou=special,$SUFFIX
+changetype: add
+objectClass: organizationalUnit
+objectClass: top
+ou: special
+
+dn: uid=webservice,ou=special,$SUFFIX
+changetype: add
+uid: webservice
+objectClass: account
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+cn: Web Service
+sn: Service
+
+dn: cn=admin,ou=groups,ou=default,$SUFFIX
+changetype: add
+description: ou=users administrators
+objectClass: top
+objectClass: groupofuniquenames
+cn: admin
diff --git a/ipa-server/ipa-install/share/certmap.conf.template b/ipa-server/ipa-install/share/certmap.conf.template
new file mode 100644
index 000000000..676d3ef35
--- /dev/null
+++ b/ipa-server/ipa-install/share/certmap.conf.template
@@ -0,0 +1,82 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# 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 of the License.
+#
+# 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.
+#
+# In addition, as a special exception, Red Hat, Inc. gives You the additional
+# right to link the code of this Program with code not covered under the GNU
+# General Public License ("Non-GPL Code") and to distribute linked combinations
+# including the two, subject to the limitations in this paragraph. Non-GPL Code
+# permitted under this exception must only link to the code of this Program
+# through those well defined interfaces identified in the file named EXCEPTION
+# found in the source code files (the "Approved Interfaces"). The files of
+# Non-GPL Code may instantiate templates or use macros or inline functions from
+# the Approved Interfaces without causing the resulting work to be covered by
+# the GNU General Public License. Only Red Hat, Inc. may make changes or
+# additions to the list of Approved Interfaces. You must obey the GNU General
+# Public License in all respects for all of the Program code and other code used
+# in conjunction with the Program except the Non-GPL Code covered by this
+# exception. If you modify this file, you may extend this exception to your
+# version of the file, but you are not obligated to do so. If you do not wish to
+# provide this exception without modification, you must delete this exception
+# statement from your version and license this file solely under the GPL without
+# exception.
+#
+#
+# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
+# Copyright (C) 2005 Red Hat, Inc.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# This file configures how a certificate is mapped to an LDAP entry. See the
+# documentation for more information on this file.
+#
+# The format of this file is as follows:
+# certmap <name> <issuerDN>
+# <name>:<prop1> [<val1>]
+# <name>:<prop2> [<val2>]
+#
+# 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 <name>=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 <path_to_shared_lib_or_dll>
+#default:InitFn <Init function's name>
+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
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 <richm@redhat.com>
-# Rob Crittenden <rcritten2redhat.com
+# Rob Crittenden <rcritten@redhat.com
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
@@ -33,6 +33,8 @@ import ldap
import cStringIO
import time
import operator
+import struct
+from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
from ldap.ldapobject import SimpleLDAPObject
@@ -197,31 +199,25 @@ class IPAdmin(SimpleLDAPObject):
raise
def __localinit__(self):
- SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port))
- # see if binddn is a dn or a uid that we need to lookup
- if self.binddn and not IPAdmin.is_a_dn(self.binddn):
- self.simple_bind("","") # anon
- ent = self.getEntry(IPAdmin.CFGSUFFIX, ldap.SCOPE_SUBTREE,
- "(uid=%s)" % self.binddn,
- ['uid'])
- if ent:
- self.binddn = ent.dn
- else:
- print "Error: could not find %s under %s" % (self.binddn, IPAdmin.CFGSUFFIX)
- self.simple_bind(self.binddn,self.bindpw)
-# self.__initPart2()
-
- def __init__(self,host,port,binddn,bindpw):
+ SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port))
+
+ def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None):
"""We just set our instance variables and wrap the methods - the real work is
done in __localinit__ and __initPart2 - these are separated out this way so
that we can call them from places other than instance creation e.g. when
using the start command, we just need to reconnect, not create a new instance"""
+# ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,cacert)
+ ldap.set_option(ldap.OPT_X_TLS_CERTFILE,bindcert)
+ ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey)
+
self.__wrapmethods()
self.port = port or 389
self.sslport = 0
self.host = host
- self.binddn = binddn
- self.bindpw = bindpw
+ self.bindcert = bindcert
+ self.bindkey = bindkey
+ self.proxydn = proxydn
# see if is local or not
host1 = IPAdmin.getfqdn(host)
host2 = IPAdmin.getfqdn()
@@ -237,7 +233,22 @@ class IPAdmin(SimpleLDAPObject):
def getEntry(self,*args):
"""This wraps the search function. It is common to just get one entry"""
- res = self.search(*args)
+ # 0x04 = Octet String
+ # 4|0x80 sets the length of the length at 4 bytes
+ # the struct() gets us the length in bytes of string s
+ # s is the proxy dn to send
+
+ if self.proxydn is not None:
+ proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
+
+ # Create the proxy control
+ sctrl=[]
+ sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
+ else:
+ sctrl=None
+
+ res = self.search_ext(args[0], args[1], filterstr=args[2], serverctrls=sctrl)
+
type, obj = self.result(res)
if not obj:
raise NoSuchEntryError("no such entry for " + str(args))
@@ -246,10 +257,38 @@ class IPAdmin(SimpleLDAPObject):
else: # assume list/tuple
return obj[0]
+ def getList(self,*args):
+ """This wraps the search function to find all users."""
+
+ res = self.search(*args)
+ type, obj = self.result(res)
+ if not obj:
+ raise NoSuchEntryError("no such entry for " + str(args))
+
+ all_users = []
+ for s in obj:
+ all_users.append(s)
+
+ return all_users
+
def addEntry(self,*args):
"""This wraps the add function. It assumes that the entry is already
populated with all of the desired objectclasses and attributes"""
+ if self.proxydn is not None:
+ proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
+
+ # Create the proxy control
+ sctrl=[]
+ sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
+ else:
+ sctrl=None
+
+ # Create the proxy control
+ sctrl=[]
+ sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
+
try:
+ self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.add_s(*args)
except ldap.ALREADY_EXISTS:
raise ldap.ALREADY_EXISTS
diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py
index 253c506f2..f4fe6001b 100644
--- a/ipa-server/ipaserver/krbinstance.py
+++ b/ipa-server/ipaserver/krbinstance.py
@@ -28,6 +28,7 @@ from time import gmtime
import os
import pwd
import socket
+import time
from util import *
def host_to_domain(fqdn):
@@ -82,6 +83,8 @@ class KrbInstance:
self.__create_ds_keytab()
+ self.__create_http_keytab()
+
self.__create_sample_bind_zone()
self.start()
@@ -175,3 +178,18 @@ class KrbInstance:
cfg_fd.close()
pent = pwd.getpwnam(self.ds_user)
os.chown("/etc/sysconfig/fedora-ds", pent.pw_uid, pent.pw_gid)
+
+ def __create_http_keytab(self):
+ (kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
+ kwrite.write("addprinc -randkey HTTP/"+self.fqdn+"@"+self.realm+"\n")
+ kwrite.flush()
+ kwrite.write("ktadd -k /etc/httpd/conf/ipa.keytab HTTP/"+self.fqdn+"@"+self.realm+"\n")
+ kwrite.flush()
+ kwrite.close()
+ kread.close()
+ kerr.close()
+
+ while not file_exists("/etc/httpd/conf/ipa.keytab"):
+ time.sleep(1)
+ pent = pwd.getpwnam("apache")
+ os.chown("/etc/httpd/conf/ipa.keytab", pent.pw_uid, pent.pw_gid)
diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index be95d4fab..9545ad892 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -24,152 +24,205 @@ import ldap
import ipaserver.dsinstance
import ipaserver.ipaldap
import ipaserver.util
-import pdb
import string
from types import *
import xmlrpclib
import ipa.config
-# FIXME, this needs to be auto-discovered
-host = 'localhost'
-port = 389
-binddn = "cn=directory manager"
-bindpw = "freeipa"
-
-ipa.config.init_config()
-basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
-import sys
-sys.stderr.write(basedn)
-scope = ldap.SCOPE_SUBTREE
-
-def get_user (username):
- """Get a specific user's entry. Return as a dict of values.
- Multi-valued fields are represented as lists.
- """
- ent=""
-
- # FIXME: Is this the filter we want or should it be more specific?
- filter = "(uid=" + username + ")"
- try:
- m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw)
- ent = m1.getEntry(basedn, scope, filter, None)
- except ldap.LDAPError, e:
- raise xmlrpclib.Fault(1, e)
- except ipaserver.ipaldap.NoSuchEntryError:
- raise xmlrpclib.Fault(2, "No such user")
-
- # Convert to LDIF
- entry = str(ent)
-
- # Strip off any junk
- entry = entry.strip()
-
- # Don't need to identify binary fields and this breaks the parser so
- # remove double colons
- entry = entry.replace('::', ':')
- specs = [spec.split(':') for spec in entry.split('\n')]
-
- # Convert into a dict. We need to handle multi-valued attributes as well
- # so we'll convert those into lists.
- user={}
- for (k,v) in specs:
- k = k.lower()
- if user.get(k) is not None:
- if isinstance(user[k],list):
- user[k].append(v.strip())
+class IPAServer:
+
+ def __init__(self):
+ # FIXME, this needs to be auto-discovered
+ self.host = 'localhost'
+ self.port = 636
+ self.bindcert = "/usr/share/ipa/cert.pem"
+ self.bindkey = "/usr/share/ipa/key.pem"
+ self.bindca = "/usr/share/ipa/cacert.asc"
+
+ ipa.config.init_config()
+ self.basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
+ self.scope = ldap.SCOPE_SUBTREE
+ self.princ = None
+
+ def set_principal(self, princ):
+ self.princ = princ
+
+ def get_dn_from_principal(self, princ):
+ """Given a kerberls principal get the LDAP uid"""
+ filter = "(krbPrincipalName=" + princ + ")"
+ try:
+ m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
+ ent = m1.getEntry(self.basedn, self.scope, filter, None)
+ except ldap.LDAPError, e:
+ raise xmlrpclib.Fault(1, e)
+ except ipaserver.ipaldap.NoSuchEntryError:
+ raise xmlrpclib.Fault(2, "No such user")
+
+ return "dn:" + ent.dn
+
+ def convert_entry(self, ent):
+
+ # Convert to LDIF
+ entry = str(ent)
+
+ # Strip off any junk
+ entry = entry.strip()
+
+ # Don't need to identify binary fields and this breaks the parser so
+ # remove double colons
+ entry = entry.replace('::', ':')
+ specs = [spec.split(':') for spec in entry.split('\n')]
+
+ # Convert into a dict. We need to handle multi-valued attributes as well
+ # so we'll convert those into lists.
+ user={}
+ for (k,v) in specs:
+ k = k.lower()
+ if user.get(k) is not None:
+ if isinstance(user[k],list):
+ user[k].append(v.strip())
+ else:
+ first = user[k]
+ user[k] = []
+ user[k].append(first)
+ user[k].append(v.strip())
else:
- first = user[k]
- user[k] = []
- user[k].append(first)
- user[k].append(v.strip())
- else:
- user[k] = v.strip()
-
- return user
-# return str(ent) # return as LDIF
-
-def add_user (user):
- """Add a user in LDAP"""
- dn="uid=%s,ou=users,ou=default,%s" % (user['uid'], basedn)
- entry = ipaserver.ipaldap.Entry(dn)
-
- # some required objectclasses
- entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux')
-
- # Fill in shadow fields
- entry.setValue('shadowMin', '0')
- entry.setValue('shadowMax', '99999')
- entry.setValue('shadowWarning', '7')
- entry.setValue('shadowExpire', '-1')
- entry.setValue('shadowInactive', '-1')
- entry.setValue('shadowFlag', '-1')
-
- # FIXME: calculate shadowLastChange
-
- # fill in our new entry with everything sent by the user
- for u in user:
- entry.setValues(u, user[u])
-
- try:
- m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw)
- res = m1.addEntry(entry)
- return res
- except ldap.ALREADY_EXISTS:
- raise xmlrpclib.Fault(3, "User already exists")
- return None
- except ldap.LDAPError, e:
- raise xmlrpclib.Fault(1, str(e))
- return None
-
-def get_add_schema ():
- """Get the list of fields to be used when adding users in the GUI."""
-
- # FIXME: this needs to be pulled from LDAP
- fields = []
-
- field1 = {
- "name": "uid" ,
- "label": "Login:",
- "type": "text",
- "validator": "text",
- "required": "true"
- }
- fields.append(field1)
-
- field1 = {
- "name": "userPassword" ,
- "label": "Password:",
- "type": "password",
- "validator": "String",
- "required": "true"
- }
- fields.append(field1)
-
- field1 = {
- "name": "givenName" ,
- "label": "First name:",
- "type": "text",
- "validator": "string",
- "required": "true"
- }
- fields.append(field1)
-
- field1 = {
- "name": "sn" ,
- "label": "Last name:",
- "type": "text",
- "validator": "string",
- "required": "true"
- }
- fields.append(field1)
-
- field1 = {
- "name": "mail" ,
- "label": "E-mail address:",
- "type": "text",
- "validator": "email",
- "required": "true"
- }
- fields.append(field1)
-
- return fields
+ user[k] = v.strip()
+
+ return user
+
+ def get_user (self, username, opts=None):
+ """Get a specific user's entry. Return as a dict of values.
+ Multi-valued fields are represented as lists.
+ """
+ ent=""
+ if opts:
+ self.set_principal(opts['remoteuser'])
+ if (isinstance(username, tuple)):
+ username = username[0]
+
+ try:
+ dn = self.get_dn_from_principal(self.princ)
+ except ldap.LDAPError, e:
+ raise xmlrpclib.Fault(1, e)
+ except ipaserver.ipaldap.NoSuchEntryError:
+ raise xmlrpclib.Fault(2, "No such user")
+
+ filter = "(uid=" + username + ")"
+ try:
+ m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
+ ent = m1.getEntry(self.basedn, self.scope, filter, None)
+ except ldap.LDAPError, e:
+ raise xmlrpclib.Fault(1, e)
+ except ipaserver.ipaldap.NoSuchEntryError:
+ raise xmlrpclib.Fault(2, "No such user")
+
+ return self.convert_entry(ent)
+
+ def add_user (self, user, opts=None):
+ """Add a user in LDAP"""
+ if (isinstance(user, tuple)):
+ user = user[0]
+ dn="uid=%s,ou=users,ou=default,%s" % (user['uid'], self.basedn)
+ entry = ipaserver.ipaldap.Entry(str(dn))
+
+ # some required objectclasses
+ entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux')
+
+ # Fill in shadow fields
+ entry.setValue('shadowMin', '0')
+ entry.setValue('shadowMax', '99999')
+ entry.setValue('shadowWarning', '7')
+ entry.setValue('shadowExpire', '-1')
+ entry.setValue('shadowInactive', '-1')
+ entry.setValue('shadowFlag', '-1')
+
+ # FIXME: calculate shadowLastChange
+
+ # fill in our new entry with everything sent by the user
+ for u in user:
+ entry.setValues(str(u), str(user[u]))
+
+ if opts:
+ self.set_principal(opts['remoteuser'])
+
+ try:
+ dn = self.get_dn_from_principal(self.princ)
+ except ldap.LDAPError, e:
+ raise xmlrpclib.Fault(1, e)
+ except ipaserver.ipaldap.NoSuchEntryError:
+ raise xmlrpclib.Fault(2, "No such user")
+
+ try:
+ m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
+ res = m1.addEntry(entry)
+ return res
+ except ldap.ALREADY_EXISTS:
+ raise xmlrpclib.Fault(3, "User already exists")
+ except ldap.LDAPError, e:
+ raise xmlrpclib.Fault(1, str(e))
+
+ def get_add_schema (self):
+ """Get the list of fields to be used when adding users in the GUI."""
+
+ # FIXME: this needs to be pulled from LDAP
+ fields = []
+
+ field1 = {
+ "name": "uid" ,
+ "label": "Login:",
+ "type": "text",
+ "validator": "text",
+ "required": "true"
+ }
+ fields.append(field1)
+
+ field1 = {
+ "name": "givenName" ,
+ "label": "First name:",
+ "type": "text",
+ "validator": "string",
+ "required": "true"
+ }
+ fields.append(field1)
+
+ field1 = {
+ "name": "sn" ,
+ "label": "Last name:",
+ "type": "text",
+ "validator": "string",
+ "required": "true"
+ }
+ fields.append(field1)
+
+ field1 = {
+ "name": "mail" ,
+ "label": "E-mail address:",
+ "type": "text",
+ "validator": "email",
+ "required": "true"
+ }
+ fields.append(field1)
+
+ return fields
+
+ def get_all_users (self):
+ """Return a list containing a User object for each
+ existing user.
+ """
+
+ # FIXME: Is this the filter we want or should it be more specific?
+ filter = "(objectclass=posixAccount)"
+ try:
+ m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
+ all_users = m1.getList(self.basedn, self.scope, filter, None)
+ except ldap.LDAPError, e:
+ raise xmlrpclib.Fault(1, e)
+ except ipaserver.ipaldap.NoSuchEntryError:
+ raise xmlrpclib.Fault(2, "No such user")
+
+ users = []
+ for u in all_users:
+ users.append(self.convert_entry(u))
+
+ return users
diff --git a/ipa-server/xmlrpc-server/ipa.conf b/ipa-server/xmlrpc-server/ipa.conf
index 1880268cf..dbe6b99fd 100644
--- a/ipa-server/xmlrpc-server/ipa.conf
+++ b/ipa-server/xmlrpc-server/ipa.conf
@@ -3,15 +3,15 @@
Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
<Directory "/usr/share/ipa/ipaserver">
-# 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