diff options
48 files changed, 1291 insertions, 513 deletions
@@ -1,4 +1,4 @@ -SUBDIRS=ipa-server ipa-admintools ipa-python ipa-client +SUBDIRS=ipa-server ipa-admintools ipa-python ipa-client ipa-radius-server PRJ_PREFIX=ipa @@ -35,6 +35,13 @@ CLI_VERSION=$(CLI_MAJOR).$(CLI_MINOR).$(CLI_RELEASE) CLI_TARBALL_PREFIX=$(PRJ_PREFIX)-client-$(CLI_VERSION) CLI_TARBALL=$(CLI_TARBALL_PREFIX).tgz +RADIUS_SERVER_MAJOR=0 +RADIUS_SERVER_MINOR=5 +RADIUS_SERVER_RELEASE=0 +RADIUS_SERVER_VERSION=$(RADIUS_SERVER_MAJOR).$(RADIUS_SERVER_MINOR).$(RADIUS_SERVER_RELEASE) +RADIUS_SERVER_TARBALL_PREFIX=$(PRJ_PREFIX)-radius-server-$(RADIUS_SERVER_VERSION) +RADIUS_SERVER_TARBALL=$(RADIUS_SERVER_TARBALL_PREFIX).tgz + LIBDIR ?= /usr/lib all: bootstrap-autogen @@ -77,6 +84,9 @@ version-update: sed s/VERSION/$(CLI_VERSION)/ ipa-client/ipa-client.spec.in \ > ipa-client/ipa-client.spec + sed s/VERSION/$(RADIUS_SERVER_VERSION)/ ipa-radius-server/ipa-radius-server.spec.in \ + > ipa-radius-server/ipa-radius-server.spec + archive: -mkdir -p dist @@ -120,6 +130,13 @@ tarballs: cd dist; tar cfz sources/$(CLI_TARBALL) $(CLI_TARBALL_PREFIX) rm -fr dist/$(CLI_TARBALL_PREFIX) + # ipa-radius-server + mv dist/ipa/ipa-radius-server dist/$(RADIUS_SERVER_TARBALL_PREFIX) + rm -f dist/sources/$(RADIUS_SERVER_TARBALL) + cd dist; tar cfz sources/$(RADIUS_SERVER_TARBALL) $(RADIUS_SERVER_TARBALL_PREFIX) + rm -fr dist/$(RADIUS_SERVER_TARBALL_PREFIX) + + rpmroot: mkdir -p $(RPMBUILD)/BUILD mkdir -p $(RPMBUILD)/RPMS @@ -155,7 +172,14 @@ rpm-ipa-client: cp rpmbuild/RPMS/*/$(PRJ_PREFIX)-client-$(CLI_VERSION)-*.rpm dist/rpms/ cp rpmbuild/SRPMS/$(PRJ_PREFIX)-client-$(CLI_VERSION)-*.src.rpm dist/srpms/ -rpms: rpmroot rpmdistdir rpm-ipa-server rpm-ipa-admin rpm-ipa-python rpm-ipa-client +rpm-ipa-radius-server: + cp dist/sources/$(RADIUS_SERVER_TARBALL) $(RPMBUILD)/SOURCES/. + rpmbuild --define "_topdir $(RPMBUILD)" -ba ipa-radius-server/ipa-radius-server.spec + cp rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-radius-server-$(RADIUS_SERVER_VERSION)-*.rpm dist/rpms/ + cp rpmbuild/SRPMS/$(PRJ_PREFIX)-radius-server-$(RADIUS_SERVER_VERSION)-*.src.rpm dist/srpms/ + + +rpms: rpmroot rpmdistdir rpm-ipa-server rpm-ipa-admin rpm-ipa-python rpm-ipa-client rpm-ipa-radius-server repodata: -createrepo -p dist diff --git a/ipa-admintools/man/Makefile b/ipa-admintools/man/Makefile index 19648f539..154124753 100644 --- a/ipa-admintools/man/Makefile +++ b/ipa-admintools/man/Makefile @@ -14,7 +14,8 @@ MANFILES=\ ipa-lockuser.1 \ ipa-moddelegation.1 \ ipa-passwd.1 \ - ipa-usermod.1 + ipa-usermod.1 \ + ipa-getkeytab.1 all: ; diff --git a/ipa-admintools/man/ipa-getkeytab.1 b/ipa-admintools/man/ipa-getkeytab.1 new file mode 100644 index 000000000..da511171a --- /dev/null +++ b/ipa-admintools/man/ipa-getkeytab.1 @@ -0,0 +1,64 @@ +.\" A man page for ipa-getkeytab +.\" Copyright (C) 2007 Red Hat, Inc. +.\" +.\" This is free software; you can redistribute it and/or modify it under +.\" the terms of the GNU Library General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" 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 Library General Public +.\" License along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" Author: Karl MacMillan <kmacmill@redhat.com> +.\" +.TH "ipa-getkeytab" "1" "Oct 10 2007" "freeipa" "" +.SH "NAME" +ipa\-getkeytab \- Get a keytab for a kerberos principal +.SH "SYNOPSIS" +ipa\-getkeytab [\fI-a\fR] \fIprincipal-name\fR \fIfile-name\fR + +.SH "DESCRIPTION" +Retrieves a kerberos \fIkeytab\fR and optionally adds a +service \fIprincipal\fR. + +Kerberos keytabs are used for services (like sshd) to +perform kerberos authentication. A keytab is a file +with one or more secrets (or keys) for a kerberos +principal. + +A kerberos service principal is a kerberos identity +that can be used for authentication. Service principals +contain the name of the service, the hostname of the +server, and the realm name. For example, the following +is an example principal for an ldap server: + + ldap/foo.example.com@EXAMPLE.COM + +When using ipa-getkeytab the realm name is already +provided, so the principal name is just the service +name and hostname (ldap/foo.example.com from the +example above). + +\fBWARNING:\fR retrieving the keytab resets the secret +rendering all other keytabs for that principal invalid. + +.SH "OPTIONS" +.TP +\fB\-a\fR +Add the service principal in addition to getting the keytab + +.SH "EXAMPLES" + +Add and retrieve a keytab for the ldap service principal on +the host foo.example.com and save it in the file ldap.keytab. + + # ipa-getkeytab -a ldap/foo.example.com ldap.keytab + +.SH "EXIT STATUS" +The exit status is 0 on success, nonzero on error. diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index bc8e7c9c3..9cc6f6f6b 100644 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -51,6 +51,13 @@ def parse_options(): return options +def ask_for_confirmation(message): + yesno = raw_input(message + " [y/N]: ") + if not yesno or yesno.lower()[0] != "y": + return False + print "\n" + return True + def logging_setup(options): # Always log everything (i.e., DEBUG) to the log # file. @@ -114,16 +121,18 @@ def main(): print "\nThe failure to use DNS to find your IPA server indicates that your resolv.conf file is not properly configured\n." print "Autodiscovery of servers for failover cannot work with this configuration.\n" print "If you proceed with the installation, services will be configured to always access the discovered server for all operation and will not fail over to other servers in case of failure\n" - yesno = raw_input("Do you want to proceed and configure the system with fixed values with no DNS discovery? [y/N] ") - if yesno.lower() != "y": + if not ask_for_confirmation("Do you want to proceed and configure the system with fixed values with no DNS discovery?"): return ret - print "\n" print "Realm: "+ds.getRealmName() print "DNS Domain: "+ds.getDomainName() print "IPA Server: "+ds.getServerName() print "BaseDN: "+ds.getBaseDN() + print "\n" + if not ask_for_confirmation("Continue to configure the system with these values?"): + return 1 + # Configure ipa.conf ipaconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer") ipaconf.setOptionAssignment(" = ") @@ -225,4 +234,4 @@ def main(): return 0 -main() +sys.exit(main()) diff --git a/ipa-python/ipa-python.spec b/ipa-python/ipa-python.spec index 2837a2832..1c838821b 100755 --- a/ipa-python/ipa-python.spec +++ b/ipa-python/ipa-python.spec @@ -14,8 +14,6 @@ Requires: PyKerberos %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} -%define pkgpythondir %{python_sitelib}/ipa - %description Ipa is a server for identity, policy, and audit. @@ -33,8 +31,7 @@ rm -rf %{buildroot} %files %defattr(-,root,root,-) -%dir %{pkgpythondir} -%{pkgpythondir}/* +%{python_sitelib}/* %config(noreplace) %{_sysconfdir}/ipa/ipa.conf %changelog diff --git a/ipa-python/ipa-python.spec.in b/ipa-python/ipa-python.spec.in index bd8ac0da6..9eb2e11c2 100755 --- a/ipa-python/ipa-python.spec.in +++ b/ipa-python/ipa-python.spec.in @@ -14,8 +14,6 @@ Requires: PyKerberos %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} -%define pkgpythondir %{python_sitelib}/ipa - %description Ipa is a server for identity, policy, and audit. @@ -33,8 +31,7 @@ rm -rf %{buildroot} %files %defattr(-,root,root,-) -%dir %{pkgpythondir} -%{pkgpythondir}/* +%{python_sitelib}/* %config(noreplace) %{_sysconfdir}/ipa/ipa.conf %changelog diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index d815afa7b..bd1fb235a 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -19,14 +19,10 @@ #!/usr/bin/python -import sys - import ipa.rpcclient as rpcclient import entity import user import group -import ipa -import config import radius_util class IPAClient: diff --git a/ipa-python/ipaerror.py b/ipa-python/ipaerror.py index e34963365..59e262018 100644 --- a/ipa-python/ipaerror.py +++ b/ipa-python/ipaerror.py @@ -129,14 +129,14 @@ LDAP_NO_CONFIG = gen_error_code( "IPA configuration not found") # -# Input errors (sample - replace me) +# Function input errors # INPUT_CATEGORY = 0x0002 -INPUT_INVALID_ERROR = gen_error_code( +INPUT_INVALID_PARAMETER = gen_error_code( INPUT_CATEGORY, 0x0001, - "Illegal input") + "Invalid parameter(s)") # # Connection errors diff --git a/ipa-python/ipautil.py b/ipa-python/ipautil.py index 3c2b37f73..c617854e5 100644 --- a/ipa-python/ipautil.py +++ b/ipa-python/ipautil.py @@ -18,6 +18,7 @@ # SHARE_DIR = "/usr/share/ipa/" +PLUGINS_SHARE_DIR = "/usr/share/ipa/plugins" import string import tempfile diff --git a/ipa-python/radius_util.py b/ipa-python/radius_util.py index e1401fb7b..fb3e581e6 100644 --- a/ipa-python/radius_util.py +++ b/ipa-python/radius_util.py @@ -79,7 +79,7 @@ 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_CONF_TEMPLATE_FILEPATH = os.path.join(ipautil.PLUGINS_SHARE_DIR, 'radius.radiusd.conf.template') RADIUSD = '/usr/sbin/radiusd' diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index 5656b99d9..c993ac991 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -24,11 +24,8 @@ import socket import config from krbtransport import KerbTransport from kerberos import GSSError -import os -import base64 -import user -import ipa from ipa import ipaerror, ipautil +from ipa import config # Some errors to catch # http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto @@ -36,7 +33,7 @@ from ipa import ipaerror, ipautil class RPCClient: def __init__(self): - ipa.config.init_config() + config.init_config() def server_url(self): """Build the XML-RPC server URL from our configuration""" @@ -47,25 +44,6 @@ class RPCClient: authentication""" return xmlrpclib.ServerProxy(self.server_url(), KerbTransport()) - 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. - obj={} - for (k) in ent: - k = k.lower() - if obj.get(k) is not None: - if isinstance(obj[k],list): - obj[k].append(ent[k].strip()) - else: - first = obj[k] - obj[k] = () - obj[k].append(first) - obj[k].append(ent[k].strip()) - else: - obj[k] = ent[k] - - return obj - # Higher-level API def get_aci_entry(self, sattrs=None): @@ -168,7 +146,8 @@ class RPCClient: def get_user_by_email(self,email,sattrs=None): """Get a specific user's entry. Return as a dict of values. - Multi-valued fields are represented as lists. + Multi-valued fields are represented as lists. The result is a + dict. """ server = self.setup_server() if sattrs is None: @@ -245,7 +224,7 @@ class RPCClient: return ipautil.unwrap_binary_data(result) def get_all_users (self): - """Return a list containing a User object for each existing user.""" + """Return a list containing a dict for each existing user.""" server = self.setup_server() try: @@ -258,7 +237,7 @@ class RPCClient: return ipautil.unwrap_binary_data(result) def find_users (self, criteria, sattrs=None, searchlimit=0, timelimit=-1): - """Return a list: counter followed by a User object for each user that + """Return a list: counter followed by a dict for each user that matches the criteria. If the results are truncated, counter will be set to -1""" @@ -381,6 +360,8 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) + return ipautil.unwrap_binary_data(result) + def find_groups (self, criteria, sattrs=None, searchlimit=0, timelimit=-1): """Return a list containing a Group object for each group that matches the criteria.""" diff --git a/ipa-python/setup.py b/ipa-python/setup.py index 3a5a6f4eb..deb84f293 100644 --- a/ipa-python/setup.py +++ b/ipa-python/setup.py @@ -34,7 +34,7 @@ def setup_package(): try: setup( - name = "freeipa-python", + name = "ipa", version = "0.5.0", license = "GPL", author = "Karl MacMillan, et.al.", diff --git a/ipa-radius-server/Makefile b/ipa-radius-server/Makefile new file mode 100644 index 000000000..7de51b469 --- /dev/null +++ b/ipa-radius-server/Makefile @@ -0,0 +1,20 @@ +PLUGINS_SHARE = $(DESTDIR)/usr/share/ipa/plugins +PLUGINS_PYTHON = $(DESTDIR)/usr/share/ipa/ipaserver/plugins +SBINDIR = $(DESTDIR)/usr/sbin + +all: + +install: + -mkdir -p $(PLUGINS_SHARE) + -mkdir -p $(PLUGINS_PYTHON) + -mkdir -p $(PLUGINS_SBINDIR) + install -m 644 plugins/*.py $(PLUGINS_PYTHON) + install -m 644 share/*.template $(PLUGINS_SHARE) + install -m 755 ipa-radius-install $(SBINDIR) + +clean: + rm -fr *.pyc *~ + +distclean: clean + +test:
\ No newline at end of file diff --git a/ipa-radius-server/ipa-radius-install b/ipa-radius-server/ipa-radius-install new file mode 100644 index 000000000..3e759d8d2 --- /dev/null +++ b/ipa-radius-server/ipa-radius-install @@ -0,0 +1,73 @@ +#! /usr/bin/python -E +# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com> +# +# Copyright (C) 2007 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 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 +sys.path.append("/usr/share/ipa") + +import traceback, logging, krbV + +from ipaserver import installutils +from ipaserver.plugins import radiusinstance + +from ipa import ipautil + +def get_host_name(): + hostname = installutils.get_fqdn() + try: + installutils.verify_fqdn(hostname) + except RuntimeError, e: + logging.error(str(e)) + sys.exit(1) + + return hostname + +def get_realm_name(): + c = krbV.default_context() + return c.default_realm + +def main(): + if not ipautil.file_exists("/etc/ipa/ipa.conf"): + print "This system does not appear to have IPA configured." + print "Has ipa-server-install been run?" + yesno = raw_input("Continue with radius install [y/N]? ") + if yesno.lower() != "y": + sys.exit(1) + + installutils.standard_logging_setup("iparadius-install.log", False) + + host_name = get_host_name() + + realm_name = get_realm_name() + + # Create a radius instance + radius = radiusinstance.RadiusInstance() + # FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL? + radius.create_instance(realm_name, host_name, 'localhost') + + +try: + main() +except Exception, e: + message = "Unexpected error - see iparadius-install.log for details:\n %s" % str(e) + print message + message = str(e) + for str in traceback.format_tb(sys.exc_info()[2]): + message = message + "\n" + str + logging.debug(message) diff --git a/ipa-radius-server/ipa-radius-server.spec b/ipa-radius-server/ipa-radius-server.spec new file mode 100755 index 000000000..2b8b61624 --- /dev/null +++ b/ipa-radius-server/ipa-radius-server.spec @@ -0,0 +1,50 @@ +Name: ipa-radius-server +Version: 0.5.0 +Release: 1%{?dist} +Summary: IPA authentication server - radius plugin + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +Source0: %{name}-%{version}.tgz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python +Requires: ipa-server +Requires: freeradius + +%description +Radius plugin for an IPA server + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sbindir} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{_sbindir}/ipa* + +%dir %{_usr}/share/ipa/plugins +%{_usr}/share/ipa/plugins/* + +%dir %{_usr}/share/ipa/ipaserver/plugins +%{_usr}/share/ipa/ipaserver/plugins/* + +%changelog +* Wed Dec 12 2007 Karl MacMillan <kmacmill@redhat.com> - 0.5.0-1 +- Initial version + + + + diff --git a/ipa-radius-server/ipa-radius-server.spec.in b/ipa-radius-server/ipa-radius-server.spec.in new file mode 100755 index 000000000..5937d3b19 --- /dev/null +++ b/ipa-radius-server/ipa-radius-server.spec.in @@ -0,0 +1,50 @@ +Name: ipa-radius-server +Version: VERSION +Release: 1%{?dist} +Summary: IPA authentication server - radius plugin + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +Source0: %{name}-%{version}.tgz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python +Requires: ipa-server +Requires: freeradius + +%description +Radius plugin for an IPA server + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sbindir} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{_sbindir}/ipa* + +%dir %{_usr}/share/ipa/plugins +%{_usr}/share/ipa/plugins/* + +%dir %{_usr}/share/ipa/ipaserver/plugins +%{_usr}/share/ipa/ipaserver/plugins/* + +%changelog +* Wed Dec 12 2007 Karl MacMillan <kmacmill@redhat.com> - 0.5.0-1 +- Initial version + + + + diff --git a/ipa-radius-server/plugins/__init__.py b/ipa-radius-server/plugins/__init__.py new file mode 100644 index 000000000..636bc1a8a --- /dev/null +++ b/ipa-radius-server/plugins/__init__.py @@ -0,0 +1 @@ +# intentionally empty diff --git a/ipa-server/ipaserver/radiusinstance.py b/ipa-radius-server/plugins/radiusinstance.py index 3b89018f0..731cd65a1 100644 --- a/ipa-server/ipaserver/radiusinstance.py +++ b/ipa-radius-server/plugins/radiusinstance.py @@ -19,6 +19,8 @@ # import sys +sys.path.append("/usr/share/ipa") + import subprocess import string import tempfile @@ -27,10 +29,10 @@ import logging import pwd import time import sys -from ipa.ipautil import * +from ipa import ipautil from ipa import radius_util -import service +from ipaserver import service import os import re @@ -42,10 +44,6 @@ from ipaserver.funcs import DefaultUserContainer, DefaultGroupContainer #------------------------------------------------------------------------------- -def ldap_mod(fd, dn, pwd): - args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name] - run(args) - def get_radius_version(): version = None try: @@ -74,14 +72,13 @@ class RadiusInstance(service.Service): def create_instance(self, realm_name, host_name, ldap_server): self.realm = realm_name.upper() - self.suffix = realm_to_suffix(self.realm) + self.suffix = ipautil.realm_to_suffix(self.realm) self.fqdn = host_name self.ldap_server = ldap_server self.principal = "%s/%s@%s" % (radius_util.RADIUS_SERVICE_NAME, self.fqdn, self.realm) self.basedn = self.suffix self.user_basedn = "%s,%s" % (DefaultUserContainer, self.basedn) # FIXME, should be utility to get this self.radius_version = get_radius_version() - self.start_creation(4, "Configuring radiusd") try: self.stop() @@ -89,22 +86,23 @@ class RadiusInstance(service.Service): # It could have been not running pass - self.__create_radius_keytab() - self.__radiusd_conf() + self.step("create radiusd keytab", self.__create_radius_keytab) + self.step("configuring radiusd.conf for radius instance", self.__radiusd_conf) + self.step("starting radiusd", self.__start_instance) + self.step("configuring radiusd to start on boot", self.chkconfig_on) + + # FIXME: + # self.step("setting ldap encrypted attributes", self.__set_ldap_encrypted_attributes) + + self.start_creation("Configuring radiusd") + def __start_instance(self): try: - self.step("starting radiusd") self.start() except: logging.error("radiusd service failed to start") - self.step("configuring radiusd to start on boot") - self.chkconfig_on() - - def __radiusd_conf(self): - self.step('configuring radiusd.conf for radius instance') - 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, @@ -117,7 +115,7 @@ class RadiusInstance(service.Service): 'SUFFIX' : self.suffix, } try: - radiusd_conf = template_file(radius_util.RADIUSD_CONF_TEMPLATE_FILEPATH, sub_dict) + radiusd_conf = ipautil.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() @@ -125,9 +123,8 @@ class RadiusInstance(service.Service): logging.error("could not create %s: %s", radius_util.RADIUSD_CONF_FILEPATH, e) def __create_radius_keytab(self): - self.step("creating a keytab for httpd") try: - if file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH): + if ipautil.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", radius_util.RADIUS_IPA_KEYTAB_FILEPATH) @@ -143,7 +140,7 @@ class RadiusInstance(service.Service): # give kadmin time to actually write the file before we go on retry = 0 - while not file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH): + while not ipautil.file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH): time.sleep(1) retry += 1 if retry > 15: @@ -155,17 +152,23 @@ class RadiusInstance(service.Service): except Exception, e: logging.error("could not chown on %s to %s: %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH, radius_util.RADIUS_USER, e) + def __ldap_mod(self, ldif): + txt = iputil.template_file(ipautil.SHARE_DIR + ldif, self.sub_dict) + fd = ipautil.write_tmp_file(txt) + + args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", + "-D", "cn=Directory Manager", "-w", self.dm_password, "-f", fd.name] + + try: + ipautil.run(args) + except ipautil.CalledProcessError, e: + logging.critical("Failed to load %s: %s" % (ldif, str(e))) + + fd.close() + #FIXME, should use IPAdmin method def __set_ldap_encrypted_attributes(self): - ldif_file = 'encrypted_attribute.ldif' - self.step("setting ldap encrypted attributes") - ldif_txt = template_file(SHARE_DIR + ldif_file, {'ENCRYPTED_ATTRIBUTE':'radiusClientSecret'}) - ldif_fd = write_tmp_file(ldif_txt) - try: - ldap_mod(ldif_fd, "cn=Directory Manager", self.dm_password) - except subprocess.CalledProcessError, e: - logging.critical("Failed to load %s: %s" % (ldif_file, str(e))) - ldif_fd.close() + self.__ldap_mod("encrypted_attribute.ldif", {"ENCRYPTED_ATTRIBUTE" : "radiusClientSecret"}) #------------------------------------------------------------------------------- diff --git a/ipa-server/ipa-install/share/radius.radiusd.conf.template b/ipa-radius-server/share/radius.radiusd.conf.template index 3bc4927dd..3bc4927dd 100644 --- a/ipa-server/ipa-install/share/radius.radiusd.conf.template +++ b/ipa-radius-server/share/radius.radiusd.conf.template diff --git a/ipa-server/ipa-gui/ipa-webgui b/ipa-server/ipa-gui/ipa-webgui index c8e3d0589..a18c2db81 100644 --- a/ipa-server/ipa-gui/ipa-webgui +++ b/ipa-server/ipa-gui/ipa-webgui @@ -63,41 +63,48 @@ def daemonize(): # stderr os.open("/dev/null", os.O_RDWR) -# To make development easier, we detect if we are in the development -# environment to load a different configuration and avoid becoming -# a daemon -devel = False -if os.path.exists(os.path.join(os.path.dirname(__file__), "Makefile.am")): - devel = True - -if not devel: - try: - daemonize() - except Exception, e: - sys.stderr.write("error becoming daemon: " + str(e)) - sys.exit(1) +def main(): + # To make development easier, we detect if we are in the development + # environment to load a different configuration and avoid becoming + # a daemon + devel = False + if os.path.exists(os.path.join(os.path.dirname(__file__), "Makefile.am")): + devel = True + + if not devel: + try: + daemonize() + except Exception, e: + sys.stderr.write("error becoming daemon: " + str(e)) + sys.exit(1) + + sys.path.append("/usr/share/ipa/") -sys.path.append("/usr/share/ipa/") + # this must be after sys.path is changed to work correctly + import pkg_resources + pkg_resources.require("TurboGears") + pkg_resources.require("ipa_gui") -# this must be after sys.path is changed to work correctly -import pkg_resources -pkg_resources.require("TurboGears") -pkg_resources.require("ipa_gui") + from turbogears import update_config, start_server + import cherrypy + cherrypy.lowercase_api = True -from turbogears import update_config, start_server -import cherrypy -cherrypy.lowercase_api = True + # Load the config - look for a local file first for development + # and then the system config file + if devel: + update_config(configfile="dev.cfg", + modulename="ipagui.config") + else: + update_config(configfile="/usr/share/ipa/ipa-webgui.cfg", + modulename="ipagui.config.app") -# Load the config - look for a local file first for development -# and then the system config file -if devel: - update_config(configfile="dev.cfg", - modulename="ipagui.config") -else: - update_config(configfile="/usr/share/ipa/ipa-webgui.cfg", - modulename="ipagui.config.app") + from ipagui.controllers import Root -from ipagui.controllers import Root + start_server(Root()) -start_server(Root()) +try: + main() +except Exception, e: + print "failed to start web gui: %s" % str(e) + sys.exit(1) diff --git a/ipa-server/ipa-gui/ipagui/forms/delegate.py b/ipa-server/ipa-gui/ipagui/forms/delegate.py index d9d5d727c..7eadfe23e 100644 --- a/ipa-server/ipa-gui/ipagui/forms/delegate.py +++ b/ipa-server/ipa-gui/ipagui/forms/delegate.py @@ -65,8 +65,9 @@ class DelegateValidator(validators.Schema): messages = { 'empty': _("Please choose a group"), }) dest_group_dn = validators.String(not_empty=True, messages = { 'empty': _("Please choose a group"), }) - attrs = validators.NotEmpty( - messages = { 'empty': _("Please select at least one value"), }) + # There is no attrs validator here because then it shows as one + # huge block of color in the form. The validation is done in + # the subcontroller. class DelegateForm(widgets.Form): params = ['delegate_fields', 'attr_list'] diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py index 74369a6ae..43a7a5477 100644 --- a/ipa-server/ipa-gui/ipagui/forms/user.py +++ b/ipa-server/ipa-gui/ipagui/forms/user.py @@ -5,8 +5,8 @@ from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm class UserFields(object): givenname = widgets.TextField(name="givenname", label="First Name") sn = widgets.TextField(name="sn", label="Last Name") - cn = widgets.TextField(name="cn", label="Common Names") - cns = ExpandingForm(name="cns", label="Common Names", fields=[cn]) + cn = widgets.TextField(name="cn", label="Full Name") + cns = ExpandingForm(name="cns", label="Full Name", fields=[cn]) title = widgets.TextField(name="title", label="Title") displayname = widgets.TextField(name="displayname", label="Display Name") initials = widgets.TextField(name="initials", label="Initials") diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py b/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py index cee239e72..2319b944a 100644 --- a/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py +++ b/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py @@ -56,6 +56,25 @@ class DelegationController(IPAController): turbogears.flash("Add delegation cancelled") raise turbogears.redirect('/delegate/list') + # Try to handle the case where the user entered just some data + # into the source/dest group name but didn't do a Find. We'll do + # our best to see if a group by that name exists and if so, use it. + dest_group_dn = kw.get('dest_group_dn') + dest_group_cn = kw.get('dest_group_cn') + if not dest_group_dn and dest_group_cn: + try: + group = client.get_entry_by_cn(dest_group_cn, ['dn']) + kw['dest_group_dn'] = group.dn + except: + kw['dest_group_cn'] = "Please choose:" + source_group_dn = kw.get('source_group_dn') + source_group_cn = kw.get('source_group_cn') + if not source_group_dn and source_group_cn: + try: + group = client.get_entry_by_cn(source_group_cn, ['dn']) + kw['source_group_dn'] = group.dn + except: + kw['source_group_cn'] = "Please choose:" tg_errors, kw = self.delegatevalidate(**kw) if tg_errors: turbogears.flash("There were validation errors.<br/>" + @@ -292,4 +311,11 @@ class DelegationController(IPAController): @validate(form=delegate_form) @identity.require(identity.not_anonymous()) def delegatevalidate(self, tg_errors=None, **kw): + # We are faking this because otherwise it shows up as one huge + # block of color in the UI when it has a not empty validator. + if not kw.get('attrs'): + if not tg_errors: + tg_errors = {} + tg_errors['attrs'] = _("Please select at least one value") + cherrypy.request.validation_errors = tg_errors return tg_errors, kw diff --git a/ipa-server/ipa-gui/ipagui/templates/delegateform.kid b/ipa-server/ipa-gui/ipagui/templates/delegateform.kid index 4eb846d53..3f885fa03 100644 --- a/ipa-server/ipa-gui/ipagui/templates/delegateform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/delegateform.kid @@ -4,6 +4,13 @@ <?python searchurl = tg.url('/delegate/group_search') ?> <script type="text/javascript"> + function lostFocus(which_group) { + /* The user has left the field, save what they put in there in case + * they don't do a Find. */ + group_cn_field = $('form_' + which_group + '_group_cn'); + group_criteria_field = $(which_group + '_criteria') + group_cn_field.value = group_criteria_field.value + } function enterDoSearch(e, which_group) { var keyPressed; @@ -104,8 +111,8 @@ py:content="tg.errors.get('source_group_dn')" /> </div> <div id="source_searcharea" style="display:none"> - <input id="source_criteria" type="text" - onkeypress="return enterDoSearch(event, 'source');" /> + <input class="requiredfield" id="source_criteria" type="text" + onkeypress="return enterDoSearch(event, 'source');" onblur="return lostFocus('source');"/> <input class="searchbutton" type="button" value="Find" onclick="return doSearch('source');" /> @@ -142,8 +149,8 @@ </div> <div id="dest_searcharea" style="display:none"> <div> - <input id="dest_criteria" type="text" - onkeypress="return enterDoSearch(event, 'dest');" /> + <input class="requiredfield" id="dest_criteria" type="text" + onkeypress="return enterDoSearch(event, 'dest');" onblur="return lostFocus('dest');"/> <input class="searchbutton" type="button" value="Find" onclick="return doSearch('dest');" /> diff --git a/ipa-server/ipa-install/Makefile.am b/ipa-server/ipa-install/Makefile.am index 1b46d354b..8a3e4a975 100644 --- a/ipa-server/ipa-install/Makefile.am +++ b/ipa-server/ipa-install/Makefile.am @@ -8,6 +8,7 @@ sbin_SCRIPTS = \ ipa-server-install \ ipa-replica-install \ ipa-replica-prepare \ + ipa-server-certinstall \ $(NULL) EXTRA_DIST = \ diff --git a/ipa-server/ipa-install/ipa-server-certinstall b/ipa-server/ipa-install/ipa-server-certinstall new file mode 100644 index 000000000..932a6be18 --- /dev/null +++ b/ipa-server/ipa-install/ipa-server-certinstall @@ -0,0 +1,156 @@ +#! /usr/bin/python -E +# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com> +# +# Copyright (C) 2007 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 or later +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import sys +sys.path.append("/usr/share/ipa") + +import traceback + +import krbV, ldap, getpass + +from ipaserver import certs, dsinstance, httpinstance, ipaldap, installutils + +def get_realm_name(): + c = krbV.default_context() + return c.default_realm + +def parse_options(): + from optparse import OptionParser + parser = OptionParser() + + parser.add_option("-d", "--dirsrv", dest="dirsrv", action="store_true", + default=False, help="install certificate for the directory server") + parser.add_option("-w", "--http", dest="http", action="store_true", + default=False, help="install certificate for the http server") + + + options, args = parser.parse_args() + + if not options.dirsrv and not options.http: + parser.error("you must specify dirsrv and/or http") + + if len(args) != 1: + parser.error("you must provide a pkcs12 filename") + + return options, args[0] + +def set_ds_cert_name(cert_name, dm_password): + conn = ipaldap.IPAdmin("127.0.0.1") + conn.simple_bind_s("cn=directory manager", dm_password) + + mod = [(ldap.MOD_REPLACE, "nsSSLPersonalitySSL", cert_name)] + + conn.modify_s("cn=RSA,cn=encryption,cn=config", mod) + + conn.unbind() + +def set_http_cert_name(cert_name): + # find the existing cert name + fd = open(httpinstance.NSS_CONF) + nick_name = None + file = [] + for line in fd: + if "NSSNickname" in line: + file.append('NSSNickname "%s"\n' % cert_name) + else: + file.append(line) + fd.close() + + fd = open(httpinstance.NSS_CONF, "w") + fd.write("".join(file)) + fd.close() + + +def choose_server_cert(server_certs): + print "Please select the certificate to use:" + num = 1 + for cert in server_certs: + print "%d. %s" % (num, cert[0]) + num += 1 + + cert_num = 0 + while 1: + cert_input = raw_input("Certificate number [1]: ") + print "" + if cert_input == "": + break + else: + try: + num = int(cert_input) + except ValueError: + print "invalid number" + continue + if num > len(server_certs): + print "number out of range" + continue + cert_num = num - 1 + break + return server_certs[cert_num] + + +def import_cert(dirname, pkcs12_fname): + cdb = certs.CertDB(dirname) + cdb.create_passwd_file(False) + cdb.create_certdbs() + try: + cdb.import_pkcs12(pkcs12_fname) + except RuntimeError, e: + print str(e) + sys.exit(1) + + server_certs = cdb.find_server_certs() + if len(server_certs) == 0: + print "could not find a suitable server cert in import" + sys.exit(1) + elif len(server_certs) == 1: + server_cert = server_certs[0] + else: + server_cert = choose_server_cert(server_certs) + + cdb.trust_root_cert(server_cert[0]) + + return server_cert + +def main(): + options, pkcs12_fname = parse_options() + + try: + if options.dirsrv: + dm_password = getpass.getpass("Directory Manager password: ") + realm = get_realm_name() + dirname = dsinstance.config_dirname(realm) + server_cert = import_cert(dirname, pkcs12_fname) + set_ds_cert_name(server_cert[0], dm_password) + + if options.http: + dirname = httpinstance.NSS_DIR + server_cert = import_cert(dirname, pkcs12_fname) + print server_cert + set_http_cert_name(server_cert[0]) + + except Exception, e: + print "an unexpected error occurred: %s" % str(e) + traceback.print_exc() + return 1 + + return 0 + + +sys.exit(main()) diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install index 646512d51..ee5e929d1 100644 --- a/ipa-server/ipa-install/ipa-server-install +++ b/ipa-server/ipa-install/ipa-server-install @@ -46,7 +46,6 @@ import ipaserver.krbinstance import ipaserver.bindinstance import ipaserver.httpinstance import ipaserver.ntpinstance -import ipaserver.radiusinstance import ipaserver.webguiinstance from ipaserver import service @@ -400,11 +399,6 @@ def main(): webgui = ipaserver.webguiinstance.WebGuiInstance() webgui.create_instance() - # Create a radius instance - radius = ipaserver.radiusinstance.RadiusInstance() - # FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL? - radius.create_instance(realm_name, host_name, 'localhost') - bind.setup(host_name, ip_address, realm_name) if options.setup_bind: skipbind = False diff --git a/ipa-server/ipa-install/share/Makefile.am b/ipa-server/ipa-install/share/Makefile.am index 36bb54e83..5d117dec5 100644 --- a/ipa-server/ipa-install/share/Makefile.am +++ b/ipa-server/ipa-install/share/Makefile.am @@ -19,7 +19,7 @@ app_DATA = \ krb.con.template \ krbrealm.con.template \ ntp.conf.server.template \ - radius.radiusd.conf.template \ + preferences.html.template \ referint-conf.ldif \ dna-posix.ldif \ master-entry.ldif \ diff --git a/ipa-server/ipa-install/share/bootstrap-template.ldif b/ipa-server/ipa-install/share/bootstrap-template.ldif index 9642070c7..0a969de38 100644 --- a/ipa-server/ipa-install/share/bootstrap-template.ldif +++ b/ipa-server/ipa-install/share/bootstrap-template.ldif @@ -2,6 +2,8 @@ dn: $SUFFIX changetype: modify add: objectClass objectClass: pilotObject +- +add: info info: IPA V1.0 dn: cn=accounts,$SUFFIX @@ -80,6 +82,7 @@ gidNumber: 1001 homeDirectory: /home/admin loginShell: /bin/bash gecos: Administrator +nsAccountLock: False dn: cn=radius,$SUFFIX changetype: add @@ -114,6 +117,7 @@ cn: admins description: Account administrators group gidNumber: 1001 member: uid=admin,cn=sysaccounts,cn=etc,$SUFFIX +nsAccountLock: False dn: cn=ipausers,cn=groups,cn=accounts,$SUFFIX changetype: add diff --git a/ipa-server/ipa-install/share/default-aci.ldif b/ipa-server/ipa-install/share/default-aci.ldif index 95743eebb..5715259a1 100644 --- a/ipa-server/ipa-install/share/default-aci.ldif +++ b/ipa-server/ipa-install/share/default-aci.ldif @@ -1,18 +1,18 @@ # $SUFFIX (base entry) -# FIXME: We need to allow truly anonymous access only to NIS data for older clients. We need to allow broad access to most attributes only to authewnticated users +# FIXME: We need to allow truly anonymous access only to NIS data for older clients. We need to allow broad access to most attributes only to authenticated users dn: $SUFFIX changetype: modify replace: aci -aci: (targetattr != "userPassword || krbPrincipalKey || krbMKey || sambaLMPassword || sambaNTPassword")(version 3.0; acl "Enable anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";) -aci: (targetattr != "userPassword || krbPrincipalKey || krbMKey || sambaLMPassword || sambaNTPassword")(version 3.0; acl "Admin can manage any entry except for passwords"; allow (all) userdn = "ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX";) -aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword")(version 3.0; acl "Admin can write passwords"; allow (write) userdn="ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX";) -aci: (targetattr = "krbPrincipalName || krbUPEnabled || krbPrincipalKey || krbMKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData")(version 3.0; acl "KDC System Account has access to kerberos material"; allow (read, search, compare) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";) -aci: (targetattr = "krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "KDC System Account can update some fields"; allow (read, search, compare, write) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";) -aci: (targetattr = "userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword || krbPasswordExpiration || krbPwdHistory || krbLastPwdChange")(version 3.0; acl "Kpasswd access to passowrd hashes for passowrd changes"; allow (read, write) userdn = "ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";) -aci: (targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup)(objectClass=radiusprofile))")(targetattr != "aci")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) +aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMkey")(version 3.0; acl "Enable Anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";) +aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMkey")(version 3.0; acl "Admin can manage any entry"; allow (all) userdn = "ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX";) +aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Admins can write passwords"; allow (write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) +aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";) +aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "KDC System Account can access passwords"; allow (all) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";) +aci: (targetattr = "krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "KDC System Account can update some fields"; allow (write) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";) +aci: (targetattr = "krbPrincipalName || krbUPEnabled || krbMKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "Only the KDC System Account has access to kerberos material"; allow (read, search, compare) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";) +aci: (targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup))")(targetattr != "aci || userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) aci: (targetfilter = "(objectClass=krbPwdPolicy)")(targetattr = "krbMaxPwdLife || krbMinPwdLife || krbPwdMinDiffChars || krbPwdMinLength || krbPwdHistoryLength")(version 3.0;acl "Admins can write password policies"; allow (read, search, compare, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) -aci: (targetattr = "givenName || sn || cn || displayName || initials || loginShell || homePhone || mobile || pager || facsimileTelephoneNumber || telephoneNumber || street || roomNumber || l || st || postalCode || manager || description || carLicense || labeledURI || inetUserHTTPURL || seeAlso")(version 3.0;acl "Self service";allow (write) userdn = "ldap:///self";) -aci: (target="ldap:///cn=radius,$SUFFIX")(version 3.0; acl "Only radius and admin can access radius service data"; deny (all) userdn!="ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX || ldap:///krbprincipalname=radius/$FQDN@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";) +aci: (targetattr = "givenName || sn || cn || displayName || title || initials || loginShell || gecos || homePhone || mobile || pager || facsimileTelephoneNumber || telephoneNumber || street || roomNumber || l || st || postalCode || manager || secretary || description || carLicense || labeledURI || inetUserHTTPURL || seeAlso || employeeType || businessCategory || ou")(version 3.0;acl "Self service";allow (write) userdn = "ldap:///self";) dn: cn=ipaConfig,cn=etc,$SUFFIX changetype: modify @@ -25,6 +25,12 @@ add: aci aci: (targetattr = "krbMaxPwdLife || krbMinPwdLife || krbPwdMinDiffChars || krbPwdMinLength || krbPwdHistoryLength")(version 3.0;acl "Admins can write password policy"; allow (write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) aci: (targetattr = "aci")(version 3.0;acl "Admins can manage delegations"; allow (write, delete) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) +dn: cn=radius,$SUFFIX +changetype: modify +add: aci +aci: (targetattr = "*")(version 3.0; acl "Only radius and admin can access radius service data"; deny (all) userdn!="ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX || ldap:///krbprincipalname=radius/$FQDN@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";) +aci: (targetfilter = "(objectClass=radiusprofile)")(targetattr != "aci || userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) + dn: cn=services,cn=accounts,$SUFFIX changetype: modify add: aci diff --git a/ipa-server/ipa-install/share/preferences.html.template b/ipa-server/ipa-install/share/preferences.html.template new file mode 100644 index 000000000..2d3684dcd --- /dev/null +++ b/ipa-server/ipa-install/share/preferences.html.template @@ -0,0 +1,33 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> + <title>Automatically set browser preferences</title> +</head> +<body> +<form action="undefined" method="get"> +<input type=button onclick="setPreferences()" name="prefs" value="Configure Firefox"> +</form> + +<script type="text/javascript"> +function setPreferences() { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalPreferencesWrite"); + try { + navigator.preference("network.negotiate-auth.using-native-gsslib", true) + navigator.preference("network.negotiate-auth.delegation-uris", ".$DOMAIN") + navigator.preference("network.negotiate-auth.trusted-uris", ".$DOMAIN") + navigator.preference("network.negotiate-auth.allow-proxies", true) + } catch (e) { + alert("Unable to store preferences: " + e) + } + netscape.security.PrivilegeManager.disablePrivilege("UniversalPreferencesWrite"); + alert("Successfully configured Firefox for single sign on.") + } catch (e) { + alert("Unable to apply recommended settings.\n\nClick on the Certificate Authority link and select trust for all, then reload this page and try again.\n\nThe error returned was: " + e); + return; + } +} +</script> + +</body> +</html> diff --git a/ipa-server/ipa-install/share/referint-conf.ldif b/ipa-server/ipa-install/share/referint-conf.ldif index 7a547ba50..533b97ded 100644 --- a/ipa-server/ipa-install/share/referint-conf.ldif +++ b/ipa-server/ipa-install/share/referint-conf.ldif @@ -2,6 +2,10 @@ dn: cn=referential integrity postoperation,cn=plugins,cn=config changetype: modify replace: nsslapd-pluginenabled nsslapd-pluginenabled: on +- +add: nsslapd-pluginArg7 nsslapd-pluginArg7: manager +- +add: nsslapd-pluginArg8 nsslapd-pluginArg8: secretary diff --git a/ipa-server/ipa-kpasswd/ipa_kpasswd.c b/ipa-server/ipa-kpasswd/ipa_kpasswd.c index 99dfe678f..ccd17c1f0 100644 --- a/ipa-server/ipa-kpasswd/ipa_kpasswd.c +++ b/ipa-server/ipa-kpasswd/ipa_kpasswd.c @@ -306,7 +306,7 @@ int ldap_pwd_change(char *client_name, char *realm_name, krb5_data pwd, char **e LDAPControl **srvctrl = NULL; char *exterr1 = NULL; char *exterr2 = NULL; - char *err; + char *err = NULL; int msgid; int ret, rc; diff --git a/ipa-server/ipa-server.spec b/ipa-server/ipa-server.spec index f84affe86..38725cf34 100755 --- a/ipa-server/ipa-server.spec +++ b/ipa-server/ipa-server.spec @@ -37,7 +37,6 @@ Requires: python-krbV Requires: TurboGears Requires: python-tgexpandingformwidget Requires: acl -Requires: freeradius Requires: pyasn1 Requires: libcap @@ -69,12 +68,32 @@ rm %{buildroot}/%{plugin_dir}/libipa-dna-plugin.la %clean rm -rf %{buildroot} +%post +if [ $1 = 1 ]; then + /sbin/chkconfig --add ipa-kpasswd + /sbin/chkconfig --add ipa-webgui +fi + +%preun +if [ $1 = 0 ]; then + /sbin/chkconfig --del ipa-kpasswd + /sbin/chkconfig --del ipa-webgui + /sbin/service ipa-kpasswd stop >/dev/null 2>&1 || : + /sbin/service ipa-webgui stop >/dev/null 2>&1 || : +fi + +%postun +if [ "$1" -ge "1" ]; then + /sbin/service ipa-kpasswd condrestart >/dev/null 2>&1 || : + /sbin/service ipa-webgui condrestart >/dev/null 2>&1 || : +fi %files %defattr(-,root,root,-) %{_sbindir}/ipa-server-install %{_sbindir}/ipa-replica-install %{_sbindir}/ipa-replica-prepare +%{_sbindir}/ipa-server-certinstall %{_sbindir}/ipa_kpasswd %{_sbindir}/ipa-webgui %attr(4750,root,apache) %{_sbindir}/ipa-keytab-util diff --git a/ipa-server/ipa-server.spec.in b/ipa-server/ipa-server.spec.in index 874d82cfd..e43f07aa2 100644 --- a/ipa-server/ipa-server.spec.in +++ b/ipa-server/ipa-server.spec.in @@ -37,7 +37,6 @@ Requires: python-krbV Requires: TurboGears Requires: python-tgexpandingformwidget Requires: acl -Requires: freeradius Requires: pyasn1 Requires: libcap @@ -69,12 +68,32 @@ rm %{buildroot}/%{plugin_dir}/libipa-dna-plugin.la %clean rm -rf %{buildroot} +%post +if [ $1 = 1 ]; then + /sbin/chkconfig --add ipa-kpasswd + /sbin/chkconfig --add ipa-webgui +fi + +%preun +if [ $1 = 0 ]; then + /sbin/chkconfig --del ipa-kpasswd + /sbin/chkconfig --del ipa-webgui + /sbin/service ipa-kpasswd stop >/dev/null 2>&1 || : + /sbin/service ipa-webgui stop >/dev/null 2>&1 || : +fi + +%postun +if [ "$1" -ge "1" ]; then + /sbin/service ipa-kpasswd condrestart >/dev/null 2>&1 || : + /sbin/service ipa-webgui condrestart >/dev/null 2>&1 || : +fi %files %defattr(-,root,root,-) %{_sbindir}/ipa-server-install %{_sbindir}/ipa-replica-install %{_sbindir}/ipa-replica-prepare +%{_sbindir}/ipa-server-certinstall %{_sbindir}/ipa_kpasswd %{_sbindir}/ipa-webgui %attr(4750,root,apache) %{_sbindir}/ipa-keytab-util diff --git a/ipa-server/ipaserver/Makefile.am b/ipa-server/ipaserver/Makefile.am index f1c094b36..b1d00a80a 100644 --- a/ipa-server/ipaserver/Makefile.am +++ b/ipa-server/ipaserver/Makefile.am @@ -9,7 +9,6 @@ app_PYTHON = \ krbinstance.py \ httpinstance.py \ ntpinstance.py \ - radiusinstance.py \ webguiinstance.py \ service.py \ installutils.py \ diff --git a/ipa-server/ipaserver/bindinstance.py b/ipa-server/ipaserver/bindinstance.py index 8a131fe79..cc99eacfa 100644 --- a/ipa-server/ipaserver/bindinstance.py +++ b/ipa-server/ipaserver/bindinstance.py @@ -23,10 +23,13 @@ import tempfile import shutil import os import socket -from ipa.ipautil import * -class BindInstance: +import service +from ipa import ipautil + +class BindInstance(service.Service): def __init__(self): + service.Service.__init__(self, "named") self.fqdn = None self.domain = None self.host = None @@ -52,7 +55,7 @@ class BindInstance: return True def create_sample_bind_zone(self): - bind_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict) + bind_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.zone.db.template", self.sub_dict) [bind_fd, bind_name] = tempfile.mkstemp(".db","sample.zone.") os.write(bind_fd, bind_txt) os.close(bind_fd) @@ -73,15 +76,6 @@ class BindInstance: except: print "named service failed to start" - def stop(self): - run(["/sbin/service", "named", "stop"]) - - def start(self): - run(["/sbin/service", "named", "start"]) - - def restart(self): - run(["/sbin/service", "named", "restart"]) - def __setup_sub_dict(self): self.sub_dict = dict(FQDN=self.fqdn, IP=self.ip_address, @@ -90,7 +84,7 @@ class BindInstance: REALM=self.realm) def __setup_zone(self): - zone_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict) + zone_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.zone.db.template", self.sub_dict) zone_fd = open('/var/named/'+self.domain+'.zone.db', 'w') zone_fd.write(zone_txt) zone_fd.close() @@ -98,7 +92,7 @@ class BindInstance: def __setup_named_conf(self): if os.path.exists('/etc/named.conf'): shutil.copy2('/etc/named.conf', '/etc/named.conf.ipabkp') - named_txt = template_file(SHARE_DIR + "bind.named.conf.template", self.sub_dict) + named_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.named.conf.template", self.sub_dict) named_fd = open('/etc/named.conf', 'w') named_fd.seek(0) named_fd.truncate(0) diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py index fb6b01d0e..eecfdf21c 100644 --- a/ipa-server/ipaserver/certs.py +++ b/ipa-server/ipaserver/certs.py @@ -17,7 +17,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -import os, stat, subprocess +import os, stat, subprocess, re import sha from ipa import ipautil @@ -77,6 +77,11 @@ class CertDB(object): new_args = new_args + args ipautil.run(new_args, stdin) + def run_signtool(self, args, stdin=None): + new_args = ["/usr/bin/signtool", "-d", self.secdir] + new_args = new_args + args + ipautil.run(new_args, stdin) + def create_noise_file(self): ipautil.backup_file(self.noise_fname) f = open(self.noise_fname, "w") @@ -108,7 +113,7 @@ class CertDB(object): self.run_certutil(["-S", "-n", self.cacert_name, "-s", "cn=CAcert", "-x", - "-t", "CT,,", + "-t", "CT,,C", "-m", self.next_serial(), "-v", self.valid_months, "-z", self.noise_fname, @@ -130,7 +135,7 @@ class CertDB(object): def load_cacert(self, cacert_fname): self.run_certutil(["-A", "-n", self.cacert_name, - "-t", "CT,CT,", + "-t", "CT,,C", "-a", "-i", cacert_fname]) @@ -139,7 +144,17 @@ class CertDB(object): if not cdb: cdb = self self.request_cert(name) - cdb.issue_cert(self.certreq_fname, self.certder_fname) + cdb.issue_server_cert(self.certreq_fname, self.certder_fname) + self.add_cert(self.certder_fname, nickname) + os.unlink(self.certreq_fname) + os.unlink(self.certder_fname) + + def create_signing_cert(self, nickname, name, other_certdb=None): + cdb = other_certdb + if not cdb: + cdb = self + self.request_cert(name) + cdb.issue_signing_cert(self.certreq_fname, self.certder_fname) self.add_cert(self.certder_fname, nickname) os.unlink(self.certreq_fname) os.unlink(self.certder_fname) @@ -151,7 +166,7 @@ class CertDB(object): "-z", self.noise_fname, "-f", self.passwd_fname]) - def issue_cert(self, certreq_fname, cert_fname): + def issue_server_cert(self, certreq_fname, cert_fname): p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, "-C", "-c", self.cacert_name, @@ -179,8 +194,37 @@ class CertDB(object): # n - not critical p.stdin.write("2\n9\nn\n1\n9\nn\n") p.wait() - - + + def issue_signing_cert(self, certreq_fname, cert_fname): + p = subprocess.Popen(["/usr/bin/certutil", + "-d", self.secdir, + "-C", "-c", self.cacert_name, + "-i", certreq_fname, + "-o", cert_fname, + "-m", self.next_serial(), + "-v", self.valid_months, + "-f", self.passwd_fname, + "-1", "-5"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + + # Bah - this sucks, but I guess it isn't possible to fully + # control this with command line arguments. + # + # What this is requesting is: + # -1 (Create key usage extension) + # 0 - Digital Signature + # 5 - Cert signing key + # 9 - done + # n - not critical + # + # -5 (Create netscape cert type extension) + # 3 - Object Signing + # 9 - done + # n - not critical + p.stdin.write("0\n5\n9\nn\n3\n9\nn\n") + p.wait() + def add_cert(self, cert_fname, nickname): self.run_certutil(["-A", "-n", nickname, "-t", "u,u,u", @@ -196,6 +240,50 @@ class CertDB(object): f.close() self.set_perms(self.pin_fname) + def trust_root_cert(self, nickname): + p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, + "-O", "-n", nickname], stdout=subprocess.PIPE) + + chain = p.stdout.read() + chain = chain.split("\n") + + root_nickname = re.match('\ *"(.*)".*', chain[0]).groups()[0] + + self.run_certutil(["-M", "-n", root_nickname, + "-t", "CT,CT,"]) + + def find_server_certs(self): + p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, + "-L"], stdout=subprocess.PIPE) + + certs = p.stdout.read() + + certs = certs.split("\n") + + server_certs = [] + + for cert in certs: + fields = cert.split() + if not len(fields): + continue + flags = fields[-1] + if 'u' in flags: + name = " ".join(fields[0:-1]) + server_certs.append((name, flags)) + + return server_certs + + + def import_pkcs12(self, pkcs12_fname): + try: + ipautil.run(["/usr/bin/pk12util", "-d", self.secdir, + "-i", pkcs12_fname]) + except ipautil.CalledProcessError, e: + if e.returncode == 17: + raise RuntimeError("incorrect password") + else: + raise RuntimeError("unknown error import pkcs#12 file") + def create_self_signed(self, passwd=True): self.create_noise_file() self.create_passwd_file(passwd) @@ -208,6 +296,3 @@ class CertDB(object): self.create_passwd_file(passwd) self.create_certdbs() self.load_cacert(cacert_fname) - - - diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py index 5edc3879e..6cbffcb84 100644 --- a/ipa-server/ipaserver/dsinstance.py +++ b/ipa-server/ipaserver/dsinstance.py @@ -35,10 +35,6 @@ import ipaldap, ldap SERVER_ROOT_64 = "/usr/lib64/dirsrv" SERVER_ROOT_32 = "/usr/lib/dirsrv" -def ldap_mod(fd, dn, pwd): - args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name] - ipautil.run(args) - def realm_to_suffix(realm_name): s = realm_name.split(".") terms = ["dc=" + x.lower() for x in s] @@ -139,38 +135,29 @@ class DsInstance(service.Service): self.domain = host_name[host_name.find(".")+1:] self.__setup_sub_dict() - if ro_replica: - self.start_creation(15, "Configuring directory server:") - else: - self.start_creation(15, "Configuring directory server:") - - self.__create_ds_user() - self.__create_instance() - self.__add_default_schemas() + self.step("creating directory server user", self.__create_ds_user) + self.step("creating directory server instance", self.__create_instance) + self.step("adding default schema", self.__add_default_schemas) if not ro_replica: - self.__add_memberof_module() - self.__add_referint_module() - self.__add_dna_module() - self.__create_indeces() - self.__enable_ssl() - self.__certmap_conf() - try: - self.step("restarting directory server") - self.restart() - except: - # TODO: roll back here? - logging.critical("Failed to restart the ds instance") - self.__add_default_layout() + self.step("enabling memberof plugin", self.__add_memberof_module) + self.step("enabling referential integrity plugin", self.__add_referint_module) + self.step("enabling distributed numeric assignment plugin", self.__add_dna_module) + self.step("creating indeces", self.__create_indeces) + self.step("configuring ssl for ds instance", self.__enable_ssl) + self.step("configuring certmap.conf", self.__certmap_conf) + self.step("restarting directory server", self.__restart_instance) + self.step("adding default layout", self.__add_default_layout) if not ro_replica: - self.__config_uidgid_gen_first_master() - self.__add_master_entry_first_master() - self.__init_memberof() - + self.step("configuring Posix uid/gid generation as first master", + self.__config_uidgid_gen_first_master) + self.step("adding master entry as first master", + self.__add_master_entry_first_master) + self.step("initializing group membership", + self.__init_memberof) - self.step("configuring directoy to start on boot") - self.chkconfig_on() + self.step("configuring directory to start on boot", self.chkconfig_on) - self.done_creation() + self.start_creation("Configuring directory server:") def __setup_sub_dict(self): server_root = find_server_root() @@ -180,7 +167,6 @@ class DsInstance(service.Service): SERVER_ROOT=server_root, DOMAIN=self.domain) def __create_ds_user(self): - self.step("creating directory server user") try: pwd.getpwnam(self.ds_user) logging.debug("ds user %s exists" % self.ds_user) @@ -194,7 +180,6 @@ class DsInstance(service.Service): logging.critical("failed to add user %s" % e) def __create_instance(self): - self.step("creating directory server instance") inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict) logging.debug(inf_txt) inf_fd = ipautil.write_tmp_file(inf_txt) @@ -219,7 +204,6 @@ class DsInstance(service.Service): logging.debug("failed to restart ds instance %s" % e) def __add_default_schemas(self): - self.step("adding default schema") shutil.copyfile(ipautil.SHARE_DIR + "60kerberos.ldif", schema_dirname(self.realm_name) + "60kerberos.ldif") shutil.copyfile(ipautil.SHARE_DIR + "60samba.ldif", @@ -229,68 +213,52 @@ class DsInstance(service.Service): shutil.copyfile(ipautil.SHARE_DIR + "60ipaconfig.ldif", schema_dirname(self.realm_name) + "60ipaconfig.ldif") - def __add_memberof_module(self): - self.step("enabling memboerof plugin") - memberof_txt = ipautil.template_file(ipautil.SHARE_DIR + "memberof-conf.ldif", self.sub_dict) - memberof_fd = ipautil.write_tmp_file(memberof_txt) + def __restart_instance(self): try: - ldap_mod(memberof_fd, "cn=Directory Manager", self.dm_password) - except ipautil.CalledProcessError, e: - logging.critical("Failed to load memberof-conf.ldif: %s" % str(e)) - memberof_fd.close() + self.restart() + except: + # TODO: roll back here? + logging.critical("Failed to restart the ds instance") + + def __ldap_mod(self, ldif, sub_dict = None): + fd = None + path = ipautil.SHARE_DIR + ldif + + if not sub_dict is None: + txt = ipautil.template_file(path, sub_dict) + fd = ipautil.write_tmp_file(txt) + path = fd.name + + args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", + "-D", "cn=Directory Manager", "-w", self.dm_password, "-f", path] - def __init_memberof(self): - self.step("initializing group membership") - memberof_txt = ipautil.template_file(ipautil.SHARE_DIR + "memberof-task.ldif", self.sub_dict) - memberof_fd = ipautil.write_tmp_file(memberof_txt) try: - ldap_mod(memberof_fd, "cn=Directory Manager", self.dm_password) + ipautil.run(args) except ipautil.CalledProcessError, e: - logging.critical("Failed to load memberof-conf.ldif: %s" % str(e)) - memberof_fd.close() + logging.critical("Failed to load %s: %s" % (ldif, str(e))) + + if not fd is None: + fd.close() + + def __add_memberof_module(self): + self.__ldap_mod("memberof-conf.ldif") + + def __init_memberof(self): + self.__ldap_mod("memberof-task.ldif", self.sub_dict) def __add_referint_module(self): - self.step("enabling referential integrity plugin") - referint_txt = ipautil.template_file(ipautil.SHARE_DIR + "referint-conf.ldif", self.sub_dict) - referint_fd = ipautil.write_tmp_file(referint_txt) - try: - ldap_mod(referint_fd, "cn=Directory Manager", self.dm_password) - except ipautil.CalledProcessError, e: - print "Failed to load referint-conf.ldif", e - referint_fd.close() + self.__ldap_mod("referint-conf.ldif") def __add_dna_module(self): - self.step("enabling distributed numeric assignment plugin") - dna_txt = ipautil.template_file(ipautil.SHARE_DIR + "dna-conf.ldif", self.sub_dict) - dna_fd = ipautil.write_tmp_file(dna_txt) - try: - ldap_mod(dna_fd, "cn=Directory Manager", self.dm_password) - except ipautil.CalledProcessError, e: - print "Failed to load dna-conf.ldif", e - dna_fd.close() + self.__ldap_mod("dna-conf.ldif") def __config_uidgid_gen_first_master(self): - self.step("configuring Posix uid/gid generation as first master") - dna_txt = ipautil.template_file(ipautil.SHARE_DIR + "dna-posix.ldif", self.sub_dict) - dna_fd = ipautil.write_tmp_file(dna_txt) - try: - ldap_mod(dna_fd, "cn=Directory Manager", self.dm_password) - except ipautil.CalledProcessError, e: - print "Failed to configure Posix uid/gid generation with dna-posix.ldif", e - dna_fd.close() + self.__ldap_mod("dna-posix.ldif", self.sub_dict) def __add_master_entry_first_master(self): - self.step("adding master entry as first master") - master_txt = ipautil.template_file(ipautil.SHARE_DIR + "master-entry.ldif", self.sub_dict) - master_fd = ipautil.write_tmp_file(master_txt) - try: - ldap_mod(master_fd, "cn=Directory Manager", self.dm_password) - except ipautil.CalledProcessError, e: - print "Failed to add master-entry.ldif", e - master_fd.close() + self.__ldap_mod("master-entry.ldif", self.sub_dict) def __enable_ssl(self): - self.step("configuring ssl for ds instance") dirname = config_dirname(self.realm_name) ca = certs.CertDB(dirname) ca.create_self_signed() @@ -322,41 +290,16 @@ class DsInstance(service.Service): conn.addEntry(entry) conn.unbind() - + def __add_default_layout(self): - self.step("adding default layout") - txt = ipautil.template_file(ipautil.SHARE_DIR + "bootstrap-template.ldif", self.sub_dict) - inf_fd = ipautil.write_tmp_file(txt) - logging.debug("adding default dfrom ipa.ipautil import *s layout") - args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager", - "-w", self.dm_password, "-f", inf_fd.name] - try: - ipautil.run(args) - logging.debug("done adding default ds layout") - except ipautil.CalledProcessError, e: - print "Failed to add default ds layout", e - logging.critical("Failed to add default ds layout %s" % e) + self.__ldap_mod("bootstrap-template.ldif", self.sub_dict) def __create_indeces(self): - self.step("creating indeces") - txt = ipautil.template_file(ipautil.SHARE_DIR + "indeces.ldif", self.sub_dict) - inf_fd = ipautil.write_tmp_file(txt) - logging.debug("adding/updating indeces") - args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager", - "-w", self.dm_password, "-f", inf_fd.name] - try: - ipautil.run(args) - logging.debug("done adding/updating indeces") - except ipautil.CalledProcessError, e: - logging.critical("Failed to add/update indeces %s" % str(e)) + self.__ldap_mod("indeces.ldif") def __certmap_conf(self): - self.step("configuring certmap.conf") - dirname = config_dirname(self.realm_name) - certmap_conf = ipautil.template_file(ipautil.SHARE_DIR + "certmap.conf.template", self.sub_dict) - certmap_fd = open(dirname+"certmap.conf", "w+") - certmap_fd.write(certmap_conf) - certmap_fd.close() + shutil.copyfile(ipautil.SHARE_DIR + "certmap.conf.template", + config_dirname(self.realm_name) + "certmap.conf") def change_admin_password(self, password): logging.debug("Changing admin password") diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py index 1799cca07..d0329ccad 100644 --- a/ipa-server/ipaserver/httpinstance.py +++ b/ipa-server/ipaserver/httpinstance.py @@ -17,6 +17,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +import os +import os.path import subprocess import string import tempfile @@ -25,11 +27,13 @@ import pwd import fileinput import sys import time +import shutil import service import certs import dsinstance -from ipa.ipautil import * +import installutils +from ipa import ipautil HTTPD_DIR = "/etc/httpd" SSL_CONF = HTTPD_DIR + "/conf.d/ssl.conf" @@ -43,52 +47,33 @@ successfully change with the command: Try updating the policycoreutils and selinux-policy packages. """ -def update_file(filename, orig, subst): - if os.path.exists(filename): - pattern = "%s" % re.escape(orig) - p = re.compile(pattern) - for line in fileinput.input(filename, inplace=1): - if not p.search(line): - sys.stdout.write(line) - else: - sys.stdout.write(p.sub(subst, line)) - fileinput.close() - return 0 - else: - print "File %s doesn't exist." % filename - return 1 - class HTTPInstance(service.Service): def __init__(self): service.Service.__init__(self, "httpd") def create_instance(self, realm, fqdn): - self.sub_dict = { "REALM" : realm, "FQDN": fqdn } self.fqdn = fqdn self.realm = realm + self.domain = fqdn[fqdn.find(".")+1:] + self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain } - self.start_creation(7, "Configuring the web interface") - - self.__disable_mod_ssl() - self.__set_mod_nss_port() - self.__configure_http() - self.__create_http_keytab() - self.__setup_ssl() - - self.step("restarting httpd") - self.restart() - - self.step("configuring httpd to start on boot") - self.chkconfig_on() - - self.done_creation() + self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl) + self.step("Setting mod_nss port to 443", self.__set_mod_nss_port) + self.step("configuring httpd", self.__configure_http) + self.step("creating a keytab for httpd", self.__create_http_keytab) + self.step("Setting up ssl", self.__setup_ssl) + self.step("Setting up browser autoconfig", self.__setup_autoconfig) + self.step("configuring SELinux for httpd", self.__selinux_config) + self.step("restarting httpd", self.restart) + self.step("configuring httpd to start on boot", self.chkconfig_on) + + self.start_creation("Configuring the web interface") def __selinux_config(self): - self.step("configuring SELinux for httpd") selinux=0 try: if (os.path.exists('/usr/sbin/selinuxenabled')): - run(["/usr/sbin/selinuxenabled"]) + ipautil.run(["/usr/sbin/selinuxenabled"]) selinux=1 except ipautil.CalledProcessError: # selinuxenabled returns 1 if not enabled @@ -98,14 +83,13 @@ class HTTPInstance(service.Service): # Allow apache to connect to the turbogears web gui # This can still fail even if selinux is enabled try: - run(["/usr/sbin/setsebool", "-P", "httpd_can_network_connect", "true"]) + ipautil.run(["/usr/sbin/setsebool", "-P", "httpd_can_network_connect", "true"]) except: self.print_msg(selinux_warning) def __create_http_keytab(self): - self.step("creating a keytab for httpd") try: - if file_exists("/etc/httpd/conf/ipa.keytab"): + if ipautil.file_exists("/etc/httpd/conf/ipa.keytab"): os.remove("/etc/httpd/conf/ipa.keytab") except os.error: print "Failed to remove /etc/httpd/conf/ipa.keytab." @@ -120,7 +104,7 @@ class HTTPInstance(service.Service): # give kadmin time to actually write the file before we go on retry = 0 - while not file_exists("/etc/httpd/conf/ipa.keytab"): + while not ipautil.file_exists("/etc/httpd/conf/ipa.keytab"): time.sleep(1) retry += 1 if retry > 15: @@ -131,28 +115,51 @@ class HTTPInstance(service.Service): os.chown("/etc/httpd/conf/ipa.keytab", pent.pw_uid, pent.pw_gid) def __configure_http(self): - self.step("configuring httpd") - http_txt = template_file(SHARE_DIR + "ipa.conf", self.sub_dict) + http_txt = ipautil.template_file(ipautil.SHARE_DIR + "ipa.conf", self.sub_dict) http_fd = open("/etc/httpd/conf.d/ipa.conf", "w") http_fd.write(http_txt) http_fd.close() def __disable_mod_ssl(self): - self.step("disabling mod_ssl in httpd") if os.path.exists(SSL_CONF): os.rename(SSL_CONF, "%s.moved_by_ipa" % SSL_CONF) def __set_mod_nss_port(self): - self.step("Setting mod_nss port to 443") - if update_file(NSS_CONF, '8443', '443') != 0: + if installutils.update_file(NSS_CONF, '8443', '443') != 0: print "Updating %s failed." % NSS_CONF def __setup_ssl(self): - self.step("Setting up ssl") ds_ca = certs.CertDB(dsinstance.config_dirname(self.realm)) ca = certs.CertDB(NSS_DIR) ds_ca.cur_serial = 2000 ca.create_from_cacert(ds_ca.cacert_fname) ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca) - + ca.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ds_ca) + + def __setup_autoconfig(self): + prefs_txt = ipautil.template_file(ipautil.SHARE_DIR + "preferences.html.template", self.sub_dict) + prefs_fd = open("/usr/share/ipa/html/preferences.html", "w") + prefs_fd.write(prefs_txt) + prefs_fd.close() + + # The signing cert is generated in __setup_ssl + ds_ca = certs.CertDB(dsinstance.config_dirname(self.realm)) + ca = certs.CertDB(NSS_DIR) + + # Publish the CA certificate + shutil.copy(ds_ca.cacert_fname, "/usr/share/ipa/html/ca.crt") + os.chmod("/usr/share/ipa/html/ca.crt", 0444) + + try: + shutil.rmtree("/tmp/ipa") + except: + pass + os.mkdir("/tmp/ipa") + shutil.copy("/usr/share/ipa/html/preferences.html", "/tmp/ipa") + + ca.run_signtool(["-k", "Signing-Cert", + "-Z", "/usr/share/ipa/html/configure.jar", + "-e", ".html", + "/tmp/ipa"]) + shutil.rmtree("/tmp/ipa") diff --git a/ipa-server/ipaserver/installutils.py b/ipa-server/ipaserver/installutils.py index a403e8154..25cd1555c 100644 --- a/ipa-server/ipaserver/installutils.py +++ b/ipa-server/ipaserver/installutils.py @@ -21,6 +21,10 @@ import logging import socket import errno import getpass +import os +import re +import fileinput +import sys def get_fqdn(): fqdn = "" @@ -105,4 +109,19 @@ def read_password(user): print "" return pwd +def update_file(filename, orig, subst): + if os.path.exists(filename): + pattern = "%s" % re.escape(orig) + p = re.compile(pattern) + for line in fileinput.input(filename, inplace=1): + if not p.search(line): + sys.stdout.write(line) + else: + sys.stdout.write(p.sub(subst, line)) + fileinput.close() + return 0 + else: + print "File %s doesn't exist." % filename + return 1 + diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py index 76818af7d..2f4454bad 100644 --- a/ipa-server/ipaserver/krbinstance.py +++ b/ipa-server/ipaserver/krbinstance.py @@ -33,6 +33,7 @@ import time import shutil import service +from ipa import ipautil from ipa import ipaerror import ipaldap @@ -46,18 +47,21 @@ import pyasn1.codec.ber.encoder import pyasn1.codec.ber.decoder import struct import base64 -from ipa.ipautil import * def host_to_domain(fqdn): s = fqdn.split(".") return ".".join(s[1:]) -def ldap_mod(fd, dn, pwd): - args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name] - run(args) - def update_key_val_in_file(filename, key, val): if os.path.exists(filename): + pattern = "^[\s#]*%s\s*=\s*%s\s*" % (re.escape(key), re.escape(val)) + p = re.compile(pattern) + for line in fileinput.input(filename): + if p.search(line): + fileinput.close() + return + fileinput.close() + pattern = "^[\s#]*%s\s*=" % re.escape(key) p = re.compile(pattern) for line in fileinput.input(filename, inplace=1): @@ -89,8 +93,8 @@ class KrbInstance(service.Service): self.host = host_name.split(".")[0] self.ip = socket.gethostbyname(host_name) self.domain = host_to_domain(host_name) - self.suffix = realm_to_suffix(self.realm) - self.kdc_password = ipa_generate_password() + self.suffix = ipautil.realm_to_suffix(self.realm) + self.kdc_password = ipautil.ipa_generate_password() self.admin_password = admin_password self.__setup_sub_dict() @@ -110,58 +114,44 @@ class KrbInstance(service.Service): pass def __common_post_setup(self): - try: - self.step("starting the KDC") - self.start() - except: - logging.critical("krb5kdc service failed to start") - - self.step("configuring KDC to start on boot") - self.chkconfig_on() - - self.step("configuring ipa-kpasswd to start on boot") - service.chkconfig_on("ipa-kpasswd") - - self.step("starting ipa-kpasswd") - service.start("ipa-kpasswd") - + self.step("starting the KDC", self.__start_instance) + self.step("configuring KDC to start on boot", self.chkconfig_on) + self.step("enabling and starting ipa-kpasswd", self.__enable_kpasswd) def create_instance(self, ds_user, realm_name, host_name, admin_password, master_password): self.master_password = master_password self.__common_setup(ds_user, realm_name, host_name, admin_password) - self.start_creation(11, "Configuring Kerberos KDC") - - self.__configure_kdc_account_password() - self.__configure_sasl_mappings() - self.__add_krb_entries() - self.__create_instance() - self.__create_ds_keytab() - self.__export_kadmin_changepw_keytab() - self.__add_pwd_extop_module() + self.step("setting KDC account password", self.__configure_kdc_account_password) + self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings) + self.step("adding kerberos entries to the DS", self.__add_krb_entries) + self.step("adding defalt ACIs", self.__add_default_acis) + self.step("configuring KDC", self.__create_instance) + self.step("creating a keytab for the directory", self.__create_ds_keytab) + self.step("creating a keytab for the machine", self.__create_host_keytab) + self.step("exporting the kadmin keytab", self.__export_kadmin_changepw_keytab) + self.step("adding the password extenstion to the directory", self.__add_pwd_extop_module) self.__common_post_setup() - self.done_creation() - + self.start_creation("Configuring Kerberos KDC") def create_replica(self, ds_user, realm_name, host_name, admin_password, ldap_passwd_filename): - + self.__copy_ldap_passwd(ldap_passwd_filename) + self.__common_setup(ds_user, realm_name, host_name, admin_password) - self.start_creation(9, "Configuring Kerberos KDC") - self.__copy_ldap_passwd(ldap_passwd_filename) - self.__configure_sasl_mappings() - self.__write_stash_from_ds() - self.__create_instance(replica=True) - self.__create_ds_keytab() - self.__export_kadmin_changepw_keytab() + self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings) + self.step("writing stash file from DS", self.__write_stash_from_ds) + self.step("configuring KDC", self.__create_replica_instance) + self.step("creating a keytab for the directory", self.__create_ds_keytab) + self.step("creating a keytab for the machine", self.__create_host_keytab) + self.step("exporting the kadmin keytab", self.__export_kadmin_changepw_keytab) self.__common_post_setup() - self.done_creation() - + self.start_creation("Configuring Kerberos KDC") def __copy_ldap_passwd(self, filename): shutil.copy(filename, "/var/kerberos/krb5kdc/ldappwd") @@ -169,7 +159,6 @@ class KrbInstance(service.Service): def __configure_kdc_account_password(self): - self.step("setting KDC account password") hexpwd = '' for x in self.kdc_password: hexpwd += (hex(ord(x))[2:]) @@ -178,6 +167,16 @@ class KrbInstance(service.Service): pwd_fd.close() os.chmod("/var/kerberos/krb5kdc/ldappwd", 0600) + def __start_instance(self): + try: + self.start() + except: + logging.critical("krb5kdc service failed to start") + + def __enable_kpasswd(self): + service.chkconfig_on("ipa-kpasswd") + service.start("ipa-kpasswd") + def __setup_sub_dict(self): self.sub_dict = dict(FQDN=self.fqdn, IP=self.ip, @@ -187,8 +186,21 @@ class KrbInstance(service.Service): HOST=self.host, REALM=self.realm) + def __ldap_mod(self, ldif): + txt = ipautil.template_file(ipautil.SHARE_DIR + ldif, self.sub_dict) + fd = ipautil.write_tmp_file(txt) + + args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", + "-D", "cn=Directory Manager", "-w", self.admin_password, "-f", fd.name] + + try: + ipautil.run(args) + except ipautil.CalledProcessError, e: + logging.critical("Failed to load %s: %s" % (ldif, str(e))) + + fd.close() + def __configure_sasl_mappings(self): - self.step("adding sasl mappings to the directory") # we need to remove any existing SASL mappings in the directory as otherwise they # they may conflict. There is no way to define the order they are used in atm. @@ -238,50 +250,38 @@ class KrbInstance(service.Service): raise e def __add_krb_entries(self): - self.step("adding kerberos entries to the DS") - - #TODO: test that the ldif is ok with any random charcter we may use in the password - kerberos_txt = template_file(SHARE_DIR + "kerberos.ldif", self.sub_dict) - kerberos_fd = write_tmp_file(kerberos_txt) - try: - ldap_mod(kerberos_fd, "cn=Directory Manager", self.admin_password) - except ipautil.CalledProcessError, e: - logging.critical("Failed to load kerberos.ldif: %s" % str(e)) - kerberos_fd.close() + self.__ldap_mod("kerberos.ldif") + def __add_default_acis(self): #Change the default ACL to avoid anonimous access to kerberos keys and othe hashes - aci_txt = template_file(SHARE_DIR + "default-aci.ldif", self.sub_dict) - aci_fd = write_tmp_file(aci_txt) - try: - ldap_mod(aci_fd, "cn=Directory Manager", self.admin_password) - except ipautil.CalledProcessError, e: - logging.critical("Failed to load default-aci.ldif: %s" % str(e)) - aci_fd.close() + self.__ldap_mod("default-aci.ldif") + + def __create_replica_instance(self): + self.__create_instance(replace=True) def __create_instance(self, replica=False): - self.step("configuring KDC") - kdc_conf = template_file(SHARE_DIR+"kdc.conf.template", self.sub_dict) + kdc_conf = ipautil.template_file(ipautil.SHARE_DIR+"kdc.conf.template", self.sub_dict) kdc_fd = open("/var/kerberos/krb5kdc/kdc.conf", "w+") kdc_fd.write(kdc_conf) kdc_fd.close() - krb5_conf = template_file(SHARE_DIR+"krb5.conf.template", self.sub_dict) + krb5_conf = ipautil.template_file(ipautil.SHARE_DIR+"krb5.conf.template", self.sub_dict) krb5_fd = open("/etc/krb5.conf", "w+") krb5_fd.write(krb5_conf) krb5_fd.close() # Windows configuration files - krb5_ini = template_file(SHARE_DIR+"krb5.ini.template", self.sub_dict) + krb5_ini = ipautil.template_file(ipautil.SHARE_DIR+"krb5.ini.template", self.sub_dict) krb5_fd = open("/usr/share/ipa/html/krb5.ini", "w+") krb5_fd.write(krb5_ini) krb5_fd.close() - krb_con = template_file(SHARE_DIR+"krb.con.template", self.sub_dict) + krb_con = ipautil.template_file(ipautil.SHARE_DIR+"krb.con.template", self.sub_dict) krb_fd = open("/usr/share/ipa/html/krb.con", "w+") krb_fd.write(krb_con) krb_fd.close() - krb_realm = template_file(SHARE_DIR+"krbrealm.con.template", self.sub_dict) + krb_realm = ipautil.template_file(ipautil.SHARE_DIR+"krbrealm.con.template", self.sub_dict) krb_fd = open("/usr/share/ipa/html/krbrealm.con", "w+") krb_fd.write(krb_realm) krb_fd.close() @@ -290,12 +290,11 @@ class KrbInstance(service.Service): #populate the directory with the realm structure args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=sysaccounts,cn=etc,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-P", self.master_password, "-r", self.realm, "-subtrees", self.suffix, "-sscope", "sub"] try: - run(args) + ipautil.run(args) except ipautil.CalledProcessError, e: print "Failed to populate the realm structure in kerberos", e def __write_stash_from_ds(self): - self.step("writing stash file from DS") try: entry = self.conn.getEntry("cn=%s, cn=kerberos, %s" % (self.realm, self.suffix), ldap.SCOPE_SUBTREE) except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND), e: @@ -317,14 +316,7 @@ class KrbInstance(service.Service): #add the password extop module def __add_pwd_extop_module(self): - self.step("adding the password extenstion to the directory") - extop_txt = template_file(SHARE_DIR + "pwd-extop-conf.ldif", self.sub_dict) - extop_fd = write_tmp_file(extop_txt) - try: - ldap_mod(extop_fd, "cn=Directory Manager", self.admin_password) - except ipautil.CalledProcessError, e: - logging.critical("Failed to load pwd-extop-conf.ldif: %s" % str(e)) - extop_fd.close() + self.__ldap_mod("pwd-extop-conf.ldif") #get the Master Key from the stash file try: @@ -353,9 +345,8 @@ class KrbInstance(service.Service): raise e def __create_ds_keytab(self): - self.step("creating a keytab for the directory") try: - if file_exists("/etc/dirsrv/ds.keytab"): + if ipautil.file_exists("/etc/dirsrv/ds.keytab"): os.remove("/etc/dirsrv/ds.keytab") except os.error: logging.critical("Failed to remove /etc/dirsrv/ds.keytab.") @@ -370,7 +361,7 @@ class KrbInstance(service.Service): # give kadmin time to actually write the file before we go on retry = 0 - while not file_exists("/etc/dirsrv/ds.keytab"): + while not ipautil.file_exists("/etc/dirsrv/ds.keytab"): time.sleep(1) retry += 1 if retry > 15: @@ -381,10 +372,37 @@ class KrbInstance(service.Service): pent = pwd.getpwnam(self.ds_user) os.chown("/etc/dirsrv/ds.keytab", pent.pw_uid, pent.pw_gid) + def __create_host_keytab(self): + try: + if ipautil.file_exists("/etc/krb5.keytab"): + os.remove("/etc/krb5.keytab") + except os.error: + logging.critical("Failed to remove /etc/krb5.keytab.") + (kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local") + kwrite.write("addprinc -randkey host/"+self.fqdn+"@"+self.realm+"\n") + kwrite.flush() + kwrite.write("ktadd -k /etc/krb5.keytab host/"+self.fqdn+"@"+self.realm+"\n") + kwrite.flush() + kwrite.close() + kread.close() + kerr.close() + + # give kadmin time to actually write the file before we go on + retry = 0 + while not ipautil.file_exists("/etc/krb5.keytab"): + time.sleep(1) + retry += 1 + if retry > 15: + logging.critical("Error timed out waiting for kadmin to finish operations") + sys.exit(1) + + # Make sure access is strictly reserved to root only for now + os.chown("/etc/krb5.keytab", 0, 0) + os.chmod("/etc/krb5.keytab", 0600) + def __export_kadmin_changepw_keytab(self): - self.step("exporting the kadmin keytab") try: - if file_exists("/var/kerberos/krb5kdc/kpasswd.keytab"): + if ipautil.file_exists("/var/kerberos/krb5kdc/kpasswd.keytab"): os.remove("/var/kerberos/krb5kdc/kpasswd.keytab") except os.error: logging.critical("Failed to remove /var/kerberos/krb5kdc/kpasswd.keytab.") @@ -404,7 +422,7 @@ class KrbInstance(service.Service): # give kadmin time to actually write the file before we go on retry = 0 - while not file_exists("/var/kerberos/krb5kdc/kpasswd.keytab"): + while not ipautil.file_exists("/var/kerberos/krb5kdc/kpasswd.keytab"): time.sleep(1) retry += 1 if retry > 15: diff --git a/ipa-server/ipaserver/ntpinstance.py b/ipa-server/ipaserver/ntpinstance.py index 46841b0b2..b321ec075 100644 --- a/ipa-server/ipaserver/ntpinstance.py +++ b/ipa-server/ipaserver/ntpinstance.py @@ -17,28 +17,25 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -from ipa.ipautil import * import shutil import service +from ipa import ipautil class NTPInstance(service.Service): def __init__(self): service.Service.__init__(self, "ntpd") - - def create_instance(self): - self.start_creation(3, "Configuring ntpd") - self.step("writing configuration") + def __write_config(self): # The template sets the config to point towards ntp.pool.org, but # they request that software not point towards the default pool. # We use the OS variable to point it towards either the rhel # or fedora pools. Other distros should be added in the future # or we can get our own pool. os = "" - if file_exists("/etc/fedora-release"): + if ipautil.file_exists("/etc/fedora-release"): os = "fedora." - elif file_exists("/etc/redhat-release"): + elif ipautil.file_exists("/etc/redhat-release"): os = "rhel." sub_dict = { } @@ -46,7 +43,7 @@ class NTPInstance(service.Service): sub_dict["SERVERB"] = "1.%spool.ntp.org" % os sub_dict["SERVERC"] = "2.%spool.ntp.org" % os - ntp_conf = template_file(SHARE_DIR + "ntp.conf.server.template", sub_dict) + ntp_conf = ipautil.template_file(ipautil.SHARE_DIR + "ntp.conf.server.template", sub_dict) shutil.copy("/etc/ntp.conf", "/etc/ntp.conf.ipasave") @@ -54,11 +51,13 @@ class NTPInstance(service.Service): fd.write(ntp_conf) fd.close() + def create_instance(self): + self.step("writing configuration", self.__write_config) + # we might consider setting the date manually using ntpd -qg in case # the current time is very far off. - self.step("starting ntpd") - self.start() - - self.step("configuring ntpd to start on boot") - self.chkconfig_on() + self.step("starting ntpd", self.start) + self.step("configuring ntpd to start on boot", self.chkconfig_on) + + self.start_creation("Configuring ntpd") diff --git a/ipa-server/ipaserver/service.py b/ipa-server/ipaserver/service.py index f0109488d..90d0e606a 100644 --- a/ipa-server/ipaserver/service.py +++ b/ipa-server/ipaserver/service.py @@ -17,24 +17,24 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -from ipa.ipautil import * import logging, sys +from ipa import ipautil def stop(service_name): - run(["/sbin/service", service_name, "stop"]) + ipautil.run(["/sbin/service", service_name, "stop"]) def start(service_name): - run(["/sbin/service", service_name, "start"]) + ipautil.run(["/sbin/service", service_name, "start"]) def restart(service_name): - run(["/sbin/service", service_name, "restart"]) + ipautil.run(["/sbin/service", service_name, "restart"]) def chkconfig_on(service_name): - run(["/sbin/chkconfig", service_name, "on"]) + ipautil.run(["/sbin/chkconfig", service_name, "on"]) def chkconfig_off(service_name): - run(["/sbin/chkconfig", service_name, "off"]) + ipautil.run(["/sbin/chkconfig", service_name, "off"]) def print_msg(message, output_fd=sys.stdout): logging.debug(message) @@ -45,8 +45,7 @@ def print_msg(message, output_fd=sys.stdout): class Service: def __init__(self, service_name): self.service_name = service_name - self.num_steps = -1 - self.current_step = -1 + self.steps = [] self.output_fd = sys.stdout def set_output(self, fd): @@ -69,18 +68,19 @@ class Service: def print_msg(self, message): print_msg(message, self.output_fd) - - def start_creation(self, num_steps, message): - self.num_steps = num_steps - self.cur_step = 0 - self.print_msg(message) - def step(self, message): - self.cur_step += 1 - self.print_msg(" [%d/%d]: %s" % (self.cur_step, self.num_steps, message)) + def step(self, message, method): + self.steps.append((message, method)) - def done_creation(self): - self.cur_step = -1 - self.num_steps = -1 + def start_creation(self, message): + self.print_msg(message) + + step = 0 + for (message, method) in self.steps: + self.print_msg(" [%d/%d]: %s" % (step, len(self.steps), message)) + method() + step += 1 + self.print_msg("done configuring %s." % self.service_name) + self.steps = [] diff --git a/ipa-server/ipaserver/webguiinstance.py b/ipa-server/ipaserver/webguiinstance.py index 757b50c5d..285435587 100644 --- a/ipa-server/ipaserver/webguiinstance.py +++ b/ipa-server/ipaserver/webguiinstance.py @@ -17,9 +17,6 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -import logging - -from ipa.ipautil import * import service class WebGuiInstance(service.Service): @@ -27,14 +24,6 @@ class WebGuiInstance(service.Service): service.Service.__init__(self, "ipa-webgui") def create_instance(self): - self.start_creation(2, "Configuring ipa-webgui") - - self.step("starting ipa-webgui") - service.start("ipa-webgui") - - self.step("configuring ipa-webgui to start on boot") - service.chkconfig_on("ipa-webgui") - - self.done_creation() - - + self.step("starting ipa-webgui", self.restart) + self.step("configuring ipa-webgui to start on boot", self.chkconfig_on) + self.start_creation("Configuring ipa-webgui") diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index 4943da24d..2d2bddbb4 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -25,17 +25,15 @@ import ldap import ldap.dn import ipaserver.dsinstance import ipaserver.ipaldap -import ipa.ipautil -import xmlrpclib import copy import attrs from ipa import ipaerror +from ipa import ipautil from urllib import quote,unquote from ipa import radius_util import string from types import * -import os import re import logging import subprocess @@ -84,7 +82,7 @@ class IPAConnPool: # This will bind the connection try: conn.set_krbccache(krbccache, cprinc.name) - except ldap.UNWILLING_TO_PERFORM, e: + except ldap.UNWILLING_TO_PERFORM: raise ipaerror.gen_exception(ipaerror.CONNECTION_UNWILLING) return conn @@ -111,7 +109,7 @@ class IPAServer: if _LDAPPool is None: _LDAPPool = IPAConnPool(128) - self.basedn = ipa.ipautil.realm_to_suffix(self.realm) + self.basedn = ipautil.realm_to_suffix(self.realm) self.scope = ldap.SCOPE_SUBTREE self.princ = None self.krbccache = None @@ -127,11 +125,11 @@ class IPAServer: global _LDAPPool princ = self.__safe_filter(princ) - filter = "(krbPrincipalName=" + princ + ")" + searchfilter = "(krbPrincipalName=" + princ + ")" # The only anonymous search we should have conn = _LDAPPool.getConn(self.host,self.sslport,self.bindca,self.bindcert,self.bindkey,None,None,debug) try: - ent = conn.getEntry(self.basedn, self.scope, filter, ['dn']) + ent = conn.getEntry(self.basedn, self.scope, searchfilter, ['dn']) finally: _LDAPPool.releaseConn(conn) @@ -220,7 +218,7 @@ class IPAServer: # they currently restrict the data coming back without # restricting scope. For now adding a __get_base/sub_entry() # calls, but the API isn't great. - def __get_entry (self, base, scope, filter, sattrs=None, opts=None): + def __get_entry (self, base, scope, searchfilter, sattrs=None, opts=None): """Get a specific entry (with a parametized scope). Return as a dict of values. Multi-valued fields are represented as lists. @@ -229,28 +227,28 @@ class IPAServer: conn = self.getConnection(opts) try: - ent = conn.getEntry(base, scope, filter, sattrs) + ent = conn.getEntry(base, scope, searchfilter, sattrs) finally: self.releaseConnection(conn) return self.convert_entry(ent) - def __get_base_entry (self, base, filter, sattrs=None, opts=None): + def __get_base_entry (self, base, searchfilter, sattrs=None, opts=None): """Get a specific entry (with a scope of BASE). Return as a dict of values. Multi-valued fields are represented as lists. """ - return self.__get_entry(base, ldap.SCOPE_BASE, filter, sattrs, opts) + return self.__get_entry(base, ldap.SCOPE_BASE, searchfilter, sattrs, opts) - def __get_sub_entry (self, base, filter, sattrs=None, opts=None): + def __get_sub_entry (self, base, searchfilter, sattrs=None, opts=None): """Get a specific entry (with a scope of SUB). Return as a dict of values. Multi-valued fields are represented as lists. """ - return self.__get_entry(base, ldap.SCOPE_SUBTREE, filter, sattrs, opts) + return self.__get_entry(base, ldap.SCOPE_SUBTREE, searchfilter, sattrs, opts) - def __get_list (self, base, filter, sattrs=None, opts=None): + def __get_list (self, base, searchfilter, sattrs=None, opts=None): """Gets a list of entries. Each is converted to a dict of values. Multi-valued fields are represented as lists. """ @@ -258,7 +256,7 @@ class IPAServer: conn = self.getConnection(opts) try: - entries = conn.getList(base, self.scope, filter, sattrs) + entries = conn.getList(base, self.scope, searchfilter, sattrs) finally: self.releaseConnection(conn) @@ -278,7 +276,7 @@ class IPAServer: # original try: moddn = oldentry['dn'] - except KeyError, e: + except KeyError: raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN) conn = self.getConnection(opts) @@ -316,7 +314,7 @@ class IPAServer: # construct the giant match for all words exact_match_filter = "(&" - partial_match_filter = "(&" + partial_match_filter = "(|" for word in criteria_words: exact_match_filter += gen_search_pattern(word) partial_match_filter += gen_search_pattern("*%s*" % word) @@ -366,43 +364,66 @@ class IPAServer: Multi-valued fields are represented as lists. """ - filter = "(objectClass=*)" - return self.__get_base_entry(dn, filter, sattrs, opts) + if not dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + searchfilter = "(objectClass=*)" + return self.__get_base_entry(dn, searchfilter, sattrs, opts) def get_entry_by_cn (self, cn, sattrs, opts=None): """Get a specific entry by cn. Return as a dict of values. Multi-valued fields are represented as lists. """ + if not cn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) cn = self.__safe_filter(cn) - filter = "(cn=" + cn + ")" - return self.__get_sub_entry(self.basedn, filter, sattrs, opts) + searchfilter = "(cn=" + cn + ")" + return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts) def update_entry (self, oldentry, newentry, opts=None): - """Update an entry in LDAP""" + """Update an entry in LDAP + + oldentry and newentry are XML-RPC structs. + + If oldentry is not empty then it is used when determine what + has changed. + + If oldentry is empty then the value of newentry is compared + to the current value of oldentry. + """ + if not newentry: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + + if not oldentry: + oldentry = self.get_entry_by_dn(newentry.get('dn'), None, opts) + if oldentry is None: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) + return self.__update_entry(oldentry, newentry, opts) # User support def __is_user_unique(self, uid, opts): - """Return 1 if the uid is unique in the tree, 0 otherwise.""" + """Return True if the uid is unique in the tree, False otherwise.""" uid = self.__safe_filter(uid) - filter = "(&(uid=%s)(objectclass=posixAccount))" % uid + searchfilter = "(&(uid=%s)(objectclass=posixAccount))" % uid try: - entry = self.__get_sub_entry(self.basedn, filter, ['dn','uid'], opts) - return 0 + entry = self.__get_sub_entry(self.basedn, searchfilter, ['dn','uid'], opts) + return False except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): - return 1 + return True def get_user_by_uid (self, uid, sattrs, opts=None): """Get a specific user's entry. Return as a dict of values. Multi-valued fields are represented as lists. """ + if not uid: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) uid = self.__safe_filter(uid) - filter = "(uid=" + uid + ")" - return self.__get_sub_entry(self.basedn, filter, sattrs, opts) + searchfilter = "(uid=" + uid + ")" + return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts) def get_user_by_principal(self, principal, sattrs, opts=None): """Get a user entry searching by Kerberos Principal Name. @@ -410,27 +431,33 @@ class IPAServer: represented as lists. """ - filter = "(krbPrincipalName="+self.__safe_filter(principal)+")" - return self.__get_sub_entry(self.basedn, filter, sattrs, opts) + if not principal: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + searchfilter = "(krbPrincipalName="+self.__safe_filter(principal)+")" + return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts) def get_user_by_email (self, email, sattrs, opts=None): """Get a specific user's entry. Return as a dict of values. Multi-valued fields are represented as lists. """ + if not email: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) email = self.__safe_filter(email) - filter = "(mail=" + email + ")" - return self.__get_sub_entry(self.basedn, filter, sattrs, opts) + searchfilter = "(mail=" + email + ")" + return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts) def get_users_by_manager (self, manager_dn, sattrs, opts=None): """Gets the users that report to a particular manager. """ + if not manager_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) manager_dn = self.__safe_filter(manager_dn) - filter = "(&(objectClass=person)(manager=%s))" % manager_dn + searchfilter = "(&(objectClass=person)(manager=%s))" % manager_dn try: - return self.__get_list(self.basedn, filter, sattrs, opts) + return self.__get_list(self.basedn, searchfilter, sattrs, opts) except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): return [] @@ -438,11 +465,16 @@ class IPAServer: """Add a user in LDAP. Takes as input a dict where the key is the attribute name and the value is either a string or in the case of a multi-valued field a list of values. user_container sets - where in the tree the user is placed.""" + where in the tree the user is placed. + """ + + if not user: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + if not user_container: user_container = DefaultUserContainer - if self.__is_user_unique(user['uid'], opts) == 0: + if not self.__is_user_unique(user['uid'], opts): raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE) # dn is set here, not by the user @@ -758,6 +790,8 @@ class IPAServer: It is displayed to the user in the order of the list. """ + if not schema: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) config = self.get_ipa_config(opts) # The schema is stored as: @@ -783,11 +817,11 @@ class IPAServer: """Return a list containing a User object for each existing user. """ - filter = "(objectclass=posixAccount)" + searchfilter = "(objectclass=posixAccount)" conn = self.getConnection(opts) try: - all_users = conn.getList(self.basedn, self.scope, filter, None) + all_users = conn.getList(self.basedn, self.scope, searchfilter, None) finally: self.releaseConnection(conn) @@ -803,6 +837,8 @@ class IPAServer: If the results are truncated, counter will be set to -1.""" logging.debug("IPA: find users %s" % criteria) + if not criteria: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) config = self.get_ipa_config(opts) if timelimit < 0: timelimit = float(config.get('ipasearchtimelimit')) @@ -875,6 +911,8 @@ class IPAServer: def convert_scalar_values(self, orig_dict): """LDAP update dicts expect all values to be a list (except for dn). This method converts single entries to a list.""" + if not orig_dict or not isinstance(orig_dict, dict): + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) new_dict={} for (k,v) in orig_dict.iteritems(): if not isinstance(v, list) and k != 'dn': @@ -886,9 +924,23 @@ class IPAServer: def update_user (self, oldentry, newentry, opts=None): """Wrapper around update_entry with user-specific handling. + oldentry and newentry are XML-RPC structs. + + If oldentry is not empty then it is used when determine what + has changed. + + If oldentry is empty then the value of newentry is compared + to the current value of oldentry. + If you want to change the RDN of a user you must use this function. update_entry will fail. """ + if not newentry: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + if not oldentry: + oldentry = self.get_entry_by_dn(newentry.get('dn'), None, opts) + if oldentry is None: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) newrdn = 0 @@ -938,6 +990,9 @@ class IPAServer: logging.debug("IPA: activating entry %s" % dn) + if not dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + res = "" # First, check the entry status entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock'], opts) @@ -970,6 +1025,9 @@ class IPAServer: logging.debug("IPA: inactivating entry %s" % dn) + if not dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock', 'memberOf'], opts) if entry.get('nsaccountlock', 'false') == "true": @@ -990,12 +1048,16 @@ class IPAServer: def mark_user_active(self, uid, opts=None): """Mark a user as active""" + if not uid: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) user = self.get_user_by_uid(uid, ['dn', 'uid'], opts) return self.mark_entry_active(user.get('dn')) def mark_user_inactive(self, uid, opts=None): """Mark a user as inactive""" + if not uid: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) user = self.get_user_by_uid(uid, ['dn', 'uid'], opts) return self.mark_entry_inactive(user.get('dn')) @@ -1008,6 +1070,8 @@ class IPAServer: The memberOf plugin handles removing the user from any other groups. """ + if not uid: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) user = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts) if user is None: raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) @@ -1026,6 +1090,8 @@ class IPAServer: oldpass is the old password (if available) newpass is the new password """ + if not principal or not newpass: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) user = self.get_user_by_principal(principal, ['krbprincipalname'], opts) if user is None or user['krbprincipalname'] != principal: raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) @@ -1040,26 +1106,28 @@ class IPAServer: # Group support def __is_group_unique(self, cn, opts): - """Return 1 if the cn is unique in the tree, 0 otherwise.""" + """Return True if the cn is unique in the tree, False otherwise.""" cn = self.__safe_filter(cn) - filter = "(&(cn=%s)(objectclass=posixGroup))" % cn + searchfilter = "(&(cn=%s)(objectclass=posixGroup))" % cn try: - entry = self.__get_sub_entry(self.basedn, filter, ['dn','cn'], opts) - return 0 + entry = self.__get_sub_entry(self.basedn, searchfilter, ['dn','cn'], opts) + return False except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): - return 1 + return True def get_groups_by_member (self, member_dn, sattrs, opts=None): """Get a specific group's entry. Return as a dict of values. Multi-valued fields are represented as lists. """ + if not member_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) member_dn = self.__safe_filter(member_dn) - filter = "(&(objectClass=posixGroup)(member=%s))" % member_dn + searchfilter = "(&(objectClass=posixGroup)(member=%s))" % member_dn try: - return self.__get_list(self.basedn, filter, sattrs, opts) + return self.__get_list(self.basedn, searchfilter, sattrs, opts) except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): return [] @@ -1068,10 +1136,13 @@ class IPAServer: attribute name and the value is either a string or in the case of a multi-valued field a list of values. group_container sets where in the tree the group is placed.""" + if not group: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + if not group_container: group_container = DefaultGroupContainer - if self.__is_group_unique(group['cn'], opts) == 0: + if not self.__is_group_unique(group['cn'], opts): raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE) # Get our configuration @@ -1102,6 +1173,8 @@ class IPAServer: """Return a list containing a User object for each existing group that matches the criteria. """ + if not criteria: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) config = self.get_ipa_config(opts) if timelimit < 0: @@ -1178,6 +1251,8 @@ class IPAServer: def add_member_to_group(self, member_dn, group_dn, opts=None): """Add a member to an existing group. """ + if not member_dn or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) old_group = self.get_entry_by_dn(group_dn, None, opts) if old_group is None: @@ -1186,6 +1261,8 @@ class IPAServer: # check to make sure member_dn exists member_entry = self.__get_base_entry(member_dn, "(objectClass=*)", ['dn','uid'], opts) + if not member_entry: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) if new_group.get('member') is not None: if ((isinstance(new_group.get('member'), str)) or (isinstance(new_group.get('member'), unicode))): @@ -1205,6 +1282,9 @@ class IPAServer: Returns a list of the member_dns that were not added to the group. """ + if not member_dns or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + failed = [] if (isinstance(member_dns, str)): @@ -1225,6 +1305,8 @@ class IPAServer: def remove_member_from_group(self, member_dn, group_dn, opts=None): """Remove a member_dn from an existing group. """ + if not member_dn or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) old_group = self.get_entry_by_dn(group_dn, None, opts) if old_group is None: @@ -1255,6 +1337,8 @@ class IPAServer: """Given a list of member dn's remove them from the group. Returns a list of the members not removed from the group. """ + if not member_dns or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) failed = [] @@ -1277,6 +1361,8 @@ class IPAServer: """Add a user to an existing group. """ + if not user_uid or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) user = self.get_user_by_uid(user_uid, ['dn', 'uid', 'objectclass'], opts) if user is None: raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) @@ -1287,6 +1373,8 @@ class IPAServer: """Given a list of user uid's add them to the group cn denoted by group Returns a list of the users were not added to the group. """ + if not user_uids or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) failed = [] @@ -1309,6 +1397,9 @@ class IPAServer: """Remove a user from an existing group. """ + if not user_uid or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + user = self.get_user_by_uid(user_uid, ['dn', 'uid', 'objectclass'], opts) if user is None: raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) @@ -1319,6 +1410,8 @@ class IPAServer: """Given a list of user uid's remove them from the group Returns a list of the user uids not removed from the group. """ + if not user_uids or not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) failed = [] @@ -1342,6 +1435,8 @@ class IPAServer: Returns a list of the group dns that were not added. """ + if not group_dns or not user_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) failed = [] @@ -1365,6 +1460,8 @@ class IPAServer: Returns a list of the group dns that were not removed. """ + if not group_dns or not user_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) failed = [] @@ -1386,9 +1483,23 @@ class IPAServer: def update_group (self, oldentry, newentry, opts=None): """Wrapper around update_entry with group-specific handling. + oldentry and newentry are XML-RPC structs. + + If oldentry is not empty then it is used when determine what + has changed. + + If oldentry is empty then the value of newentry is compared + to the current value of oldentry. + If you want to change the RDN of a group you must use this function. update_entry will fail. """ + if not newentry: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + if not oldentry: + oldentry = self.get_entry_by_dn(newentry.get('dn'), None, opts) + if oldentry is None: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) newrdn = 0 @@ -1451,6 +1562,8 @@ class IPAServer: The memberOf plugin handles removing the group from any other groups. """ + if not group_dn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) group = self.get_entry_by_dn(group_dn, ['dn', 'cn'], opts) if group is None: raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) @@ -1478,6 +1591,8 @@ class IPAServer: tgroup is the DN of the target group to be added to """ + if not group or not tgroup: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) old_group = self.get_entry_by_dn(tgroup, None, opts) if old_group is None: raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) @@ -1514,19 +1629,21 @@ class IPAServer: """Do a memberOf search of groupdn and return the attributes in attr_list (an empty list returns everything).""" + if not groupdn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) config = self.get_ipa_config(opts) timelimit = float(config.get('ipasearchtimelimit')) searchlimit = float(config.get('ipasearchrecordslimit')) groupdn = self.__safe_filter(groupdn) - filter = "(memberOf=%s)" % groupdn + searchfilter = "(memberOf=%s)" % groupdn conn = self.getConnection(opts) try: try: results = conn.getListAsync(self.basedn, self.scope, - filter, attr_list, 0, None, None, timelimit, + searchfilter, attr_list, 0, None, None, timelimit, searchlimit) except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): results = [0] @@ -1545,33 +1662,42 @@ class IPAServer: def mark_group_active(self, cn, opts=None): """Mark a group as active""" + if not cn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) group = self.get_entry_by_cn(cn, ['dn', 'cn'], opts) return self.mark_entry_active(group.get('dn')) def mark_group_inactive(self, cn, opts=None): """Mark a group as inactive""" + if not cn: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) group = self.get_entry_by_cn(cn, ['dn', 'uid'], opts) return self.mark_entry_inactive(group.get('dn')) def __is_service_unique(self, name, opts): - """Return 1 if the uid is unique in the tree, 0 otherwise.""" + """Return True if the uid is unique in the tree, False otherwise.""" name = self.__safe_filter(name) - filter = "(&(krbprincipalname=%s)(objectclass=krbPrincipal))" % name + searchfilter = "(&(krbprincipalname=%s)(objectclass=krbPrincipal))" % name try: - entry = self.__get_sub_entry(self.basedn, filter, ['dn','krbprincipalname'], opts) - return 0 + entry = self.__get_sub_entry(self.basedn, searchfilter, ['dn','krbprincipalname'], opts) + return False except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): - return 1 + return True def add_service_principal(self, name, opts=None): + """Given a name of the form: service/FQDN create a service + principal for it in the default realm.""" + if not name: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + service_container = DefaultServiceContainer princ_name = name + "@" + self.realm conn = self.getConnection(opts) - if self.__is_service_unique(name, opts) == 0: + if not self.__is_service_unique(name, opts): raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE) dn = "krbprincipalname=%s,%s,%s" % (ldap.dn.escape_dn_chars(princ_name), @@ -1591,6 +1717,8 @@ class IPAServer: timelimit=-1, opts=None): """Returns a list: counter followed by the results. If the results are truncated, counter will be set to -1.""" + if not criteria: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) config = self.get_ipa_config(opts) if timelimit < 0: @@ -1658,7 +1786,10 @@ class IPAServer: return entries def get_keytab(self, name, opts=None): - """get a keytab""" + """Return a keytab for an existing service principal. Note that + this increments the secret thus invalidating any older keys.""" + if not name: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) princ_name = name + "@" + self.realm @@ -1699,8 +1830,24 @@ class IPAServer: return config def update_ipa_config(self, oldconfig, newconfig, opts=None): - """Update the IPA configuration""" - + """Update the IPA configuration. + + oldconfig and newconfig are XML-RPC structs. + + If oldconfig is not empty then it is used when determine what + has changed. + + If oldconfig is empty then the value of newconfig is compared + to the current value of oldconfig. + + """ + if not newconfig: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + if not oldconfig: + oldconfig = self.get_entry_by_dn(newconfig.get('dn'), None, opts) + if oldconfig is None: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) + # The LDAP routines want strings, not ints, so convert a few # things. Otherwise it sees a string -> int conversion as a change. try: @@ -1749,7 +1896,24 @@ class IPAServer: return policy def update_password_policy(self, oldpolicy, newpolicy, opts=None): - """Update the IPA configuration""" + """Update the IPA configuration + + oldpolicy and newpolicy are XML-RPC structs. + + If oldpolicy is not empty then it is used when determine what + has changed. + + If oldpolicy is empty then the value of newpolicy is compared + to the current value of oldpolicy. + + """ + if not newpolicy: + raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) + if not oldpolicy: + oldpolicy = self.get_entry_by_dn(newpolicy.get('dn'), None, opts) + if oldpolicy is None: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) + # The LDAP routines want strings, not ints, so convert a few # things. Otherwise it sees a string -> int conversion as a change. diff --git a/ipa-server/xmlrpc-server/ipa.conf b/ipa-server/xmlrpc-server/ipa.conf index fbf26b67c..4e8bf528f 100644 --- a/ipa-server/xmlrpc-server/ipa.conf +++ b/ipa-server/xmlrpc-server/ipa.conf @@ -12,9 +12,12 @@ RewriteRule ^/(.*) http://$FQDN/$$1 [L,R=301] # Redirect to the secure port if not displaying an error or retrieving # configuration. RewriteCond %{SERVER_PORT} !^443$$ -RewriteCond %{REQUEST_URI} !^/(errors|config)/ +RewriteCond %{REQUEST_URI} !^/(errors|config|favicon.ico) RewriteRule ^/(.*) https://$FQDN/$$1 [L,R=301,NC] +# This is required so the auto-configuration works with Firefox 2+ +AddType application/java-archive jar + <Proxy *> AuthType Kerberos AuthName "Kerberos Login" diff --git a/ipa-server/xmlrpc-server/unauthorized.html b/ipa-server/xmlrpc-server/unauthorized.html index 23a8d5c7d..eba1266ad 100644 --- a/ipa-server/xmlrpc-server/unauthorized.html +++ b/ipa-server/xmlrpc-server/unauthorized.html @@ -9,6 +9,20 @@ have <a href="/errors/ssbrowser.html">configured your browser correctly</a>. If you are still unable to access the IPA Web interface, please contact the helpdesk on for additional assistance. </p> +<p> +Import the <a href="/errors/ca.crt">IPA Certificate Authority</a>. +</p> +<p> +<script type="text/javascript"> + if (navigator.userAgent.indexOf("Firefox") != -1 || + navigator.userAgent.indexOf("SeaMonkey") != -1) + { + document.write("<p>You can automatically configure your browser to work with Kerberos by importing the Certificate Authority below and clicking on the Configure Browser button.</p>"); + document.write("<p>You <strong>must</strong> reload this page after importing the Certificate Authority for the automatic settings to work</p>"); + document.write("<object data=\"jar:/errors/configure.jar!/preferences.html\" type=\"text/html\"><\/object"); + } +</script> +</p> </ul> </body> </html> |