Implement an installer for the Dogtag certificate system.

The CA is currently not automatically installed. You have to pass in the
--ca flag to install it.

What works:
- installation
- unistallation
- cert/ra plugins can issue and retrieve server certs

What doesn't work:
- self-signed CA is still created and issues Apache and DS certs
- dogtag and python-nss not in rpm requires
- requires that CS be in the "pre" install state from pkicreate
This commit is contained in:
Rob Crittenden 2009-04-01 22:39:44 -04:00
parent a6294ba041
commit 484eff1016
8 changed files with 945 additions and 21 deletions

View File

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

View File

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

150
ipapython/nsslib.py Normal file
View File

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

View File

@ -4,6 +4,7 @@ appdir = $(pythondir)/ipaserver
app_PYTHON = \
__init__.py \
bindinstance.py \
cainstance.py \
dsinstance.py \
ipaldap.py \
krbinstance.py \

View File

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

View File

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

View File

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

View File

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