added ldap3 lib and ldap3 backend
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
slapdtest - module for spawning test instances of OpenLDAP's slapd server
|
||||
|
||||
See https://www.python-ldap.org/ for details.
|
||||
"""
|
||||
|
||||
__version__ = '3.1.0'
|
||||
|
||||
from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler
|
||||
from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls
|
||||
from slapdtest._slapdtest import skip_unless_ci
|
||||
@@ -0,0 +1,598 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
slapdtest - module for spawning test instances of OpenLDAP's slapd server
|
||||
|
||||
See https://www.python-ldap.org/ for details.
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import logging
|
||||
import atexit
|
||||
from logging.handlers import SysLogHandler
|
||||
import unittest
|
||||
|
||||
# Switch off processing .ldaprc or ldap.conf before importing _ldap
|
||||
os.environ['LDAPNOINIT'] = '1'
|
||||
|
||||
import ldap
|
||||
from ldap.compat import quote_plus, which
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# a template string for generating simple slapd.conf file
|
||||
SLAPD_CONF_TEMPLATE = r"""
|
||||
serverID %(serverid)s
|
||||
moduleload back_%(database)s
|
||||
%(include_directives)s
|
||||
loglevel %(loglevel)s
|
||||
allow bind_v2
|
||||
|
||||
authz-regexp
|
||||
"gidnumber=%(root_gid)s\\+uidnumber=%(root_uid)s,cn=peercred,cn=external,cn=auth"
|
||||
"%(rootdn)s"
|
||||
|
||||
database %(database)s
|
||||
directory "%(directory)s"
|
||||
suffix "%(suffix)s"
|
||||
rootdn "%(rootdn)s"
|
||||
rootpw "%(rootpw)s"
|
||||
|
||||
TLSCACertificateFile "%(cafile)s"
|
||||
TLSCertificateFile "%(servercert)s"
|
||||
TLSCertificateKeyFile "%(serverkey)s"
|
||||
# ignore missing client cert but fail with invalid client cert
|
||||
TLSVerifyClient try
|
||||
|
||||
authz-regexp
|
||||
"C=DE, O=python-ldap, OU=slapd-test, CN=([A-Za-z]+)"
|
||||
"ldap://ou=people,dc=local???($1)"
|
||||
|
||||
"""
|
||||
|
||||
LOCALHOST = '127.0.0.1'
|
||||
|
||||
CI_DISABLED = set(os.environ.get('CI_DISABLED', '').split(':'))
|
||||
if 'LDAPI' in CI_DISABLED:
|
||||
HAVE_LDAPI = False
|
||||
else:
|
||||
HAVE_LDAPI = hasattr(socket, 'AF_UNIX')
|
||||
|
||||
|
||||
def identity(test_item):
|
||||
"""Identity decorator
|
||||
|
||||
"""
|
||||
return test_item
|
||||
|
||||
|
||||
def skip_unless_ci(reason, feature=None):
|
||||
"""Skip test unless test case is executed on CI like Travis CI
|
||||
"""
|
||||
if not os.environ.get('CI', False):
|
||||
return unittest.skip(reason)
|
||||
elif feature in CI_DISABLED:
|
||||
return unittest.skip(reason)
|
||||
else:
|
||||
# Don't skip on Travis
|
||||
return identity
|
||||
|
||||
|
||||
def requires_tls():
|
||||
"""Decorator for TLS tests
|
||||
|
||||
Tests are not skipped on CI (e.g. Travis CI)
|
||||
"""
|
||||
if not ldap.TLS_AVAIL:
|
||||
return skip_unless_ci("test needs ldap.TLS_AVAIL", feature='TLS')
|
||||
else:
|
||||
return identity
|
||||
|
||||
|
||||
def requires_sasl():
|
||||
if not ldap.SASL_AVAIL:
|
||||
return skip_unless_ci(
|
||||
"test needs ldap.SASL_AVAIL", feature='SASL')
|
||||
else:
|
||||
return identity
|
||||
|
||||
|
||||
def requires_ldapi():
|
||||
if not HAVE_LDAPI:
|
||||
return skip_unless_ci(
|
||||
"test needs ldapi support (AF_UNIX)", feature='LDAPI')
|
||||
else:
|
||||
return identity
|
||||
|
||||
def _add_sbin(path):
|
||||
"""Add /sbin and related directories to a command search path"""
|
||||
directories = path.split(os.pathsep)
|
||||
if sys.platform != 'win32':
|
||||
for sbin in '/usr/local/sbin', '/sbin', '/usr/sbin':
|
||||
if sbin not in directories:
|
||||
directories.append(sbin)
|
||||
return os.pathsep.join(directories)
|
||||
|
||||
def combined_logger(
|
||||
log_name,
|
||||
log_level=logging.WARN,
|
||||
sys_log_format='%(levelname)s %(message)s',
|
||||
console_log_format='%(asctime)s %(levelname)s %(message)s',
|
||||
):
|
||||
"""
|
||||
Returns a combined SysLogHandler/StreamHandler logging instance
|
||||
with formatters
|
||||
"""
|
||||
if 'LOGLEVEL' in os.environ:
|
||||
log_level = os.environ['LOGLEVEL']
|
||||
try:
|
||||
log_level = int(log_level)
|
||||
except ValueError:
|
||||
pass
|
||||
# for writing to syslog
|
||||
new_logger = logging.getLogger(log_name)
|
||||
if sys_log_format and os.path.exists('/dev/log'):
|
||||
my_syslog_formatter = logging.Formatter(
|
||||
fmt=' '.join((log_name, sys_log_format)))
|
||||
my_syslog_handler = logging.handlers.SysLogHandler(
|
||||
address='/dev/log',
|
||||
facility=SysLogHandler.LOG_DAEMON,
|
||||
)
|
||||
my_syslog_handler.setFormatter(my_syslog_formatter)
|
||||
new_logger.addHandler(my_syslog_handler)
|
||||
if console_log_format:
|
||||
my_stream_formatter = logging.Formatter(fmt=console_log_format)
|
||||
my_stream_handler = logging.StreamHandler()
|
||||
my_stream_handler.setFormatter(my_stream_formatter)
|
||||
new_logger.addHandler(my_stream_handler)
|
||||
new_logger.setLevel(log_level)
|
||||
return new_logger # end of combined_logger()
|
||||
|
||||
|
||||
class SlapdObject(object):
|
||||
"""
|
||||
Controller class for a slapd instance, OpenLDAP's server.
|
||||
|
||||
This class creates a temporary data store for slapd, runs it
|
||||
listening on a private Unix domain socket and TCP port,
|
||||
and initializes it with a top-level entry and the root user.
|
||||
|
||||
When a reference to an instance of this class is lost, the slapd
|
||||
server is shut down.
|
||||
|
||||
An instance can be used as a context manager. When exiting the context
|
||||
manager, the slapd server is shut down and the temporary data store is
|
||||
removed.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Added context manager functionality
|
||||
"""
|
||||
slapd_conf_template = SLAPD_CONF_TEMPLATE
|
||||
database = 'mdb'
|
||||
suffix = 'dc=slapd-test,dc=python-ldap,dc=org'
|
||||
root_cn = 'Manager'
|
||||
root_pw = 'password'
|
||||
slapd_loglevel = 'stats stats2'
|
||||
local_host = '127.0.0.1'
|
||||
testrunsubdirs = (
|
||||
'schema',
|
||||
)
|
||||
openldap_schema_files = (
|
||||
'core.schema',
|
||||
)
|
||||
|
||||
TMPDIR = os.environ.get('TMP', os.getcwd())
|
||||
if 'SCHEMA' in os.environ:
|
||||
SCHEMADIR = os.environ['SCHEMA']
|
||||
elif os.path.isdir("/etc/openldap/schema"):
|
||||
SCHEMADIR = "/etc/openldap/schema"
|
||||
elif os.path.isdir("/etc/ldap/schema"):
|
||||
SCHEMADIR = "/etc/ldap/schema"
|
||||
else:
|
||||
SCHEMADIR = None
|
||||
|
||||
BIN_PATH = os.environ.get('BIN', os.environ.get('PATH', os.defpath))
|
||||
SBIN_PATH = os.environ.get('SBIN', _add_sbin(BIN_PATH))
|
||||
|
||||
# time in secs to wait before trying to access slapd via LDAP (again)
|
||||
_start_sleep = 1.5
|
||||
|
||||
# create loggers once, multiple calls mess up refleak tests
|
||||
_log = combined_logger('python-ldap-test')
|
||||
|
||||
def __init__(self):
|
||||
self._proc = None
|
||||
self._port = self._avail_tcp_port()
|
||||
self.server_id = self._port % 4096
|
||||
self.testrundir = os.path.join(self.TMPDIR, 'python-ldap-test-%d' % self._port)
|
||||
self._schema_prefix = os.path.join(self.testrundir, 'schema')
|
||||
self._slapd_conf = os.path.join(self.testrundir, 'slapd.conf')
|
||||
self._db_directory = os.path.join(self.testrundir, "openldap-data")
|
||||
self.ldap_uri = "ldap://%s:%d/" % (LOCALHOST, self._port)
|
||||
if HAVE_LDAPI:
|
||||
ldapi_path = os.path.join(self.testrundir, 'ldapi')
|
||||
self.ldapi_uri = "ldapi://%s" % quote_plus(ldapi_path)
|
||||
self.default_ldap_uri = self.ldapi_uri
|
||||
# use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools
|
||||
self.cli_sasl_external = ldap.SASL_AVAIL
|
||||
else:
|
||||
self.ldapi_uri = None
|
||||
self.default_ldap_uri = self.ldap_uri
|
||||
# Use simple bind via LDAP uri
|
||||
self.cli_sasl_external = False
|
||||
|
||||
self._find_commands()
|
||||
|
||||
if self.SCHEMADIR is None:
|
||||
raise ValueError('SCHEMADIR is None, ldap schemas are missing.')
|
||||
|
||||
# TLS certs
|
||||
self.cafile = os.path.join(HERE, 'certs/ca.pem')
|
||||
self.servercert = os.path.join(HERE, 'certs/server.pem')
|
||||
self.serverkey = os.path.join(HERE, 'certs/server.key')
|
||||
self.clientcert = os.path.join(HERE, 'certs/client.pem')
|
||||
self.clientkey = os.path.join(HERE, 'certs/client.key')
|
||||
|
||||
@property
|
||||
def root_dn(self):
|
||||
return 'cn={self.root_cn},{self.suffix}'.format(self=self)
|
||||
|
||||
def _find_commands(self):
|
||||
self.PATH_LDAPADD = self._find_command('ldapadd')
|
||||
self.PATH_LDAPDELETE = self._find_command('ldapdelete')
|
||||
self.PATH_LDAPMODIFY = self._find_command('ldapmodify')
|
||||
self.PATH_LDAPWHOAMI = self._find_command('ldapwhoami')
|
||||
|
||||
self.PATH_SLAPD = os.environ.get('SLAPD', None)
|
||||
if not self.PATH_SLAPD:
|
||||
self.PATH_SLAPD = self._find_command('slapd', in_sbin=True)
|
||||
self.PATH_SLAPTEST = self._find_command('slaptest', in_sbin=True)
|
||||
|
||||
def _find_command(self, cmd, in_sbin=False):
|
||||
if in_sbin:
|
||||
path = self.SBIN_PATH
|
||||
var_name = 'SBIN'
|
||||
else:
|
||||
path = self.BIN_PATH
|
||||
var_name = 'BIN'
|
||||
command = which(cmd, path=path)
|
||||
if command is None:
|
||||
raise ValueError(
|
||||
"Command '{}' not found. Set the {} environment variable to "
|
||||
"override slapdtest's search path.".format(cmd, var_name)
|
||||
)
|
||||
return command
|
||||
|
||||
def setup_rundir(self):
|
||||
"""
|
||||
creates rundir structure
|
||||
|
||||
for setting up a custom directory structure you have to override
|
||||
this method
|
||||
"""
|
||||
os.mkdir(self.testrundir)
|
||||
os.mkdir(self._db_directory)
|
||||
self._create_sub_dirs(self.testrunsubdirs)
|
||||
self._ln_schema_files(self.openldap_schema_files, self.SCHEMADIR)
|
||||
|
||||
def _cleanup_rundir(self):
|
||||
"""
|
||||
Recursively delete whole directory specified by `path'
|
||||
"""
|
||||
# cleanup_rundir() is called in atexit handler. Until Python 3.4,
|
||||
# the rest of the world is already destroyed.
|
||||
import os, os.path
|
||||
if not os.path.exists(self.testrundir):
|
||||
return
|
||||
self._log.debug('clean-up %s', self.testrundir)
|
||||
for dirpath, dirnames, filenames in os.walk(
|
||||
self.testrundir,
|
||||
topdown=False
|
||||
):
|
||||
for filename in filenames:
|
||||
self._log.debug('remove %s', os.path.join(dirpath, filename))
|
||||
os.remove(os.path.join(dirpath, filename))
|
||||
for dirname in dirnames:
|
||||
self._log.debug('rmdir %s', os.path.join(dirpath, dirname))
|
||||
os.rmdir(os.path.join(dirpath, dirname))
|
||||
os.rmdir(self.testrundir)
|
||||
self._log.info('cleaned-up %s', self.testrundir)
|
||||
|
||||
def _avail_tcp_port(self):
|
||||
"""
|
||||
find an available port for TCP connection
|
||||
"""
|
||||
sock = socket.socket()
|
||||
try:
|
||||
sock.bind((self.local_host, 0))
|
||||
port = sock.getsockname()[1]
|
||||
finally:
|
||||
sock.close()
|
||||
self._log.info('Found available port %d', port)
|
||||
return port
|
||||
|
||||
def gen_config(self):
|
||||
"""
|
||||
generates a slapd.conf and returns it as one string
|
||||
|
||||
for generating specific static configuration files you have to
|
||||
override this method
|
||||
"""
|
||||
include_directives = '\n'.join(
|
||||
'include "{schema_prefix}/{schema_file}"'.format(
|
||||
schema_prefix=self._schema_prefix,
|
||||
schema_file=schema_file,
|
||||
)
|
||||
for schema_file in self.openldap_schema_files
|
||||
)
|
||||
config_dict = {
|
||||
'serverid': hex(self.server_id),
|
||||
'schema_prefix':self._schema_prefix,
|
||||
'include_directives': include_directives,
|
||||
'loglevel': self.slapd_loglevel,
|
||||
'database': self.database,
|
||||
'directory': self._db_directory,
|
||||
'suffix': self.suffix,
|
||||
'rootdn': self.root_dn,
|
||||
'rootpw': self.root_pw,
|
||||
'root_uid': os.getuid(),
|
||||
'root_gid': os.getgid(),
|
||||
'cafile': self.cafile,
|
||||
'servercert': self.servercert,
|
||||
'serverkey': self.serverkey,
|
||||
}
|
||||
return self.slapd_conf_template % config_dict
|
||||
|
||||
def _create_sub_dirs(self, dir_names):
|
||||
"""
|
||||
create sub-directories beneath self.testrundir
|
||||
"""
|
||||
for dname in dir_names:
|
||||
dir_name = os.path.join(self.testrundir, dname)
|
||||
self._log.debug('Create directory %s', dir_name)
|
||||
os.mkdir(dir_name)
|
||||
|
||||
def _ln_schema_files(self, file_names, source_dir):
|
||||
"""
|
||||
write symbolic links to original schema files
|
||||
"""
|
||||
for fname in file_names:
|
||||
ln_source = os.path.join(source_dir, fname)
|
||||
ln_target = os.path.join(self._schema_prefix, fname)
|
||||
self._log.debug('Create symlink %s -> %s', ln_source, ln_target)
|
||||
os.symlink(ln_source, ln_target)
|
||||
|
||||
def _write_config(self):
|
||||
"""Writes the slapd.conf file out, and returns the path to it."""
|
||||
self._log.debug('Writing config to %s', self._slapd_conf)
|
||||
with open(self._slapd_conf, 'w') as config_file:
|
||||
config_file.write(self.gen_config())
|
||||
self._log.info('Wrote config to %s', self._slapd_conf)
|
||||
|
||||
def _test_config(self):
|
||||
self._log.debug('testing config %s', self._slapd_conf)
|
||||
popen_list = [
|
||||
self.PATH_SLAPTEST,
|
||||
"-f", self._slapd_conf,
|
||||
'-u',
|
||||
]
|
||||
if self._log.isEnabledFor(logging.DEBUG):
|
||||
popen_list.append('-v')
|
||||
popen_list.extend(['-d', 'config'])
|
||||
else:
|
||||
popen_list.append('-Q')
|
||||
proc = subprocess.Popen(popen_list)
|
||||
if proc.wait() != 0:
|
||||
raise RuntimeError("configuration test failed")
|
||||
self._log.info("config ok: %s", self._slapd_conf)
|
||||
|
||||
def _start_slapd(self):
|
||||
"""
|
||||
Spawns/forks the slapd process
|
||||
"""
|
||||
urls = [self.ldap_uri]
|
||||
if self.ldapi_uri:
|
||||
urls.append(self.ldapi_uri)
|
||||
slapd_args = [
|
||||
self.PATH_SLAPD,
|
||||
'-f', self._slapd_conf,
|
||||
'-F', self.testrundir,
|
||||
'-h', ' '.join(urls),
|
||||
]
|
||||
if self._log.isEnabledFor(logging.DEBUG):
|
||||
slapd_args.extend(['-d', '-1'])
|
||||
else:
|
||||
slapd_args.extend(['-d', '0'])
|
||||
self._log.info('starting slapd: %r', ' '.join(slapd_args))
|
||||
self._proc = subprocess.Popen(slapd_args)
|
||||
# Waits until the LDAP server socket is open, or slapd crashed
|
||||
# no cover to avoid spurious coverage changes, see
|
||||
# https://github.com/python-ldap/python-ldap/issues/127
|
||||
for _ in range(10): # pragma: no cover
|
||||
if self._proc.poll() is not None:
|
||||
self._stopped()
|
||||
raise RuntimeError("slapd exited before opening port")
|
||||
time.sleep(self._start_sleep)
|
||||
try:
|
||||
self._log.debug(
|
||||
"slapd connection check to %s", self.default_ldap_uri
|
||||
)
|
||||
self.ldapwhoami()
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
return
|
||||
raise RuntimeError("slapd did not start properly")
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starts the slapd server process running, and waits for it to come up.
|
||||
"""
|
||||
|
||||
if self._proc is None:
|
||||
# prepare directory structure
|
||||
atexit.register(self.stop)
|
||||
self._cleanup_rundir()
|
||||
self.setup_rundir()
|
||||
self._write_config()
|
||||
self._test_config()
|
||||
self._start_slapd()
|
||||
self._log.debug(
|
||||
'slapd with pid=%d listening on %s and %s',
|
||||
self._proc.pid, self.ldap_uri, self.ldapi_uri
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stops the slapd server, and waits for it to terminate and cleans up
|
||||
"""
|
||||
if self._proc is not None:
|
||||
self._log.debug('stopping slapd with pid %d', self._proc.pid)
|
||||
self._proc.terminate()
|
||||
self.wait()
|
||||
self._cleanup_rundir()
|
||||
if hasattr(atexit, 'unregister'):
|
||||
# Python 3
|
||||
atexit.unregister(self.stop)
|
||||
elif hasattr(atexit, '_exithandlers'):
|
||||
# Python 2, can be None during process shutdown
|
||||
try:
|
||||
atexit._exithandlers.remove(self.stop)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def restart(self):
|
||||
"""
|
||||
Restarts the slapd server with same data
|
||||
"""
|
||||
self._proc.terminate()
|
||||
self.wait()
|
||||
self._start_slapd()
|
||||
|
||||
def wait(self):
|
||||
"""Waits for the slapd process to terminate by itself."""
|
||||
if self._proc:
|
||||
self._proc.wait()
|
||||
self._stopped()
|
||||
|
||||
def _stopped(self):
|
||||
"""Called when the slapd server is known to have terminated"""
|
||||
if self._proc is not None:
|
||||
self._log.info('slapd[%d] terminated', self._proc.pid)
|
||||
self._proc = None
|
||||
|
||||
def _cli_auth_args(self):
|
||||
if self.cli_sasl_external:
|
||||
authc_args = [
|
||||
'-Y', 'EXTERNAL',
|
||||
]
|
||||
if not self._log.isEnabledFor(logging.DEBUG):
|
||||
authc_args.append('-Q')
|
||||
else:
|
||||
authc_args = [
|
||||
'-x',
|
||||
'-D', self.root_dn,
|
||||
'-w', self.root_pw,
|
||||
]
|
||||
return authc_args
|
||||
|
||||
# no cover to avoid spurious coverage changes
|
||||
def _cli_popen(self, ldapcommand, extra_args=None, ldap_uri=None,
|
||||
stdin_data=None): # pragma: no cover
|
||||
if ldap_uri is None:
|
||||
ldap_uri = self.default_ldap_uri
|
||||
args = [
|
||||
ldapcommand,
|
||||
'-H', ldap_uri,
|
||||
] + self._cli_auth_args() + (extra_args or [])
|
||||
self._log.debug('Run command: %r', ' '.join(args))
|
||||
proc = subprocess.Popen(
|
||||
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
self._log.debug('stdin_data=%r', stdin_data)
|
||||
stdout_data, stderr_data = proc.communicate(stdin_data)
|
||||
if stdout_data is not None:
|
||||
self._log.debug('stdout_data=%r', stdout_data)
|
||||
if stderr_data is not None:
|
||||
self._log.debug('stderr_data=%r', stderr_data)
|
||||
if proc.wait() != 0:
|
||||
raise RuntimeError(
|
||||
'{!r} process failed:\n{!r}\n{!r}'.format(
|
||||
args, stdout_data, stderr_data
|
||||
)
|
||||
)
|
||||
return stdout_data, stderr_data
|
||||
|
||||
def ldapwhoami(self, extra_args=None):
|
||||
"""
|
||||
Runs ldapwhoami on this slapd instance
|
||||
"""
|
||||
self._cli_popen(self.PATH_LDAPWHOAMI, extra_args=extra_args)
|
||||
|
||||
def ldapadd(self, ldif, extra_args=None):
|
||||
"""
|
||||
Runs ldapadd on this slapd instance, passing it the ldif content
|
||||
"""
|
||||
self._cli_popen(self.PATH_LDAPADD, extra_args=extra_args,
|
||||
stdin_data=ldif.encode('utf-8'))
|
||||
|
||||
def ldapmodify(self, ldif, extra_args=None):
|
||||
"""
|
||||
Runs ldapadd on this slapd instance, passing it the ldif content
|
||||
"""
|
||||
self._cli_popen(self.PATH_LDAPMODIFY, extra_args=extra_args,
|
||||
stdin_data=ldif.encode('utf-8'))
|
||||
|
||||
def ldapdelete(self, dn, recursive=False, extra_args=None):
|
||||
"""
|
||||
Runs ldapdelete on this slapd instance, deleting 'dn'
|
||||
"""
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
if recursive:
|
||||
extra_args.append('-r')
|
||||
extra_args.append(dn)
|
||||
self._cli_popen(self.PATH_LDAPDELETE, extra_args=extra_args)
|
||||
|
||||
def __enter__(self):
|
||||
self.start()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.stop()
|
||||
|
||||
|
||||
class SlapdTestCase(unittest.TestCase):
|
||||
"""
|
||||
test class which also clones or initializes a running slapd
|
||||
"""
|
||||
|
||||
server_class = SlapdObject
|
||||
server = None
|
||||
ldap_object_class = None
|
||||
|
||||
def _open_ldap_conn(self, who=None, cred=None, **kwargs):
|
||||
"""
|
||||
return a LDAPObject instance after simple bind
|
||||
"""
|
||||
ldap_conn = self.ldap_object_class(self.server.ldap_uri, **kwargs)
|
||||
ldap_conn.protocol_version = 3
|
||||
#ldap_conn.set_option(ldap.OPT_REFERRALS, 0)
|
||||
ldap_conn.simple_bind_s(who or self.server.root_dn, cred or self.server.root_pw)
|
||||
return ldap_conn
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.server = cls.server_class()
|
||||
cls.server.start()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.server.stop()
|
||||
@@ -0,0 +1,24 @@
|
||||
python-ldap test certificates
|
||||
=============================
|
||||
|
||||
Certificates and keys
|
||||
---------------------
|
||||
|
||||
* ``ca.pem``: internal root CA certificate
|
||||
* ``server.pem``: TLS server certificate for slapd, signed by root CA. The
|
||||
server cert is valid for DNS Name ``localhost`` and IPs ``127.0.0.1`` and
|
||||
``:1``.
|
||||
* ``server.key``: private key for ``server.pem``, no password protection
|
||||
* ``client.pem``: certificate for TLS client cert authentication, signed by
|
||||
root CA.
|
||||
* ``client.key``: private key for ``client.pem``, no password protection
|
||||
|
||||
Configuration and scripts
|
||||
-------------------------
|
||||
|
||||
* ``ca.conf`` contains the CA definition as well as extensions for the
|
||||
client and server certificates.
|
||||
* ``client.conf`` and ``server.conf`` hold the subject and base configuration
|
||||
for server and client certs.
|
||||
* ``gencerts.sh`` creates new CA, client and server certificates.
|
||||
* ``gennssdb.sh`` can be used to create a NSSDB for all certs and keys.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Written by Christian Heimes
|
||||
|
||||
[default]
|
||||
ca = "ca"
|
||||
tmpdir = $ENV::CATMPDIR
|
||||
outdir = $ENV::CAOUTDIR
|
||||
name_opt = multiline,-esc_msb,utf8
|
||||
|
||||
[req]
|
||||
default_bits = 2048
|
||||
encrypt_key = no
|
||||
default_md = sha256
|
||||
utf8 = yes
|
||||
string_mask = utf8only
|
||||
prompt = no
|
||||
distinguished_name = ca_dn
|
||||
|
||||
[ca_dn]
|
||||
countryName = "DE"
|
||||
organizationName = "python-ldap"
|
||||
organizationalUnitName = "slapd-test"
|
||||
commonName = "Python LDAP Test CA"
|
||||
|
||||
[ca]
|
||||
default_ca = python_ldap_ca
|
||||
|
||||
[python_ldap_ca]
|
||||
certificate = $outdir/$ca.pem
|
||||
private_key = $outdir/$ca.key
|
||||
new_certs_dir = $tmpdir
|
||||
serial = $tmpdir/$ca.crt.srl
|
||||
crlnumber = $tmpdir/$ca.crl.srl
|
||||
database = $tmpdir/$ca.db
|
||||
unique_subject = no
|
||||
default_days = 3652
|
||||
default_md = sha256
|
||||
policy = match_pol
|
||||
email_in_dn = no
|
||||
preserve = no
|
||||
name_opt = $name_opt
|
||||
cert_opt = ca_default
|
||||
copy_extensions = none
|
||||
default_crl_days = 3651
|
||||
|
||||
[match_pol]
|
||||
countryName = match
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = match
|
||||
organizationalUnitName = match
|
||||
commonName = supplied
|
||||
|
||||
[ca_ext]
|
||||
basicConstraints = critical,CA:true
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
[server_san]
|
||||
DNS.1 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = ::1
|
||||
|
||||
[server_ext]
|
||||
basicConstraints = critical,CA:false
|
||||
keyUsage = critical,digitalSignature,keyEncipherment
|
||||
extendedKeyUsage = critical,serverAuth
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
subjectAltName = @server_san
|
||||
|
||||
[client_ext]
|
||||
basicConstraints = critical,CA:false
|
||||
keyUsage = critical,digitalSignature
|
||||
extendedKeyUsage = critical,clientAuth
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
@@ -0,0 +1,80 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=DE, O=python-ldap, OU=slapd-test, CN=Python LDAP Test CA
|
||||
Validity
|
||||
Not Before: Dec 2 11:57:47 2017 GMT
|
||||
Not After : Sep 4 11:57:47 2027 GMT
|
||||
Subject: C=DE, O=python-ldap, OU=slapd-test, CN=Python LDAP Test CA
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:af:1f:cf:0f:c5:95:66:2d:eb:85:cc:21:fc:0d:
|
||||
0f:44:d8:2f:a8:85:08:ef:60:67:57:fa:0b:c5:e4:
|
||||
b3:fb:f1:6f:cb:30:7a:47:0d:a7:f1:b5:37:81:5f:
|
||||
f6:39:28:e2:f9:4d:6c:2e:a6:5c:0e:3c:db:4d:c9:
|
||||
2a:64:ce:0d:15:30:c7:75:52:b8:74:c5:0b:00:4c:
|
||||
2f:94:1b:dd:fb:83:2c:58:02:73:b0:86:3a:6a:aa:
|
||||
55:f2:d5:49:99:17:a5:e2:44:ec:dd:62:5f:8d:ce:
|
||||
77:29:0b:8d:87:23:e2:4b:d6:1c:25:f3:06:a9:ee:
|
||||
33:6f:ac:ed:22:9e:35:ec:55:e7:1b:38:68:7e:46:
|
||||
e3:c3:42:ac:06:0b:0a:7a:84:c9:3d:ef:3d:a5:6e:
|
||||
e9:10:24:c3:28:fe:1f:4a:9a:23:8a:3c:db:0a:66:
|
||||
5d:07:f8:c5:17:68:53:e4:0e:37:33:c4:d2:ad:58:
|
||||
62:6b:8a:87:ab:73:eb:bc:2b:ac:07:69:84:8d:e3:
|
||||
c4:a9:78:9b:6c:1e:03:63:df:b4:96:18:bd:3c:2e:
|
||||
be:7f:2c:d5:a8:f8:12:b9:ab:27:52:b0:de:38:62:
|
||||
3c:54:a7:f3:aa:37:a3:11:12:b2:a7:6f:8d:96:10:
|
||||
ce:01:cb:25:24:a6:51:18:93:69:9b:9e:5c:8a:ff:
|
||||
fe:89
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:TRUE
|
||||
X509v3 Key Usage: critical
|
||||
Certificate Sign, CRL Sign
|
||||
X509v3 Subject Key Identifier:
|
||||
3B:1F:32:F4:FE:57:D1:6F:49:91:55:F2:24:F1:0A:66:3B:A5:EE:D4
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3B:1F:32:F4:FE:57:D1:6F:49:91:55:F2:24:F1:0A:66:3B:A5:EE:D4
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
0a:e7:dc:38:ce:03:dd:a8:99:11:d0:24:be:ef:1a:18:9d:7c:
|
||||
95:75:4a:4a:29:44:23:28:fc:66:d5:81:ce:05:c2:c0:6b:71:
|
||||
d6:8d:33:a9:53:a6:1c:f1:4e:50:ae:a3:b1:72:d6:69:53:ad:
|
||||
a9:62:a9:45:27:68:17:35:41:97:ec:e9:65:91:62:12:ed:eb:
|
||||
45:3a:9b:cc:09:bc:e3:ad:22:6b:13:6b:b0:67:ef:ce:01:83:
|
||||
5e:6c:95:e2:b3:73:b9:69:9a:33:49:f9:5f:52:4e:39:94:c9:
|
||||
db:93:6f:d8:ba:10:92:ce:fa:12:6b:bc:31:ff:c1:67:70:63:
|
||||
07:dc:53:7a:3a:a3:51:20:15:44:cf:1c:a9:cd:b7:30:1d:8e:
|
||||
55:93:8a:56:8c:3d:e9:8b:ae:0c:77:8d:5c:8b:fd:22:d8:4c:
|
||||
3e:e4:76:e8:d9:e8:c3:98:f4:98:ff:02:60:95:8e:3e:26:7a:
|
||||
e2:fe:2c:0a:a4:52:8d:4c:3d:dd:4c:fd:2f:2c:db:83:4c:2b:
|
||||
25:24:37:78:9a:07:27:52:f9:1c:c0:65:65:cb:50:77:b4:2d:
|
||||
fa:f4:af:bb:42:1c:43:65:c6:01:6e:f1:4b:fe:b8:4a:3c:29:
|
||||
8b:b6:84:1e:17:99:61:98:65:fe:f2:e9:ce:bb:ac:87:69:cb:
|
||||
e6:13:42:bf
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDijCCAnKgAwIBAgIBATANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJERTEU
|
||||
MBIGA1UECgwLcHl0aG9uLWxkYXAxEzARBgNVBAsMCnNsYXBkLXRlc3QxHDAaBgNV
|
||||
BAMME1B5dGhvbiBMREFQIFRlc3QgQ0EwHhcNMTcxMjAyMTE1NzQ3WhcNMjcwOTA0
|
||||
MTE1NzQ3WjBWMQswCQYDVQQGEwJERTEUMBIGA1UECgwLcHl0aG9uLWxkYXAxEzAR
|
||||
BgNVBAsMCnNsYXBkLXRlc3QxHDAaBgNVBAMME1B5dGhvbiBMREFQIFRlc3QgQ0Ew
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvH88PxZVmLeuFzCH8DQ9E
|
||||
2C+ohQjvYGdX+gvF5LP78W/LMHpHDafxtTeBX/Y5KOL5TWwuplwOPNtNySpkzg0V
|
||||
MMd1Urh0xQsATC+UG937gyxYAnOwhjpqqlXy1UmZF6XiROzdYl+NzncpC42HI+JL
|
||||
1hwl8wap7jNvrO0injXsVecbOGh+RuPDQqwGCwp6hMk97z2lbukQJMMo/h9KmiOK
|
||||
PNsKZl0H+MUXaFPkDjczxNKtWGJrioerc+u8K6wHaYSN48SpeJtsHgNj37SWGL08
|
||||
Lr5/LNWo+BK5qydSsN44YjxUp/OqN6MRErKnb42WEM4ByyUkplEYk2mbnlyK//6J
|
||||
AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud
|
||||
DgQWBBQ7HzL0/lfRb0mRVfIk8QpmO6Xu1DAfBgNVHSMEGDAWgBQ7HzL0/lfRb0mR
|
||||
VfIk8QpmO6Xu1DANBgkqhkiG9w0BAQsFAAOCAQEACufcOM4D3aiZEdAkvu8aGJ18
|
||||
lXVKSilEIyj8ZtWBzgXCwGtx1o0zqVOmHPFOUK6jsXLWaVOtqWKpRSdoFzVBl+zp
|
||||
ZZFiEu3rRTqbzAm8460iaxNrsGfvzgGDXmyV4rNzuWmaM0n5X1JOOZTJ25Nv2LoQ
|
||||
ks76Emu8Mf/BZ3BjB9xTejqjUSAVRM8cqc23MB2OVZOKVow96YuuDHeNXIv9IthM
|
||||
PuR26Nnow5j0mP8CYJWOPiZ64v4sCqRSjUw93Uz9Lyzbg0wrJSQ3eJoHJ1L5HMBl
|
||||
ZctQd7Qt+vSvu0IcQ2XGAW7xS/64Sjwpi7aEHheZYZhl/vLpzrush2nL5hNCvw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,16 @@
|
||||
# Written by Christian Heimes
|
||||
|
||||
[req]
|
||||
default_bits = 2048
|
||||
encrypt_key = no
|
||||
default_md = sha256
|
||||
utf8 = yes
|
||||
string_mask = utf8only
|
||||
prompt = no
|
||||
distinguished_name = client_dn
|
||||
|
||||
[client_dn]
|
||||
countryName = "DE"
|
||||
organizationName = "python-ldap"
|
||||
organizationalUnitName = "slapd-test"
|
||||
commonName = "client"
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGxvbMEbahViK4
|
||||
P6aoWUkciIf1dEYTBuU8M1eShREUZ3Ytq/ee425pXRyxxDrAa8ygRjqs7tauwhgA
|
||||
KNuPRGyw7hyZ1Ku4vQObwX9rzyHQ6Fj606U4HHbYfAVb0AF7OzLbhNH3isCRtNcm
|
||||
EUSYG1Nkfn9zQkV4pz2KJM4ePt4GyGJV0NhRUdHdwgsWiuRt2EIRPwLXdkf/4svm
|
||||
2EahAJax+SMaZYe/lg9w9SBxl6DTaF9lFpoPqzeW+KnXCmE8e+qSWea8EjkzIRXa
|
||||
4KJ/EnUMP2fUL+Je2agGF2YfCId9YvLTQV8YDv7Im1Sp0sbIUDoWX14GDUbZ8cBr
|
||||
wfOyI0TfAgMBAAECggEATWv1eGp1zcU05Lq1+OA938U1316YZJTM+HOu6jy1+FKL
|
||||
7yIJ4nMG8Db6FCswDv5txwdTl0O3jn2+x2Eik1y9UPSNY0U4VU4Zd7MYJC+bJjk5
|
||||
XwjMU1yS1aMIm0gbK5pVJrdG6Lm8Y4QiQIt9Qhlyk7PJhGUNlf7ds06+kX0/ETiO
|
||||
vx5SatExeKu5F+JRnGFdAN0106SF5vBum+UbrgOSnJmfwX5VoOXARD21ppxgMzAr
|
||||
JyGBpgBgy++GpV15gXGuA7DVMIADdHw8hV4OuBLjpkUL+ntArjhpUi7TP7VU3WKR
|
||||
uUmvLm9CX1l8O/xZMpt9N1+o71a//7asnz8AMtT6cQKBgQD4FgefUkVnXDA1xKDW
|
||||
1JbArVQeHiLGlRdLakRUY/HdGj72YgAOLt3UsrON4VQXl0C6rks/8HKCFaMexBlF
|
||||
OecJNWsEVgBEAfsQ+NvrApOQsTszc8Zqna0Kqe2vA0VNa+SAzdHzhBbFcaVkzXJb
|
||||
JB7M0/OIt5IaqXg6Y5eX2eZF1QKBgQDNHkIoJ/2hYtlSgXpGaniM+0XemQJgJXig
|
||||
edAQdGKKfqwmjSFjByDM01ZaidMu5fEkeGhMRE73IbwNw0pWsMXylD6bI6+sk7yQ
|
||||
biM+fslFEEDbgSJe41Jy2eerh5am+dnrMWNhd7QZV1K6tmaqrIzkmIV21/EPXIPp
|
||||
BNHO8GV14wKBgGOybrO/GzcTXChvcXeEDWU3AqPr1mvZhHgBJ56GX69MGdtnvL/2
|
||||
Y51Th0bQM7wbQ58B5im21j2itl/pzIH+Z/NSbURbz1WFOkEy0SYbbfPq1XCy6Rz1
|
||||
apHrgiIf/VzErBp7HBFxlrkYF7Bvw7IOzPXhg3AA3Y0rZ66HUWdr4NdVAoGBAJfC
|
||||
E2Bydgy5feC1OypuC9MC9abDviY0kxLoDTCfa2jcX7IGKPWDiJkCo5lI7557Mfax
|
||||
vzjuMR5XLzNfkdih4VKgq9FMjeU5SQHy+tB6LZ+Tbuj4md1qgs3GuskGAEh6Auko
|
||||
GUc7sVwuZ18NJNiR4Ywf7F8JVajv4gi9MB3Tbr3RAoGARSnVu+6rYSQTyEqvbsaB
|
||||
gIW7Ezea5q06GcQF072nk3tNSXuU/52YMlodAJ1UfFPbBAtaa7wEFN8oRG1IyKON
|
||||
MGyf6RD8GoInJjaDihkdCsR28RkchwymG1UMPnPzqRxSAb7da5YuMR8PEioVbL68
|
||||
dxhsgNi1Wtc2nGqN96qufG0=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,83 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 3 (0x3)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=DE, O=python-ldap, OU=slapd-test, CN=Python LDAP Test CA
|
||||
Validity
|
||||
Not Before: Dec 2 11:57:48 2017 GMT
|
||||
Not After : Dec 2 11:57:48 2027 GMT
|
||||
Subject: C=DE, O=python-ldap, OU=slapd-test, CN=client
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:c6:c6:f6:cc:11:b6:a1:56:22:b8:3f:a6:a8:59:
|
||||
49:1c:88:87:f5:74:46:13:06:e5:3c:33:57:92:85:
|
||||
11:14:67:76:2d:ab:f7:9e:e3:6e:69:5d:1c:b1:c4:
|
||||
3a:c0:6b:cc:a0:46:3a:ac:ee:d6:ae:c2:18:00:28:
|
||||
db:8f:44:6c:b0:ee:1c:99:d4:ab:b8:bd:03:9b:c1:
|
||||
7f:6b:cf:21:d0:e8:58:fa:d3:a5:38:1c:76:d8:7c:
|
||||
05:5b:d0:01:7b:3b:32:db:84:d1:f7:8a:c0:91:b4:
|
||||
d7:26:11:44:98:1b:53:64:7e:7f:73:42:45:78:a7:
|
||||
3d:8a:24:ce:1e:3e:de:06:c8:62:55:d0:d8:51:51:
|
||||
d1:dd:c2:0b:16:8a:e4:6d:d8:42:11:3f:02:d7:76:
|
||||
47:ff:e2:cb:e6:d8:46:a1:00:96:b1:f9:23:1a:65:
|
||||
87:bf:96:0f:70:f5:20:71:97:a0:d3:68:5f:65:16:
|
||||
9a:0f:ab:37:96:f8:a9:d7:0a:61:3c:7b:ea:92:59:
|
||||
e6:bc:12:39:33:21:15:da:e0:a2:7f:12:75:0c:3f:
|
||||
67:d4:2f:e2:5e:d9:a8:06:17:66:1f:08:87:7d:62:
|
||||
f2:d3:41:5f:18:0e:fe:c8:9b:54:a9:d2:c6:c8:50:
|
||||
3a:16:5f:5e:06:0d:46:d9:f1:c0:6b:c1:f3:b2:23:
|
||||
44:df
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:FALSE
|
||||
X509v3 Key Usage: critical
|
||||
Digital Signature
|
||||
X509v3 Extended Key Usage: critical
|
||||
TLS Web Client Authentication
|
||||
X509v3 Subject Key Identifier:
|
||||
67:63:38:F4:B4:BC:F3:6B:BC:74:0E:7C:27:C9:BB:C2:CC:58:AC:16
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3B:1F:32:F4:FE:57:D1:6F:49:91:55:F2:24:F1:0A:66:3B:A5:EE:D4
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
76:24:42:6b:33:4f:d6:59:07:48:5b:04:9c:3c:d3:3f:63:80:
|
||||
75:4d:78:d7:d5:85:b1:77:81:31:a3:91:cb:c9:a3:8c:0e:00:
|
||||
28:08:74:71:6c:fc:83:8c:80:ec:1c:e8:ee:83:e0:7f:49:3b:
|
||||
f3:42:33:5a:1f:68:0c:a5:41:42:ce:bf:77:29:07:f2:18:a7:
|
||||
81:17:d7:76:47:04:d9:8a:dd:e8:5a:26:26:ea:a4:76:70:e1:
|
||||
f1:fa:e1:db:bc:f2:24:b2:37:a8:58:2f:e3:66:89:77:02:55:
|
||||
87:ef:3c:1f:66:ce:4e:86:b3:4c:57:43:86:7f:4c:ab:5a:33:
|
||||
dd:ca:e3:2f:3b:af:b4:43:5a:53:8b:e0:12:da:e7:c0:13:76:
|
||||
b2:68:d5:14:f8:1a:07:ce:8a:87:5c:91:bd:35:d7:83:c6:2a:
|
||||
a4:e0:92:50:01:b9:c2:fa:69:06:5c:8a:80:ee:9c:24:f9:49:
|
||||
64:e3:59:c1:a6:69:29:ce:b7:89:20:a9:7c:d6:9f:df:2a:d1:
|
||||
a4:98:2a:6d:7b:93:6a:52:e3:ae:de:1a:d8:f3:2e:cf:02:7e:
|
||||
ba:9a:fa:f4:b3:b5:6e:9a:23:10:70:53:53:30:d5:8a:32:35:
|
||||
01:52:58:6d:9d:f5:8e:bb:b9:76:bd:41:16:88:26:f8:d3:ce:
|
||||
70:03:c8:59
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkjCCAnqgAwIBAgIBAzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJERTEU
|
||||
MBIGA1UECgwLcHl0aG9uLWxkYXAxEzARBgNVBAsMCnNsYXBkLXRlc3QxHDAaBgNV
|
||||
BAMME1B5dGhvbiBMREFQIFRlc3QgQ0EwHhcNMTcxMjAyMTE1NzQ4WhcNMjcxMjAy
|
||||
MTE1NzQ4WjBJMQswCQYDVQQGEwJERTEUMBIGA1UECgwLcHl0aG9uLWxkYXAxEzAR
|
||||
BgNVBAsMCnNsYXBkLXRlc3QxDzANBgNVBAMMBmNsaWVudDCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAMbG9swRtqFWIrg/pqhZSRyIh/V0RhMG5TwzV5KF
|
||||
ERRndi2r957jbmldHLHEOsBrzKBGOqzu1q7CGAAo249EbLDuHJnUq7i9A5vBf2vP
|
||||
IdDoWPrTpTgcdth8BVvQAXs7MtuE0feKwJG01yYRRJgbU2R+f3NCRXinPYokzh4+
|
||||
3gbIYlXQ2FFR0d3CCxaK5G3YQhE/Atd2R//iy+bYRqEAlrH5Ixplh7+WD3D1IHGX
|
||||
oNNoX2UWmg+rN5b4qdcKYTx76pJZ5rwSOTMhFdrgon8SdQw/Z9Qv4l7ZqAYXZh8I
|
||||
h31i8tNBXxgO/sibVKnSxshQOhZfXgYNRtnxwGvB87IjRN8CAwEAAaN4MHYwDAYD
|
||||
VR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUH
|
||||
AwIwHQYDVR0OBBYEFGdjOPS0vPNrvHQOfCfJu8LMWKwWMB8GA1UdIwQYMBaAFDsf
|
||||
MvT+V9FvSZFV8iTxCmY7pe7UMA0GCSqGSIb3DQEBCwUAA4IBAQB2JEJrM0/WWQdI
|
||||
WwScPNM/Y4B1TXjX1YWxd4Exo5HLyaOMDgAoCHRxbPyDjIDsHOjug+B/STvzQjNa
|
||||
H2gMpUFCzr93KQfyGKeBF9d2RwTZit3oWiYm6qR2cOHx+uHbvPIksjeoWC/jZol3
|
||||
AlWH7zwfZs5OhrNMV0OGf0yrWjPdyuMvO6+0Q1pTi+AS2ufAE3ayaNUU+BoHzoqH
|
||||
XJG9NdeDxiqk4JJQAbnC+mkGXIqA7pwk+Ulk41nBpmkpzreJIKl81p/fKtGkmCpt
|
||||
e5NqUuOu3hrY8y7PAn66mvr0s7VumiMQcFNTMNWKMjUBUlhtnfWOu7l2vUEWiCb4
|
||||
085wA8hZ
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
# Written by Christian Heimes
|
||||
set -e
|
||||
|
||||
export CAOUTDIR=.
|
||||
export CATMPDIR=tmp
|
||||
|
||||
rm -rf $CATMPDIR
|
||||
rm -rf ca.pem ca.key server.pem server.key client.pem client.key
|
||||
rm -rf cert9.db key4.db pkcs11.tx
|
||||
|
||||
mkdir -p $CAOUTDIR
|
||||
mkdir -p $CATMPDIR
|
||||
|
||||
touch $CATMPDIR/ca.db
|
||||
touch $CATMPDIR/ca.db.attr
|
||||
echo '01' > $CATMPDIR/ca.crt.srl
|
||||
echo '01' > $CATMPDIR/ca.crl.srl
|
||||
|
||||
# root CA
|
||||
openssl req -new \
|
||||
-config ca.conf \
|
||||
-out $CATMPDIR/ca.csr \
|
||||
-keyout $CAOUTDIR/ca.key \
|
||||
-batch
|
||||
|
||||
openssl ca -selfsign \
|
||||
-config ca.conf \
|
||||
-in $CATMPDIR/ca.csr \
|
||||
-out $CAOUTDIR/ca.pem \
|
||||
-extensions ca_ext \
|
||||
-days 3563 \
|
||||
-batch
|
||||
|
||||
# server cert
|
||||
openssl req -new \
|
||||
-config server.conf \
|
||||
-out $CATMPDIR/server.csr \
|
||||
-keyout $CAOUTDIR/server.key \
|
||||
-batch
|
||||
|
||||
openssl ca \
|
||||
-config ca.conf \
|
||||
-in $CATMPDIR/server.csr \
|
||||
-out $CAOUTDIR/server.pem \
|
||||
-policy match_pol \
|
||||
-extensions server_ext \
|
||||
-batch
|
||||
|
||||
# client cert
|
||||
openssl req -new \
|
||||
-config client.conf \
|
||||
-out $CATMPDIR/client.csr \
|
||||
-keyout $CAOUTDIR/client.key \
|
||||
-batch
|
||||
|
||||
openssl ca \
|
||||
-config ca.conf \
|
||||
-in $CATMPDIR/client.csr \
|
||||
-out $CAOUTDIR/client.pem \
|
||||
-policy match_pol \
|
||||
-extensions client_ext \
|
||||
-batch
|
||||
|
||||
# cleanup
|
||||
rm -rf $CATMPDIR ca.key
|
||||
|
||||
echo DONE
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Written by Christian Heimes
|
||||
set -e
|
||||
|
||||
CATMPDIR=tmp
|
||||
PASSFILE=${CATMPDIR}/passwd.txt
|
||||
NSSDB=sql:${CAOUTDIR}
|
||||
|
||||
mkdir -p $CATMPDIR
|
||||
|
||||
# Create PKCS#12 files for NSSDB import
|
||||
echo "dummy" > $PASSFILE
|
||||
openssl pkcs12 -name "servercert" -in server.pem -inkey server.key \
|
||||
-caname "testca" -CAfile ca.pem \
|
||||
-password "file:${PASSFILE}" -export -out server.p12
|
||||
openssl pkcs12 -name "clientcert" -in client.pem -inkey client.key \
|
||||
-caname "testca" -CAfile ca.pem \
|
||||
-password "file:${PASSFILE}" -export -out client.p12
|
||||
|
||||
# Create NSS DB
|
||||
certutil -d $NSSDB -N --empty-password
|
||||
certutil -d $NSSDB -A -n "testca" -t CT,, -a -i ca.pem
|
||||
pk12util -d $NSSDB -i server.p12 -w ${PASSFILE}
|
||||
pk12util -d $NSSDB -i client.p12 -w ${PASSFILE}
|
||||
certutil -d $NSSDB -L
|
||||
|
||||
# cleanup
|
||||
rm -rf $CATMPDIR server.p12 client.p12
|
||||
@@ -0,0 +1,16 @@
|
||||
# Written by Christian Heimes
|
||||
|
||||
[req]
|
||||
default_bits = 2048
|
||||
encrypt_key = no
|
||||
default_md = sha256
|
||||
utf8 = yes
|
||||
string_mask = utf8only
|
||||
prompt = no
|
||||
distinguished_name = server_dn
|
||||
|
||||
[server_dn]
|
||||
countryName = "DE"
|
||||
organizationName = "python-ldap"
|
||||
organizationalUnitName = "slapd-test"
|
||||
commonName = "server cert for localhost"
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAgcI7Pj89Aw4r
|
||||
rb+N8j3t1ynJgXRhQNxbxQcQmUCi8AtpGKNXu+aM9u2HxZ677ALfhsEivtQA5QKz
|
||||
Ll5G2G2IQa7uzgIco73OL/kMZIJt7sKfnvfACtSoOlD0IyOzVEEu0AVA7hMHb6Ul
|
||||
I0mCNfdk2FWFbc1nUmIAEBhIODbFoNW+Rc6lz94NsUqArDayvWpAsXkUbubQikpi
|
||||
KNX1OpOOC9GUbEhwI/G90ZnUg9STk/2oxsvwLlYdOhGhpyfl53tTu7eLMBriMxFl
|
||||
UTvkY1GUgPj0fA/giWtCerGOyeKu1hFlbS4LjborsfrlyYPwfwTg3YL4hVnt9fF0
|
||||
rpjzY1mlAgMBAAECggEAJY6rSEeiqtKXxynEv3rNXkOmIWwiOn8e/sB32mMr2x4d
|
||||
+8kUxR8hocrjGKQTjfJDtTxjHdZBIlOLrU2UkxnSdMzrxidm/hNsCngNjL9nOu9k
|
||||
BSRMjakPSCrodFkOtAPyG6H2BG7uQ3siqxYxVzgUJhaWyMtdUZUfDYgWVLCy7udU
|
||||
5ML/OTOi7virueMmshjXoyrDug9OpiEMKiLu3ndAaDk/26m05ePAXB6TjW8SFw1B
|
||||
qn7cITSG0G5MZ9pOw0KwT9irY1SdppBHVWIg7dkYWRCni0BPCFewastU+GVKH5PJ
|
||||
+dYSvafhkEGD1bBu484KN9yX1BcHV41ZKR8pGgMM2QKBgQD3/0R2vZsTxoO1CHNI
|
||||
IT7nBnuPIOP45iTFm/SNRY7e4dhQBy6HM6JD3Sr6Iksm8jRoboz+tnAso6l6QHRS
|
||||
842uqBiOHdnka2RslDmrEun1lJv1MWuPM8JN0o8pYjVG/IRtaAFnYSEk72UoNy2h
|
||||
bHC4OGFNwMbAadVm7DK5OiMfXwKBgQDGuBRxz7jkVZoMbbaeIqmGZAIejWkJweDZ
|
||||
AK+txM+6Sg+Li14t190N3Xf6tyyidKhUAEWaINzLjZB+luxNaDXtxqWzLYHCwQKA
|
||||
qfrjWVeZOS1clLya7jwl1jJqBtBiGKHv9eRL21hgX/9gX3odxqFMvX3vm6L7F1q1
|
||||
5CNApW0ZewKBgGO8qNcsWBLy8oM7G8n1fOvCwqyEaMrwG/fRSeALCnN+1tUQnljH
|
||||
nkm2yBMC+cB3Bja9xzylOKXrSDyfcWjvBJsqhX2aacggnKnCTxMLL0aR9sr8jipw
|
||||
gYN03Bijo5Oh+MxbWL0v5fmJweATmOljyE1+dzui/QvjRGz5L0kpJXj3AoGBAIa4
|
||||
3+t1B4WN312TuB4no8Tf4mvyNQcPcS/Nfk0RxD8o3Lcfal8sHMq8ng3Ux6bv7frd
|
||||
IFLo+qfpts+L5HJqNz2X0ljSfkmZ7udp1hTySigwEmfU0rU61H5WZGFrczU+O/Ni
|
||||
Qj+HWrgj/Q/KSxEKy+oqAcpDOtB+Odpc6+V1Aa0nAoGBAItWHP9UjTNFqOfyjZhG
|
||||
qaUiZd1S2KyRR0l/lVcn+rJ46Yg5i+lMGwHMF1xPyWH4ELz+QCUX3doOI4yB2ikg
|
||||
XXFcc8/bqgaR4AfOvP98T86s7+f33kaAKZsgyAFB2cjo+fz8ArTz+GjPeHbiOPaR
|
||||
Ra7+BVwl9GE0+bCdirq+99GO
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,86 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 2 (0x2)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=DE, O=python-ldap, OU=slapd-test, CN=Python LDAP Test CA
|
||||
Validity
|
||||
Not Before: Dec 2 11:57:48 2017 GMT
|
||||
Not After : Dec 2 11:57:48 2027 GMT
|
||||
Subject: C=DE, O=python-ldap, OU=slapd-test, CN=server cert for localhost
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:c0:81:c2:3b:3e:3f:3d:03:0e:2b:ad:bf:8d:f2:
|
||||
3d:ed:d7:29:c9:81:74:61:40:dc:5b:c5:07:10:99:
|
||||
40:a2:f0:0b:69:18:a3:57:bb:e6:8c:f6:ed:87:c5:
|
||||
9e:bb:ec:02:df:86:c1:22:be:d4:00:e5:02:b3:2e:
|
||||
5e:46:d8:6d:88:41:ae:ee:ce:02:1c:a3:bd:ce:2f:
|
||||
f9:0c:64:82:6d:ee:c2:9f:9e:f7:c0:0a:d4:a8:3a:
|
||||
50:f4:23:23:b3:54:41:2e:d0:05:40:ee:13:07:6f:
|
||||
a5:25:23:49:82:35:f7:64:d8:55:85:6d:cd:67:52:
|
||||
62:00:10:18:48:38:36:c5:a0:d5:be:45:ce:a5:cf:
|
||||
de:0d:b1:4a:80:ac:36:b2:bd:6a:40:b1:79:14:6e:
|
||||
e6:d0:8a:4a:62:28:d5:f5:3a:93:8e:0b:d1:94:6c:
|
||||
48:70:23:f1:bd:d1:99:d4:83:d4:93:93:fd:a8:c6:
|
||||
cb:f0:2e:56:1d:3a:11:a1:a7:27:e5:e7:7b:53:bb:
|
||||
b7:8b:30:1a:e2:33:11:65:51:3b:e4:63:51:94:80:
|
||||
f8:f4:7c:0f:e0:89:6b:42:7a:b1:8e:c9:e2:ae:d6:
|
||||
11:65:6d:2e:0b:8d:ba:2b:b1:fa:e5:c9:83:f0:7f:
|
||||
04:e0:dd:82:f8:85:59:ed:f5:f1:74:ae:98:f3:63:
|
||||
59:a5
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:FALSE
|
||||
X509v3 Key Usage: critical
|
||||
Digital Signature, Key Encipherment
|
||||
X509v3 Extended Key Usage: critical
|
||||
TLS Web Server Authentication
|
||||
X509v3 Subject Key Identifier:
|
||||
1B:78:45:40:0D:50:8A:8B:3B:C1:0A:F8:3F:7A:48:7B:A6:3C:28:09
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3B:1F:32:F4:FE:57:D1:6F:49:91:55:F2:24:F1:0A:66:3B:A5:EE:D4
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
ad:08:3f:7d:b1:09:a1:a5:6c:c3:58:80:1d:e5:33:a5:bb:c0:
|
||||
33:39:95:aa:88:ee:c4:8e:38:3b:59:a7:0e:39:74:6c:fe:11:
|
||||
33:5e:fa:50:cb:20:4b:67:b7:c9:5e:96:a7:9e:d8:47:46:e1:
|
||||
ab:fe:5d:8b:9a:2d:1a:1b:43:08:f9:93:0f:2a:e3:ce:83:4a:
|
||||
94:cd:02:f0:8e:25:f2:41:0d:55:10:f5:4c:5b:39:8b:77:5e:
|
||||
ab:78:16:64:a1:48:d5:e1:f6:69:9a:0f:d8:30:a6:cc:92:4d:
|
||||
81:df:46:74:ab:cf:1d:b7:d4:01:b9:6d:d5:f4:14:b8:d5:54:
|
||||
84:79:11:42:69:55:7f:74:ce:01:96:2f:3f:51:23:b3:11:fb:
|
||||
72:dc:4c:b9:a3:89:ef:31:e4:c0:49:06:fa:8d:09:71:e1:c1:
|
||||
74:a9:ed:f8:96:87:67:16:b5:5d:16:5d:59:70:ff:1c:b5:a1:
|
||||
6c:d2:22:11:3a:0e:6f:76:9b:69:cb:f3:85:a7:79:ad:53:f5:
|
||||
34:e8:87:cc:dd:09:51:25:e0:28:ee:79:a0:a3:dc:0a:dd:f0:
|
||||
1b:e3:c9:5f:14:d3:95:f5:12:4d:23:95:45:2c:3c:32:94:ad:
|
||||
ce:1e:a0:5f:e6:e8:28:c6:f9:c7:fb:57:06:ad:0b:eb:86:ca:
|
||||
0e:d2:a8:67
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID1TCCAr2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJERTEU
|
||||
MBIGA1UECgwLcHl0aG9uLWxkYXAxEzARBgNVBAsMCnNsYXBkLXRlc3QxHDAaBgNV
|
||||
BAMME1B5dGhvbiBMREFQIFRlc3QgQ0EwHhcNMTcxMjAyMTE1NzQ4WhcNMjcxMjAy
|
||||
MTE1NzQ4WjBcMQswCQYDVQQGEwJERTEUMBIGA1UECgwLcHl0aG9uLWxkYXAxEzAR
|
||||
BgNVBAsMCnNsYXBkLXRlc3QxIjAgBgNVBAMMGXNlcnZlciBjZXJ0IGZvciBsb2Nh
|
||||
bGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAgcI7Pj89Aw4r
|
||||
rb+N8j3t1ynJgXRhQNxbxQcQmUCi8AtpGKNXu+aM9u2HxZ677ALfhsEivtQA5QKz
|
||||
Ll5G2G2IQa7uzgIco73OL/kMZIJt7sKfnvfACtSoOlD0IyOzVEEu0AVA7hMHb6Ul
|
||||
I0mCNfdk2FWFbc1nUmIAEBhIODbFoNW+Rc6lz94NsUqArDayvWpAsXkUbubQikpi
|
||||
KNX1OpOOC9GUbEhwI/G90ZnUg9STk/2oxsvwLlYdOhGhpyfl53tTu7eLMBriMxFl
|
||||
UTvkY1GUgPj0fA/giWtCerGOyeKu1hFlbS4LjborsfrlyYPwfwTg3YL4hVnt9fF0
|
||||
rpjzY1mlAgMBAAGjgacwgaQwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAw
|
||||
FgYDVR0lAQH/BAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFBt4RUANUIqLO8EK+D96
|
||||
SHumPCgJMB8GA1UdIwQYMBaAFDsfMvT+V9FvSZFV8iTxCmY7pe7UMCwGA1UdEQQl
|
||||
MCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEArQg/fbEJoaVsw1iAHeUzpbvAMzmVqojuxI44O1mnDjl0bP4RM176
|
||||
UMsgS2e3yV6Wp57YR0bhq/5di5otGhtDCPmTDyrjzoNKlM0C8I4l8kENVRD1TFs5
|
||||
i3deq3gWZKFI1eH2aZoP2DCmzJJNgd9GdKvPHbfUAblt1fQUuNVUhHkRQmlVf3TO
|
||||
AZYvP1EjsxH7ctxMuaOJ7zHkwEkG+o0JceHBdKnt+JaHZxa1XRZdWXD/HLWhbNIi
|
||||
EToOb3abacvzhad5rVP1NOiHzN0JUSXgKO55oKPcCt3wG+PJXxTTlfUSTSOVRSw8
|
||||
MpStzh6gX+boKMb5x/tXBq0L64bKDtKoZw==
|
||||
-----END CERTIFICATE-----
|
||||
Reference in New Issue
Block a user