mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Issue DS and Apache server certs during CA installation.
Notes: - will create a CA instance (pki-ca) if it doesn't exist - maintains support for a self-signed CA - A signing cert is still not created so Firefox autoconfig still won't work
This commit is contained in:
@@ -42,6 +42,7 @@ from ipaserver.install import krbinstance
|
||||
from ipaserver.install import bindinstance
|
||||
from ipaserver.install import httpinstance
|
||||
from ipaserver.install import ntpinstance
|
||||
from ipaserver.install import certs
|
||||
|
||||
from ipaserver.install import service
|
||||
from ipapython import version
|
||||
@@ -512,6 +513,12 @@ def main():
|
||||
print >> sys.stderr, "Import failed: %s" % sys.exc_value
|
||||
sys.exit(1)
|
||||
|
||||
# Clean up any previous self-signed CA that may exist
|
||||
try:
|
||||
os.remove(certs.CA_SERIALNO)
|
||||
except:
|
||||
pass
|
||||
|
||||
cs = cainstance.CADSInstance()
|
||||
cs.create_instance("dirsrv", realm_name, host_name, domain_name, dm_password)
|
||||
ca = cainstance.CAInstance()
|
||||
@@ -526,7 +533,7 @@ def main():
|
||||
finally:
|
||||
os.remove(pw_name)
|
||||
else:
|
||||
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password)
|
||||
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, self_signed_ca=not options.ca)
|
||||
|
||||
# Create a kerberos instance
|
||||
krb = krbinstance.KrbInstance(fstore)
|
||||
@@ -545,7 +552,7 @@ def main():
|
||||
http.create_instance(realm_name, host_name, domain_name, autoconfig=False, pkcs12_info=pkcs12_info)
|
||||
os.remove(pw_name)
|
||||
else:
|
||||
http.create_instance(realm_name, host_name, domain_name, autoconfig=True)
|
||||
http.create_instance(realm_name, host_name, domain_name, autoconfig=True, self_signed_ca=not options.ca)
|
||||
|
||||
# Create the config file
|
||||
fstore.backup_file("/etc/ipa/ipa.conf")
|
||||
|
||||
@@ -34,11 +34,6 @@ 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
|
||||
@@ -309,7 +304,7 @@ class CADSInstance(service.Service):
|
||||
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:
|
||||
except Exception:
|
||||
# TODO: roll back here?
|
||||
logging.critical("Failed to restart the directory server. See the installation log for details.")
|
||||
|
||||
@@ -341,6 +336,18 @@ class CADSInstance(service.Service):
|
||||
|
||||
|
||||
class CAInstance(service.Service):
|
||||
"""
|
||||
In the self-signed case (all done in certs.py) the CA exists in the DS
|
||||
database. When using a dogtag CA the DS database contains just the
|
||||
server cert for DS. The mod_nss database will contain the RA agent
|
||||
cert that will be used to do authenticated requests against dogtag.
|
||||
|
||||
This is done because we use python-nss and will inherit the opened
|
||||
NSS database in mod_python. In nsslib.py we do an nssinit but this will
|
||||
return success if the database is already initialized. It doesn't care
|
||||
if the database is different or not.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
service.Service.__init__(self, "pki-ca")
|
||||
self.pki_user = None
|
||||
@@ -348,10 +355,14 @@ class CAInstance(service.Service):
|
||||
self.admin_password = None
|
||||
self.host_name = None
|
||||
|
||||
# The same database is used for mod_nss because the NSS context
|
||||
# will already have been initialized by Apache by the time
|
||||
# mod_python wants to do things.
|
||||
self.canickname = "CA certificate"
|
||||
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.ra_agent_db = "/etc/httpd/alias"
|
||||
self.ra_agent_pwd = self.ra_agent_db + "/pwdfile.txt"
|
||||
self.ds_port = DEFAULT_DSPORT
|
||||
self.domain_name = "IPA"
|
||||
self.server_root = "/var/lib"
|
||||
@@ -368,6 +379,8 @@ class CAInstance(service.Service):
|
||||
self.admin_password = admin_password
|
||||
self.ds_port = ds_port
|
||||
|
||||
if not ipautil.dir_exists("/var/lib/pki-ca"):
|
||||
self.step("creating pki-ca instance", self.create_instance)
|
||||
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)
|
||||
@@ -382,6 +395,33 @@ class CAInstance(service.Service):
|
||||
|
||||
self.start_creation("Configuring certificate server:")
|
||||
|
||||
def create_instance(self):
|
||||
"""
|
||||
If for some reason the instance doesn't exist, create a new one."
|
||||
|
||||
These values come from /usr/share/pki/ca/setup/postinstall
|
||||
"""
|
||||
PKI_INSTANCE_NAME="pki-ca"
|
||||
AGENT_SECURE_PORT="9443"
|
||||
EE_SECURE_PORT="9444"
|
||||
ADMIN_SECURE_PORT="9445"
|
||||
UNSECURE_PORT="9180"
|
||||
TOMCAT_SERVER_PORT="9701"
|
||||
|
||||
args = ['/usr/bin/pkicreate',
|
||||
'-pki_instance_root', '/var/lib',
|
||||
'-pki_instance_name', PKI_INSTANCE_NAME,
|
||||
'-subsystem_type', 'ca',
|
||||
'-agent_secure_port', AGENT_SECURE_PORT,
|
||||
'-ee_secure_port', EE_SECURE_PORT,
|
||||
'-admin_secure_port', ADMIN_SECURE_PORT,
|
||||
'-unsecure_port', UNSECURE_PORT,
|
||||
'-tomcat_server_port', TOMCAT_SERVER_PORT,
|
||||
'-redirect', 'conf=/etc/pki-ca',
|
||||
'-redirect', 'logs=/var/log/pki-ca',
|
||||
]
|
||||
ipautil.run(args)
|
||||
|
||||
def __enable(self):
|
||||
self.backup_state("enabled", self.is_enabled())
|
||||
self.chkconfig_on()
|
||||
@@ -500,7 +540,7 @@ class CAInstance(service.Service):
|
||||
def __restart_instance(self):
|
||||
try:
|
||||
self.restart()
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
# TODO: roll back here?
|
||||
logging.critical("Failed to restart the certificate server. See the installation log for details.")
|
||||
|
||||
@@ -526,35 +566,43 @@ class CAInstance(service.Service):
|
||||
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")
|
||||
# to issue a certificate. Use sslget here because this is a
|
||||
# temporary database and nsslib doesn't currently support gracefully
|
||||
# opening and closing an NSS database. This would leave the installer
|
||||
# process stuck using this database during the entire cycle. We need
|
||||
# to use the final RA agent database when issuing certs for DS and
|
||||
# mod_nss.
|
||||
args = [
|
||||
'/usr/bin/sslget',
|
||||
'-n', 'ipa-ca-agent',
|
||||
'-p', self.admin_password,
|
||||
'-d', self.ca_agent_db,
|
||||
'-r', '/ca/agent/ca/profileReview?requestId=7',
|
||||
'%s:%d' % (self.host_name, 9443),
|
||||
]
|
||||
(stdout, stderr) = ipautil.run(args)
|
||||
|
||||
data = data.split('\r\n')
|
||||
data = stdout.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")
|
||||
args = [
|
||||
'/usr/bin/sslget',
|
||||
'-n', 'ipa-ca-agent',
|
||||
'-p', self.admin_password,
|
||||
'-d', self.ca_agent_db,
|
||||
'-e', params,
|
||||
'-r', '/ca/agent/ca/profileProcess',
|
||||
'%s:%d' % (self.host_name, 9443),
|
||||
]
|
||||
(stdout, stderr) = ipautil.run(args)
|
||||
|
||||
data = data.split('\r\n')
|
||||
data = stdout.split('\r\n')
|
||||
outputList = get_outputList(data)
|
||||
|
||||
self.ra_cert = outputList['b64_cert']
|
||||
@@ -562,7 +610,7 @@ class CAInstance(service.Service):
|
||||
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
|
||||
# Add the new RA cert to the database in /etc/httpd/alias
|
||||
(agent_fd, agent_name) = tempfile.mkstemp()
|
||||
os.write(agent_fd, self.ra_cert)
|
||||
os.close(agent_fd)
|
||||
@@ -618,8 +666,10 @@ class CAInstance(service.Service):
|
||||
|
||||
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)
|
||||
ipautil.backup_file(self.ra_agent_db + "/cert8.db")
|
||||
ipautil.backup_file(self.ra_agent_db + "/key3.db")
|
||||
ipautil.backup_file(self.ra_agent_db + "/secmod.db")
|
||||
ipautil.backup_file(self.ra_agent_db + "/pwdfile.txt")
|
||||
|
||||
if not ipautil.dir_exists(self.ra_agent_db):
|
||||
os.mkdir(self.ra_agent_db)
|
||||
@@ -647,9 +697,8 @@ class CAInstance(service.Service):
|
||||
|
||||
return chain
|
||||
else:
|
||||
# FIXME: raise proper exception
|
||||
conn.close()
|
||||
raise ValueError("Unable to retrieve CA chain")
|
||||
raise RuntimeError("Unable to retrieve CA chain")
|
||||
|
||||
def __create_ca_agent_pkcs12(self):
|
||||
(pwd_fd, pwd_name) = tempfile.mkstemp()
|
||||
@@ -672,7 +721,7 @@ class CAInstance(service.Service):
|
||||
os.close(chain_fd)
|
||||
try:
|
||||
self.__run_certutil(
|
||||
['-A', '-t', 'CT,C,C', '-n', 'caCert', '-a',
|
||||
['-A', '-t', 'CT,C,C', '-n', self.canickname, '-a',
|
||||
'-i', chain_name]
|
||||
)
|
||||
finally:
|
||||
@@ -709,13 +758,14 @@ class CAInstance(service.Service):
|
||||
res = conn.getresponse()
|
||||
if res.status == 200:
|
||||
data = res.read()
|
||||
# FIXME: pull the requestId out so of the response so it isn't
|
||||
# later hard-coded at 7
|
||||
# print data
|
||||
|
||||
conn.close()
|
||||
else:
|
||||
conn.close()
|
||||
# FIXME: raise proper exception
|
||||
raise ValueError("Unable to submit RA cert request")
|
||||
raise RuntimeError("Unable to submit RA cert request")
|
||||
|
||||
def __fix_ra_perms(self):
|
||||
os.chmod(self.ra_agent_db + "/cert8.db", 0640)
|
||||
@@ -731,7 +781,7 @@ class CAInstance(service.Service):
|
||||
def uninstall(self):
|
||||
try:
|
||||
ipautil.run(["/usr/bin/pkiremove", "-pki_instance_root=/var/lib",
|
||||
"-pki_instance_name=pki-ca", "-force"])
|
||||
"-pki_instance_name=pki-ca", "--force"])
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to uninstall CA instance %s" % e)
|
||||
|
||||
|
||||
@@ -22,15 +22,46 @@ import sha
|
||||
import errno
|
||||
import tempfile
|
||||
import shutil
|
||||
import logging
|
||||
import urllib
|
||||
import xml.dom.minidom
|
||||
|
||||
from ipapython import nsslib
|
||||
from ipapython import sysrestore
|
||||
from ipapython import ipautil
|
||||
|
||||
from nss.error import NSPRError
|
||||
import nss.nss as nss
|
||||
|
||||
CA_SERIALNO="/var/lib/ipa/ca_serialno"
|
||||
|
||||
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
|
||||
|
||||
class CertDB(object):
|
||||
def __init__(self, dir, fstore=None):
|
||||
self.secdir = dir
|
||||
def __init__(self, nssdir, fstore=None, host_name=None):
|
||||
self.secdir = nssdir
|
||||
|
||||
self.noise_fname = self.secdir + "/noise.txt"
|
||||
self.passwd_fname = self.secdir + "/pwdfile.txt"
|
||||
@@ -40,9 +71,16 @@ class CertDB(object):
|
||||
self.cacert_fname = self.secdir + "/cacert.asc"
|
||||
self.pk12_fname = self.secdir + "/cacert.p12"
|
||||
self.pin_fname = self.secdir + "/pin.txt"
|
||||
self.pwd_conf = "/etc/httpd/conf/password.conf"
|
||||
self.reqdir = tempfile.mkdtemp('', 'ipa-', '/var/lib/ipa')
|
||||
self.certreq_fname = self.reqdir + "/tmpcertreq"
|
||||
self.certder_fname = self.reqdir + "/tmpcert.der"
|
||||
self.host_name = host_name
|
||||
|
||||
if ipautil.file_exists(CA_SERIALNO):
|
||||
self.self_signed_ca = True
|
||||
else:
|
||||
self.self_signed_ca = False
|
||||
|
||||
# Making this a starting value that will generate
|
||||
# unique values for the current DB is the
|
||||
@@ -72,6 +110,22 @@ class CertDB(object):
|
||||
def __del__(self):
|
||||
shutil.rmtree(self.reqdir, ignore_errors=True)
|
||||
|
||||
def find_cert_from_txt(self, cert):
|
||||
"""
|
||||
Given a cert blob (str) which may or may not contian leading and
|
||||
trailing text, pull out just the certificate part. This will return
|
||||
the FIRST cert in a stream of data.
|
||||
"""
|
||||
s = cert.find('-----BEGIN CERTIFICATE-----')
|
||||
e = cert.find('-----END CERTIFICATE-----')
|
||||
if e > 0: e = e + 25
|
||||
|
||||
if s < 0 or e < 0:
|
||||
raise RuntimeError("Unable to find certificate")
|
||||
|
||||
cert = cert[s:e]
|
||||
return cert
|
||||
|
||||
def set_serial_from_pkcs12(self):
|
||||
"""A CA cert was loaded from a PKCS#12 file. Set up our serial file"""
|
||||
|
||||
@@ -94,6 +148,7 @@ class CertDB(object):
|
||||
f.close()
|
||||
except IOError, e:
|
||||
if e.errno == errno.ENOENT:
|
||||
self.self_signed_ca = True
|
||||
self.cur_serial = 1000
|
||||
f=open(CA_SERIALNO,"w")
|
||||
f.write(str(self.cur_serial))
|
||||
@@ -131,7 +186,8 @@ class CertDB(object):
|
||||
ipautil.run(new_args, stdin)
|
||||
|
||||
def create_noise_file(self):
|
||||
ipautil.backup_file(self.noise_fname)
|
||||
if ipautil.file_exists(self.noise_fname):
|
||||
os.remove(self.noise_fname)
|
||||
f = open(self.noise_fname, "w")
|
||||
f.write(self.gen_password())
|
||||
self.set_perms(self.noise_fname)
|
||||
@@ -193,6 +249,14 @@ class CertDB(object):
|
||||
"-a",
|
||||
"-i", cacert_fname])
|
||||
|
||||
def get_cert_from_db(self, nickname):
|
||||
try:
|
||||
args = ["-L", "-n", nickname, "-a"]
|
||||
(cert, err) = self.run_certutil(args)
|
||||
return cert
|
||||
except ipautil.CalledProcessError:
|
||||
return ''
|
||||
|
||||
def find_cacert_serial(self):
|
||||
(out,err) = self.run_certutil(["-L", "-n", self.cacert_name])
|
||||
data = out.split('\n')
|
||||
@@ -203,11 +267,20 @@ class CertDB(object):
|
||||
|
||||
raise RuntimeError("Unable to find serial number")
|
||||
|
||||
def create_server_cert(self, nickname, name, other_certdb=None):
|
||||
def create_server_cert(self, nickname, subject, other_certdb=None):
|
||||
"""
|
||||
other_certdb can mean one of two things, depending on the context.
|
||||
|
||||
If we are using a self-signed CA then other_certdb contains the
|
||||
CA that will be signing our CSR.
|
||||
|
||||
If we are using a dogtag CA then it contains the RA agent key
|
||||
that will issue our cert.
|
||||
"""
|
||||
cdb = other_certdb
|
||||
if not cdb:
|
||||
cdb = self
|
||||
self.request_cert(name)
|
||||
(out, err) = self.request_cert(subject)
|
||||
cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
|
||||
self.add_cert(self.certder_fname, nickname)
|
||||
os.unlink(self.certreq_fname)
|
||||
@@ -223,41 +296,103 @@ class CertDB(object):
|
||||
os.unlink(self.certreq_fname)
|
||||
os.unlink(self.certder_fname)
|
||||
|
||||
def request_cert(self, name):
|
||||
self.run_certutil(["-R", "-s", name,
|
||||
"-o", self.certreq_fname,
|
||||
"-g", self.keysize,
|
||||
"-z", self.noise_fname,
|
||||
"-f", self.passwd_fname])
|
||||
def request_cert(self, subject, certtype="rsa", keysize="2048"):
|
||||
self.create_noise_file()
|
||||
args = ["-R", "-s", subject,
|
||||
"-o", self.certreq_fname,
|
||||
"-k", certtype,
|
||||
"-g", keysize,
|
||||
"-z", self.noise_fname,
|
||||
"-f", self.passwd_fname]
|
||||
if not self.self_signed_ca:
|
||||
args.append("-a")
|
||||
(stdout, stderr) = self.run_certutil(args)
|
||||
os.remove(self.noise_fname)
|
||||
|
||||
return (stdout, stderr)
|
||||
|
||||
def issue_server_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)
|
||||
if self.self_signed_ca:
|
||||
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)
|
||||
# 2 - Key encipherment
|
||||
# 9 - done
|
||||
# n - not critical
|
||||
#
|
||||
# -5 (Create netscape cert type extension)
|
||||
# 1 - SSL Server
|
||||
# 9 - done
|
||||
# n - not critical
|
||||
p.stdin.write("2\n9\nn\n1\n9\nn\n")
|
||||
p.wait()
|
||||
# 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)
|
||||
# 2 - Key encipherment
|
||||
# 9 - done
|
||||
# n - not critical
|
||||
#
|
||||
# -5 (Create netscape cert type extension)
|
||||
# 1 - SSL Server
|
||||
# 9 - done
|
||||
# n - not critical
|
||||
p.stdin.write("2\n9\nn\n1\n9\nn\n")
|
||||
p.wait()
|
||||
else:
|
||||
if self.host_name is None:
|
||||
raise RuntimeError("CA Host is not set.")
|
||||
|
||||
f = open(certreq_fname, "r")
|
||||
csr = f.readlines()
|
||||
f.close()
|
||||
csr = "".join(csr)
|
||||
|
||||
# We just want the CSR bits, make sure there is nothing else
|
||||
s = csr.find("-----BEGIN NEW CERTIFICATE REQUEST-----")
|
||||
e = csr.find("-----END NEW CERTIFICATE REQUEST-----")
|
||||
if e > 0:
|
||||
e = e + 37
|
||||
if s >= 0:
|
||||
csr = csr[s:]
|
||||
|
||||
params = urllib.urlencode({'profileId': 'caRAserverCert',
|
||||
'cert_request_type': 'pkcs10',
|
||||
'requestor_name': 'IPA Installer',
|
||||
'cert_request': csr,
|
||||
'xmlOutput': 'true'})
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded",
|
||||
"Accept": "text/plain"}
|
||||
|
||||
# Send the CSR request to the CA
|
||||
f = open(self.passwd_fname)
|
||||
password = f.readline()
|
||||
f.close()
|
||||
conn = nsslib.NSSConnection(self.host_name, 9444, dbdir=self.secdir)
|
||||
conn.sslsock.set_client_auth_data_callback(client_auth_data_callback, "ipaCert", password, nss.get_default_certdb())
|
||||
conn.set_debuglevel(0)
|
||||
|
||||
conn.request("POST", "/ca/ee/ca/profileSubmit", params, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
conn.close()
|
||||
if res.status != 200:
|
||||
raise RuntimeError("Unable to submit cert request")
|
||||
|
||||
# The result is an XML blob. Pull the certificate out of that
|
||||
doc = xml.dom.minidom.parseString(data)
|
||||
item_node = doc.getElementsByTagName("b64")
|
||||
cert = item_node[0].childNodes[0].data
|
||||
doc.unlink()
|
||||
|
||||
# Write the certificate to a file. It will be imported in a later
|
||||
# step.
|
||||
f = open(cert_fname, "w")
|
||||
f.write(cert)
|
||||
f.close()
|
||||
|
||||
return
|
||||
|
||||
def issue_signing_cert(self, certreq_fname, cert_fname):
|
||||
p = subprocess.Popen(["/usr/bin/certutil",
|
||||
@@ -290,12 +425,18 @@ class CertDB(object):
|
||||
p.wait()
|
||||
|
||||
def add_cert(self, cert_fname, nickname):
|
||||
self.run_certutil(["-A", "-n", nickname,
|
||||
"-t", "u,u,u",
|
||||
"-i", cert_fname,
|
||||
"-f", cert_fname])
|
||||
args = ["-A", "-n", nickname,
|
||||
"-t", "u,u,u",
|
||||
"-i", cert_fname,
|
||||
"-f", cert_fname]
|
||||
if not self.self_signed_ca:
|
||||
args.append("-a")
|
||||
self.run_certutil(args)
|
||||
|
||||
def create_pin_file(self):
|
||||
"""
|
||||
This is the format of Directory Server pin files.
|
||||
"""
|
||||
ipautil.backup_file(self.pin_fname)
|
||||
f = open(self.pin_fname, "w")
|
||||
f.write("Internal (Software) Token:")
|
||||
@@ -304,6 +445,17 @@ class CertDB(object):
|
||||
f.close()
|
||||
self.set_perms(self.pin_fname)
|
||||
|
||||
def create_password_conf(self):
|
||||
"""
|
||||
This is the format of mod_nss pin files.
|
||||
"""
|
||||
ipautil.backup_file(self.pwd_conf)
|
||||
f = open(self.pwd_conf, "w")
|
||||
f.write("internal:")
|
||||
pwd = open(self.passwd_fname)
|
||||
f.write(pwd.read())
|
||||
f.close()
|
||||
|
||||
def find_root_cert(self, nickname):
|
||||
p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir,
|
||||
"-O", "-n", nickname], stdout=subprocess.PIPE)
|
||||
@@ -375,7 +527,24 @@ class CertDB(object):
|
||||
self.create_pin_file()
|
||||
|
||||
def create_from_cacert(self, cacert_fname, passwd=""):
|
||||
self.create_noise_file()
|
||||
if ipautil.file_exists(self.certdb_fname):
|
||||
# We already have a cert db, see if it is for the same CA.
|
||||
# If it is we leave things as they are.
|
||||
f = open(cacert_fname, "r")
|
||||
newca = f.readlines()
|
||||
f.close()
|
||||
newca = "".join(newca)
|
||||
newca = self.find_cert_from_txt(newca)
|
||||
|
||||
cacert = self.get_cert_from_db(self.cacert_name)
|
||||
if cacert != '':
|
||||
cacert = self.find_cert_from_txt(cacert)
|
||||
|
||||
if newca == cacert:
|
||||
return
|
||||
|
||||
# The CA certificates are different or something went wrong. Start with
|
||||
# a new certificate database.
|
||||
self.create_passwd_file(passwd)
|
||||
self.create_certdbs()
|
||||
self.load_cacert(cacert_fname)
|
||||
@@ -403,6 +572,7 @@ class CertDB(object):
|
||||
self.trust_root_cert(nickname)
|
||||
self.create_pin_file()
|
||||
self.export_ca_cert(self.cacert_name, False)
|
||||
self.self_signed_ca=False
|
||||
|
||||
# This file implies that we have our own self-signed CA. Ensure
|
||||
# that it no longer exists (from previous installs, for example).
|
||||
|
||||
@@ -154,7 +154,7 @@ class DsInstance(service.Service):
|
||||
else:
|
||||
self.suffix = None
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None):
|
||||
def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None, self_signed_ca=False):
|
||||
self.ds_user = ds_user
|
||||
self.realm_name = realm_name.upper()
|
||||
self.serverid = realm_to_serverid(self.realm_name)
|
||||
@@ -163,6 +163,7 @@ class DsInstance(service.Service):
|
||||
self.dm_password = dm_password
|
||||
self.domain = domain_name
|
||||
self.pkcs12_info = pkcs12_info
|
||||
self.self_signed_ca = self_signed_ca
|
||||
self.__setup_sub_dict()
|
||||
|
||||
self.step("creating directory server user", self.__create_ds_user)
|
||||
@@ -341,19 +342,26 @@ class DsInstance(service.Service):
|
||||
|
||||
def __enable_ssl(self):
|
||||
dirname = config_dirname(self.serverid)
|
||||
ca = certs.CertDB(dirname)
|
||||
dsdb = certs.CertDB(dirname)
|
||||
if self.pkcs12_info:
|
||||
ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1])
|
||||
server_certs = ca.find_server_certs()
|
||||
dsdb.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1])
|
||||
server_certs = dsdb.find_server_certs()
|
||||
if len(server_certs) == 0:
|
||||
raise RuntimeError("Could not find a suitable server cert in import in %s" % pkcs12_info[0])
|
||||
|
||||
# We only handle one server cert
|
||||
nickname = server_certs[0][0]
|
||||
else:
|
||||
ca.create_self_signed()
|
||||
ca.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name)
|
||||
nickname = "Server-Cert"
|
||||
if self.self_signed_ca:
|
||||
dsdb.create_self_signed()
|
||||
dsdb.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name)
|
||||
else:
|
||||
cadb = certs.CertDB("/etc/httpd/alias", host_name=self.host_name)
|
||||
cadb.export_ca_cert(cadb.cacert_name, False)
|
||||
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
|
||||
dsdb.create_server_cert("Server-Cert", "CN=%s,OU=pki-ipa,O=IPA" % self.host_name, cadb)
|
||||
dsdb.create_pin_file()
|
||||
|
||||
conn = ipaldap.IPAdmin("127.0.0.1")
|
||||
conn.simple_bind_s("cn=directory manager", self.dm_password)
|
||||
|
||||
@@ -59,21 +59,25 @@ class HTTPInstance(service.Service):
|
||||
else:
|
||||
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
|
||||
|
||||
def create_instance(self, realm, fqdn, domain_name, autoconfig=True, pkcs12_info=None):
|
||||
def create_instance(self, realm, fqdn, domain_name, autoconfig=True, pkcs12_info=None, self_signed_ca=False):
|
||||
self.fqdn = fqdn
|
||||
self.realm = realm
|
||||
self.domain = domain_name
|
||||
self.pkcs12_info = pkcs12_info
|
||||
self.self_signed_ca = self_signed_ca
|
||||
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
|
||||
|
||||
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
|
||||
self.step("Setting mod_nss port to 443", self.__set_mod_nss_port)
|
||||
if not self_signed_ca:
|
||||
self.step("Setting mod_nss password file", self.__set_mod_nss_passwordfile)
|
||||
self.step("Adding URL rewriting rules", self.__add_include)
|
||||
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)
|
||||
if autoconfig:
|
||||
self.step("Setting up browser autoconfig", self.__setup_autoconfig)
|
||||
self.step("publish CA cert", self.__publish_ca_cert)
|
||||
self.step("configuring SELinux for httpd", self.__selinux_config)
|
||||
self.step("restarting httpd", self.__start)
|
||||
self.step("configuring httpd to start on boot", self.__enable)
|
||||
@@ -148,17 +152,23 @@ class HTTPInstance(service.Service):
|
||||
def __set_mod_nss_nickname(self, nickname):
|
||||
installutils.set_directive(NSS_CONF, 'NSSNickname', nickname)
|
||||
|
||||
def __set_mod_nss_passwordfile(self):
|
||||
installutils.set_directive(NSS_CONF, 'NSSPassPhraseDialog', 'file:/etc/httpd/conf/password.conf')
|
||||
|
||||
def __add_include(self):
|
||||
"""This should run after __set_mod_nss_port so is already backed up"""
|
||||
if installutils.update_file(NSS_CONF, '</VirtualHost>', 'Include conf.d/ipa-rewrite.conf\n</VirtualHost>') != 0:
|
||||
print "Adding Include conf.d/ipa-rewrite to %s failed." % NSS_CONF
|
||||
|
||||
def __setup_ssl(self):
|
||||
ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm)))
|
||||
ca = certs.CertDB(NSS_DIR)
|
||||
if self.self_signed_ca:
|
||||
ca_db = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm)))
|
||||
else:
|
||||
ca_db = certs.CertDB(NSS_DIR, host_name=self.fqdn)
|
||||
db = certs.CertDB(NSS_DIR)
|
||||
if self.pkcs12_info:
|
||||
ca.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], passwd="")
|
||||
server_certs = ca.find_server_certs()
|
||||
db.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], passwd="")
|
||||
server_certs = db.find_server_certs()
|
||||
if len(server_certs) == 0:
|
||||
raise RuntimeError("Could not find a suitable server cert in import in %s" % pkcs12_info[0])
|
||||
|
||||
@@ -167,9 +177,13 @@ class HTTPInstance(service.Service):
|
||||
|
||||
self.__set_mod_nss_nickname(nickname)
|
||||
else:
|
||||
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)
|
||||
if self.self_signed_ca:
|
||||
db.create_from_cacert(ca_db.cacert_fname)
|
||||
db.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ca_db)
|
||||
db.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ca_db)
|
||||
else:
|
||||
db.create_server_cert("Server-Cert", "CN=%s,OU=ipa-pki,O=IPA" % self.fqdn, ca_db)
|
||||
db.create_password_conf()
|
||||
|
||||
# Fix the database permissions
|
||||
os.chmod(NSS_DIR + "/cert8.db", 0640)
|
||||
@@ -182,27 +196,38 @@ class HTTPInstance(service.Service):
|
||||
os.chown(NSS_DIR + "/secmod.db", 0, pent.pw_gid )
|
||||
|
||||
def __setup_autoconfig(self):
|
||||
# FIXME. Need to issue the self-signed cert from the CA as well.
|
||||
# A special profile is needed from the CS team to do this.
|
||||
if not self.self_signed_ca:
|
||||
return
|
||||
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(dsinstance.realm_to_serverid(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)
|
||||
if self.self_signed_ca:
|
||||
ca_db = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm)))
|
||||
else:
|
||||
ca_db = certs.CertDB(NSS_DIR)
|
||||
db = certs.CertDB(NSS_DIR)
|
||||
|
||||
tmpdir = tempfile.mkdtemp(prefix = "tmp-")
|
||||
shutil.copy("/usr/share/ipa/html/preferences.html", tmpdir)
|
||||
ca.run_signtool(["-k", "Signing-Cert",
|
||||
db.run_signtool(["-k", "Signing-Cert",
|
||||
"-Z", "/usr/share/ipa/html/configure.jar",
|
||||
"-e", ".html",
|
||||
tmpdir])
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
def __publish_ca_cert(self):
|
||||
if self.self_signed_ca:
|
||||
ca_db = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm)))
|
||||
else:
|
||||
ca_db = certs.CertDB(NSS_DIR)
|
||||
shutil.copy(ca_db.cacert_fname, "/usr/share/ipa/html/ca.crt")
|
||||
os.chmod("/usr/share/ipa/html/ca.crt", 0444)
|
||||
|
||||
def uninstall(self):
|
||||
running = self.restore_state("running")
|
||||
enabled = self.restore_state("enabled")
|
||||
|
||||
Reference in New Issue
Block a user