Imported Upstream version 4.3.1

This commit is contained in:
Mario Fetka
2021-08-10 02:37:58 +02:00
parent a791de49a2
commit 2f177da8f2
2056 changed files with 421730 additions and 1668138 deletions

View File

@@ -17,40 +17,38 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import
import logging
import os
import stat
import sys
import tempfile
import shutil
import xml.dom.minidom
import grp
import pwd
import base64
from hashlib import sha1
import fcntl
import time
import datetime
import six
from six.moves import configparser
from ipalib.install import certmonger, sysrestore
from ipapython.ipa_log_manager import root_logger
from ipapython import dogtag
from ipapython import sysrestore
from ipapython import ipautil
from ipapython.certdb import EMPTY_TRUST_FLAGS, IPA_CA_TRUST_FLAGS
from ipapython import certmonger
from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase
from ipapython.dn import DN
from ipalib import x509, api
from ipalib import pkcs10, x509, api
from ipalib.errors import CertificateOperationError
from ipalib.install import certstore
from ipalib.util import strip_csr_header
from ipalib.text import _
from ipaplatform import services
from ipaplatform.constants import constants
from ipaplatform.paths import paths
logger = logging.getLogger(__name__)
# Apache needs access to this database so we need to create it
# where apache can reach
NSS_DIR = paths.HTTPD_ALIAS_DIR
def get_cert_nickname(cert):
@@ -63,184 +61,75 @@ def get_cert_nickname(cert):
representation of the first RDN in the subject and subject_dn
is a DN object.
"""
dn = DN(cert.subject)
nsscert = x509.load_certificate(cert)
subject = str(nsscert.subject)
dn = DN(subject)
return (str(dn[0]), dn)
def install_pem_from_p12(p12_fname, p12_passwd, pem_fname):
pwd = ipautil.write_tmp_file(p12_passwd)
ipautil.run([paths.OPENSSL, "pkcs12", "-nokeys", "-clcerts",
"-in", p12_fname, "-out", pem_fname,
"-passin", "file:" + pwd.name])
def install_key_from_p12(
p12_fname, p12_passwd, pem_fname, out_passwd_fname=None):
pwd = ipautil.write_tmp_file(p12_passwd)
args = [
paths.OPENSSL, "pkcs12", "-nocerts",
"-in", p12_fname, "-out", pem_fname,
"-passin", "file:" + pwd.name]
if out_passwd_fname is not None:
args.extend(['-passout', 'file:{}'.format(out_passwd_fname)])
else:
args.append('-nodes')
ipautil.run(args, umask=0o077)
def export_pem_p12(pkcs12_fname, pkcs12_pwd_fname, nickname, pem_fname):
ipautil.run([paths.OPENSSL, "pkcs12",
"-export", "-name", nickname,
"-in", pem_fname, "-out", pkcs12_fname,
"-passout", "file:" + pkcs12_pwd_fname])
def pkcs12_to_certkeys(p12_fname, p12_passwd=None):
"""
Deserializes pkcs12 file to python objects
:param p12_fname: A PKCS#12 filename
:param p12_passwd: Optional password for the pkcs12_fname file
"""
args = [paths.OPENSSL, "pkcs12", "-in", p12_fname, "-nodes"]
if p12_passwd:
pwd = ipautil.write_tmp_file(p12_passwd)
args.extend(["-passin", "file:{fname}".format(fname=pwd.name)])
else:
args.extend(["-passin", "pass:"])
pems = ipautil.run(args, capture_output=True).raw_output
certs = x509.load_certificate_list(pems)
priv_keys = x509.load_private_key_list(pems)
return (certs, priv_keys)
def is_ipa_issued_cert(api, cert):
"""
Return True if the certificate has been issued by IPA
Note that this method can only be executed if the api has been
initialized.
:param api: The pre-initialized IPA API
:param cert: The IPACertificate certificiate to test
"""
cacert_subject = certstore.get_ca_subject(
api.Backend.ldap2,
api.env.container_ca,
api.env.basedn)
return DN(cert.issuer) == cacert_subject
class CertDB(object):
"""An IPA-server-specific wrapper around NSS
This class knows IPA-specific details such as nssdir location, or the
CA cert name.
``subject_base``
Realm subject base DN. This argument is required when creating
server or object signing certs.
``ca_subject``
IPA CA subject DN. This argument is required when importing
CA certificates into the certificate database.
"""
# TODO: Remove all selfsign code
def __init__(self, realm, nssdir, fstore=None,
host_name=None, subject_base=None, ca_subject=None,
user=None, group=None, mode=None, create=False,
dbtype='auto'):
self.nssdb = NSSDatabase(nssdir, dbtype=dbtype)
def __init__(self, realm, nssdir=NSS_DIR, fstore=None, host_name=None, subject_base=None):
self.nssdb = NSSDatabase(nssdir)
self.secdir = nssdir
self.realm = realm
self.noise_fname = os.path.join(self.secdir, "noise.txt")
self.pk12_fname = os.path.join(self.secdir, "cacert.p12")
self.pin_fname = os.path.join(self.secdir + "pin.txt")
self.noise_fname = self.secdir + "/noise.txt"
self.passwd_fname = self.secdir + "/pwdfile.txt"
self.certdb_fname = self.secdir + "/cert8.db"
self.keydb_fname = self.secdir + "/key3.db"
self.secmod_fname = self.secdir + "/secmod.db"
self.cacert_fname = self.secdir + "/cacert.asc"
self.pk12_fname = self.secdir + "/cacert.p12"
self.pin_fname = self.secdir + "/pin.txt"
self.pwd_conf = paths.HTTPD_PASSWORD_CONF
self.reqdir = None
self.certreq_fname = None
self.certder_fname = None
self.host_name = host_name
self.ca_subject = ca_subject
self.subject_base = subject_base
try:
self.cwd = os.getcwd()
except OSError as e:
raise RuntimeError("Unable to determine the current directory: %s" % str(e))
if not subject_base:
self.subject_base = DN(('O', 'IPA'))
self.cacert_name = get_ca_nickname(self.realm)
self.valid_months = "120"
self.keysize = "1024"
self.user = user
self.group = group
self.mode = mode
self.uid = 0
self.gid = 0
if not create:
if os.path.isdir(self.secdir):
# We are going to set the owner of all of the cert
# files to the owner of the containing directory
# instead of that of the process. This works when
# this is called by root for a daemon that runs as
# a normal user
mode = os.stat(self.secdir)
self.uid = mode[stat.ST_UID]
self.gid = mode[stat.ST_GID]
else:
if user is not None:
pu = pwd.getpwnam(user)
self.uid = pu.pw_uid
self.gid = pu.pw_gid
if group is not None:
self.gid = grp.getgrnam(group).gr_gid
self.create_certdbs()
# We are going to set the owner of all of the cert
# files to the owner of the containing directory
# instead of that of the process. This works when
# this is called by root for a daemon that runs as
# a normal user
mode = os.stat(self.secdir)
self.uid = mode[stat.ST_UID]
self.gid = mode[stat.ST_GID]
if fstore:
self.fstore = fstore
else:
self.fstore = sysrestore.FileStore(paths.SYSRESTORE)
ca_subject = ipautil.dn_attribute_property('_ca_subject')
subject_base = ipautil.dn_attribute_property('_subject_base')
# migration changes paths, just forward attribute lookup to nssdb
@property
def secdir(self):
return self.nssdb.secdir
@property
def dbtype(self):
return self.nssdb.dbtype
@property
def certdb_fname(self):
return self.nssdb.certdb
@property
def keydb_fname(self):
return self.nssdb.keydb
@property
def secmod_fname(self):
return self.nssdb.secmod
@property
def passwd_fname(self):
return self.nssdb.pwd_file
def exists(self):
"""
Checks whether all NSS database files + our pwd_file exist
"""
return self.nssdb.exists()
def __del__(self):
if self.reqdir is not None:
shutil.rmtree(self.reqdir, ignore_errors=True)
self.reqdir = None
self.nssdb.close()
try:
os.chdir(self.cwd)
except:
pass
def setup_cert_request(self):
"""
@@ -257,46 +146,59 @@ class CertDB(object):
self.certreq_fname = self.reqdir + "/tmpcertreq"
self.certder_fname = self.reqdir + "/tmpcert.der"
def set_perms(self, fname, write=False):
# 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_perms(self, fname, write=False, uid=None):
if uid:
pent = pwd.getpwnam(uid)
os.chown(fname, pent.pw_uid, pent.pw_gid)
else:
os.chown(fname, self.uid, self.gid)
perms = stat.S_IRUSR
if write:
perms |= stat.S_IWUSR
if hasattr(fname, 'fileno'):
os.fchown(fname.fileno(), self.uid, self.gid)
os.fchmod(fname.fileno(), perms)
else:
os.chown(fname, self.uid, self.gid)
os.chmod(fname, perms)
os.chmod(fname, perms)
def gen_password(self):
return sha1(ipautil.ipa_generate_password()).hexdigest()
def run_certutil(self, args, stdin=None, **kwargs):
return self.nssdb.run_certutil(args, stdin, **kwargs)
def run_signtool(self, args, stdin=None):
with open(self.passwd_fname, "r") as f:
password = f.readline()
new_args = [paths.SIGNTOOL, "-d", self.secdir, "-p", password]
new_args = new_args + args
ipautil.run(new_args, stdin)
def create_noise_file(self):
if os.path.isfile(self.noise_fname):
if ipautil.file_exists(self.noise_fname):
os.remove(self.noise_fname)
with open(self.noise_fname, "w") as f:
self.set_perms(f)
f.write(ipautil.ipa_generate_password())
f = open(self.noise_fname, "w")
f.write(self.gen_password())
self.set_perms(self.noise_fname)
def create_passwd_file(self, passwd=None):
ipautil.backup_file(self.passwd_fname)
with open(self.passwd_fname, "w") as f:
self.set_perms(f)
if passwd is not None:
f.write("%s\n" % passwd)
else:
f.write(ipautil.ipa_generate_password())
f = open(self.passwd_fname, "w")
if passwd is not None:
f.write("%s\n" % passwd)
else:
f.write(self.gen_password())
f.close()
self.set_perms(self.passwd_fname)
def create_certdbs(self):
self.nssdb.create_db(
user=self.user, group=self.group, mode=self.mode,
backup=True
)
ipautil.backup_file(self.certdb_fname)
ipautil.backup_file(self.keydb_fname)
ipautil.backup_file(self.secmod_fname)
self.nssdb.create_db(self.passwd_fname)
self.set_perms(self.passwd_fname, write=True)
def restore(self):
self.nssdb.restore()
def list_certs(self):
"""
Return a tuple of tuples containing (nickname, trust)
@@ -324,25 +226,23 @@ class CertDB(object):
of the CA or not. If we are running on a replica then we won't
have the private key to make a PKCS#12 file so we don't need to
do that step."""
cacert_fname = paths.IPA_CA_CRT
# export the CA cert for use with other apps
ipautil.backup_file(cacert_fname)
ipautil.backup_file(self.cacert_fname)
root_nicknames = self.find_root_cert(nickname)[:-1]
with open(cacert_fname, "w") as f:
os.fchmod(f.fileno(), stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
for root in root_nicknames:
result = self.run_certutil(["-L", "-n", root, "-a"],
capture_output=True)
f.write(result.output)
fd = open(self.cacert_fname, "w")
for root in root_nicknames:
result = self.run_certutil(["-L", "-n", root, "-a"],
capture_output=True)
fd.write(result.output)
fd.close()
os.chmod(self.cacert_fname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
if create_pkcs12:
ipautil.backup_file(self.pk12_fname)
self.nssdb.run_pk12util([
"-o", self.pk12_fname,
"-n", self.cacert_name,
"-k", self.passwd_fname,
"-w", self.passwd_fname,
])
ipautil.run([paths.PK12UTIL, "-d", self.secdir,
"-o", self.pk12_fname,
"-n", self.cacert_name,
"-w", self.passwd_fname,
"-k", self.passwd_fname])
self.set_perms(self.pk12_fname)
def load_cacert(self, cacert_fname, trust_flags):
@@ -350,48 +250,63 @@ class CertDB(object):
Load all the certificates from a given file. It is assumed that
this file creates CA certificates.
"""
with open(cacert_fname) as f:
certs = f.read()
fd = open(cacert_fname)
certs = fd.read()
fd.close()
ca_dn = DN(('CN','Certificate Authority'), self.subject_base)
st = 0
while True:
try:
(cert, st) = find_cert_from_txt(certs, st)
_rdn, subject_dn = get_cert_nickname(cert)
if subject_dn == self.ca_subject:
(rdn, subject_dn) = get_cert_nickname(cert)
if subject_dn == ca_dn:
nick = get_ca_nickname(self.realm)
else:
nick = str(subject_dn)
self.nssdb.add_cert(cert, nick, trust_flags)
self.nssdb.add_cert(cert, nick, trust_flags, pem=True)
except RuntimeError:
break
def get_cert_from_db(self, nickname):
def get_cert_from_db(self, nickname, pem=True):
"""
Retrieve a certificate from the current NSS database for nickname.
pem controls whether the value returned PEM or DER-encoded. The
default is the data straight from certutil -a.
"""
try:
args = ["-L", "-n", nickname, "-a"]
result = self.run_certutil(args, capture_output=True)
return x509.load_pem_x509_certificate(result.raw_output)
cert = result.output
if pem:
return cert
else:
(cert, start) = find_cert_from_txt(cert, start=0)
cert = x509.strip_header(cert)
dercert = base64.b64decode(cert)
return dercert
except ipautil.CalledProcessError:
return None
return ''
def track_server_cert(self, nickname, principal, password_file=None, command=None):
"""
Tell certmonger to track the given certificate nickname.
If command is not a full path then it is prefixed with
/usr/lib[64]/ipa/certmonger.
"""
if command is not None and not os.path.isabs(command):
command = paths.CERTMONGER_COMMAND_TEMPLATE % (command)
try:
request_id = certmonger.start_tracking(
self.secdir, nickname=nickname, pinfile=password_file,
post_command=command)
request_id = certmonger.start_tracking(nickname, self.secdir, password_file, command)
except RuntimeError as e:
logger.error("certmonger failed starting to track certificate: %s",
str(e))
root_logger.error("certmonger failed starting to track certificate: %s" % str(e))
return
cert = self.get_cert_from_db(nickname)
subject = str(DN(cert.subject))
nsscert = x509.load_certificate(cert, dbdir=self.secdir)
subject = str(nsscert.subject)
certmonger.add_principal(request_id, principal)
certmonger.add_subject(request_id, subject)
@@ -402,10 +317,9 @@ class CertDB(object):
try:
certmonger.stop_tracking(self.secdir, nickname=nickname)
except RuntimeError as e:
logger.error("certmonger failed to stop tracking certificate: %s",
str(e))
root_logger.error("certmonger failed to stop tracking certificate: %s" % str(e))
def create_server_cert(self, nickname, hostname, subject=None):
def create_server_cert(self, nickname, hostname, other_certdb=None, subject=None):
"""
If we are using a dogtag CA then other_certdb contains the RA agent key
that will issue our cert.
@@ -414,26 +328,36 @@ class CertDB(object):
Returns a certificate in DER format.
"""
cdb = other_certdb
if not cdb:
cdb = self
if subject is None:
subject=DN(('CN', hostname), self.subject_base)
self.request_cert(subject, san_dnsnames=[hostname])
try:
self.issue_server_cert(self.certreq_fname, self.certder_fname)
self.import_cert(self.certder_fname, nickname)
self.request_cert(subject)
cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
self.import_cert(self.certder_fname, nickname)
fd = open(self.certder_fname, "r")
dercert = fd.read()
fd.close()
with open(self.certder_fname, "rb") as f:
dercert = f.read()
return x509.load_der_x509_certificate(dercert)
finally:
for fname in (self.certreq_fname, self.certder_fname):
try:
os.unlink(fname)
except OSError:
pass
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
def request_cert(
self, subject, certtype="rsa", keysize="2048",
san_dnsnames=None):
return dercert
def create_signing_cert(self, nickname, hostname, other_certdb=None, subject=None):
cdb = other_certdb
if not cdb:
cdb = self
if subject is None:
subject=DN(('CN', hostname), self.subject_base)
self.request_cert(subject)
cdb.issue_signing_cert(self.certreq_fname, self.certder_fname)
self.import_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
def request_cert(self, subject, certtype="rsa", keysize="2048"):
assert isinstance(subject, DN)
self.create_noise_file()
self.setup_cert_request()
@@ -444,8 +368,6 @@ class CertDB(object):
"-z", self.noise_fname,
"-f", self.passwd_fname,
"-a"]
if san_dnsnames is not None and len(san_dnsnames) > 0:
args += ['-8', ','.join(san_dnsnames)]
result = self.run_certutil(args,
capture_output=True, capture_error=True)
os.remove(self.noise_fname)
@@ -457,11 +379,13 @@ class CertDB(object):
if self.host_name is None:
raise RuntimeError("CA Host is not set.")
with open(certreq_fname, "rb") as f:
csr = f.read()
f = open(certreq_fname, "r")
csr = f.readlines()
f.close()
csr = "".join(csr)
# We just want the CSR bits, make sure there is no thing else
csr = strip_csr_header(csr).decode('utf8')
# We just want the CSR bits, make sure there is nothing else
csr = pkcs10.strip_header(csr)
params = {'profileId': dogtag.DEFAULT_PROFILE,
'cert_request_type': 'pkcs10',
@@ -470,15 +394,13 @@ class CertDB(object):
'xmlOutput': 'true'}
# Send the request to the CA
f = open(self.passwd_fname, "r")
password = f.readline()
f.close()
result = dogtag.https_request(
self.host_name, 8443,
url="/ca/ee/ca/profileSubmitSSLClient",
cafile=api.env.tls_ca_cert,
client_certfile=paths.RA_AGENT_PEM,
client_keyfile=paths.RA_AGENT_KEY,
**params)
http_status, _http_headers, http_body = result
logger.debug("CA answer: %r", http_body)
self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
self.secdir, password, "ipaCert", **params)
http_status, http_headers, http_body = result
if http_status != 200:
raise CertificateOperationError(
@@ -500,11 +422,58 @@ class CertDB(object):
# Write the certificate to a file. It will be imported in a later
# step. This file will be read later to be imported.
with open(cert_fname, "wb") as f:
f.write(cert)
f = open(cert_fname, "w")
f.write(cert)
f.close()
def add_cert(self, cert, nick, flags):
self.nssdb.add_cert(cert, nick, flags)
def issue_signing_cert(self, certreq_fname, cert_fname):
self.setup_cert_request()
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 no thing else
csr = pkcs10.strip_header(csr)
params = {'profileId': 'caJarSigningCert',
'cert_request_type': 'pkcs10',
'requestor_name': 'IPA Installer',
'cert_request': csr,
'xmlOutput': 'true'}
# Send the request to the CA
f = open(self.passwd_fname, "r")
password = f.readline()
f.close()
result = dogtag.https_request(
self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
self.secdir, password, "ipaCert", **params)
http_status, http_headers, http_body = result
if http_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(http_body)
item_node = doc.getElementsByTagName("b64")
cert = item_node[0].childNodes[0].data
doc.unlink()
# base64-decode the cert for uniformity
cert = base64.b64decode(cert)
# Write the certificate to a file. It will be imported in a later
# step. This file will be read later to be imported.
f = open(cert_fname, "w")
f.write(cert)
f.close()
def add_cert(self, cert, nick, flags, pem=False):
self.nssdb.add_cert(cert, nick, flags, pem)
def import_cert(self, cert_fname, nickname):
"""
@@ -524,11 +493,26 @@ class CertDB(object):
This is the format of Directory Server pin files.
"""
ipautil.backup_file(self.pin_fname)
with open(self.pin_fname, "w") as pinfile:
self.set_perms(pinfile)
pinfile.write("Internal (Software) Token:")
with open(self.passwd_fname) as pwdfile:
pinfile.write(pwdfile.read())
f = open(self.pin_fname, "w")
f.write("Internal (Software) Token:")
pwdfile = open(self.passwd_fname)
f.write(pwdfile.read())
f.close()
pwdfile.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:")
pwdfile = open(self.passwd_fname)
f.write(pwdfile.read())
f.close()
pwdfile.close()
self.set_perms(self.pwd_conf, uid=constants.HTTPD_USER)
def find_root_cert(self, nickname):
"""
@@ -539,10 +523,9 @@ class CertDB(object):
return root_nicknames
def trust_root_cert(self, root_nickname, trust_flags):
def trust_root_cert(self, root_nickname, trust_flags=None):
if root_nickname is None:
logger.debug("Unable to identify root certificate to trust. "
"Continuing but things are likely to fail.")
root_logger.debug("Unable to identify root certificate to trust. Continuing but things are likely to fail.")
return
try:
@@ -554,56 +537,57 @@ class CertDB(object):
return self.nssdb.find_server_certs()
def import_pkcs12(self, pkcs12_fname, pkcs12_passwd=None):
return self.nssdb.import_pkcs12(pkcs12_fname,
return self.nssdb.import_pkcs12(pkcs12_fname, self.passwd_fname,
pkcs12_passwd=pkcs12_passwd)
def export_pkcs12(self, pkcs12_fname, pkcs12_pwd_fname, nickname=None):
if nickname is None:
nickname = get_ca_nickname(api.env.realm)
self.nssdb.run_pk12util([
"-o", pkcs12_fname,
"-n", nickname,
"-k", self.passwd_fname,
"-w", pkcs12_pwd_fname
])
ipautil.run([paths.PK12UTIL, "-d", self.secdir,
"-o", pkcs12_fname,
"-n", nickname,
"-k", self.passwd_fname,
"-w", pkcs12_pwd_fname])
def create_from_cacert(self):
"""
Ensure that a CA chain is in the NSS database.
def export_pem_p12(self, pkcs12_fname, pkcs12_pwd_fname,
nickname, pem_fname):
ipautil.run([paths.OPENSSL, "pkcs12",
"-export", "-name", nickname,
"-in", pem_fname, "-out", pkcs12_fname,
"-passout", "file:" + pkcs12_pwd_fname])
If an NSS database already exists ensure that the CA chain
we want to load is in there and if not add it. If there is no
database then create an NSS database and load the CA chain.
"""
cacert_fname = paths.IPA_CA_CRT
if self.nssdb.exists():
def create_from_cacert(self, cacert_fname, passwd=None):
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.
with open(cacert_fname, "r") as f:
newca = f.read()
newca, _st = find_cert_from_txt(newca)
f = open(cacert_fname, "r")
newca = f.readlines()
f.close()
newca = "".join(newca)
(newca, st) = find_cert_from_txt(newca)
cacert = self.get_cert_from_db(self.cacert_name)
if cacert != '':
(cacert, st) = 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()
self.create_passwd_file(passwd)
self.create_certdbs()
self.load_cacert(cacert_fname, IPA_CA_TRUST_FLAGS)
self.load_cacert(cacert_fname, 'CT,C,C')
def create_from_pkcs12(self, pkcs12_fname, pkcs12_passwd,
ca_file, trust_flags):
def create_from_pkcs12(self, pkcs12_fname, pkcs12_passwd, passwd=None,
ca_file=None, trust_flags=None):
"""Create a new NSS database using the certificates in a PKCS#12 file.
pkcs12_fname: the filename of the PKCS#12 file
pkcs12_pwd_fname: the file containing the pin for the PKCS#12 file
nickname: the nickname/friendly-name of the cert we are loading
passwd: The password to use for the new NSS database we are creating
The global CA may be added as well in case it wasn't included in the
PKCS#12 file. Extra certs won't hurt in any case.
@@ -611,16 +595,8 @@ class CertDB(object):
The global CA may be specified in ca_file, as a PEM filename.
"""
self.create_noise_file()
self.create_passwd_file()
self.create_passwd_file(passwd)
self.create_certdbs()
self.init_from_pkcs12(
pkcs12_fname,
pkcs12_passwd,
ca_file=ca_file,
trust_flags=trust_flags)
def init_from_pkcs12(self, pkcs12_fname, pkcs12_passwd,
ca_file, trust_flags):
self.import_pkcs12(pkcs12_fname, pkcs12_passwd)
server_certs = self.find_server_certs()
if len(server_certs) == 0:
@@ -640,7 +616,7 @@ class CertDB(object):
cert, st = find_cert_from_txt(certs, st)
except RuntimeError:
break
self.add_cert(cert, 'CA %s' % num, EMPTY_TRUST_FLAGS)
self.add_cert(cert, 'CA %s' % num, ',,', pem=True)
num += 1
# We only handle one server cert
@@ -653,62 +629,33 @@ class CertDB(object):
self.cacert_name = ca_names[-1]
self.trust_root_cert(self.cacert_name, trust_flags)
self.create_pin_file()
self.export_ca_cert(nickname, False)
def install_pem_from_p12(self, p12_fname, p12_passwd, pem_fname):
pwd = ipautil.write_tmp_file(p12_passwd)
ipautil.run([paths.OPENSSL, "pkcs12", "-nodes",
"-in", p12_fname, "-out", pem_fname,
"-passin", "file:" + pwd.name])
def publish_ca_cert(self, location):
shutil.copy(self.cacert_fname, location)
os.chmod(location, 0o444)
def export_pem_cert(self, nickname, location):
return self.nssdb.export_pem_cert(nickname, location)
def request_service_cert(self, nickname, principal, host,
resubmit_timeout=None):
if resubmit_timeout is None:
resubmit_timeout = api.env.replication_wait_timeout
return certmonger.request_and_wait_for_cert(
certpath=self.secdir,
storage='NSSDB',
nickname=nickname,
principal=principal,
subject=host,
passwd_fname=self.passwd_fname,
resubmit_timeout=resubmit_timeout
)
def is_ipa_issued_cert(self, api, nickname):
"""
Return True if the certificate contained in the CertDB with the
provided nickname has been issued by IPA.
Note that this method can only be executed if the api has been
initialized.
This method needs to compare the cert issuer (from the NSS DB
and the subject from the CA (from LDAP), because nicknames are not
always aligned.
The cert can be issued directly by IPA. In this case, the cert
issuer is IPA CA subject.
"""
cert = self.get_cert_from_db(nickname)
if cert is None:
raise RuntimeError("Could not find the cert %s in %s"
% (nickname, self.secdir))
return is_ipa_issued_cert(api, cert)
def needs_upgrade_format(self):
"""Check if NSSDB file format needs upgrade
Only upgrade if it's an existing dbm database and default
database type is no 'dbm'.
"""
return (
self.nssdb.dbtype == 'dbm' and
self.exists()
)
def upgrade_format(self):
"""Upgrade NSSDB to new file format
"""
self.nssdb.convert_db()
def request_service_cert(self, nickname, principal, host, pwdconf=False):
self.create_from_cacert(paths.IPA_CA_CRT)
if pwdconf:
self.create_password_conf()
reqid = certmonger.request_cert(nssdb=self.secdir,
nickname=nickname,
principal=principal,
subject=host,
passwd_fname=self.passwd_fname)
# Now wait for the cert to appear. Check three times then abort
certmonger.wait_for_request(reqid, timeout=15)
class _CrossProcessLock(object):
@@ -775,10 +722,7 @@ class _CrossProcessLock(object):
def _read(self, fileobj):
p = configparser.RawConfigParser()
if six.PY2:
p.readfp(fileobj) # pylint: disable=deprecated-method
else:
p.read_file(fileobj) # pylint: disable=no-member
p.readfp(fileobj)
try:
self._locked = p.getboolean('lock', 'locked')