summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xinstall/tools/ipa-server-install34
-rw-r--r--ipalib/cli.py1
-rw-r--r--ipapython/nsslib.py150
-rw-r--r--ipaserver/install/Makefile.am1
-rw-r--r--ipaserver/install/cainstance.py743
-rw-r--r--ipaserver/install/dsinstance.py4
-rw-r--r--ipaserver/install/service.py28
-rw-r--r--ipaserver/plugins/ra.py5
8 files changed, 945 insertions, 21 deletions
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index c0f8e7a6b..cefdbf85e 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -69,6 +69,8 @@ def parse_options():
help="admin user kerberos password")
parser.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="print debugging information")
+ parser.add_option("", "--ca", dest="ca", action="store_true",
+ default=False, help="Configure a CA instance")
parser.add_option("--hostname", dest="host_name", help="fully qualified name of server")
parser.add_option("--ip-address", dest="ip_address", help="Master Server IP Address")
parser.add_option("--setup-bind", dest="setup_bind", action="store_true",
@@ -298,7 +300,7 @@ def check_dirsrv(unattended):
print "\t636"
sys.exit(1)
-def uninstall():
+def uninstall(ca = False):
try:
run(["/usr/sbin/ipa-client-install", "--on-master", "--unattended", "--uninstall"])
except Exception, e:
@@ -307,6 +309,14 @@ def uninstall():
pass
ntpinstance.NTPInstance(fstore).uninstall()
+ if ca:
+ try:
+ from ipaserver.install import cainstance
+ except ImportError:
+ print >> sys.stderr, "Import failed: %s" % sys.exc_value
+ sys.exit(1)
+ cainstance.CADSInstance().uninstall()
+ cainstance.CAInstance().uninstall()
bindinstance.BindInstance(fstore).uninstall()
httpinstance.HTTPInstance(fstore).uninstall()
krbinstance.KrbInstance(fstore).uninstall()
@@ -345,7 +355,7 @@ def main():
print "Aborting uninstall operation."
sys.exit(1)
- return uninstall()
+ return uninstall(options.ca)
print "=============================================================================="
print "This program will setup the FreeIPA Server."
@@ -495,12 +505,26 @@ def main():
os.write(pw_fd, options.dirsrv_pin)
os.close(pw_fd)
+ if options.ca:
+ try:
+ from ipaserver.install import cainstance
+ except ImportError:
+ print >> sys.stderr, "Import failed: %s" % sys.exc_value
+ sys.exit(1)
+
+ cs = cainstance.CADSInstance()
+ cs.create_instance("dirsrv", realm_name, host_name, domain_name, dm_password)
+ ca = cainstance.CAInstance()
+ ca.configure_instance("pkiuser", host_name, dm_password, dm_password)
+
# Create a directory server instance
ds = dsinstance.DsInstance()
if options.dirsrv_pkcs12:
pkcs12_info = (options.dirsrv_pkcs12, pw_name)
- ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info)
- os.remove(pw_name)
+ try:
+ ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info)
+ finally:
+ os.remove(pw_name)
else:
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password)
@@ -540,6 +564,8 @@ def main():
fd.write("realm=" + realm_name + "\n")
fd.write("domain=" + domain_name + "\n")
fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % host_name)
+ if options.ca:
+ fd.write("enable_ra=True\n")
fd.close()
bind = bindinstance.BindInstance(fstore)
diff --git a/ipalib/cli.py b/ipalib/cli.py
index 4e04b531c..11b56e36f 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -37,6 +37,7 @@ import backend
import plugable
import util
from errors2 import PublicError, CommandError, HelpError, InternalError
+import errors
from constants import CLI_TAB
from parameters import Password, Bytes
from request import ugettext as _
diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
new file mode 100644
index 000000000..fd27028e7
--- /dev/null
+++ b/ipapython/nsslib.py
@@ -0,0 +1,150 @@
+# Authors: Rob Crittenden <rcritten@redhat.com>
+#
+# Copyright (C) 2009 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 httplib
+import getpass
+import socket
+import errno
+
+from nss.error import NSPRError
+import nss.io as io
+import nss.nss as nss
+import nss.ssl as ssl
+
+def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
+ cert = None
+ if chosen_nickname:
+ try:
+ cert = nss.find_cert_from_nickname(chosen_nickname, password)
+ priv_key = nss.find_key_by_any_cert(cert, password)
+ return cert, priv_key
+ except NSPRError:
+ return False
+ else:
+ nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER)
+ for nickname in nicknames:
+ try:
+ cert = nss.find_cert_from_nickname(nickname, password)
+ if cert.check_valid_times():
+ if cert.has_signer_in_ca_names(ca_names):
+ priv_key = nss.find_key_by_any_cert(cert, password)
+ return cert, priv_key
+ except NSPRError:
+ return False
+ return False
+
+class SSLFile(httplib.SSLFile):
+ """
+ Override the _read method so we can use the NSS recv method.
+ """
+ def _read(self):
+ buf = ''
+ while True:
+ try:
+ buf = self._ssl.recv(self._bufsize)
+ except NSPRError, e:
+ raise e
+ else:
+ break
+ return buf
+
+class NSSFakeSocket(httplib.FakeSocket):
+ def makefile(self, mode, bufsize=None):
+ if mode != 'r' and mode != 'rb':
+ raise httplib.UnimplementedFileMode()
+ return SSLFile(self._shared, self._ssl, bufsize)
+
+ def send(self, stuff, flags = 0):
+ return self._ssl.send(stuff)
+
+ sendall = send
+
+class NSSConnection(httplib.HTTPConnection):
+ default_port = httplib.HTTPSConnection.default_port
+
+ def __init__(self, host, port=None, key_file=None, cert_file=None,
+ ca_file='/etc/pki/tls/certs/ca-bundle.crt', strict=None,
+ dbdir=None):
+ httplib.HTTPConnection.__init__(self, host, port, strict)
+ self.key_file = key_file
+ self.cert_file = cert_file
+ self.ca_file = ca_file
+
+ if not dbdir:
+ raise RuntimeError("dbdir is required")
+
+ ssl.nssinit(dbdir)
+ ssl.set_domestic_policy()
+ nss.set_password_callback(self.password_callback)
+
+ # Create the socket here so we can do things like let the caller
+ # override the NSS callbacks
+ self.sslsock = ssl.SSLSocket()
+ self.sslsock.set_ssl_option(ssl.SSL_SECURITY, True)
+ self.sslsock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True)
+ self.sslsock.set_handshake_callback(self.handshake_callback)
+
+ def password_callback(self, slot, retry, password):
+ if not retry and password: return password
+ return getpass.getpass("Enter password for %s: " % slot.token_name);
+
+ def handshake_callback(self, sock):
+ """
+ Verify callback. If we get here then the certificate is ok.
+ """
+ if self.debuglevel > 0:
+ print "handshake complete, peer = %s" % (sock.get_peer_name())
+ pass
+
+ def connect(self):
+ self.sslsock.set_hostname(self.host)
+
+ net_addr = io.NetworkAddress(self.host, self.port)
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sslsock.connect(net_addr)
+ self.sock = NSSFakeSocket(sock, self.sslsock)
+
+class NSSHTTPS(httplib.HTTP):
+ _connection_class = NSSConnection
+
+ def __init__(self, host='', port=None, key_file=None, cert_file=None,
+ ca_file='/etc/pki/tls/certs/ca-bundle.crt', strict=None):
+ # provide a default host, pass the X509 cert info
+
+ # urf. compensate for bad input.
+ if port == 0:
+ port = None
+ self._setup(self._connection_class(host, port, key_file,
+ cert_file, ca_file, strict))
+ # we never actually use these for anything, but we keep them
+ # here for compatibility with post-1.5.2 CVS.
+ self.key_file = key_file
+ self.cert_file = cert_file
+ self.ca_file = ca_file
+
+if __name__ == "__main__":
+ h = NSSConnection("www.verisign.com", 443, dbdir="/etc/pki/nssdb")
+ h.set_debuglevel(1)
+ h.request("GET", "/")
+ res = h.getresponse()
+ print res.status
+ data = res.read()
+ print data
+ h.close()
diff --git a/ipaserver/install/Makefile.am b/ipaserver/install/Makefile.am
index 999dcf248..964837cb9 100644
--- a/ipaserver/install/Makefile.am
+++ b/ipaserver/install/Makefile.am
@@ -4,6 +4,7 @@ appdir = $(pythondir)/ipaserver
app_PYTHON = \
__init__.py \
bindinstance.py \
+ cainstance.py \
dsinstance.py \
ipaldap.py \
krbinstance.py \
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
new file mode 100644
index 000000000..c402fb04c
--- /dev/null
+++ b/ipaserver/install/cainstance.py
@@ -0,0 +1,743 @@
+# Authors: Rob Crittenden <rcritten@redhat.com>
+# Ade Lee <alee@redhat.com>
+# Andrew Wnuk <awnuk@redhat.com>
+#
+# Copyright (C) 2009 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 logging
+import pwd
+import os
+import sys
+import re
+import time
+import ldap
+import base64
+import array
+import tempfile
+import binascii
+import shutil
+import httplib
+import urllib
+import xml.dom.minidom
+import getopt
+import urlparse
+import getpass
+import socket
+import errno
+
+from nss.error import NSPRError
+import nss.nss as nss
+
+from ipapython import ipautil
+from ipapython import nsslib
+
+from ipaserver.install import service
+from ipaserver.install import installutils
+from ipaserver import ipaldap
+from ipaserver.install import ldapupdate
+from ipaserver.install import dsinstance
+from ipalib import util
+
+DEFAULT_DSPORT=7389
+
+# We need to reset the template because the CA uses the regular boot
+# information
+INF_TEMPLATE = """
+[General]
+FullMachineName= $FQHN
+SuiteSpotUserID= $USER
+ServerRoot= $SERVER_ROOT
+[slapd]
+ServerPort= $DSPORT
+ServerIdentifier= $SERVERID
+Suffix= $SUFFIX
+RootDN= cn=Directory Manager
+RootDNPwd= $PASSWORD
+"""
+
+def get_preop_pin(instance_root, instance_name):
+ preop_pin = None
+
+ filename = instance_root + "/" + instance_name + "/conf/CS.cfg"
+
+ # read the config file and get the preop pin
+ try:
+ f=open(filename)
+ except IOError, e:
+ logging.error("Cannot open configuration file." + str(e))
+ raise e
+ data = f.read()
+ data = data.split('\n')
+ pattern = re.compile("preop.pin=(.*)" )
+ for line in data:
+ match = re.search(pattern, line)
+ if (match):
+ preop_pin=match.group(1)
+ break
+
+ return preop_pin
+
+def export_pkcs12(output_file, output_passwd, nickname, cert_database,
+ cert_passwd):
+ ipautil.run(["/usr/bin/pk12util", "-d", cert_database,
+ "-o", output_file,
+ "-n", nickname,
+ "-k", cert_passwd,
+ "-w", output_passwd])
+
+def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
+ cert = None
+ if chosen_nickname:
+ try:
+ cert = nss.find_cert_from_nickname(chosen_nickname, password)
+ priv_key = nss.find_key_by_any_cert(cert, password)
+ return cert, priv_key
+ except NSPRError, e:
+ logging.debug("client auth callback failed %s" % str(e))
+ return False
+ else:
+ nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER)
+ for nickname in nicknames:
+ try:
+ cert = nss.find_cert_from_nickname(nickname, password)
+ if cert.check_valid_times():
+ if cert.has_signer_in_ca_names(ca_names):
+ priv_key = nss.find_key_by_any_cert(cert, password)
+ return cert, priv_key
+ except NSPRError, e:
+ logging.debug("client auth callback failed %s" % str(e))
+ return False
+ return False
+
+def get_value(s):
+ """
+ Parse out a name/value pair from a Javascript variable.
+ """
+ try:
+ expr = s.split('=',1)
+ value = expr[1]
+ value = value.replace('\"', '')
+ value = value.replace(';','')
+ value = value.replace('\\n','\n')
+ value = value.replace('\\r','\r')
+ return value
+ except IndexError:
+ return None
+
+def find_substring(data, value):
+ """
+ Scan through a list looking for a string that starts with value.
+ """
+ for d in data:
+ if d.startswith(value):
+ return get_value(d)
+
+def get_defList(data):
+ """
+ Return a dictionary of defList name/value pairs.
+
+ A certificate signing request is specfied as a series of these.
+ """
+ varname = None
+ value = None
+ skip = False
+ defdict = {}
+ for d in data:
+ if d.startswith("defList = new Object"):
+ varname = None
+ value = None
+ skip = False
+ if d.startswith("defList.defId"):
+ varname = get_value(d)
+ if d.startswith("defList.defVal"):
+ value = get_value(d)
+ if skip:
+ varname = None
+ value = None
+ skip = False
+ if d.startswith("defList.defConstraint"):
+ ctype = get_value(d)
+ if ctype == "readonly":
+ skip = True
+
+ if varname and value:
+ defdict[varname] = value
+ varname = None
+ value = None
+
+ return defdict
+
+def get_outputList(data):
+ """
+ Return a dictionary of outputList name/value pairs.
+
+ The output from issuing a certificate is a series of these.
+ """
+ varname = None
+ value = None
+ outputdict = {}
+ for d in data:
+ if d.startswith("outputList = new"):
+ varname = None
+ value = None
+ if d.startswith("outputList.outputId"):
+ varname = get_value(d)
+ if d.startswith("outputList.outputVal"):
+ value = get_value(d)
+
+ if varname and value:
+ outputdict[varname] = value
+ varname = None
+ value = None
+
+ return outputdict
+
+class CADSInstance(service.Service):
+ def __init__(self, realm_name=None, domain_name=None, dm_password=None):
+ service.Service.__init__(self, "pkids")
+ self.realm_name = realm_name
+ self.dm_password = dm_password
+ self.sub_dict = None
+ self.domain = domain_name
+ self.serverid = None
+ self.host_name = None
+ self.pkcs12_info = None
+ self.ds_user = None
+ self.ds_port = None
+ if realm_name:
+ self.suffix = util.realm_to_suffix(self.realm_name)
+ self.__setup_sub_dict()
+ else:
+ self.suffix = None
+
+ def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None, ds_port=DEFAULT_DSPORT):
+ self.ds_user = ds_user
+ self.ds_port = ds_port
+ self.realm_name = realm_name.upper()
+ self.serverid = "PKI-IPA"
+ self.suffix = util.realm_to_suffix(self.realm_name)
+ self.host_name = host_name
+ self.dm_password = dm_password
+ self.domain = domain_name
+ self.pkcs12_info = pkcs12_info
+ self.__setup_sub_dict()
+
+ self.step("creating directory server user", self.__create_ds_user)
+ self.step("creating directory server instance", self.__create_instance)
+ self.step("configuring directory to start on boot", self.__enable)
+ self.step("restarting directory server", self.__restart_instance)
+
+ self.start_creation("Configuring directory server for the CA:")
+
+ def __setup_sub_dict(self):
+ server_root = dsinstance.find_server_root()
+ self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid,
+ PASSWORD=self.dm_password, SUFFIX=self.suffix.lower(),
+ REALM=self.realm_name, USER=self.ds_user,
+ SERVER_ROOT=server_root, DOMAIN=self.domain,
+ TIME=int(time.time()), DSPORT=self.ds_port)
+
+ def __enable(self):
+ name = self.service_name
+ self.service_name="dirsrv"
+ self.backup_state("enabled", self.is_enabled())
+ self.chkconfig_on()
+ self.service_name = name
+
+ def __create_ds_user(self):
+ user_exists = True
+ try:
+ pwd.getpwnam(self.ds_user)
+ logging.debug("ds user %s exists" % self.ds_user)
+ except KeyError:
+ user_exists = False
+ logging.debug("adding ds user %s" % self.ds_user)
+ args = ["/usr/sbin/useradd", "-c", "DS System User", "-d", "/var/lib/dirsrv", "-M", "-r", "-s", "/sbin/nologin", self.ds_user]
+ try:
+ ipautil.run(args)
+ logging.debug("done adding user")
+ except ipautil.CalledProcessError, e:
+ logging.critical("failed to add user %s" % e)
+
+ self.backup_state("user", self.ds_user)
+ self.backup_state("user_exists", user_exists)
+
+ def __create_instance(self):
+ self.backup_state("running", dsinstance.is_ds_running())
+ self.backup_state("serverid", self.serverid)
+
+ inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict)
+ logging.debug("writing inf template")
+ inf_fd = ipautil.write_tmp_file(inf_txt)
+ inf_txt = re.sub(r"RootDNPwd=.*\n", "", inf_txt)
+ logging.debug(inf_txt)
+ if ipautil.file_exists("/usr/sbin/setup-ds.pl"):
+ args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name]
+ logging.debug("calling setup-ds.pl")
+ else:
+ args = ["/usr/bin/ds_newinst.pl", inf_fd.name]
+ logging.debug("calling ds_newinst.pl")
+ try:
+ ipautil.run(args)
+ logging.debug("completed creating ds instance")
+ except ipautil.CalledProcessError, e:
+ logging.critical("failed to restart ds instance %s" % e)
+ inf_fd.close()
+
+ def __restart_instance(self):
+ try:
+ # Have to trick the base class to use the right service name
+ sav_name = self.service_name
+ self.service_name="dirsrv"
+ self.restart(self.serverid)
+ self.service_name=sav_name
+ if not dsinstance.is_ds_running():
+ logging.critical("Failed to restart the directory server. See the installation log for details.")
+ sys.exit(1)
+ except Exception, e:
+ # TODO: roll back here?
+ logging.critical("Failed to restart the directory server. See the installation log for details.")
+
+ def uninstall(self):
+ running = self.restore_state("running")
+ enabled = self.restore_state("enabled")
+ serverid = self.restore_state("serverid")
+ sav_name = self.service_name
+ self.service_name="dirsrv"
+
+ if not running is None:
+ self.stop(serverid)
+
+ if not enabled is None and not enabled:
+ self.chkconfig_off()
+
+ if not serverid is None:
+ dsinstance.erase_ds_instance_data(serverid)
+
+ ds_user = self.restore_state("user")
+ user_exists = self.restore_state("user_exists")
+
+ if not ds_user is None and not user_exists is None and not user_exists:
+ try:
+ ipautil.run(["/usr/sbin/userdel", ds_user])
+ except ipautil.CalledProcessError, e:
+ logging.critical("failed to delete user %s" % e)
+ self.service_name = sav_name
+
+
+class CAInstance(service.Service):
+ def __init__(self):
+ service.Service.__init__(self, "pki-ca")
+ self.pki_user = None
+ self.dm_password = None
+ self.admin_password = None
+ self.host_name = None
+
+ self.basedn = "o=ipaca"
+ self.ca_agent_db = tempfile.mkdtemp(prefix = "tmp-")
+ self.ra_agent_db = "/etc/ipa/ra/alias"
+ self.ra_agent_pwd = self.ra_agent_db + "/.pwd"
+ self.ds_port = DEFAULT_DSPORT
+ self.domain_name = "IPA"
+ self.server_root = "/var/lib"
+ self.secure_port = 9444
+ self.ra_cert = None
+
+ def __del__(self):
+ shutil.rmtree(self.ca_agent_db, ignore_errors=True)
+
+ def configure_instance(self, pki_user, host_name, dm_password, admin_password, ds_port=DEFAULT_DSPORT):
+ self.pki_user = pki_user
+ self.host_name = host_name
+ self.dm_password = dm_password
+ self.admin_password = admin_password
+ self.ds_port = ds_port
+
+ self.step("creating certificate server user", self.__create_ca_user)
+ self.step("configuring certificate server instance", self.__configure_instance)
+ self.step("creating CA agent PKCS#12 file in /root", self.__create_ca_agent_pkcs12)
+ self.step("creating RA agent certificate database", self.__create_ra_agent_db)
+ self.step("importing CA chain to RA certificate database", self.__import_ca_chain)
+ self.step("requesting RA certificate from CA", self.__request_ra_certificate)
+ self.step("issuing RA agent certificate", self.__issue_ra_cert)
+ self.step("adding RA agent as a trusted user", self.__configure_ra)
+ self.step("fixing RA database permissions", self.__fix_ra_perms)
+ self.step("configuring certificate server to start on boot", self.__enable)
+ self.step("restarting certificate server", self.__restart_instance)
+
+ self.start_creation("Configuring certificate server:")
+
+ def __enable(self):
+ self.backup_state("enabled", self.is_enabled())
+ self.chkconfig_on()
+
+ def __create_ca_user(self):
+ user_exists = True
+ try:
+ pwd.getpwnam(self.pki_user)
+ logging.debug("ca user %s exists" % self.pki_user)
+ except KeyError:
+ user_exists = False
+ logging.debug("adding ca user %s" % self.pki_user)
+ args = ["/usr/sbin/useradd", "-c", "CA System User", "-d", "/var/lib", "-M", "-r", "-s", "/sbin/nologin", self.pki_user]
+ try:
+ ipautil.run(args)
+ logging.debug("done adding user")
+ except ipautil.CalledProcessError, e:
+ logging.critical("failed to add user %s" % e)
+
+ self.backup_state("user", self.pki_user)
+ self.backup_state("user_exists", user_exists)
+
+ def __configure_instance(self):
+#--skipcreate -u pkiuser -g pkiuser -p password -a password -d --hostname `hostname` -n IPA
+
+ preop_pin = get_preop_pin(self.server_root, self.service_name)
+
+ try:
+ args = ["/usr/bin/perl", "/usr/bin/pkisilent", "ConfigureCA",
+ "-cs_hostname", self.host_name,
+ "-cs_port", str(self.secure_port),
+ "-client_certdb_dir", self.ca_agent_db,
+ "-client_certdb_pwd", self.admin_password,
+ "-preop_pin" , preop_pin,
+ "-domain_name", self.domain_name,
+ "-admin_user", "admin",
+ "-admin_email", "root@localhost",
+ "-admin_password", self.admin_password,
+ "-agent_name", "ipa-ca-agent",
+ "-agent_key_size", "2048",
+ "-agent_key_type", "rsa",
+ "-agent_cert_subject", "\"CN=ipa-ca-agent,O=" + self.domain_name + "\"",
+ "-ldap_host", self.host_name,
+ "-ldap_port", str(self.ds_port),
+ "-bind_dn", "\"cn=Directory Manager\"",
+ "-bind_password", self.dm_password,
+ "-base_dn", self.basedn,
+ "-db_name", "ipaca",
+ "-key_size", "2048",
+ "-key_type", "rsa",
+ "-save_p12", "true",
+ "-backup_pwd", self.admin_password,
+ "-subsystem_name", self.service_name,
+ "-token_name", "internal",
+ "-ca_subsystem_cert_subject_name", "\"CN=CA Subsystem Certificate,O=" + self.domain_name + "\"",
+ "-ca_ocsp_cert_subject_name", "\"CN=OCSP Signing Certificate,O=" + self.domain_name + "\"",
+ "-ca_server_cert_subject_name", "CN=" + self.host_name + ",O=" + self.domain_name,
+ "-ca_audit_signing_cert_subject_name", "\"CN=CA Audit Signing Certificate,O=" + self.domain_name + "\"",
+ "-ca_sign_cert_subject_name", "\"CN=Certificate Authority,O=" + self.domain_name + "\"" ]
+# if (options.external):
+# pass
+# args.append("-external")
+# args.append("true")
+# args.append("-ext_csr_file")
+# args.append(ext_csr_file)
+# if (options.cacertfile):
+# args.append("-ext_ca_cert_file")
+# args.append(options.cacertfile)
+# if (options.cacertchainfile):
+# args.append("-ext_ca_cert_chain_file")
+# args.append(options.cacertchainfile)
+# else:
+# args.append("-external")
+# args.append("false")
+# if (options.clone):
+# pass
+# args.append("-clone")
+# args.append("true")
+# args.append("-clone_p12_file")
+# args.append(options.clonefile)
+# args.append("-clone_p12_password")
+# args.append(options.clonepasswd)
+# args.append("-clone_uri")
+# args.append(options.cloneURI)
+# args.append("-sd_hostname")
+# args.append(options.sd_hostname)
+# args.append("-sd_ssl_port")
+# args.append(options.sd_ssl_port)
+# args.append("-sd_admin_name")
+# args.append(options.sd_admin_name)
+# args.append("-sd_admin_password")
+# args.append(options.sd_admin_password)
+# else:
+# args.append("-clone")
+# args.append("false")
+
+ # FIXME
+ args.append("-external")
+ args.append("false")
+ args.append("-clone")
+ args.append("false")
+
+ logging.debug(args)
+ ipautil.run(args)
+ logging.debug("completed creating ca instance")
+ except ipautil.CalledProcessError, e:
+ logging.critical("failed to restart ca instance %s" % e)
+ logging.debug("restarting ca instance")
+ try:
+ self.restart()
+ logging.debug("done restarting ca instance")
+ except ipautil.CalledProcessError, e:
+ print "failed to restart ca instance", e
+ logging.debug("failed to restart ca instance %s" % e)
+
+ def __restart_instance(self):
+ try:
+ self.restart()
+ except Exception, e:
+ # TODO: roll back here?
+ logging.critical("Failed to restart the certificate server. See the installation log for details.")
+
+ def __get_agent_cert(self, nickname):
+ args = ["/usr/bin/certutil", "-L", "-d", self.ca_agent_db, "-n", nickname, "-a"]
+ (out, err) = ipautil.run(args)
+ out = out.replace('-----BEGIN CERTIFICATE-----', '')
+ out = out.replace('-----END CERTIFICATE-----', '')
+ return out
+
+ def __issue_ra_cert(self):
+ # The CA certificate is in the agent DB but isn't trusted
+ (admin_fd, admin_name) = tempfile.mkstemp()
+ os.write(admin_fd, self.admin_password)
+ os.close(admin_fd)
+
+ try:
+ self.__run_certutil(
+ ['-M', '-t', 'CT,C,C', '-n',
+ 'Certificate Authority - %s' % self.domain_name
+ ], database=self.ca_agent_db, pwd_file=self.admin_password)
+ finally:
+ os.remove(admin_name)
+
+ # Retrieve the certificate request so we can get the values needed
+ # to issue a certificate.
+ conn = nsslib.NSSConnection(self.host_name,9443,dbdir=self.ca_agent_db)
+ conn.sslsock.set_client_auth_data_callback(client_auth_data_callback, "ipa-ca-agent", self.admin_password, nss.get_default_certdb())
+ conn.set_debuglevel(0)
+ conn.request("GET", "/ca/agent/ca/profileReview?requestId=7")
+ res = conn.getresponse()
+ data = res.read()
+ if res.status != 200:
+ raise RuntimeError("Unable to retrieve certificate request from CA")
+
+ data = data.split('\r\n')
+ params = get_defList(data)
+ params['requestId'] = find_substring(data, "requestId")
+ params['op'] = 'approve'
+ params['submit'] = 'submit'
+ params['requestNotes'] = ''
+ params = urllib.urlencode(params)
+ headers = {"Content-type": "application/x-www-form-urlencoded",
+ "Accept": "text/plain"}
+
+ # Now issue the RA certificate.
+ conn.request("POST", "/ca/agent/ca/profileProcess", params, headers)
+ res = conn.getresponse()
+ data = res.read()
+ conn.close()
+ if res.status != 200:
+ raise RuntimeError("Unable to issue RA certificate")
+
+ data = data.split('\r\n')
+ outputList = get_outputList(data)
+
+ self.ra_cert = outputList['b64_cert']
+ self.ra_cert = self.ra_cert.replace('\\n','')
+ self.ra_cert = self.ra_cert.replace('-----BEGIN CERTIFICATE-----','')
+ self.ra_cert = self.ra_cert.replace('-----END CERTIFICATE-----','')
+
+ # Add the new RA cert to the database in /etc/ipa/ra
+ (agent_fd, agent_name) = tempfile.mkstemp()
+ os.write(agent_fd, self.ra_cert)
+ os.close(agent_fd)
+ try:
+ self.__run_certutil(
+ ['-A', '-t', 'u,u,u', '-n', 'ipaCert', '-a',
+ '-i', agent_name]
+ )
+ finally:
+ os.remove(agent_name)
+
+ def __configure_ra(self):
+ # Create an RA user in the CA LDAP server and add that user to
+ # the appropriate groups so it can issue certificates without
+ # manual intervention.
+ ld = ldap.initialize("ldap://%s:%d" % (self.host_name, self.ds_port))
+ ld.protocol_version=ldap.VERSION3
+ ld.simple_bind_s("cn=Directory Manager", self.dm_password)
+
+ decoded = base64.b64decode(self.ra_cert)
+
+ entry_dn = "uid=%s,ou=People,%s" % ("ipara", self.basedn)
+ entry = [
+ ('objectClass', ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'cmsuser']),
+ ('uid', "ipara"),
+ ('sn', "ipara"),
+ ('cn', "ipara"),
+ ('usertype', "agentType"),
+ ('userstate', "1"),
+ ('userCertificate;binary', decoded),
+ ('description', '2;7;CN=Certificate Authority,O=%s;CN=RA Subsystem Certificate,OU=pki-ipa,O=%s' % (self.domain_name, self.domain_name)),]
+
+ ld.add_s(entry_dn, entry)
+
+ dn = "cn=Certificate Manager Agents,ou=groups,%s" % self.basedn
+ modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
+ ld.modify_s(dn, modlist)
+
+ dn = "cn=Registration Manager Agents,ou=groups,%s" % self.basedn
+ modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
+ ld.modify_s(dn, modlist)
+
+ ld.unbind_s()
+
+ def __run_certutil(self, args, database=None, pwd_file=None,stdin=None):
+ if not database:
+ database = self.ra_agent_db
+ if not pwd_file:
+ pwd_file = self.ra_agent_pwd
+ new_args = ["/usr/bin/certutil", "-d", database, "-f", pwd_file]
+ new_args = new_args + args
+ return ipautil.run(new_args, stdin)
+
+ def __create_ra_agent_db(self):
+ if ipautil.file_exists(self.ra_agent_db + "/cert8.db"):
+ # FIXME, use proper exception
+ raise ValueError("The RA Agent database already exists: %s" % self.ra_agent_db)
+
+ if not ipautil.dir_exists(self.ra_agent_db):
+ os.mkdir(self.ra_agent_db)
+
+ # Create the password file for this db
+ hex_str = binascii.hexlify(os.urandom(10))
+ f = os.open(self.ra_agent_pwd, os.O_CREAT | os.O_RDWR)
+ os.write(f, hex_str)
+ os.close(f)
+
+ stdout, stderr = self.__run_certutil(["-N"])
+
+ def __get_ca_chain(self):
+ conn = httplib.HTTPConnection(self.host_name, 9180)
+ conn.request("GET", "/ca/ee/ca/getCertChain")
+ res = conn.getresponse()
+ if res.status == 200:
+ data = res.read()
+
+ doc = xml.dom.minidom.parseString(data)
+ item_node = doc.getElementsByTagName("ChainBase64")
+ chain = item_node[0].childNodes[0].data
+ doc.unlink()
+ conn.close()
+
+ return chain
+ else:
+ # FIXME: raise proper exception
+ conn.close()
+ raise ValueError("Unable to retrieve CA chain")
+
+ def __create_ca_agent_pkcs12(self):
+ (pwd_fd, pwd_name) = tempfile.mkstemp()
+ os.write(pwd_fd, self.admin_password)
+ os.close(pwd_fd)
+ try:
+ ipautil.run(["/usr/bin/pk12util",
+ "-n", "ipa-ca-agent",
+ "-o", "/root/ca-agent.p12",
+ "-d", self.ca_agent_db,
+ "-k", pwd_name,
+ "-w", pwd_name])
+ finally:
+ os.remove(pwd_name)
+
+ def __import_ca_chain(self):
+ chain = self.__get_ca_chain()
+ (chain_fd, chain_name) = tempfile.mkstemp()
+ os.write(chain_fd, chain)
+ os.close(chain_fd)
+ try:
+ self.__run_certutil(
+ ['-A', '-t', 'CT,C,C', '-n', 'caCert', '-a',
+ '-i', chain_name]
+ )
+ finally:
+ os.remove(chain_name)
+
+ def __request_ra_certificate(self):
+ # Create a noise file for generating our private key
+ noise = array.array('B', os.urandom(128))
+ (noise_fd, noise_name) = tempfile.mkstemp()
+ os.write(noise_fd, noise)
+ os.close(noise_fd)
+
+ # Generate our CSR. The result gets put into stdout
+ try:
+ (stdout, stderr) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem Certificate,OU=pki-ipa,O=%s" % self.domain_name, "-z", noise_name, "-a"])
+ finally:
+ os.remove(noise_name)
+
+ csr = stdout.find("-----BEGIN NEW CERTIFICATE REQUEST-----")
+ if csr >= 0:
+ csr = stdout[csr:]
+
+ # Send the request to the CA
+ conn = httplib.HTTPConnection(self.host_name, 9180)
+ params = urllib.urlencode({'profileId': 'caServerCert',
+ 'cert_request_type': 'pkcs10',
+ 'requestor_name': 'IPA Installer',
+ 'cert_request': csr,
+ 'xmlOutput': 'true'})
+ headers = {"Content-type": "application/x-www-form-urlencoded",
+ "Accept": "text/plain"}
+
+ conn.request("POST", "/ca/ee/ca/profileSubmit", params, headers)
+ res = conn.getresponse()
+ if res.status == 200:
+ data = res.read()
+# print data
+
+ conn.close()
+ else:
+ conn.close()
+ # FIXME: raise proper exception
+ raise ValueError("Unable to submit RA cert request")
+
+ def __fix_ra_perms(self):
+ os.chmod(self.ra_agent_db + "/cert8.db", 0640)
+ os.chmod(self.ra_agent_db + "/key3.db", 0640)
+ os.chmod(self.ra_agent_db + "/secmod.db", 0640)
+
+ pent = pwd.getpwnam("apache")
+ os.chown(self.ra_agent_db + "/cert8.db", 0, pent.pw_gid )
+ os.chown(self.ra_agent_db + "/key3.db", 0, pent.pw_gid )
+ os.chown(self.ra_agent_db + "/secmod.db", 0, pent.pw_gid )
+ os.chown(self.ra_agent_pwd, 0, pent.pw_gid)
+
+ def uninstall(self):
+ try:
+ ipautil.run(["/usr/bin/pkiremove", "-pki_instance_root=/var/lib",
+ "-pki_instance_name=pki-ca", "-force"])
+ except ipautil.CalledProcessError, e:
+ logging.critical("failed to uninstall CA instance %s" % e)
+
+if __name__ == "__main__":
+ installutils.standard_logging_setup("install.log", False)
+ cs = CADSInstance()
+ cs.create_instance("dirsrv", "GREYOAK.COM", "catest.greyoak.com", "greyoak.com", "password")
+ ca = CAInstance()
+ ca.configure_instance("pkiuser", "catest.greyoak.com", "password", "password")
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index e8dfdaedf..d6a386e47 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -248,7 +248,7 @@ class DsInstance(service.Service):
logging.critical("failed to restart ds instance %s" % e)
logging.debug("restarting ds instance")
try:
- self.restart()
+ self.restart(self.serverid)
logging.debug("done restarting ds instance")
except ipautil.CalledProcessError, e:
print "failed to restart ds instance", e
@@ -276,7 +276,7 @@ class DsInstance(service.Service):
def __restart_instance(self):
try:
- self.restart()
+ self.restart(self.serverid)
if not is_ds_running():
logging.critical("Failed to restart the directory server. See the installation log for details.")
sys.exit(1)
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index e091af381..ba539a210 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -22,19 +22,19 @@ from ipapython import sysrestore
from ipapython import ipautil
-def stop(service_name):
- ipautil.run(["/sbin/service", service_name, "stop"])
+def stop(service_name, instance_name=""):
+ ipautil.run(["/sbin/service", service_name, "stop", instance_name])
-def start(service_name):
- ipautil.run(["/sbin/service", service_name, "start"])
+def start(service_name, instance_name=""):
+ ipautil.run(["/sbin/service", service_name, "start", instance_name])
-def restart(service_name):
- ipautil.run(["/sbin/service", service_name, "restart"])
+def restart(service_name, instance_name=""):
+ ipautil.run(["/sbin/service", service_name, "restart", instance_name])
-def is_running(service_name):
+def is_running(service_name, instance_name=""):
ret = True
try:
- ipautil.run(["/sbin/service", service_name, "status"])
+ ipautil.run(["/sbin/service", service_name, "status", instance_name])
except ipautil.CalledProcessError:
ret = False
return ret
@@ -91,14 +91,14 @@ class Service:
def set_output(self, fd):
self.output_fd = fd
- def stop(self):
- stop(self.service_name)
+ def stop(self, instance_name=""):
+ stop(self.service_name, instance_name)
- def start(self):
- start(self.service_name)
+ def start(self, instance_name=""):
+ start(self.service_name, instance_name)
- def restart(self):
- restart(self.service_name)
+ def restart(self, instance_name=""):
+ restart(self.service_name, instance_name)
def is_running(self):
return is_running(self.service_name)
diff --git a/ipaserver/plugins/ra.py b/ipaserver/plugins/ra.py
index a52909f68..d8a70864d 100644
--- a/ipaserver/plugins/ra.py
+++ b/ipaserver/plugins/ra.py
@@ -55,7 +55,10 @@ class ra(Backend):
Request Authority backend plugin.
"""
def __init__(self):
- self.sec_dir = api.env.dot_ipa + os.sep + 'alias'
+ if api.env.home:
+ self.sec_dir = api.env.dot_ipa + os.sep + 'alias'
+ else:
+ self.sec_dir = "/etc/ipa/ra" + os.sep + 'alias'
self.pwd_file = self.sec_dir + os.sep + '.pwd'
self.noise_file = self.sec_dir + os.sep + '.noise'
self.ipa_key_size = "2048"