mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-09-03 20:52:56 -05:00
User-defined certificate subjects
Let the user, upon installation, set the certificate subject base for the dogtag CA. Certificate requests will automatically be given this subject base, regardless of what is in the CSR. The selfsign plugin does not currently support this dynamic name re-assignment and will reject any incoming requests that don't conform to the subject base. The certificate subject base is stored in cn=ipaconfig but it does NOT dynamically update the configuration, for dogtag at least. The file /var/lib/pki-ca/profiles/ca/caIPAserviceCert.cfg would need to be updated and pki-cad restarted.
This commit is contained in:
@@ -46,7 +46,6 @@ from ipapython import nsslib
|
||||
|
||||
from ipaserver.install import service
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver import ipaldap
|
||||
from ipaserver.install import dsinstance
|
||||
from ipalib import util
|
||||
|
||||
@@ -414,7 +413,8 @@ class CAInstance(service.Service):
|
||||
def configure_instance(self, pki_user, host_name, dm_password,
|
||||
admin_password, ds_port=DEFAULT_DSPORT,
|
||||
pkcs12_info=None, master_host=None, csr_file=None,
|
||||
cert_file=None, cert_chain_file=None):
|
||||
cert_file=None, cert_chain_file=None,
|
||||
subject_base="O=IPA"):
|
||||
"""Create a CA instance. This may involve creating the pki-ca instance
|
||||
dogtag instance.
|
||||
|
||||
@@ -434,6 +434,7 @@ class CAInstance(service.Service):
|
||||
if self.pkcs12_info is not None:
|
||||
self.clone = True
|
||||
self.master_host = master_host
|
||||
self.subject_base = subject_base
|
||||
|
||||
# Determine if we are installing as an externally-signed CA and
|
||||
# what stage we're in.
|
||||
@@ -540,7 +541,7 @@ class CAInstance(service.Service):
|
||||
"-agent_name", "ipa-ca-agent",
|
||||
"-agent_key_size", "2048",
|
||||
"-agent_key_type", "rsa",
|
||||
"-agent_cert_subject", "\"CN=ipa-ca-agent,O=" + self.domain_name + "\"",
|
||||
"-agent_cert_subject", "\"CN=ipa-ca-agent,%s\"" % self.subject_base,
|
||||
"-ldap_host", self.host_name,
|
||||
"-ldap_port", str(self.ds_port),
|
||||
"-bind_dn", "\"cn=Directory Manager\"",
|
||||
@@ -553,11 +554,11 @@ class CAInstance(service.Service):
|
||||
"-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 + "\"" ]
|
||||
"-ca_subsystem_cert_subject_name", "\"CN=CA Subsystem,%s\"" % self.subject_base,
|
||||
"-ca_ocsp_cert_subject_name", "\"CN=OCSP Subsystem,%s\"" % self.subject_base,
|
||||
"-ca_server_cert_subject_name", "\"CN=%s,%s\"" % (self.host_name, self.subject_base),
|
||||
"-ca_audit_signing_cert_subject_name", "\"CN=CA Audit,%s\"" % self.subject_base,
|
||||
"-ca_sign_cert_subject_name", "\"CN=Certificate Authority,%s\"" % self.subject_base ]
|
||||
if self.external == 1:
|
||||
args.append("-external")
|
||||
args.append("true")
|
||||
@@ -770,7 +771,7 @@ class CAInstance(service.Service):
|
||||
('usertype', "agentType"),
|
||||
('userstate', "1"),
|
||||
('userCertificate', decoded),
|
||||
('description', '2;%s;CN=Certificate Authority,O=%s;CN=RA Subsystem Certificate,OU=pki-ipa,O=%s' % (str(self.requestId), self.domain_name, self.domain_name)),]
|
||||
('description', '2;%s;CN=Certificate Authority,%s;CN=RA Subsystem,%s' % (str(self.requestId), self.subject_base, self.subject_base)),]
|
||||
|
||||
ld.add_s(entry_dn, entry)
|
||||
|
||||
@@ -886,7 +887,7 @@ class CAInstance(service.Service):
|
||||
|
||||
# Generate our CSR. The result gets put into stdout
|
||||
try:
|
||||
(stdout, stderr, returncode) = 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"])
|
||||
(stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem,%s" % self.subject_base, "-z", noise_name, "-a"])
|
||||
finally:
|
||||
os.remove(noise_name)
|
||||
|
||||
@@ -1000,6 +1001,13 @@ class CAInstance(service.Service):
|
||||
|
||||
ipautil.run(["/usr/sbin/semodule", "-i", "/usr/share/selinux/targeted/ipa_dogtag.pp"])
|
||||
|
||||
def set_subject_in_config(self, suffix):
|
||||
# dogtag ships with an IPA-specific profile that forces a subject
|
||||
# format. We need to update that template with our base subject
|
||||
if installutils.update_file("/var/lib/%s/profiles/ca/caIPAserviceCert.cfg" % PKI_INSTANCE_NAME, 'OU=pki-ipa, O=IPA', self.subject_base):
|
||||
print "Updating subject_base in CA template failed"
|
||||
self.__restart_instance()
|
||||
|
||||
def uninstall(self):
|
||||
try:
|
||||
ipautil.run(["/usr/bin/pkiremove", "-pki_instance_root=/var/lib",
|
||||
|
||||
@@ -173,7 +173,7 @@ def next_replica(serial_file=CA_SERIALNO):
|
||||
return str(serial)
|
||||
|
||||
class CertDB(object):
|
||||
def __init__(self, nssdir, fstore=None, host_name=None):
|
||||
def __init__(self, nssdir, fstore=None, host_name=None, subject_base=None):
|
||||
self.secdir = nssdir
|
||||
|
||||
self.noise_fname = self.secdir + "/noise.txt"
|
||||
@@ -189,13 +189,14 @@ class CertDB(object):
|
||||
self.certreq_fname = None
|
||||
self.certder_fname = None
|
||||
self.host_name = host_name
|
||||
self.cwd = os.getcwd()
|
||||
|
||||
self.self_signed_ca = ipa_self_signed()
|
||||
|
||||
if self.self_signed_ca:
|
||||
self.subject_format = "CN=%s,ou=test-ipa,O=IPA"
|
||||
if subject_base:
|
||||
self.subject_format = "CN=%%s,%s" % subject_base
|
||||
else:
|
||||
self.subject_format = "CN=%s,OU=pki-ipa,O=IPA"
|
||||
self.subject_format = "CN=%s,O=IPA"
|
||||
|
||||
self.cacert_name = "CA certificate"
|
||||
self.valid_months = "120"
|
||||
@@ -218,6 +219,10 @@ class CertDB(object):
|
||||
def __del__(self):
|
||||
if self.reqdir is not None:
|
||||
shutil.rmtree(self.reqdir, ignore_errors=True)
|
||||
try:
|
||||
os.chdir(self.cwd)
|
||||
except:
|
||||
pass
|
||||
|
||||
def setup_cert_request(self):
|
||||
"""
|
||||
@@ -234,6 +239,10 @@ class CertDB(object):
|
||||
self.certreq_fname = self.reqdir + "/tmpcertreq"
|
||||
self.certder_fname = self.reqdir + "/tmpcert.der"
|
||||
|
||||
# When certutil makes a request it creates a file in the cwd, make
|
||||
# sure we are in a unique place when this happens
|
||||
os.chdir(self.reqdir)
|
||||
|
||||
def set_serial_from_pkcs12(self):
|
||||
"""A CA cert was loaded from a PKCS#12 file. Set up our serial file"""
|
||||
|
||||
@@ -584,6 +593,9 @@ class CertDB(object):
|
||||
doc.unlink()
|
||||
conn.close()
|
||||
|
||||
# base64-decode the result
|
||||
cert = base64.b64decode(cert)
|
||||
|
||||
# Write the certificate to a file. It will be imported in a later
|
||||
# step.
|
||||
f = open(cert_fname, "w")
|
||||
@@ -670,6 +682,9 @@ class CertDB(object):
|
||||
doc.unlink()
|
||||
conn.close()
|
||||
|
||||
# base64-decode the cert
|
||||
cert = base64.b64decode(cert)
|
||||
|
||||
f = open(cert_fname, "w")
|
||||
f.write(cert)
|
||||
f.close()
|
||||
@@ -684,8 +699,6 @@ class CertDB(object):
|
||||
"-t", "u,u,u",
|
||||
"-i", cert_fname,
|
||||
"-f", self.passwd_fname]
|
||||
if not self.self_signed_ca:
|
||||
args.append("-a")
|
||||
self.run_certutil(args)
|
||||
|
||||
def create_pin_file(self):
|
||||
|
||||
@@ -153,7 +153,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, self_signed_ca=False, uidstart=1100, gidstart=1100):
|
||||
def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None, self_signed_ca=False, uidstart=1100, gidstart=1100, subject_base=None):
|
||||
self.ds_user = ds_user
|
||||
self.realm_name = realm_name.upper()
|
||||
self.serverid = realm_to_serverid(self.realm_name)
|
||||
@@ -166,6 +166,7 @@ class DsInstance(service.Service):
|
||||
self.uidstart = uidstart
|
||||
self.gidstart = gidstart
|
||||
self.principal = "ldap/%s@%s" % (self.host_name, self.realm_name)
|
||||
self.subject_base = subject_base
|
||||
self.__setup_sub_dict()
|
||||
|
||||
self.step("creating directory server user", self.__create_ds_user)
|
||||
@@ -328,7 +329,7 @@ class DsInstance(service.Service):
|
||||
|
||||
def __enable_ssl(self):
|
||||
dirname = config_dirname(self.serverid)
|
||||
dsdb = certs.CertDB(dirname)
|
||||
dsdb = certs.CertDB(dirname, subject_base=self.subject_base)
|
||||
if self.pkcs12_info:
|
||||
dsdb.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1])
|
||||
server_certs = dsdb.find_server_certs()
|
||||
@@ -340,7 +341,7 @@ class DsInstance(service.Service):
|
||||
self.dercert = dsdb.get_cert_from_db(nickname)
|
||||
else:
|
||||
nickname = "Server-Cert"
|
||||
cadb = certs.CertDB(httpinstance.NSS_DIR, host_name=self.host_name)
|
||||
cadb = certs.CertDB(httpinstance.NSS_DIR, host_name=self.host_name, subject_base=self.subject_base)
|
||||
if self.self_signed_ca:
|
||||
cadb.create_self_signed()
|
||||
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
|
||||
@@ -466,7 +467,7 @@ class DsInstance(service.Service):
|
||||
self.stop()
|
||||
|
||||
dirname = config_dirname(realm_to_serverid(self.realm_name))
|
||||
certdb = certs.CertDB(dirname)
|
||||
certdb = certs.CertDB(dirname, subject_base=self.subject_base)
|
||||
if not cacert_name or len(cacert_name) == 0:
|
||||
cacert_name = "Imported CA"
|
||||
# we can't pass in the nickname, so we set the instance variable
|
||||
|
||||
@@ -56,7 +56,7 @@ class HTTPInstance(service.Service):
|
||||
else:
|
||||
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
|
||||
|
||||
def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False):
|
||||
def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False, subject_base=None):
|
||||
self.fqdn = fqdn
|
||||
self.realm = realm
|
||||
self.domain = domain_name
|
||||
@@ -66,6 +66,7 @@ class HTTPInstance(service.Service):
|
||||
self.self_signed_ca = self_signed_ca
|
||||
self.principal = "HTTP/%s@%s" % (self.fqdn, self.realm)
|
||||
self.dercert = None
|
||||
self.subject_base = subject_base
|
||||
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
|
||||
|
||||
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
|
||||
@@ -164,10 +165,10 @@ class HTTPInstance(service.Service):
|
||||
|
||||
def __setup_ssl(self):
|
||||
if self.self_signed_ca:
|
||||
ca_db = certs.CertDB(NSS_DIR)
|
||||
ca_db = certs.CertDB(NSS_DIR, subject_base=self.subject_base)
|
||||
else:
|
||||
ca_db = certs.CertDB(NSS_DIR, host_name=self.fqdn)
|
||||
db = certs.CertDB(NSS_DIR)
|
||||
ca_db = certs.CertDB(NSS_DIR, host_name=self.fqdn, subject_base=self.subject_base)
|
||||
db = certs.CertDB(NSS_DIR, subject_base=self.subject_base)
|
||||
if self.pkcs12_info:
|
||||
db.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], passwd="")
|
||||
server_certs = db.find_server_certs()
|
||||
@@ -221,7 +222,7 @@ class HTTPInstance(service.Service):
|
||||
prefs_fd.close()
|
||||
|
||||
# The signing cert is generated in __setup_ssl
|
||||
db = certs.CertDB(NSS_DIR)
|
||||
db = certs.CertDB(NSS_DIR, subject_base=self.subject_base)
|
||||
|
||||
pwdfile = open(db.passwd_fname)
|
||||
pwd = pwdfile.read()
|
||||
|
||||
@@ -160,9 +160,15 @@ class Service:
|
||||
Add a certificate to a service
|
||||
|
||||
This should be passed in DER format but we'll be nice and convert
|
||||
a base64-encoded cert if needed.
|
||||
a base64-encoded cert if needed (like when we add certs that come
|
||||
from PKCS#12 files.)
|
||||
"""
|
||||
try:
|
||||
s = self.dercert.find('-----BEGIN CERTIFICATE-----')
|
||||
if s > -1:
|
||||
e = self.dercert.find('-----END CERTIFICATE-----')
|
||||
s = s + 27
|
||||
self.dercert = self.dercert[s:e]
|
||||
self.dercert = base64.b64decode(self.dercert)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -38,13 +38,18 @@ if api.env.ra_plugin != 'selfsign':
|
||||
from ipalib import Backend
|
||||
from ipalib import errors
|
||||
from ipalib import x509
|
||||
from ipalib import pkcs10
|
||||
import subprocess
|
||||
import os
|
||||
import re
|
||||
from ipaserver.plugins import rabase
|
||||
from ipaserver.install import certs
|
||||
import tempfile
|
||||
from pyasn1 import error
|
||||
from ipalib.request import ugettext as _
|
||||
from pyasn1.codec.der import encoder
|
||||
import base64
|
||||
from ipalib.plugins.cert import get_csr_hostname
|
||||
|
||||
class ra(rabase.rabase):
|
||||
"""
|
||||
@@ -79,6 +84,28 @@ class ra(rabase.rabase):
|
||||
.. [2] Base64 encoded
|
||||
|
||||
"""
|
||||
try:
|
||||
config = api.Command['config_show']()['result']
|
||||
subject_base = config.get('ipacertificatesubjectbase')[0]
|
||||
hostname = get_csr_hostname(csr)
|
||||
request = pkcs10.load_certificate_request(csr)
|
||||
base = re.split(',\s*(?=\w+=)', subject_base)
|
||||
base.reverse()
|
||||
base.append("CN=%s" % hostname)
|
||||
request_subject = request.get_subject().get_components()
|
||||
new_request = []
|
||||
for r in request_subject:
|
||||
new_request.append("%s=%s" % (r[0], r[1]))
|
||||
|
||||
if str(base).lower() != str(new_request).lower():
|
||||
subject_base='CN=%s, %s' % (hostname, subject_base)
|
||||
new_request.reverse()
|
||||
raise errors.CertificateOperationError(error=_('Request subject \'%s\' does not match the form \'%s\'' % (", ".join(new_request), subject_base)))
|
||||
except errors.CertificateOperationError, e:
|
||||
raise e
|
||||
except Exception, e:
|
||||
raise errors.CertificateOperationError(error=_('unable to decode csr: %s' % e))
|
||||
|
||||
# certutil wants the CSR to have have a header and footer. Add one
|
||||
# if it isn't there.
|
||||
s = csr.find('-----BEGIN NEW CERTIFICATE REQUEST-----')
|
||||
@@ -86,7 +113,7 @@ class ra(rabase.rabase):
|
||||
s = csr.find('-----BEGIN CERTIFICATE REQUEST-----')
|
||||
if s == -1:
|
||||
csr = '-----BEGIN NEW CERTIFICATE REQUEST-----\n' + csr + \
|
||||
'-----END NEW CERTIFICATE REQUEST-----\n'
|
||||
'\n-----END NEW CERTIFICATE REQUEST-----\n'
|
||||
|
||||
try:
|
||||
(csr_fd, csr_name) = tempfile.mkstemp()
|
||||
|
||||
Reference in New Issue
Block a user