Move Custodia secrets handler to scripts

Implement the import and export handlers for Custodia keys as external
scripts. It's a prerequisite to drop DAC override permission and proper
SELinux rules for ipa-custodia.

Except for DMLDAP,  handlers no longer run as root but as handler
specific users with reduced privileges. The Dogtag-related handlers run
as pkiuser, which also help with HSM support.

The export and import handles are designed to be executed by sudo, too.
In the future, ipa-custodia could be executed as an unprivileged process
that runs the minimal helper scripts with higher privileges.

Fixes: https://pagure.io/freeipa/issue/6888
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
Christian Heimes
2019-01-30 14:08:38 +01:00
parent d2c5ce1a82
commit beffa7bcda
18 changed files with 641 additions and 184 deletions

4
.gitignore vendored
View File

@@ -132,6 +132,10 @@ daemons/dnssec/ipa-dnskeysync-replica
daemons/dnssec/ipa-ods-exporter
install/certmonger/dogtag-ipa-ca-renew-agent-submit
install/certmonger/ipa-server-guard
install/custodia/ipa-custodia-dmldap
install/custodia/ipa-custodia-pki-tomcat
install/custodia/ipa-custodia-pki-tomcat-wrapped
install/custodia/ipa-custodia-ra-agent
install/oddjob/com.redhat.idm.trust-fetch-domains
install/oddjob/etc/oddjobd.conf.d/ipa-server.conf
install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf

View File

@@ -538,6 +538,7 @@ AC_CONFIG_FILES([
init/Makefile
install/Makefile
install/certmonger/Makefile
install/custodia/Makefile
install/html/Makefile
install/migration/Makefile
install/share/Makefile

View File

@@ -1003,6 +1003,10 @@ fi
%{_sbindir}/ipa-crlgen-manage
%{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
%{_libexecdir}/certmonger/ipa-server-guard
%{_libexecdir}/ipa/custodia/ipa-custodia-dmldap
%{_libexecdir}/ipa/custodia/ipa-custodia-pki-tomcat
%{_libexecdir}/ipa/custodia/ipa-custodia-pki-tomcat-wrapped
%{_libexecdir}/ipa/custodia/ipa-custodia-ra-agent
%dir %{_libexecdir}/ipa
%{_libexecdir}/ipa/ipa-custodia
%{_libexecdir}/ipa/ipa-custodia-check

View File

@@ -6,6 +6,7 @@ NULL =
SUBDIRS = \
certmonger \
custodia \
html \
migration \
share \

View File

@@ -0,0 +1,22 @@
NULL =
appdir = $(libexecdir)/ipa/custodia/
nodist_app_SCRIPTS = \
ipa-custodia-dmldap \
ipa-custodia-pki-tomcat \
ipa-custodia-pki-tomcat-wrapped \
ipa-custodia-ra-agent \
$(NULL)
dist_noinst_DATA = \
ipa-custodia-dmldap.in \
ipa-custodia-pki-tomcat.in \
ipa-custodia-pki-tomcat-wrapped.in \
ipa-custodia-ra-agent.in \
$(NULL)
PYTHON_SHEBANG = $(nodist_app_SCRIPTS)
CLEANFILES = $(PYTHON_SHEBANG)
include $(top_srcdir)/Makefile.pythonscripts.am

View File

@@ -0,0 +1,8 @@
@PYTHONSHEBANG@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
from ipaserver.secrets.handlers.dmldap import main
main()

View File

@@ -0,0 +1,8 @@
@PYTHONSHEBANG@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
from ipaserver.secrets.handlers.nsswrappedcert import main, pki_tomcat_parser
main(pki_tomcat_parser())

View File

@@ -0,0 +1,8 @@
@PYTHONSHEBANG@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
from ipaserver.secrets.handlers.nsscert import main, pki_tomcat_parser
main(pki_tomcat_parser())

View File

@@ -0,0 +1,8 @@
@PYTHONSHEBANG@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
from ipaserver.secrets.handlers.pemfile import main, ra_agent_parser
main(ra_agent_parser())

View File

@@ -362,6 +362,7 @@ class BasePathNamespace:
IPA_CUSTODIA_KEYS = '/etc/ipa/custodia/server.keys'
IPA_CUSTODIA_SOCKET = '/run/httpd/ipa-custodia.sock'
IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log'
IPA_CUSTODIA_HANDLER = "/usr/libexec/ipa/custodia"
IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab'
EXTERNAL_SCHEMA_DIR = '/usr/share/ipa/schema.d'
GSSPROXY_CONF = '/etc/gssproxy/10-ipa.conf'

View File

@@ -0,0 +1,2 @@
"""Export / import handlers
"""

View File

@@ -0,0 +1,75 @@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
"""Common helpers for handlers
"""
import argparse
import base64
import json
import shutil
import tempfile
def default_json(obj):
"""JSON encoder default handler
"""
if isinstance(obj, (bytes, bytearray)):
return base64.b64encode(obj).decode('ascii')
raise TypeError(
"Object of type {} is not JSON serializable".format(type(obj))
)
def json_dump(data, exportfile):
"""Dump JSON to file
"""
json.dump(
data,
exportfile,
default=default_json,
separators=(',', ':'),
sort_keys=True
)
def mkparser(supports_import=True, **kwargs):
"""Create default parser for handler with export / import args
All commands support export to file or stdout. Most commands can also
import from a file or stdin. Export and import are mutually exclusive
options.
"""
parser = argparse.ArgumentParser(**kwargs)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--export',
help='JSON export file ("-" for stdout)',
dest='exportfile',
type=argparse.FileType('w')
)
if supports_import:
group.add_argument(
'--import',
help='JSON import file ("-" for stdin)',
dest='importfile',
type=argparse.FileType('r')
)
return parser
def main(parser, export_func, import_func=None, **kwargs):
"""Common main function for handlers
"""
args = parser.parse_args()
if args.exportfile is not None:
func = export_func
else:
func = import_func
tmpdir = tempfile.mkdtemp()
try:
func(args, tmpdir, **kwargs)
finally:
shutil.rmtree(tmpdir)

View File

@@ -0,0 +1,63 @@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
"""Export / import Directory Manager password hash
"""
import json
import os
from ipalib import api
from ipalib import errors
from ipapython.dn import DN
from ipapython.ipaldap import LDAPClient
from ipaserver.install.installutils import realm_to_ldapi_uri
from . import common
CN_CONFIG = DN(('cn', 'config'))
ROOTPW = 'nsslapd-rootpw'
def export_key(args, tmpdir, conn):
entry = conn.get_entry(CN_CONFIG, [ROOTPW])
data = {
'dmhash': entry.single_value[ROOTPW],
}
common.json_dump(data, args.exportfile)
def import_key(args, tmpdir, conn):
data = json.load(args.importfile)
dmhash = data['dmhash'].encode('ascii')
entry = conn.get_entry(CN_CONFIG, [ROOTPW])
entry.single_value[ROOTPW] = dmhash
try:
conn.update_entry(entry)
except errors.EmptyModlist:
pass
def main():
parser = common.mkparser(
description='ipa-custodia LDAP DM hash handler'
)
if os.getegid() != 0:
parser.error("Must be run as root user.\n")
# create LDAP connection using LDAPI and EXTERNAL bind as root
if not api.isdone('bootstrap'):
api.bootstrap()
realm = api.env.realm
ldap_uri = realm_to_ldapi_uri(realm)
conn = LDAPClient(ldap_uri=ldap_uri, no_schema=True)
try:
conn.external_bind()
except Exception as e:
parser.error("Failed to connect to {}: {}\n".format(ldap_uri, e))
with conn:
common.main(parser, export_key, import_key, conn=conn)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,122 @@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
"""Export / import cert and key from NSS DB as PKCS#12 data
"""
import base64
import json
import os
from ipaplatform.paths import paths
from ipapython import ipautil
from ipapython.certdb import NSSDatabase
from . import common
def export_key(args, tmpdir):
"""Export key and certificate from the NSS DB to a PKCS#12 file.
The PKCS#12 file is encrypted with a password.
"""
pk12file = os.path.join(tmpdir, 'export.p12')
password = ipautil.ipa_generate_password()
pk12pk12pwfile = os.path.join(tmpdir, 'passwd')
with open(pk12pk12pwfile, 'w') as f:
f.write(password)
nssdb = NSSDatabase(args.nssdb_path)
nssdb.run_pk12util([
"-o", pk12file,
"-n", args.nickname,
"-k", args.nssdb_pwdfile,
"-w", pk12pk12pwfile,
])
with open(pk12file, 'rb') as f:
p12data = f.read()
data = {
'export password': password,
'pkcs12 data': p12data,
}
common.json_dump(data, args.exportfile)
def import_key(args, tmpdir):
"""Import key and certificate from a PKCS#12 file to a NSS DB.
"""
data = json.load(args.importfile)
password = data['export password']
p12data = base64.b64decode(data['pkcs12 data'])
pk12pwfile = os.path.join(tmpdir, 'passwd')
with open(pk12pwfile, 'w') as f:
f.write(password)
pk12file = os.path.join(tmpdir, 'import.p12')
with open(pk12file, 'wb') as f:
f.write(p12data)
nssdb = NSSDatabase(args.nssdb_path)
nssdb.run_pk12util([
"-i", pk12file,
"-n", args.nickname,
"-k", args.nssdb_pwdfile,
"-w", pk12pwfile,
])
def default_parser():
"""Generic interface
"""
parser = common.mkparser(
description='ipa-custodia NSS cert handler'
)
parser.add_argument(
'--nssdb',
dest='nssdb_path',
help='path to NSS DB',
required=True
)
parser.add_argument(
'--pwdfile',
dest='nssdb_pwdfile',
help='path to password file for NSS DB',
required=True
)
parser.add_argument(
'--nickname',
help='nick name of certificate',
required=True
)
return parser
def pki_tomcat_parser():
"""Hard-code Dogtag's NSSDB and its password file
"""
parser = common.mkparser(
description='ipa-custodia pki-tomcat NSS cert handler'
)
parser.add_argument(
'--nickname',
help='nick name of certificate',
required=True
)
parser.set_defaults(
nssdb_path=paths.PKI_TOMCAT_ALIAS_DIR,
nssdb_pwdfile=paths.PKI_TOMCAT_ALIAS_PWDFILE_TXT,
)
return parser
def main(parser=None):
if parser is None:
parser = default_parser()
common.main(parser, export_key, import_key)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,114 @@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
"""Export and wrap key from NSS DB
"""
import os
from ipaplatform.paths import paths
from ipapython import ipautil
from ipapython.certdb import NSSDatabase
from . import common
def export_key(args, tmpdir):
"""Export key and certificate from the NSS DB
The private key is encrypted using key wrapping.
"""
wrapped_key_file = os.path.join(tmpdir, 'wrapped_key')
certificate_file = os.path.join(tmpdir, 'certificate')
ipautil.run([
paths.PKI,
'-d', args.nssdb_path,
'-C', args.nssdb_pwdfile,
'ca-authority-key-export',
'--wrap-nickname', args.wrap_nickname,
'--target-nickname', args.nickname,
'-o', wrapped_key_file
])
nssdb = NSSDatabase(args.nssdb_path)
nssdb.run_certutil([
'-L',
'-n', args.nickname,
'-a',
'-o', certificate_file,
])
with open(wrapped_key_file, 'rb') as f:
wrapped_key = f.read()
with open(certificate_file, 'r') as f:
certificate = f.read()
data = {
'wrapped_key': wrapped_key,
'certificate': certificate
}
common.json_dump(data, args.exportfile)
def default_parser():
"""Generic interface
"""
parser = common.mkparser(
supports_import=False,
description='ipa-custodia NSS wrapped cert handler',
)
parser.add_argument(
'--nssdb',
dest='nssdb_path',
help='path to NSS DB',
required=True
)
parser.add_argument(
'--pwdfile',
dest='nssdb_pwdfile',
help='path to password file for NSS DB',
required=True
)
parser.add_argument(
'--wrap-nickname',
dest='wrap_nickname',
help='nick name of wrapping key',
required=True
)
parser.add_argument(
'--nickname',
dest='nickname',
help='nick name of target key',
required=True
)
return parser
def pki_tomcat_parser():
"""Hard-code Dogtag's NSS DB, its password file, and CA key for wrapping
"""
parser = common.mkparser(
supports_import=False,
description='ipa-custodia pki-tomcat NSS wrapped cert handler',
)
parser.add_argument(
'--nickname',
dest='nickname',
help='nick name of target key',
required=True
)
parser.set_defaults(
nssdb_path=paths.PKI_TOMCAT_ALIAS_DIR,
nssdb_pwdfile=paths.PKI_TOMCAT_ALIAS_PWDFILE_TXT,
wrap_nickname='caSigningCert cert-pki-ca',
)
return parser
def main(parser=None):
if parser is None:
parser = default_parser()
common.main(parser, export_key, None)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,118 @@
#
# Copyright (C) 2019 IPA Project Contributors, see COPYING for license
#
"""Export / import PEM cert and key file as PKCS#12 data
"""
import base64
import json
import os
from ipaplatform.paths import paths
from ipapython import ipautil
from . import common
def export_key(args, tmpdir):
"""Export cert and private from PEM files as PKCS#12 file.
The PKCS#12 file is encrypted with a password.
"""
pk12file = os.path.join(tmpdir, 'export.p12')
password = ipautil.ipa_generate_password()
pk12pwfile = os.path.join(tmpdir, 'passwd')
with open(pk12pwfile, 'w') as f:
f.write(password)
# OpenSSL does not support pkcs12 export of a cert without key
ipautil.run([
paths.OPENSSL, 'pkcs12', '-export',
'-in', args.certfile,
'-out', pk12file,
'-inkey', args.keyfile,
'-password', 'file:{pk12pwfile}'.format(pk12pwfile=pk12pwfile),
])
with open(pk12file, 'rb') as f:
p12data = f.read()
data = {
'export password': password,
'pkcs12 data': p12data,
}
common.json_dump(data, args.exportfile)
def import_key(args, tmpdir):
"""Export key and certificate from a PKCS#12 file to key and cert files.
"""
data = json.load(args.importfile)
password = data['export password']
p12data = base64.b64decode(data['pkcs12 data'])
pk12pwfile = os.path.join(tmpdir, 'passwd')
with open(pk12pwfile, 'w') as f:
f.write(password)
pk12file = os.path.join(tmpdir, 'import.p12')
with open(pk12file, 'wb') as f:
f.write(p12data)
# get the certificate from the file
cmd = [
paths.OPENSSL, 'pkcs12',
'-in', pk12file,
'-clcerts', '-nokeys',
'-out', args.certfile,
'-password', 'file:{pk12pwfile}'.format(pk12pwfile=pk12pwfile),
]
ipautil.run(cmd, umask=0o027)
# get the private key from the file
cmd = [
paths.OPENSSL, 'pkcs12',
'-in', pk12file,
'-nocerts', '-nodes',
'-out', args.keyfile,
'-password', 'file:{pk12pwfile}'.format(pk12pwfile=pk12pwfile),
]
ipautil.run(cmd, umask=0o027)
def default_parser():
parser = common.mkparser(
description='ipa-custodia PEM file handler'
)
parser.add_argument(
'--certfile',
help='path to PEM encoded cert file',
required=True
)
parser.add_argument(
'keyfile',
help='path to PEM encoded key file',
required=True
)
return parser
def ra_agent_parser():
parser = common.mkparser(
description='ipa-custodia RA agent cert handler'
)
parser.set_defaults(
certfile=paths.RA_AGENT_PEM,
keyfile=paths.RA_AGENT_KEY
)
return parser
def main(parser=None):
if parser is None:
parser = default_parser()
common.main(parser, export_key, import_key)
if __name__ == '__main__':
main()

View File

@@ -1,18 +1,14 @@
# Copyright (C) 2015 IPA Project Contributors, see COPYING for license
from __future__ import print_function, absolute_import
from base64 import b64encode, b64decode
from custodia.store.interface import CSStore # pylint: disable=relative-import
from jwcrypto.common import json_decode, json_encode
from ipaplatform.paths import paths
from ipapython import ipautil
from ipapython.certdb import NSSDatabase
from ipaserver.secrets.common import iSecLdap
import ldap
import os
import shutil
import sys
import tempfile
from custodia.plugin import CSStore
from ipaplatform.paths import paths
from ipaplatform.constants import constants
from ipapython import ipautil
class UnknownKeyName(Exception):
@@ -20,9 +16,19 @@ class UnknownKeyName(Exception):
class DBMAPHandler:
dbtype = None
def __init__(self, config, dbmap, nickname):
raise NotImplementedError
dbtype = dbmap.get('type')
if dbtype is None or dbtype != self.dbtype:
raise ValueError(
"Invalid type '{}', expected '{}'".format(
dbtype, self.dbtype
)
)
self.config = config
self.dbmap = dbmap
self.nickname = nickname
def export_key(self):
raise NotImplementedError
@@ -31,229 +37,120 @@ class DBMAPHandler:
raise NotImplementedError
class DBMAPCommandHandler(DBMAPHandler):
def __init__(self, config, dbmap, nickname):
super().__init__(config, dbmap, nickname)
self.runas = dbmap.get('runas')
self.command = os.path.join(
paths.IPA_CUSTODIA_HANDLER,
dbmap['command']
)
def run_handler(self, extra_args=(), stdin=None):
"""Run handler script to export / import key material
"""
args = [self.command]
args.extend(extra_args)
kwargs = dict(
runas=self.runas,
encoding='utf-8',
)
if stdin:
args.extend(['--import', '-'])
kwargs.update(stdin=stdin)
else:
args.extend(['--export', '-'])
kwargs.update(capture_output=True)
result = ipautil.run(args, **kwargs)
if stdin is None:
return result.output
else:
return None
def log_error(error):
print(error, file=sys.stderr)
class NSSWrappedCertDB(DBMAPHandler):
'''
class NSSWrappedCertDB(DBMAPCommandHandler):
"""
Store that extracts private keys from an NSSDB, wrapped with the
private key of the primary CA.
'''
def __init__(self, config, dbmap, nickname):
if 'path' not in dbmap:
raise ValueError(
'Configuration does not provide NSSDB path')
if 'pwdfile' not in dbmap:
raise ValueError('Configuration does not provide password file')
if 'wrap_nick' not in dbmap:
raise ValueError(
'Configuration does not provide nickname of wrapping key')
self.nssdb_path = dbmap['path']
self.nssdb_pwdfile = dbmap['pwdfile']
self.wrap_nick = dbmap['wrap_nick']
self.target_nick = nickname
"""
dbtype = 'NSSDB'
def export_key(self):
tdir = tempfile.mkdtemp(dir=paths.TMP)
try:
wrapped_key_file = os.path.join(tdir, 'wrapped_key')
certificate_file = os.path.join(tdir, 'certificate')
ipautil.run([
paths.PKI, '-d', self.nssdb_path, '-C', self.nssdb_pwdfile,
'ca-authority-key-export',
'--wrap-nickname', self.wrap_nick,
'--target-nickname', self.target_nick,
'-o', wrapped_key_file])
nssdb = NSSDatabase(self.nssdb_path)
nssdb.run_certutil([
'-L', '-n', self.target_nick,
'-a', '-o', certificate_file,
])
with open(wrapped_key_file, 'rb') as f:
wrapped_key = f.read()
with open(certificate_file, 'r') as f:
certificate = f.read()
finally:
shutil.rmtree(tdir)
return json_encode({
'wrapped_key': b64encode(wrapped_key).decode('ascii'),
'certificate': certificate})
return self.run_handler(['--nickname', self.nickname])
class NSSCertDB(DBMAPHandler):
def __init__(self, config, dbmap, nickname):
if 'type' not in dbmap or dbmap['type'] != 'NSSDB':
raise ValueError('Invalid type "%s",'
' expected "NSSDB"' % (dbmap['type'],))
if 'path' not in dbmap:
raise ValueError('Configuration does not provide NSSDB path')
if 'pwdfile' not in dbmap:
raise ValueError('Configuration does not provide password file')
self.nssdb_path = dbmap['path']
self.nssdb_pwdfile = dbmap['pwdfile']
self.nickname = nickname
class NSSCertDB(DBMAPCommandHandler):
dbtype = 'NSSDB'
def export_key(self):
tdir = tempfile.mkdtemp(dir=paths.TMP)
try:
pk12pwfile = os.path.join(tdir, 'pk12pwfile')
password = ipautil.ipa_generate_password()
with open(pk12pwfile, 'w') as f:
f.write(password)
pk12file = os.path.join(tdir, 'pk12file')
nssdb = NSSDatabase(self.nssdb_path)
nssdb.run_pk12util([
"-o", pk12file,
"-n", self.nickname,
"-k", self.nssdb_pwdfile,
"-w", pk12pwfile,
])
with open(pk12file, 'rb') as f:
data = f.read()
finally:
shutil.rmtree(tdir)
return json_encode({'export password': password,
'pkcs12 data': b64encode(data).decode('ascii')})
return self.run_handler(['--nickname', self.nickname])
def import_key(self, value):
v = json_decode(value)
tdir = tempfile.mkdtemp(dir=paths.TMP)
try:
pk12pwfile = os.path.join(tdir, 'pk12pwfile')
with open(pk12pwfile, 'w') as f:
f.write(v['export password'])
pk12file = os.path.join(tdir, 'pk12file')
with open(pk12file, 'wb') as f:
f.write(b64decode(v['pkcs12 data']))
nssdb = NSSDatabase(self.nssdb_path)
nssdb.run_pk12util([
"-i", pk12file,
"-n", self.nickname,
"-k", self.nssdb_pwdfile,
"-w", pk12pwfile,
])
finally:
shutil.rmtree(tdir)
return self.run_handler(
['--nickname', self.nickname],
stdin=value
)
# Exfiltrate the DM password Hash so it can be set in replica's and this
# way let a replica be install without knowing the DM password and yet
# still keep the DM password synchronized across replicas
class DMLDAP(DBMAPHandler):
class DMLDAP(DBMAPCommandHandler):
dbtype = 'DMLDAP'
def __init__(self, config, dbmap, nickname):
if 'type' not in dbmap or dbmap['type'] != 'DMLDAP':
raise ValueError('Invalid type "%s",'
' expected "DMLDAP"' % (dbmap['type'],))
super().__init__(config, dbmap, nickname)
if nickname != 'DMHash':
raise UnknownKeyName("Unknown Key Named '%s'" % nickname)
self.ldap = iSecLdap(config['ldap_uri'],
config.get('auth_type', None))
def export_key(self):
conn = self.ldap.connect()
r = conn.search_s('cn=config', ldap.SCOPE_BASE,
attrlist=['nsslapd-rootpw'])
if len(r) != 1:
raise RuntimeError('DM Hash not found!')
rootpw = r[0][1]['nsslapd-rootpw'][0]
return json_encode({'dmhash': rootpw.decode('ascii')})
return self.run_handler()
def import_key(self, value):
v = json_decode(value)
rootpw = v['dmhash'].encode('ascii')
conn = self.ldap.connect()
mods = [(ldap.MOD_REPLACE, 'nsslapd-rootpw', rootpw)]
conn.modify_s('cn=config', mods)
self.run_handler(stdin=value)
class PEMFileHandler(DBMAPHandler):
def __init__(self, config, dbmap, nickname=None):
if 'type' not in dbmap or dbmap['type'] != 'PEM':
raise ValueError('Invalid type "{t}", expected PEM'
.format(t=dbmap['type']))
self.certfile = dbmap['certfile']
self.keyfile = dbmap.get('keyfile')
class PEMFileHandler(DBMAPCommandHandler):
dbtype = 'PEM'
def export_key(self):
_fd, tmpfile = tempfile.mkstemp(dir=paths.TMP)
password = ipautil.ipa_generate_password()
args = [
paths.OPENSSL,
"pkcs12", "-export",
"-in", self.certfile,
"-out", tmpfile,
"-password", "pass:{pwd}".format(pwd=password)
]
if self.keyfile is not None:
args.extend(["-inkey", self.keyfile])
try:
ipautil.run(args, nolog=(password, ))
with open(tmpfile, 'rb') as f:
data = f.read()
finally:
os.remove(tmpfile)
return json_encode({'export password': password,
'pkcs12 data': b64encode(data).decode('ascii')})
return self.run_handler()
def import_key(self, value):
v = json_decode(value)
data = b64decode(v['pkcs12 data'])
password = v['export password']
fd, tmpdata = tempfile.mkstemp(dir=paths.TMP)
os.close(fd)
try:
with open(tmpdata, 'wb') as f:
f.write(data)
# get the certificate from the file
ipautil.run([paths.OPENSSL,
"pkcs12",
"-in", tmpdata,
"-clcerts", "-nokeys",
"-out", self.certfile,
"-passin", "pass:{pwd}".format(pwd=password)],
nolog=(password, ))
if self.keyfile is not None:
# get the private key from the file
ipautil.run([paths.OPENSSL,
"pkcs12",
"-in", tmpdata,
"-nocerts", "-nodes",
"-out", self.keyfile,
"-passin", "pass:{pwd}".format(pwd=password)],
nolog=(password, ))
finally:
os.remove(tmpdata)
return self.run_handler(stdin=value)
NAME_DB_MAP = {
'ca': {
'type': 'NSSDB',
'path': paths.PKI_TOMCAT_ALIAS_DIR,
'handler': NSSCertDB,
'pwdfile': paths.PKI_TOMCAT_ALIAS_PWDFILE_TXT,
'command': 'ipa-custodia-pki-tomcat',
'runas': constants.PKI_USER,
},
'ca_wrapped': {
'type': 'NSSDB',
'handler': NSSWrappedCertDB,
'path': paths.PKI_TOMCAT_ALIAS_DIR,
'pwdfile': paths.PKI_TOMCAT_ALIAS_PWDFILE_TXT,
'wrap_nick': 'caSigningCert cert-pki-ca',
'command': 'ipa-custodia-pki-tomcat-wrapped',
'runas': constants.PKI_USER,
},
'ra': {
'type': 'PEM',
'handler': PEMFileHandler,
'certfile': paths.RA_AGENT_PEM,
'keyfile': paths.RA_AGENT_KEY,
'command': 'ipa-custodia-ra-agent',
'runas': None, # import needs root permission to write to directory
},
'dm': {
'type': 'DMLDAP',
'handler': DMLDAP,
'command': 'ipa-custodia-dmldap',
'runas': None, # root
}
}

View File

@@ -39,6 +39,7 @@ if __name__ == '__main__':
'ipaserver.dnssec',
'ipaserver.plugins',
'ipaserver.secrets',
'ipaserver.secrets.handlers',
'ipaserver.install',
'ipaserver.install.plugins',
'ipaserver.install.server',