This commit is contained in:
Karl MacMillan
-
48 changed files with 1290 additions and 512 deletions

View File

@@ -1,4 +1,4 @@
SUBDIRS=ipa-server ipa-admintools ipa-python ipa-client
SUBDIRS=ipa-server ipa-admintools ipa-python ipa-client ipa-radius-server
PRJ_PREFIX=ipa
@@ -35,6 +35,13 @@ CLI_VERSION=$(CLI_MAJOR).$(CLI_MINOR).$(CLI_RELEASE)
CLI_TARBALL_PREFIX=$(PRJ_PREFIX)-client-$(CLI_VERSION)
CLI_TARBALL=$(CLI_TARBALL_PREFIX).tgz
RADIUS_SERVER_MAJOR=0
RADIUS_SERVER_MINOR=5
RADIUS_SERVER_RELEASE=0
RADIUS_SERVER_VERSION=$(RADIUS_SERVER_MAJOR).$(RADIUS_SERVER_MINOR).$(RADIUS_SERVER_RELEASE)
RADIUS_SERVER_TARBALL_PREFIX=$(PRJ_PREFIX)-radius-server-$(RADIUS_SERVER_VERSION)
RADIUS_SERVER_TARBALL=$(RADIUS_SERVER_TARBALL_PREFIX).tgz
LIBDIR ?= /usr/lib
all: bootstrap-autogen
@@ -77,6 +84,9 @@ version-update:
sed s/VERSION/$(CLI_VERSION)/ ipa-client/ipa-client.spec.in \
> ipa-client/ipa-client.spec
sed s/VERSION/$(RADIUS_SERVER_VERSION)/ ipa-radius-server/ipa-radius-server.spec.in \
> ipa-radius-server/ipa-radius-server.spec
archive:
-mkdir -p dist
@@ -120,6 +130,13 @@ tarballs:
cd dist; tar cfz sources/$(CLI_TARBALL) $(CLI_TARBALL_PREFIX)
rm -fr dist/$(CLI_TARBALL_PREFIX)
# ipa-radius-server
mv dist/ipa/ipa-radius-server dist/$(RADIUS_SERVER_TARBALL_PREFIX)
rm -f dist/sources/$(RADIUS_SERVER_TARBALL)
cd dist; tar cfz sources/$(RADIUS_SERVER_TARBALL) $(RADIUS_SERVER_TARBALL_PREFIX)
rm -fr dist/$(RADIUS_SERVER_TARBALL_PREFIX)
rpmroot:
mkdir -p $(RPMBUILD)/BUILD
mkdir -p $(RPMBUILD)/RPMS
@@ -155,7 +172,14 @@ rpm-ipa-client:
cp rpmbuild/RPMS/*/$(PRJ_PREFIX)-client-$(CLI_VERSION)-*.rpm dist/rpms/
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-client-$(CLI_VERSION)-*.src.rpm dist/srpms/
rpms: rpmroot rpmdistdir rpm-ipa-server rpm-ipa-admin rpm-ipa-python rpm-ipa-client
rpm-ipa-radius-server:
cp dist/sources/$(RADIUS_SERVER_TARBALL) $(RPMBUILD)/SOURCES/.
rpmbuild --define "_topdir $(RPMBUILD)" -ba ipa-radius-server/ipa-radius-server.spec
cp rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-radius-server-$(RADIUS_SERVER_VERSION)-*.rpm dist/rpms/
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-radius-server-$(RADIUS_SERVER_VERSION)-*.src.rpm dist/srpms/
rpms: rpmroot rpmdistdir rpm-ipa-server rpm-ipa-admin rpm-ipa-python rpm-ipa-client rpm-ipa-radius-server
repodata:
-createrepo -p dist

View File

@@ -14,7 +14,8 @@ MANFILES=\
ipa-lockuser.1 \
ipa-moddelegation.1 \
ipa-passwd.1 \
ipa-usermod.1
ipa-usermod.1 \
ipa-getkeytab.1
all: ;

View File

@@ -0,0 +1,64 @@
.\" A man page for ipa-getkeytab
.\" Copyright (C) 2007 Red Hat, Inc.
.\"
.\" This is free software; you can redistribute it and/or modify it under
.\" the terms of the GNU Library General Public License as published by
.\" the Free Software Foundation; either version 2 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful, but
.\" WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
.\" General Public License for more details.
.\"
.\" You should have received a copy of the GNU Library General Public
.\" License along with this program; if not, write to the Free Software
.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
.\"
.\" Author: Karl MacMillan <kmacmill@redhat.com>
.\"
.TH "ipa-getkeytab" "1" "Oct 10 2007" "freeipa" ""
.SH "NAME"
ipa\-getkeytab \- Get a keytab for a kerberos principal
.SH "SYNOPSIS"
ipa\-getkeytab [\fI-a\fR] \fIprincipal-name\fR \fIfile-name\fR
.SH "DESCRIPTION"
Retrieves a kerberos \fIkeytab\fR and optionally adds a
service \fIprincipal\fR.
Kerberos keytabs are used for services (like sshd) to
perform kerberos authentication. A keytab is a file
with one or more secrets (or keys) for a kerberos
principal.
A kerberos service principal is a kerberos identity
that can be used for authentication. Service principals
contain the name of the service, the hostname of the
server, and the realm name. For example, the following
is an example principal for an ldap server:
ldap/foo.example.com@EXAMPLE.COM
When using ipa-getkeytab the realm name is already
provided, so the principal name is just the service
name and hostname (ldap/foo.example.com from the
example above).
\fBWARNING:\fR retrieving the keytab resets the secret
rendering all other keytabs for that principal invalid.
.SH "OPTIONS"
.TP
\fB\-a\fR
Add the service principal in addition to getting the keytab
.SH "EXAMPLES"
Add and retrieve a keytab for the ldap service principal on
the host foo.example.com and save it in the file ldap.keytab.
# ipa-getkeytab -a ldap/foo.example.com ldap.keytab
.SH "EXIT STATUS"
The exit status is 0 on success, nonzero on error.

View File

@@ -51,6 +51,13 @@ def parse_options():
return options
def ask_for_confirmation(message):
yesno = raw_input(message + " [y/N]: ")
if not yesno or yesno.lower()[0] != "y":
return False
print "\n"
return True
def logging_setup(options):
# Always log everything (i.e., DEBUG) to the log
# file.
@@ -114,16 +121,18 @@ def main():
print "\nThe failure to use DNS to find your IPA server indicates that your resolv.conf file is not properly configured\n."
print "Autodiscovery of servers for failover cannot work with this configuration.\n"
print "If you proceed with the installation, services will be configured to always access the discovered server for all operation and will not fail over to other servers in case of failure\n"
yesno = raw_input("Do you want to proceed and configure the system with fixed values with no DNS discovery? [y/N] ")
if yesno.lower() != "y":
if not ask_for_confirmation("Do you want to proceed and configure the system with fixed values with no DNS discovery?"):
return ret
print "\n"
print "Realm: "+ds.getRealmName()
print "DNS Domain: "+ds.getDomainName()
print "IPA Server: "+ds.getServerName()
print "BaseDN: "+ds.getBaseDN()
print "\n"
if not ask_for_confirmation("Continue to configure the system with these values?"):
return 1
# Configure ipa.conf
ipaconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer")
ipaconf.setOptionAssignment(" = ")
@@ -225,4 +234,4 @@ def main():
return 0
main()
sys.exit(main())

View File

@@ -14,8 +14,6 @@ Requires: PyKerberos
%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
%define pkgpythondir %{python_sitelib}/ipa
%description
Ipa is a server for identity, policy, and audit.
@@ -33,8 +31,7 @@ rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%dir %{pkgpythondir}
%{pkgpythondir}/*
%{python_sitelib}/*
%config(noreplace) %{_sysconfdir}/ipa/ipa.conf
%changelog

View File

@@ -14,8 +14,6 @@ Requires: PyKerberos
%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
%define pkgpythondir %{python_sitelib}/ipa
%description
Ipa is a server for identity, policy, and audit.
@@ -33,8 +31,7 @@ rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%dir %{pkgpythondir}
%{pkgpythondir}/*
%{python_sitelib}/*
%config(noreplace) %{_sysconfdir}/ipa/ipa.conf
%changelog

View File

@@ -19,14 +19,10 @@
#!/usr/bin/python
import sys
import ipa.rpcclient as rpcclient
import entity
import user
import group
import ipa
import config
import radius_util
class IPAClient:

View File

@@ -129,14 +129,14 @@ LDAP_NO_CONFIG = gen_error_code(
"IPA configuration not found")
#
# Input errors (sample - replace me)
# Function input errors
#
INPUT_CATEGORY = 0x0002
INPUT_INVALID_ERROR = gen_error_code(
INPUT_INVALID_PARAMETER = gen_error_code(
INPUT_CATEGORY,
0x0001,
"Illegal input")
"Invalid parameter(s)")
#
# Connection errors

View File

@@ -18,6 +18,7 @@
#
SHARE_DIR = "/usr/share/ipa/"
PLUGINS_SHARE_DIR = "/usr/share/ipa/plugins"
import string
import tempfile

View File

@@ -79,7 +79,7 @@ RADIUS_USER = 'radiusd'
RADIUS_IPA_KEYTAB_FILEPATH = os.path.join(RADIUS_PKG_CONFIG_DIR, 'ipa.keytab')
RADIUS_LDAP_ATTR_MAP_FILEPATH = os.path.join(RADIUS_PKG_CONFIG_DIR, 'ldap.attrmap')
RADIUSD_CONF_FILEPATH = os.path.join(RADIUS_PKG_CONFIG_DIR, 'radiusd.conf')
RADIUSD_CONF_TEMPLATE_FILEPATH = os.path.join(ipautil.SHARE_DIR, 'radius.radiusd.conf.template')
RADIUSD_CONF_TEMPLATE_FILEPATH = os.path.join(ipautil.PLUGINS_SHARE_DIR, 'radius.radiusd.conf.template')
RADIUSD = '/usr/sbin/radiusd'

View File

@@ -24,11 +24,8 @@ import socket
import config
from krbtransport import KerbTransport
from kerberos import GSSError
import os
import base64
import user
import ipa
from ipa import ipaerror, ipautil
from ipa import config
# Some errors to catch
# http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto
@@ -36,7 +33,7 @@ from ipa import ipaerror, ipautil
class RPCClient:
def __init__(self):
ipa.config.init_config()
config.init_config()
def server_url(self):
"""Build the XML-RPC server URL from our configuration"""
@@ -47,25 +44,6 @@ class RPCClient:
authentication"""
return xmlrpclib.ServerProxy(self.server_url(), KerbTransport())
def convert_entry(self,ent):
# Convert into a dict. We need to handle multi-valued attributes as well
# so we'll convert those into lists.
obj={}
for (k) in ent:
k = k.lower()
if obj.get(k) is not None:
if isinstance(obj[k],list):
obj[k].append(ent[k].strip())
else:
first = obj[k]
obj[k] = ()
obj[k].append(first)
obj[k].append(ent[k].strip())
else:
obj[k] = ent[k]
return obj
# Higher-level API
def get_aci_entry(self, sattrs=None):
@@ -168,7 +146,8 @@ class RPCClient:
def get_user_by_email(self,email,sattrs=None):
"""Get a specific user's entry. Return as a dict of values.
Multi-valued fields are represented as lists.
Multi-valued fields are represented as lists. The result is a
dict.
"""
server = self.setup_server()
if sattrs is None:
@@ -245,7 +224,7 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
def get_all_users (self):
"""Return a list containing a User object for each existing user."""
"""Return a list containing a dict for each existing user."""
server = self.setup_server()
try:
@@ -258,7 +237,7 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
def find_users (self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
"""Return a list: counter followed by a User object for each user that
"""Return a list: counter followed by a dict for each user that
matches the criteria. If the results are truncated, counter will
be set to -1"""
@@ -381,6 +360,8 @@ class RPCClient:
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
return ipautil.unwrap_binary_data(result)
def find_groups (self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
"""Return a list containing a Group object for each group that matches
the criteria."""

View File

@@ -34,7 +34,7 @@ def setup_package():
try:
setup(
name = "freeipa-python",
name = "ipa",
version = "0.5.0",
license = "GPL",
author = "Karl MacMillan, et.al.",

View File

@@ -0,0 +1,20 @@
PLUGINS_SHARE = $(DESTDIR)/usr/share/ipa/plugins
PLUGINS_PYTHON = $(DESTDIR)/usr/share/ipa/ipaserver/plugins
SBINDIR = $(DESTDIR)/usr/sbin
all:
install:
-mkdir -p $(PLUGINS_SHARE)
-mkdir -p $(PLUGINS_PYTHON)
-mkdir -p $(PLUGINS_SBINDIR)
install -m 644 plugins/*.py $(PLUGINS_PYTHON)
install -m 644 share/*.template $(PLUGINS_SHARE)
install -m 755 ipa-radius-install $(SBINDIR)
clean:
rm -fr *.pyc *~
distclean: clean
test:

View File

@@ -0,0 +1,73 @@
#! /usr/bin/python -E
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
import sys
sys.path.append("/usr/share/ipa")
import traceback, logging, krbV
from ipaserver import installutils
from ipaserver.plugins import radiusinstance
from ipa import ipautil
def get_host_name():
hostname = installutils.get_fqdn()
try:
installutils.verify_fqdn(hostname)
except RuntimeError, e:
logging.error(str(e))
sys.exit(1)
return hostname
def get_realm_name():
c = krbV.default_context()
return c.default_realm
def main():
if not ipautil.file_exists("/etc/ipa/ipa.conf"):
print "This system does not appear to have IPA configured."
print "Has ipa-server-install been run?"
yesno = raw_input("Continue with radius install [y/N]? ")
if yesno.lower() != "y":
sys.exit(1)
installutils.standard_logging_setup("iparadius-install.log", False)
host_name = get_host_name()
realm_name = get_realm_name()
# Create a radius instance
radius = radiusinstance.RadiusInstance()
# FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL?
radius.create_instance(realm_name, host_name, 'localhost')
try:
main()
except Exception, e:
message = "Unexpected error - see iparadius-install.log for details:\n %s" % str(e)
print message
message = str(e)
for str in traceback.format_tb(sys.exc_info()[2]):
message = message + "\n" + str
logging.debug(message)

View File

@@ -0,0 +1,50 @@
Name: ipa-radius-server
Version: 0.5.0
Release: 1%{?dist}
Summary: IPA authentication server - radius plugin
Group: System Environment/Base
License: GPL
URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
Requires: python
Requires: ipa-server
Requires: freeradius
%description
Radius plugin for an IPA server
%prep
%setup -q
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_sbindir}
make install DESTDIR=%{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa*
%dir %{_usr}/share/ipa/plugins
%{_usr}/share/ipa/plugins/*
%dir %{_usr}/share/ipa/ipaserver/plugins
%{_usr}/share/ipa/ipaserver/plugins/*
%changelog
* Wed Dec 12 2007 Karl MacMillan <kmacmill@redhat.com> - 0.5.0-1
- Initial version

View File

@@ -0,0 +1,50 @@
Name: ipa-radius-server
Version: VERSION
Release: 1%{?dist}
Summary: IPA authentication server - radius plugin
Group: System Environment/Base
License: GPL
URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
Requires: python
Requires: ipa-server
Requires: freeradius
%description
Radius plugin for an IPA server
%prep
%setup -q
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_sbindir}
make install DESTDIR=%{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa*
%dir %{_usr}/share/ipa/plugins
%{_usr}/share/ipa/plugins/*
%dir %{_usr}/share/ipa/ipaserver/plugins
%{_usr}/share/ipa/ipaserver/plugins/*
%changelog
* Wed Dec 12 2007 Karl MacMillan <kmacmill@redhat.com> - 0.5.0-1
- Initial version

View File

@@ -0,0 +1 @@
# intentionally empty

View File

@@ -19,6 +19,8 @@
#
import sys
sys.path.append("/usr/share/ipa")
import subprocess
import string
import tempfile
@@ -27,10 +29,10 @@ import logging
import pwd
import time
import sys
from ipa.ipautil import *
from ipa import ipautil
from ipa import radius_util
import service
from ipaserver import service
import os
import re
@@ -42,10 +44,6 @@ from ipaserver.funcs import DefaultUserContainer, DefaultGroupContainer
#-------------------------------------------------------------------------------
def ldap_mod(fd, dn, pwd):
args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name]
run(args)
def get_radius_version():
version = None
try:
@@ -74,14 +72,13 @@ class RadiusInstance(service.Service):
def create_instance(self, realm_name, host_name, ldap_server):
self.realm = realm_name.upper()
self.suffix = realm_to_suffix(self.realm)
self.suffix = ipautil.realm_to_suffix(self.realm)
self.fqdn = host_name
self.ldap_server = ldap_server
self.principal = "%s/%s@%s" % (radius_util.RADIUS_SERVICE_NAME, self.fqdn, self.realm)
self.basedn = self.suffix
self.user_basedn = "%s,%s" % (DefaultUserContainer, self.basedn) # FIXME, should be utility to get this
self.radius_version = get_radius_version()
self.start_creation(4, "Configuring radiusd")
try:
self.stop()
@@ -89,22 +86,23 @@ class RadiusInstance(service.Service):
# It could have been not running
pass
self.__create_radius_keytab()
self.__radiusd_conf()
self.step("create radiusd keytab", self.__create_radius_keytab)
self.step("configuring radiusd.conf for radius instance", self.__radiusd_conf)
self.step("starting radiusd", self.__start_instance)
self.step("configuring radiusd to start on boot", self.chkconfig_on)
# FIXME:
# self.step("setting ldap encrypted attributes", self.__set_ldap_encrypted_attributes)
self.start_creation("Configuring radiusd")
def __start_instance(self):
try:
self.step("starting radiusd")
self.start()
except:
logging.error("radiusd service failed to start")
self.step("configuring radiusd to start on boot")
self.chkconfig_on()
def __radiusd_conf(self):
self.step('configuring radiusd.conf for radius instance')
version = 'IPA_RADIUS_VERSION=%s FREE_RADIUS_VERSION=%s' % (IPA_RADIUS_VERSION, self.radius_version)
sub_dict = {'CONFIG_FILE_VERSION_INFO' : version,
'LDAP_SERVER' : self.ldap_server,
@@ -117,7 +115,7 @@ class RadiusInstance(service.Service):
'SUFFIX' : self.suffix,
}
try:
radiusd_conf = template_file(radius_util.RADIUSD_CONF_TEMPLATE_FILEPATH, sub_dict)
radiusd_conf = ipautil.template_file(radius_util.RADIUSD_CONF_TEMPLATE_FILEPATH, sub_dict)
radiusd_fd = open(radius_util.RADIUSD_CONF_FILEPATH, 'w+')
radiusd_fd.write(radiusd_conf)
radiusd_fd.close()
@@ -125,9 +123,8 @@ class RadiusInstance(service.Service):
logging.error("could not create %s: %s", radius_util.RADIUSD_CONF_FILEPATH, e)
def __create_radius_keytab(self):
self.step("creating a keytab for httpd")
try:
if file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
if ipautil.file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
os.remove(radius_util.RADIUS_IPA_KEYTAB_FILEPATH)
except os.error:
logging.error("Failed to remove %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH)
@@ -143,7 +140,7 @@ class RadiusInstance(service.Service):
# give kadmin time to actually write the file before we go on
retry = 0
while not file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
while not ipautil.file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
time.sleep(1)
retry += 1
if retry > 15:
@@ -155,17 +152,23 @@ class RadiusInstance(service.Service):
except Exception, e:
logging.error("could not chown on %s to %s: %s", radius_util.RADIUS_IPA_KEYTAB_FILEPATH, radius_util.RADIUS_USER, e)
def __ldap_mod(self, ldif):
txt = iputil.template_file(ipautil.SHARE_DIR + ldif, self.sub_dict)
fd = ipautil.write_tmp_file(txt)
args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv",
"-D", "cn=Directory Manager", "-w", self.dm_password, "-f", fd.name]
try:
ipautil.run(args)
except ipautil.CalledProcessError, e:
logging.critical("Failed to load %s: %s" % (ldif, str(e)))
fd.close()
#FIXME, should use IPAdmin method
def __set_ldap_encrypted_attributes(self):
ldif_file = 'encrypted_attribute.ldif'
self.step("setting ldap encrypted attributes")
ldif_txt = template_file(SHARE_DIR + ldif_file, {'ENCRYPTED_ATTRIBUTE':'radiusClientSecret'})
ldif_fd = write_tmp_file(ldif_txt)
try:
ldap_mod(ldif_fd, "cn=Directory Manager", self.dm_password)
except subprocess.CalledProcessError, e:
logging.critical("Failed to load %s: %s" % (ldif_file, str(e)))
ldif_fd.close()
self.__ldap_mod("encrypted_attribute.ldif", {"ENCRYPTED_ATTRIBUTE" : "radiusClientSecret"})
#-------------------------------------------------------------------------------

View File

@@ -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
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)
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)

View File

@@ -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']

View File

@@ -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")

View File

@@ -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

View File

@@ -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');"
/>

View File

@@ -8,6 +8,7 @@ sbin_SCRIPTS = \
ipa-server-install \
ipa-replica-install \
ipa-replica-prepare \
ipa-server-certinstall \
$(NULL)
EXTRA_DIST = \

View File

@@ -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())

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -9,7 +9,6 @@ app_PYTHON = \
krbinstance.py \
httpinstance.py \
ntpinstance.py \
radiusinstance.py \
webguiinstance.py \
service.py \
installutils.py \

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 directory to start on boot", self.chkconfig_on)
self.step("configuring directoy 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)
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]
try:
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.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)
except ipautil.CalledProcessError, e:
logging.critical("Failed to load memberof-conf.ldif: %s" % str(e))
memberof_fd.close()
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")

View File

@@ -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("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.step("restarting httpd")
self.restart()
self.step("configuring httpd to start on boot")
self.chkconfig_on()
self.done_creation()
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")

View File

@@ -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

View File

@@ -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 __export_kadmin_changepw_keytab(self):
self.step("exporting the kadmin keytab")
def __create_host_keytab(self):
try:
if file_exists("/var/kerberos/krb5kdc/kpasswd.keytab"):
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):
try:
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:

View File

@@ -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")

View File

@@ -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
def step(self, message, method):
self.steps.append((message, method))
def start_creation(self, message):
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 done_creation(self):
self.cur_step = -1
self.num_steps = -1
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 = []

View File

@@ -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")

View File

@@ -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.

View File

@@ -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"

View File

@@ -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>