diff options
author | Karl MacMillan <kmacmill@redhat.com> | 2007-12-18 16:25:46 -0500 |
---|---|---|
committer | Karl MacMillan <kmacmill@redhat.com> | 2007-12-18 16:25:46 -0500 |
commit | 6575aa606f18f8d998dcad5552e4f770e9addcdf (patch) | |
tree | d75cb650b3b9f27cad0786d7aa40a5cd3f188061 /ipa-server | |
parent | a0eacec8e587c0fbc7e24378cef58fa7835ae64a (diff) | |
parent | 4814c0d3f6c11971b841eb6eedcd7925d8840f26 (diff) | |
download | freeipa.git-6575aa606f18f8d998dcad5552e4f770e9addcdf.tar.gz freeipa.git-6575aa606f18f8d998dcad5552e4f770e9addcdf.tar.xz freeipa.git-6575aa606f18f8d998dcad5552e4f770e9addcdf.zip |
Merge.
Diffstat (limited to 'ipa-server')
31 files changed, 943 insertions, 888 deletions
diff --git a/ipa-server/ipa-gui/ipa-webgui b/ipa-server/ipa-gui/ipa-webgui index c8e3d058..a18c2db8 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 d9d5d727..7eadfe23 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 74369a6a..43a7a547 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 cee239e7..2319b944 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 4eb846d5..3f885fa0 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 1b46d354..8a3e4a97 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 00000000..932a6be1 --- /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 646512d5..ee5e929d 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 36bb54e8..5d117dec 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 9642070c..0a969de3 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 95743eeb..5715259a 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 00000000..2d3684dc --- /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/radius.radiusd.conf.template b/ipa-server/ipa-install/share/radius.radiusd.conf.template deleted file mode 100644 index 3bc4927d..00000000 --- a/ipa-server/ipa-install/share/radius.radiusd.conf.template +++ /dev/null @@ -1,285 +0,0 @@ -# -# WARNING: This file is automatically generated, do not edit -# -# $CONFIG_FILE_VERSION_INFO -# -prefix = /usr -exec_prefix = /usr -sysconfdir = /etc -localstatedir = /var -sbindir = /usr/sbin -logdir = $${localstatedir}/log/radius -raddbdir = $${sysconfdir}/raddb -radacctdir = $${logdir}/radacct -confdir = $${raddbdir} -run_dir = $${localstatedir}/run/radiusd -db_dir = $${localstatedir}/lib/radiusd -log_file = $${logdir}/radius.log -libdir = /usr/lib -pidfile = $${run_dir}/radiusd.pid -user = radiusd -group = radiusd -max_request_time = 30 -delete_blocked_requests = no -cleanup_delay = 5 -max_requests = 1024 -bind_address = * -port = 0 -hostname_lookups = no -allow_core_dumps = no -regular_expressions = yes -extended_expressions = yes -log_stripped_names = no -log_auth = no -log_auth_badpass = no -log_auth_goodpass = no -usercollide = no -lower_user = no -lower_pass = no -nospace_user = no -nospace_pass = no -checkrad = $${sbindir}/checkrad -security { - max_attributes = 200 - reject_delay = 1 - status_server = no -} -proxy_requests = yes -$$INCLUDE $${confdir}/proxy.conf -$$INCLUDE $${confdir}/clients.conf -snmp = no -$$INCLUDE $${confdir}/snmp.conf -thread pool { - start_servers = 5 - max_servers = 32 - min_spare_servers = 3 - max_spare_servers = 10 - max_requests_per_server = 0 -} -modules { - chap { - authtype = CHAP - } - pam { - pam_auth = radiusd - } - unix { - cache = no - cache_reload = 600 - shadow = /etc/shadow - radwtmp = $${logdir}/radwtmp - } -$$INCLUDE $${confdir}/eap.conf - mschap { - } - ldap { - server = "$LDAP_SERVER" - use_sasl = yes - sasl_mech = "GSSAPI" - krb_keytab = "$RADIUS_KEYTAB" - krb_principal = "$RADIUS_PRINCIPAL" - basedn = "$RADIUS_USER_BASE_DN" - filter = "(uid=%{Stripped-User-Name:-%{User-Name}})" - base_filter = "(objectclass=radiusprofile)" - start_tls = no - profile_attribute = "radiusProfileDn" - default_profile = "uid=ipa_default,cn=profiles,cn=radius,cn=services,cn=etc,$SUFFIX - # FIXME: we'll want to toggle the access_attr feature on/off, - # but it needs a control, so disable it for now. - #access_attr = "$ACCESS_ATTRIBUTE" - #access_attr_used_for_allow = "$ACCESS_ATTRIBUTE_DEFAULT" - dictionary_mapping = $${raddbdir}/ldap.attrmap - ldap_connections_number = 5 - edir_account_policy_check=no - timeout = 4 - timelimit = 3 - net_timeout = 1 - clients_basedn = "$CLIENTS_BASEDN" - } - realm IPASS { - format = prefix - delimiter = "/" - ignore_default = no - ignore_null = no - } - realm suffix { - format = suffix - delimiter = "@" - ignore_default = no - ignore_null = no - } - realm realmpercent { - format = suffix - delimiter = "%" - ignore_default = no - ignore_null = no - } - realm ntdomain { - format = prefix - delimiter = "\\" - ignore_default = no - ignore_null = no - } - checkval { - item-name = Calling-Station-Id - check-name = Calling-Station-Id - data-type = string - } - preprocess { - huntgroups = $${confdir}/huntgroups - hints = $${confdir}/hints - with_ascend_hack = no - ascend_channels_per_line = 23 - with_ntdomain_hack = no - with_specialix_jetstream_hack = no - with_cisco_vsa_hack = no - } - files { - usersfile = $${confdir}/users - acctusersfile = $${confdir}/acct_users - preproxy_usersfile = $${confdir}/preproxy_users - compat = no - } - detail { - detailfile = $${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d - detailperm = 0600 - } - acct_unique { - key = "User-Name, Acct-Session-Id, NAS-IP-Address, Client-IP-Address, NAS-Port" - } - radutmp { - filename = $${logdir}/radutmp - username = %{User-Name} - case_sensitive = yes - check_with_nas = yes - perm = 0600 - callerid = "yes" - } - radutmp sradutmp { - filename = $${logdir}/sradutmp - perm = 0644 - callerid = "no" - } - attr_filter { - attrsfile = $${confdir}/attrs - } - counter daily { - filename = $${db_dir}/db.daily - key = User-Name - count-attribute = Acct-Session-Time - reset = daily - counter-name = Daily-Session-Time - check-name = Max-Daily-Session - allowed-servicetype = Framed-User - cache-size = 5000 - } - sqlcounter dailycounter { - counter-name = Daily-Session-Time - check-name = Max-Daily-Session - reply-name = Session-Timeout - sqlmod-inst = sql - key = User-Name - reset = daily - query = "SELECT SUM(AcctSessionTime - \ - GREATEST((%b - UNIX_TIMESTAMP(AcctStartTime)), 0)) \ - FROM radacct WHERE UserName='%{%k}' AND \ - UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'" - } - sqlcounter monthlycounter { - counter-name = Monthly-Session-Time - check-name = Max-Monthly-Session - reply-name = Session-Timeout - sqlmod-inst = sql - key = User-Name - reset = monthly - query = "SELECT SUM(AcctSessionTime - \ - GREATEST((%b - UNIX_TIMESTAMP(AcctStartTime)), 0)) \ - FROM radacct WHERE UserName='%{%k}' AND \ - UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'" - } - always fail { - rcode = fail - } - always reject { - rcode = reject - } - always ok { - rcode = ok - simulcount = 0 - mpp = no - } - expr { - } - digest { - } - exec { - wait = yes - input_pairs = request - } - exec echo { - wait = yes - program = "/bin/echo %{User-Name}" - input_pairs = request - output_pairs = reply - } - ippool main_pool { - range-start = 192.168.1.1 - range-stop = 192.168.3.254 - netmask = 255.255.255.0 - cache-size = 800 - session-db = $${db_dir}/db.ippool - ip-index = $${db_dir}/db.ipindex - override = no - maximum-timeout = 0 - } - krb5 { - keytab = "$RADIUS_KEYTAB" - service_principal = "$RADIUS_PRINCIPAL" - } -} -instantiate { - exec - expr -} -authorize { - preprocess - chap - mschap - suffix - eap - #files - ldap -} -authenticate { - Auth-Type CHAP { - chap - } - Auth-Type MS-CHAP { - mschap - } - eap - Auth-Type Kerberos { - krb5 - } -} -preacct { - preprocess - acct_unique - suffix - files -} -accounting { - detail - unix - radutmp -} -session { - radutmp -} -post-auth { -} -pre-proxy { -} -post-proxy { - eap -} diff --git a/ipa-server/ipa-install/share/referint-conf.ldif b/ipa-server/ipa-install/share/referint-conf.ldif index 7a547ba5..533b97de 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 99dfe678..ccd17c1f 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 f84affe8..38725cf3 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 874d82cf..e43f07aa 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 f1c094b3..b1d00a80 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 8a131fe7..cc99eacf 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 fb6b01d0..eecfdf21 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 5edc3879..6cbffcb8 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 1799cca0..d0329cca 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 a403e815..25cd1555 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 76818af7..2f4454ba 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 46841b0b..b321ec07 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/radiusinstance.py b/ipa-server/ipaserver/radiusinstance.py deleted file mode 100644 index 3b89018f..00000000 --- a/ipa-server/ipaserver/radiusinstance.py +++ /dev/null @@ -1,171 +0,0 @@ -#! /usr/bin/python -E -# Authors: John Dennis <jdennis@redhat.com> -# -# Copyright (C) 2007 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 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 -import subprocess -import string -import tempfile -import shutil -import logging -import pwd -import time -import sys -from ipa.ipautil import * -from ipa import radius_util - -import service - -import os -import re - -IPA_RADIUS_VERSION = '0.0.0' - -# FIXME there should a utility to get the user base dn -from ipaserver.funcs import DefaultUserContainer, DefaultGroupContainer - -#------------------------------------------------------------------------------- - -def ldap_mod(fd, dn, pwd): - args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name] - run(args) - -def get_radius_version(): - version = None - try: - p = subprocess.Popen([radius_util.RADIUSD, '-v'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - status = p.returncode - - if status == 0: - match = re.search("radiusd: FreeRADIUS Version (.+), for host", stdout) - if match: - version = match.group(1) - except Exception, e: - pass - return version - - -#------------------------------------------------------------------------------- - -class RadiusInstance(service.Service): - def __init__(self): - service.Service.__init__(self, "radiusd") - self.fqdn = None - self.realm = None - self.principal = None - - def create_instance(self, realm_name, host_name, ldap_server): - self.realm = realm_name.upper() - self.suffix = realm_to_suffix(self.realm) - self.fqdn = host_name - self.ldap_server = ldap_server - self.principal = "%s/%s@%s" % (radius_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() - except: - # It could have been not running - pass - - self.__create_radius_keytab() - self.__radiusd_conf() - - 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, - 'RADIUS_KEYTAB' : radius_util.RADIUS_IPA_KEYTAB_FILEPATH, - 'RADIUS_PRINCIPAL' : self.principal, - 'RADIUS_USER_BASE_DN' : self.user_basedn, - 'ACCESS_ATTRIBUTE' : '', - 'ACCESS_ATTRIBUTE_DEFAULT' : 'TRUE', - 'CLIENTS_BASEDN' : radius_util.radius_clients_basedn(None, self.suffix), - 'SUFFIX' : self.suffix, - } - try: - radiusd_conf = template_file(radius_util.RADIUSD_CONF_TEMPLATE_FILEPATH, sub_dict) - radiusd_fd = open(radius_util.RADIUSD_CONF_FILEPATH, 'w+') - radiusd_fd.write(radiusd_conf) - radiusd_fd.close() - except Exception, e: - logging.error("could not create %s: %s", 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): - os.remove(radius_util.RADIUS_IPA_KEYTAB_FILEPATH) - except os.error: - logging.error("Failed to remove %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH) - - (kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local") - kwrite.write("addprinc -randkey %s\n" % (self.principal)) - kwrite.flush() - kwrite.write("ktadd -k %s %s\n" % (radius_util.RADIUS_IPA_KEYTAB_FILEPATH, self.principal)) - kwrite.flush() - kwrite.close() - kread.close() - kerr.close() - - # give kadmin time to actually write the file before we go on - retry = 0 - while not file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH): - time.sleep(1) - retry += 1 - if retry > 15: - print "Error timed out waiting for kadmin to finish operations\n" - sys.exit(1) - try: - pent = pwd.getpwnam(radius_util.RADIUS_USER) - os.chown(radius_util.RADIUS_IPA_KEYTAB_FILEPATH, pent.pw_uid, pent.pw_gid) - except Exception, e: - logging.error("could not chown on %s to %s: %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH, radius_util.RADIUS_USER, e) - - #FIXME, should use IPAdmin method - def __set_ldap_encrypted_attributes(self): - ldif_file = 'encrypted_attribute.ldif' - self.step("setting ldap encrypted attributes") - ldif_txt = template_file(SHARE_DIR + ldif_file, {'ENCRYPTED_ATTRIBUTE':'radiusClientSecret'}) - ldif_fd = write_tmp_file(ldif_txt) - try: - ldap_mod(ldif_fd, "cn=Directory Manager", self.dm_password) - except subprocess.CalledProcessError, e: - logging.critical("Failed to load %s: %s" % (ldif_file, str(e))) - ldif_fd.close() - -#------------------------------------------------------------------------------- - diff --git a/ipa-server/ipaserver/service.py b/ipa-server/ipaserver/service.py index f0109488..90d0e606 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 757b50c5..28543558 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 4943da24..2d2bddbb 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 fbf26b67..4e8bf528 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 23a8d5c7..eba1266a 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> |