This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository 389-ds-base.
commit d9ad6fd9b0cec2b41f0a21798958f97d37e018e2
Author: William Brown <firstyear(a)redhat.com>
Date: Mon Nov 27 14:47:44 2017 +0100
Ticket 49218 - Certmap - support TLS tests
Bug Description: This adds support for pluggable certificate
mapping libraries. To achieve this, this replaces the existing
baked in certificate mapping code.
Fix Description: Improve our tls tests to cover more cases,
support external signing cas, user certs, and addition of
TLS by default to tests. This fixes some tests to use the
new interfaces, as well as extending topologies to allow tls
enabling.
https://pagure.io/389-ds-base/issue/49218
https://pagure.io/lib389/issue/95
https://pagure.io/lib389/issue/84
Author: wibrown
Review by: spichugi (Thank you!)
---
.../tests/stress/reliabilty/reliab_7_5_test.py | 26 ++-
.../tests/stress/reliabilty/reliab_conn_test.py | 15 +-
dirsrvtests/tests/suites/sasl/plain_test.py | 32 +--
dirsrvtests/tests/tickets/ticket48784_test.py | 68 +-----
dirsrvtests/tests/tickets/ticket48798_test.py | 73 +------
ldap/ldif/template-dse.ldif.in | 15 ++
ldap/schema/30ns-common.ldif | 3 +-
rpm/389-ds-base.spec.in | 8 +-
src/lib389/lib389/__init__.py | 195 +++++++++++------
src/lib389/lib389/_mapped_object.py | 43 ++--
src/lib389/lib389/idm/account.py | 26 +++
src/lib389/lib389/idm/directorymanager.py | 42 ++++
src/lib389/lib389/idm/services.py | 5 +-
src/lib389/lib389/idm/user.py | 17 ++
src/lib389/lib389/instance/options.py | 5 +
src/lib389/lib389/instance/setup.py | 47 +++-
src/lib389/lib389/nss_ssl.py | 242 ++++++++++++++++-----
src/lib389/lib389/tests/nss_ssl_test.py | 89 +++++---
src/lib389/lib389/topologies.py | 15 +-
19 files changed, 590 insertions(+), 376 deletions(-)
diff --git a/dirsrvtests/tests/stress/reliabilty/reliab_7_5_test.py
b/dirsrvtests/tests/stress/reliabilty/reliab_7_5_test.py
index 2be6167..3fdbd67 100644
--- a/dirsrvtests/tests/stress/reliabilty/reliab_7_5_test.py
+++ b/dirsrvtests/tests/stress/reliabilty/reliab_7_5_test.py
@@ -19,6 +19,8 @@ from lib389.properties import *
from lib389.tasks import *
from lib389.utils import *
+from lib389.idm.directorymanager import DirectoryManager
+
logging.getLogger(__name__).setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s' +
' - %(message)s')
@@ -34,6 +36,7 @@ CHECK_CONVERGENCE = True
ENABLE_VALGRIND = False
RUNNING = True
+DEBUGGING = os.getenv('DEBUGGING', default=False)
class TopologyReplication(object):
def __init__(self, master1, master2):
@@ -50,9 +53,10 @@ def topology(request):
args_instance[SER_DEPLOYED_DIR] = installation1_prefix
# Creating master 1...
- master1 = DirSrv(verbose=False)
+ master1 = DirSrv(verbose=DEBUGGING)
args_instance[SER_HOST] = HOST_MASTER_1
args_instance[SER_PORT] = PORT_MASTER_1
+ args_instance[SER_SECURE_PORT] = SECUREPORT_MASTER_1
args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
args_master = args_instance.copy()
@@ -66,9 +70,10 @@ def topology(request):
replicaId=REPLICAID_MASTER_1)
# Creating master 2...
- master2 = DirSrv(verbose=False)
+ master2 = DirSrv(verbose=DEBUGGING)
args_instance[SER_HOST] = HOST_MASTER_2
args_instance[SER_PORT] = PORT_MASTER_2
+ args_instance[SER_SECURE_PORT] = SECUREPORT_MASTER_2
args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
args_master = args_instance.copy()
@@ -220,7 +225,8 @@ class AddDelUsers(threading.Thread):
idx = 0
RDN = 'uid=add_del_master_' + self.id + '-'
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
+
while idx < NUM_USERS:
USER_DN = RDN + str(idx) + ',' + DEFAULT_SUFFIX
try:
@@ -236,7 +242,7 @@ class AddDelUsers(threading.Thread):
conn.close()
# Delete 5000 entries
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
idx = 0
while idx < NUM_USERS:
USER_DN = RDN + str(idx) + ',' + DEFAULT_SUFFIX
@@ -259,7 +265,7 @@ class ModUsers(threading.Thread):
def run(self):
# Mod existing entries
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
idx = 0
while idx < NUM_USERS:
USER_DN = ('uid=master' + self.id + '_entry' + str(idx) +
',' +
@@ -275,7 +281,7 @@ class ModUsers(threading.Thread):
conn.close()
# Modrdn existing entries
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
idx = 0
while idx < NUM_USERS:
USER_DN = ('uid=master' + self.id + '_entry' + str(idx) +
',' +
@@ -290,7 +296,7 @@ class ModUsers(threading.Thread):
conn.close()
# Undo modrdn to we can rerun this test
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
idx = 0
while idx < NUM_USERS:
USER_DN = ('cn=master' + self.id + '_entry' + str(idx) +
',' +
@@ -315,7 +321,7 @@ class DoSearches(threading.Thread):
def run(self):
# Equality
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
idx = 0
while idx < NUM_USERS:
search_filter = ('(|(uid=master' + self.id + '_entry' +
str(idx) +
@@ -333,7 +339,7 @@ class DoSearches(threading.Thread):
conn.close()
# Substring
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
idx = 0
while idx < NUM_USERS:
search_filter = ('(|(uid=master' + self.id + '_entry' +
str(idx) +
@@ -360,7 +366,7 @@ class DoFullSearches(threading.Thread):
def run(self):
global RUNNING
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind()
while RUNNING:
time.sleep(2)
try:
diff --git a/dirsrvtests/tests/stress/reliabilty/reliab_conn_test.py
b/dirsrvtests/tests/stress/reliabilty/reliab_conn_test.py
index d3ee773..c14f88d 100644
--- a/dirsrvtests/tests/stress/reliabilty/reliab_conn_test.py
+++ b/dirsrvtests/tests/stress/reliabilty/reliab_conn_test.py
@@ -12,8 +12,9 @@ from lib389._constants import *
from lib389.properties import *
from lib389.tasks import *
from lib389.utils import *
+from lib389.idm.directorymanager import DirectoryManager
-DEBUGGING = False
+DEBUGGING = os.getenv('DEBUGGING', default=False)
if DEBUGGING:
logging.getLogger(__name__).setLevel(logging.DEBUG)
@@ -41,12 +42,10 @@ def topology(request):
"""Create DS Deployment"""
# Creating standalone instance ...
- if DEBUGGING:
- standalone = DirSrv(verbose=True)
- else:
- standalone = DirSrv(verbose=False)
+ standalone = DirSrv(verbose=DEBUGGING)
args_instance[SER_HOST] = HOST_STANDALONE
args_instance[SER_PORT] = PORT_STANDALONE
+ args_instance[SER_SECURE_PORT] = SECUREPORT_STANDALONE
args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
args_standalone = args_instance.copy()
@@ -129,7 +128,7 @@ class BindOnlyConn(threading.Thread):
global STOP
while idx < MAX_CONNS and not STOP:
try:
- conn = self.inst.openConnection()
+ conn = DirectoryManager(self.inst).bind(connOnly=True)
conn.unbind_s()
time.sleep(.2)
err_count = 0
@@ -160,7 +159,7 @@ class IdleConn(threading.Thread):
global STOP
while idx < (MAX_CONNS / 10) and not STOP:
try:
- conn = self.inst.openConnection()
+ conn = self.inst.clone()
conn.simple_bind_s('uid=entry0,dc=example,dc=com',
'password')
conn.search_s('dc=example,dc=com', ldap.SCOPE_SUBTREE,
'uid=*')
@@ -197,7 +196,7 @@ class LongConn(threading.Thread):
global STOP
while idx < MAX_CONNS and not STOP:
try:
- conn = self.inst.openConnection()
+ conn = self.inst.clone()
conn.search_s('dc=example,dc=com', ldap.SCOPE_SUBTREE,
'objectclass=*')
conn.search_s('dc=example,dc=com', ldap.SCOPE_SUBTREE,
diff --git a/dirsrvtests/tests/suites/sasl/plain_test.py
b/dirsrvtests/tests/suites/sasl/plain_test.py
index cff2507..b907126 100644
--- a/dirsrvtests/tests/suites/sasl/plain_test.py
+++ b/dirsrvtests/tests/suites/sasl/plain_test.py
@@ -15,7 +15,7 @@ from lib389.topologies import topology_st
from lib389.utils import *
from lib389._constants import DEFAULT_SUFFIX, DEFAULT_SECURE_PORT
from lib389.sasl import PlainSASL
-from lib389.idm.services import ServiceAccounts
+from lib389.idm.services import ServiceAccounts, ServiceAccount
log = logging.getLogger(__name__)
@@ -58,27 +58,7 @@ def test_basic_feature(topology_st):
"""
standalone = topology_st.standalone
-
- # SETUP TLS
- standalone.stop()
- # Prepare SSL but don't enable it.
- for f in ('key3.db', 'cert8.db', 'key4.db',
'cert9.db', 'secmod.db', 'pkcs11.txt'):
- try:
- os.remove("%s/%s" % (standalone.confdir, f))
- except:
- pass
- assert(standalone.nss_ssl.reinit() is True)
- assert(standalone.nss_ssl.create_rsa_ca() is True)
- assert(standalone.nss_ssl.create_rsa_key_and_cert() is True)
- # Start again
- standalone.start()
- standalone.rsa.create()
- # Set the secure port and nsslapd-security
- # Could this fail with selinux?
- standalone.config.set('nsslapd-secureport', str(DEFAULT_SECURE_PORT))
- standalone.config.set('nsslapd-security', 'on')
- # Do we need to restart to allow starttls?
- standalone.restart()
+ standalone.enable_tls()
# Create a user
sas = ServiceAccounts(standalone, DEFAULT_SUFFIX)
@@ -96,20 +76,18 @@ def test_basic_feature(topology_st):
# Check that it fails without TLS
with pytest.raises(ldap.AUTH_UNKNOWN):
- standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens,
starttls=False, connOnly=True)
+ conn = sa.sasl_bind(uri=standalone.get_ldap_uri(), saslmethod='PLAIN',
sasltoken=auth_tokens, connOnly=True)
# We *have* to use REQCERT NEVER here because python ldap fails cert verification for
.... some reason that even
# I can not solve. I think it's leaking state across connections in start_tls_s?
# Check that it works with TLS
- conn = standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens,
starttls=True, connOnly=True,
- certdir=standalone.get_cert_dir(),
reqcert=ldap.OPT_X_TLS_NEVER)
+ conn = sa.sasl_bind(uri=standalone.get_ldaps_uri(), saslmethod='PLAIN',
sasltoken=auth_tokens, connOnly=True)
conn.close()
# Check that it correct fails our bind if we don't have the password.
auth_tokens = PlainSASL("dn:%s" % sa.dn, 'password-wrong')
with pytest.raises(ldap.INVALID_CREDENTIALS):
- standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens,
starttls=True, connOnly=True,
- certdir=standalone.get_cert_dir(),
reqcert=ldap.OPT_X_TLS_NEVER)
+ conn = sa.sasl_bind(uri=standalone.get_ldaps_uri(), saslmethod='PLAIN',
sasltoken=auth_tokens, connOnly=True)
# Done!
diff --git a/dirsrvtests/tests/tickets/ticket48784_test.py
b/dirsrvtests/tests/tickets/ticket48784_test.py
index 0f63302..8924fe2 100644
--- a/dirsrvtests/tests/tickets/ticket48784_test.py
+++ b/dirsrvtests/tests/tickets/ticket48784_test.py
@@ -44,70 +44,6 @@ def add_entry(server, name, rdntmpl, start, num):
log.error('Failed to add %s ' % dn + e.message['desc'])
assert False
-
-def enable_ssl(server, ldapsport, copy_serv=False):
- server.stop()
- server.nss_ssl.reinit()
- if copy_serv:
- ca_cert = copy_serv.get_cert_dir() + "/ca.crt"
- os.system('cp %s/*.db %s' % (copy_serv.get_cert_dir(),
server.get_cert_dir()))
- os.system('cp %s %s' % (ca_cert, server.get_cert_dir()))
- os.system('cp %s/noise* %s' % (copy_serv.get_cert_dir(),
server.get_cert_dir()))
- os.system('cp %s/p* %s' % (copy_serv.get_cert_dir(),
server.get_cert_dir()))
- else:
- server.nss_ssl.create_rsa_ca()
- server.nss_ssl.create_rsa_key_and_cert()
- server.start()
-
- server.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3', 'off'),
- (ldap.MOD_REPLACE, 'nsTLS1', 'on'),
- (ldap.MOD_REPLACE, 'nsSSLClientAuth',
'allowed'),
- (ldap.MOD_REPLACE, 'allowWeakCipher',
'on'),
- (ldap.MOD_REPLACE, 'nsSSL3Ciphers',
'+all')])
-
- time.sleep(1)
- server.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-security',
'on'),
- (ldap.MOD_REPLACE, 'nsslapd-ssl-check-hostname',
'off'),
- (ldap.MOD_REPLACE, 'nsslapd-secureport',
ldapsport)])
-
- time.sleep(1)
- server.add_s(Entry((RSA_DN, {'objectclass': "top
nsEncryptionModule".split(),
- 'cn': RSA,
- 'nsSSLPersonalitySSL': SERVERCERT,
- 'nsSSLToken': 'internal (software)',
- 'nsSSLActivation': 'on'})))
- time.sleep(1)
- server.restart()
-
-
-def doAndPrintIt(cmdline, filename):
- proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- if filename is None:
- log.info(" OUT:")
- else:
- log.info(" OUT: %s" % filename)
- fd = open(filename, "w")
- while True:
- l = proc.stdout.readline()
- if l == "":
- break
- if filename is None:
- log.info(" %s" % l)
- else:
- fd.write(l)
- log.info(" ERR:")
- while True:
- l = proc.stderr.readline()
- if l == "" or l == "\n":
- break
- log.info(" <%s>" % l)
- assert False
-
- if filename is not None:
- fd.close()
- time.sleep(1)
-
-
def config_tls_agreements(topology_m2):
log.info("######################### Configure SSL/TLS agreements
######################")
log.info("######################## master1 <-- startTLS -> master2
#####################")
@@ -152,8 +88,8 @@ def test_ticket48784(topology_m2):
log.info("Ticket 48784 - Allow usage of OpenLDAP libraries that don't use
NSS for crypto")
#create_keys_certs(topology_m2)
- enable_ssl(topology_m2.ms["master1"], '636')
- enable_ssl(topology_m2.ms["master2"], '637',
topology_m2.ms["master1"])
+ [i.enable_tls() for i in topology_m2]
+
config_tls_agreements(topology_m2)
add_entry(topology_m2.ms["master1"], 'master1',
'uid=m1user', 0, 5)
diff --git a/dirsrvtests/tests/tickets/ticket48798_test.py
b/dirsrvtests/tests/tickets/ticket48798_test.py
index 80f307c..f1f8d98 100644
--- a/dirsrvtests/tests/tickets/ticket48798_test.py
+++ b/dirsrvtests/tests/tickets/ticket48798_test.py
@@ -4,6 +4,7 @@ import pytest
from lib389.tasks import *
from lib389.utils import *
from lib389.topologies import topology_st
+from lib389.config import Encryption
from lib389._constants import DEFAULT_SUFFIX, DEFAULT_SECURE_PORT
@@ -18,9 +19,9 @@ def check_socket_dh_param_size(hostname, port):
HOSTNAME=hostname,
PORT=port)
output = check_output(cmd, shell=True)
- dhheader = output.split('\n')[1]
+ dhheader = output.split(b'\n')[1]
# Get rid of all the other whitespace.
- dhheader = dhheader.replace(' ', '')
+ dhheader = dhheader.replace(b' ', b'')
# Example is 0c00040b0100ffffffffffffffffadf8
# We need the bits 0100 here. Which means 256 bytes aka 256 * 8, for 2048 bit.
dhheader = dhheader[8:12]
@@ -34,78 +35,26 @@ def test_ticket48798(topology_st):
Test DH param sizes offered by DS.
"""
-
- # Create a CA
- # This is a trick. The nss db that ships with DS is broken fundamentally.
- ## THIS ASSUMES old nss format. SQLite will bite us!
- for f in ('key3.db', 'cert8.db', 'key4.db',
'cert9.db', 'secmod.db', 'pkcs11.txt'):
- try:
- os.remove("%s/%s" % (topology_st.standalone.confdir, f))
- except:
- pass
-
- # Check if the db exists. Should be false.
- assert (topology_st.standalone.nss_ssl._db_exists() is False)
- time.sleep(0.5)
-
- # Create it. Should work.
- assert (topology_st.standalone.nss_ssl.reinit() is True)
- time.sleep(0.5)
-
- # Check if the db exists. Should be true
- assert (topology_st.standalone.nss_ssl._db_exists() is True)
- time.sleep(0.5)
-
- # Check if ca exists. Should be false.
- assert (topology_st.standalone.nss_ssl._rsa_ca_exists() is False)
- time.sleep(0.5)
-
- # Create it. Should work.
- assert (topology_st.standalone.nss_ssl.create_rsa_ca() is True)
- time.sleep(0.5)
-
- # Check if ca exists. Should be true
- assert (topology_st.standalone.nss_ssl._rsa_ca_exists() is True)
- time.sleep(0.5)
-
- # Check if we have a server cert / key. Should be false.
- assert (topology_st.standalone.nss_ssl._rsa_key_and_cert_exists() is False)
- time.sleep(0.5)
-
- # Create it. Should work.
- assert (topology_st.standalone.nss_ssl.create_rsa_key_and_cert() is True)
- time.sleep(0.5)
-
- # Check if server cert and key exist. Should be true.
- assert (topology_st.standalone.nss_ssl._rsa_key_and_cert_exists() is True)
- time.sleep(0.5)
-
- topology_st.standalone.config.enable_ssl(secport=DEFAULT_SECURE_PORT,
secargs={'nsSSL3Ciphers': '+all'})
-
- topology_st.standalone.restart(30)
+ topology_st.standalone.enable_tls()
# Confirm that we have a connection, and that it has DH
# Open a socket to the port.
# Check the security settings.
- size = check_socket_dh_param_size(topology_st.standalone.host, DEFAULT_SECURE_PORT)
+ size = check_socket_dh_param_size(topology_st.standalone.host,
topology_st.standalone.sslport)
- assert (size == 2048)
+ assert size == 2048
# Now toggle the settings.
- mod = [(ldap.MOD_REPLACE, 'allowWeakDHParam', 'on')]
- dn_enc = 'cn=encryption,cn=config'
- topology_st.standalone.modify_s(dn_enc, mod)
+ enc = Encryption(topology_st.standalone)
+ enc.set('allowWeakDHParam', 'on')
- topology_st.standalone.restart(30)
+ topology_st.standalone.restart()
# Check the DH params are less than 1024.
- size = check_socket_dh_param_size(topology_st.standalone.host, DEFAULT_SECURE_PORT)
-
- assert (size == 1024)
-
- log.info('Test complete')
+ size = check_socket_dh_param_size(topology_st.standalone.host,
topology_st.standalone.sslport)
+ assert size == 1024
if __name__ == '__main__':
# Run isolated
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 3107903..a034325 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -21,6 +21,21 @@ nsslapd-auditfaillog: %log_dir%/audit
nsslapd-rootdn: %rootdn%
nsslapd-rootpw: %ds_passwd%
+dn: cn=encryption,cn=config
+objectClass: top
+objectClass: nsEncryptionConfig
+cn: encryption
+nsSSLSessionTimeout: 0
+nsSSLClientAuth: allowed
+
+dn: cn=RSA,cn=encryption,cn=config
+objectClass: top
+objectClass: nsEncryptionModule
+cn: RSA
+nsSSLPersonalitySSL: Server-Cert
+nsSSLActivation: on
+nsSSLToken: internal (software)
+
dn: cn=features,cn=config
objectclass: top
objectclass: nsContainer
diff --git a/ldap/schema/30ns-common.ldif b/ldap/schema/30ns-common.ldif
index b095909..2b6edc1 100644
--- a/ldap/schema/30ns-common.ldif
+++ b/ldap/schema/30ns-common.ldif
@@ -53,6 +53,7 @@ attributeTypes: ( nsExecRef-oid NAME 'nsExecRef' DESC
'Netscape defined attribut
attributeTypes: ( nsLogSuppress-oid NAME 'nsLogSuppress' DESC 'Netscape
defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN
'Netscape' )
attributeTypes: ( nsJarfilename-oid NAME 'nsJarfilename' DESC 'Netscape
defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN
'Netscape' )
attributeTypes: ( nsClassname-oid NAME 'nsClassname' DESC 'Netscape defined
attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2337 NAME 'nsCertSubjectDN' DESC 'An
x509 DN from a certificate used to map during a TLS bind process' SYNTAX
1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN '389 Directory Server Project' )
objectClasses: ( nsAdminDomain-oid NAME 'nsAdminDomain' DESC 'Netscape
defined objectclass' SUP organizationalUnit MAY ( nsAdminDomainName ) X-ORIGIN
'Netscape' )
objectClasses: ( nsHost-oid NAME 'nsHost' DESC 'Netscape defined
objectclass' SUP top MUST ( cn ) MAY ( serverHostName $ description $ l $
nsHostLocation $ nsHardwarePlatform $ nsOsVersion ) X-ORIGIN 'Netscape' )
objectClasses: ( nsAdminGroup-oid NAME 'nsAdminGroup' DESC 'Netscape defined
objectclass' SUP top MUST ( cn ) MAY ( nsAdminGroupName $ description $ nsConfigRoot $
nsAdminSIEDN ) X-ORIGIN 'Netscape' )
@@ -64,4 +65,4 @@ objectClasses: ( nsAdminObject-oid NAME 'nsAdminObject' DESC
'Netscape defined o
objectClasses: ( nsConfig-oid NAME 'nsConfig' DESC 'Netscape defined
objectclass' SUP top MUST ( cn ) MAY ( description $ nsServerPort $ nsServerAddress $
nsSuiteSpotUser $ nsErrorLog $ nsPidLog $ nsAccessLog $ nsDefaultAcceptLanguage $
nsServerSecurity ) X-ORIGIN 'Netscape' )
objectClasses: ( nsDirectoryInfo-oid NAME 'nsDirectoryInfo' DESC 'Netscape
defined objectclass' SUP top MUST ( cn ) MAY ( nsBindDN $ nsBindPassword $
nsDirectoryURL $ nsDirectoryFailoverList $ nsDirectoryInfoRef ) X-ORIGIN
'Netscape' )
objectClasses: ( 2.16.840.1.113730.3.2.329 NAME 'nsMemberOf' DESC 'Allow
memberOf assignment on groups for nesting and users' SUP top AUXILIARY MAY ( memberOf
) X-ORIGIN '389 Directory Server Project' )
-
+objectClasses: ( 2.16.840.1.113730.3.2.331 NAME 'nsAccount' DESC 'A
representation of a user in a directory server' SUP top AUXILIARY MAY (
userCertificate $ nsCertSubjectDN ) X-ORIGIN '389 Directory Server Project' )
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
index 7938d16..76fb96e 100644
--- a/rpm/389-ds-base.spec.in
+++ b/rpm/389-ds-base.spec.in
@@ -235,9 +235,10 @@ Summary: A library for accessing, testing, and configuring the 389
Directory Se
BuildArch: noarch
Group: Development/Libraries
Requires: krb5-workstation
-Requires: krb5-server
Requires: openssl
Requires: iproute
+# This is for /usr/sbin/cacertdir_rehash
+Requires: authconfig
Requires: python%{python3_pkgversion}
Requires: python%{python3_pkgversion}-pytest
Requires: python%{python3_pkgversion}-pyldap
@@ -254,8 +255,9 @@ This module contains tools and libraries for accessing, testing,
Summary: The lib389 Continuous Integration Tests
Group: Development/Libraries
BuildArch: noarch
-Requires: python%{python3_pkgversion}
-Requires: python%{python3_pkgversion}-lib389
+Requires: krb5-server
+Requires: python%{python3_pkgversion}
+Requires: python%{python3_pkgversion}-lib389
%description -n python%{python3_pkgversion}-%{srcname}-tests
The lib389 CI tests that can be run against the Directory Server.
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
index 1f15a6d..e23ae91 100644
--- a/src/lib389/lib389/__init__.py
+++ b/src/lib389/lib389/__init__.py
@@ -73,6 +73,7 @@ from lib389._ldifconn import LDIFConn
from lib389.tools import DirSrvTools
from lib389.mit_krb5 import MitKrb5
from lib389.utils import (
+ ds_is_older,
isLocalHost,
is_a_dn,
normalizeDN,
@@ -83,6 +84,7 @@ from lib389.utils import (
ensure_bytes,
ensure_str)
from lib389.paths import Paths
+from lib389.nss_ssl import NssSsl
# mixin
# from lib389.tools import DirSrvTools
@@ -292,7 +294,6 @@ class DirSrv(SimpleLDAPObject, object):
def __add_brookers__(self):
from lib389.config import Config
from lib389.aci import Aci
- from lib389.nss_ssl import NssSsl
from lib389.config import RSA
from lib389.config import Encryption
from lib389.dirsrv_log import DirsrvAccessLog, DirsrvErrorLog
@@ -334,7 +335,6 @@ class DirSrv(SimpleLDAPObject, object):
self.mappingtrees = MappingTrees(self)
self.replicas = Replicas(self)
self.aci = Aci(self)
- self.nss_ssl = NssSsl(self)
self.rsa = RSA(self)
self.encryption = Encryption(self)
self.ds_access_log = DirsrvAccessLog(self)
@@ -503,51 +503,48 @@ class DirSrv(SimpleLDAPObject, object):
raise ValueError("invalid state for calling allocate: %s" %
self.state)
+ self.isLocal = False
if SER_SERVERID_PROP not in args:
self.log.debug('SER_SERVERID_PROP not provided, assuming non-local
instance')
# The lack of this value basically rules it out in most cases
- self.isLocal = False
self.ds_paths = Paths(instance=self)
else:
self.ds_paths = Paths(args[SER_SERVERID_PROP], instance=self)
-
+ # Settings from args of server attributes
+ self.serverid = args.get(SER_SERVERID_PROP, None)
+ # Probably local?
+ self.isLocal = True
# Do we have ldapi settings?
# Do we really need .strip() on this?
self.ldapi_enabled = args.get(SER_LDAPI_ENABLED, 'off')
self.ldapi_socket = args.get(SER_LDAPI_SOCKET, None)
- self.host = None
- self.ldapuri = None
- self.sslport = None
- self.port = None
+ self.ldapuri = args.get(SER_LDAP_URL, None)
+ self.log.debug("Allocate %s with %s" % (self.__class__, self.ldapuri))
+ # Still needed in setup, even if ldapuri over writes.
+ self.host = args.get(SER_HOST, LOCALHOST)
+ self.port = args.get(SER_PORT, DEFAULT_PORT)
+ self.sslport = args.get(SER_SECURE_PORT)
+
self.inst_scripts = args.get(SER_INST_SCRIPTS_ENABLED, None)
+
# Or do we have tcp / ip settings?
if self.ldapi_enabled == 'on' and self.ldapi_socket is not None:
self.ldapi_autobind = args.get(SER_LDAPI_AUTOBIND, 'off')
self.isLocal = True
if self.verbose:
self.log.info("Allocate %s with %s" % (self.__class__,
self.ldapi_socket))
- elif args.get(SER_LDAP_URL, None) is not None:
- self.ldapuri = args.get(SER_LDAP_URL)
- if self.verbose:
- self.log.info("Allocate %s with %s" % (self.__class__,
self.ldapuri))
- else:
- # Settings from args of server attributes
- self.strict_hostname = args.get(SER_STRICT_HOSTNAME_CHECKING, False)
- if self.strict_hostname is True:
- self.host = args.get(SER_HOST, LOCALHOST)
- if self.host == LOCALHOST:
- DirSrvTools.testLocalhost()
- else:
- # Make sure our name is in hosts
- DirSrvTools.searchHostsFile(self.host, None)
+ # Settings from args of server attributes
+ self.strict_hostname = args.get(SER_STRICT_HOSTNAME_CHECKING, False)
+ if self.strict_hostname is True:
+ if self.host == LOCALHOST:
+ DirSrvTools.testLocalhost()
else:
- self.host = args.get(SER_HOST, LOCALHOST_SHORT)
- self.port = args.get(SER_PORT, DEFAULT_PORT)
- self.sslport = args.get(SER_SECURE_PORT)
+ # Make sure our name is in hosts
+ DirSrvTools.searchHostsFile(self.host, None)
self.isLocal = isLocalHost(self.host)
- if self.verbose:
- self.log.info("Allocate %s with %s:%s" % (self.__class__,
self.host, (self.sslport or self.port)))
+
+ self.log.debug("Allocate %s with %s:%s" % (self.__class__, self.host,
(self.sslport or self.port)))
self.binddn = args.get(SER_ROOT_DN, DN_DM)
self.bindpw = args.get(SER_ROOT_PW, PW_DM)
@@ -562,8 +559,6 @@ class DirSrv(SimpleLDAPObject, object):
else:
self.userid = pwd.getpwuid(os.getuid())[0]
- # Settings from args of server attributes
- self.serverid = args.get(SER_SERVERID_PROP, None)
self.groupid = args.get(SER_GROUP_ID, self.userid)
self.backupdir = args.get(SER_BACKUP_INST_DIR, DEFAULT_BACKUPDIR)
# Allocate from the args, or use our env, or use /
@@ -589,23 +584,22 @@ class DirSrv(SimpleLDAPObject, object):
(self.sslport or
self.port)))
- def openConnection(self, *args, **kwargs):
+ def clone(self, args_instance={}):
"""
Open a new connection to our LDAP server
*IMPORTANT*
- This is different to re-opening on the same dirsrv, as bugs in pyldap
+ This is different to re-opening on the same dirsrv, as quirks in pyldap
mean that ldap.set_option doesn't take effect! You need to use this
- to allow some of the start TLS options to work!
+ to allow all of the start TLS options to work!
"""
server = DirSrv(verbose=self.verbose)
+ args_instance[SER_LDAP_URL] = self.ldapuri
args_instance[SER_HOST] = self.host
args_instance[SER_PORT] = self.port
- if self.sslport is not None:
- args_instance[SER_SECURE_PORT] = self.sslport
+ args_instance[SER_SECURE_PORT] = self.sslport
args_instance[SER_SERVERID_PROP] = self.serverid
args_standalone = args_instance.copy()
server.allocate(args_standalone)
- server.open(*args, **kwargs)
return server
@@ -919,8 +913,6 @@ class DirSrv(SimpleLDAPObject, object):
(self.prefix, self.serverid))
self.restart()
- # Restart the instance
-
def _createPythonDirsrv(self, version):
"""
Create a new dirsrv instance based on the new python installer, rather
@@ -944,6 +936,8 @@ class DirSrv(SimpleLDAPObject, object):
slapd_options.set('secure_port', self.sslport)
slapd_options.set('root_password', self.bindpw)
slapd_options.set('root_dn', self.binddn)
+ #We disable TLS during setup, we use a function in tests to enable instead.
+ slapd_options.set('self_sign_cert', False)
slapd_options.set('defaults', version)
slapd_options.verify()
@@ -1003,6 +997,10 @@ class DirSrv(SimpleLDAPObject, object):
else:
self._createDirsrv()
+ # Because of how this works, we force ldap:// only for now.
+ # A real install will have ldaps, and won't go via this path.
+ self.use_ldap_uri()
+
# Retrieve sroot from the sys/priv config file
assert(self.exists())
self.sroot = self.list()[0][CONF_SERVER_DIR]
@@ -1071,7 +1069,7 @@ class DirSrv(SimpleLDAPObject, object):
# Now, we are still an allocated ds object so we can be re-installed
self.state = DIRSRV_STATE_ALLOCATED
- def open(self, saslmethod=None, sasltoken=None, certdir=None, starttls=False,
connOnly=False, reqcert=ldap.OPT_X_TLS_HARD,
+ def open(self, uri=None, saslmethod=None, sasltoken=None, certdir=None,
starttls=False, connOnly=False, reqcert=ldap.OPT_X_TLS_HARD,
usercert=None, userkey=None):
'''
It opens a ldap bound connection to dirsrv so that online
@@ -1090,60 +1088,55 @@ class DirSrv(SimpleLDAPObject, object):
@raise LDAPError
'''
- ##################
- # WARNING: While you have a python ldap connection open some settings like
- # ldap.set_option MAY NOT WORK AS YOU EXPECT.
- # There are cases (especially CACERT/USERCERTS) where when one connection
- # is open set_option SILENTLY fails!!!!
- #
- # You MAY need to set post_open=False in your DirSrv start/restart instance!
- ##################
+ # Force our state offline to prevent paths from trying to search
+ # cn=config while we startup.
+ self.state = DIRSRV_STATE_OFFLINE
- uri = self.toLDAPURL()
+ if not uri:
+ uri = self.toLDAPURL()
+ self.log.debug('open(): Connecting to uri %s' % uri)
+ super(DirSrv, self).__init__(uri, bytes_mode=False, trace_level=TRACE_LEVEL)
if certdir is None and self.isLocal:
certdir = self.get_cert_dir()
self.log.debug("Using dirsrv ca certificate %s" % certdir)
+ if certdir is not None:
+ """
+ We have a certificate directory, so lets start up TLS negotiations
+ """
+ # Note this sets LDAP.OPT not SELF. Because once self has opened
+ # it can NOT change opts AT ALL.
+ self.set_option(ldap.OPT_X_TLS_CACERTDIR, ensure_str(certdir))
+ self.log.debug("Using external ca certificate %s" % certdir)
+
if userkey is not None:
# Note this sets LDAP.OPT not SELF. Because once self has opened
# it can NOT change opts AT ALL.
- ldap.set_option(ldap.OPT_X_TLS_KEYFILE, ensure_str(userkey))
self.log.debug("Using user private key %s" % userkey)
+ self.set_option(ldap.OPT_X_TLS_KEYFILE, ensure_str(userkey))
+
if usercert is not None:
- # Note this sets LDAP.OPT not SELF. Because once self has opened
- # it can NOT change opts AT ALL.
- ldap.set_option(ldap.OPT_X_TLS_CERTFILE, ensure_str(usercert))
self.log.debug("Using user certificate %s" % usercert)
+ self.set_option(ldap.OPT_X_TLS_CERTFILE, ensure_str(usercert))
if certdir is not None:
- """
- We have a certificate directory, so lets start up TLS negotiations
- """
- # Note this sets LDAP.OPT not SELF. Because once self has opened
- # it can NOT change opts AT ALL.
- ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, ensure_str(certdir))
self.log.debug("Using external ca certificate %s" % certdir)
+ self.set_option(ldap.OPT_X_TLS_CACERTDIR, ensure_str(certdir))
if certdir or starttls:
try:
# Note this sets LDAP.OPT not SELF. Because once self has opened
# it can NOT change opts on reused (ie restart)
- ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, reqcert)
+ self.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, reqcert)
self.log.debug("Using certificate policy %s" % reqcert)
self.log.debug("ldap.OPT_X_TLS_REQUIRE_CERT = %s" % reqcert)
except ldap.LDAPError as e:
self.log.fatal('TLS negotiation failed: %s' % str(e))
raise e
- ## NOW INIT THIS. This MUST be after all the ldap.OPT set above,
- # so that we inherit the settings correctly!!!!
- if self.verbose:
- self.log.info('open(): Connecting to uri %s' % uri)
- if hasattr(ldap, 'PYLDAP_VERSION') and MAJOR >= 3:
- super(DirSrv, self).__init__(uri, bytes_mode=False, trace_level=TRACE_LEVEL)
- else:
- super(DirSrv, self).__init__(uri, trace_level=TRACE_LEVEL)
+ # Tell python ldap to make a new TLS context with this information.
+ self.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
if starttls and not uri.startswith('ldaps'):
self.start_tls_s()
@@ -1664,6 +1657,69 @@ class DirSrv(SimpleLDAPObject, object):
"""Check if autobind/LDAPI is enabled."""
return self.ldapi_enabled == 'on' and self.ldapi_socket is not None and
self.ldapi_autobind == 'on'
+ def enable_tls(self, post_open=True):
+ """ If it doesn't exist, create a self-signed system CA. Using
that,
+ we create certificates for our instance, as well as configuring the
+ servers security settings. This is mainly used for test cases, if
+ you want to enable_Tls on a real instance, there are better ways to
+ achieve this.
+
+ :param post_open: Open the server connection after restart.
+ :type post_open: bool
+ """
+ # If it doesn't exist, create a cadb.
+ ssca_path = os.path.join(self.get_sysconf_dir(), 'dirsrv/ssca/')
+ ssca = NssSsl(dbpath=ssca_path)
+ if not ssca._db_exists():
+ ssca.reinit()
+ ssca.create_rsa_ca()
+
+ # Create certificate database.
+ tlsdb = NssSsl(dbpath=self.get_cert_dir())
+ # Remember, DS breaks the db, so force reinit it.
+ tlsdb.reinit()
+ csr = tlsdb.create_rsa_key_and_csr()
+ (ca, crt) = ssca.rsa_ca_sign_csr(csr)
+ tlsdb.import_rsa_crt(ca, crt)
+
+ self.config.set('nsslapd-security', 'on')
+ self.use_ldaps_uri()
+
+ if self.ds_paths.perl_enabled:
+ # We don't setup sslport correctly in perl installer ....
+ self.config.set('nsslapd-secureport', '%s' % self.sslport)
+ # If we are old, we don't have template dse, so enable manually.
+ if ds_is_older('1.4.0'):
+ if not self.encryption.exists():
+ self.encryption.create()
+ if not self.rsa.exists():
+ self.rsa.create()
+
+ # Restart the instance
+ self.restart(post_open=post_open)
+
+ def use_ldaps_uri(self):
+ """Change this connection to use ldaps (TLS) on the next .open()
call"""
+ self.ldapuri = 'ldaps://%s:%s' % (self.host, self.sslport)
+
+ def get_ldaps_uri(self):
+ """Return what our ldaps (TLS) uri would be for this instance
+
+ :returns: The string of the servers ldaps (TLS) uri.
+ """
+ return 'ldaps://%s:%s' % (self.host, self.sslport)
+
+ def use_ldap_uri(self):
+ """Change this connection to use ldap on the next .open()
call"""
+ self.ldapuri = 'ldap://%s:%s' % (self.host, self.port)
+
+ def get_ldap_uri(self):
+ """Return what our ldap uri would be for this instance
+
+ :returns: The string of the servers ldap uri.
+ """
+ return 'ldap://%s:%s' % (self.host, self.port)
+
def getServerId(self):
"""Return the server identifier."""
return self.serverid
@@ -1688,6 +1744,13 @@ class DirSrv(SimpleLDAPObject, object):
def get_sysconf_dir(self):
return self.ds_paths.sysconf_dir
+ def get_ssca_dir(self):
+ """Get the system self signed CA path.
+
+ :returns: The path to the CA nss db
+ """
+ return os.path.join(self.ds_paths.sysconf_dir, 'dirsrv/ssca')
+
def get_initconfig_dir(self):
return self.ds_paths.initconfig_dir
diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py
index 9f1d3d0..3644193 100644
--- a/src/lib389/lib389/_mapped_object.py
+++ b/src/lib389/lib389/_mapped_object.py
@@ -13,7 +13,7 @@ import logging
from functools import partial
from lib389._entry import Entry
-from lib389._constants import DIRSRV_STATE_ONLINE
+from lib389._constants import DIRSRV_STATE_ONLINE, SER_ROOT_DN, SER_ROOT_PW
from lib389.utils import (
ensure_bytes, ensure_str, ensure_int, ensure_list_bytes, ensure_list_str,
ensure_list_int
@@ -535,21 +535,6 @@ class DSLdapObject(DSLogging):
def set_values(self, values, action=ldap.MOD_REPLACE):
pass
- # If the account can be bound to, this will attempt to do so. We don't check
- # for exceptions, just pass them back!
- def bind(self, password=None, *args, **kwargs):
- """Open a new connection and bind with the entry.
- You can pass arguments that will be passed to openConnection.
-
- :param password: An entry password
- :type password: str
- :returns: Connection with a binding as the entry
- """
-
- conn = self._instance.openConnection(*args, **kwargs)
- conn.simple_bind_s(self.dn, password)
- return conn
-
# Modifies the DN of an entry to the new fqdn provided
def rename(self, new_rdn, newsuperior=None):
"""Renames the object within the tree.
@@ -640,7 +625,9 @@ class DSLdapObject(DSLogging):
if basedn is None:
raise ldap.UNWILLING_TO_PERFORM('Invalid request to create. basedn
cannot be None')
- if properties.get(self._rdn_attribute, None) is not None:
+ if rdn is not None:
+ tdn = ensure_str('%s,%s' % (rdn, basedn))
+ elif properties.get(self._rdn_attribute, None) is not None:
# Favour the value in the properties dictionary
v = properties.get(self._rdn_attribute)
rdn = ensure_str(v[0])
@@ -852,17 +839,17 @@ class DSLdapObjects(DSLogging):
raise ldap.UNWILLING_TO_PERFORM("properties must be a dictionary")
# Get the rdn out of the properties if it's unset???
- if rdn is None and self._rdn_attribute in properties:
- # First see if we can get it from the properties.
- trdn = properties.get(self._rdn_attribute)
- if type(trdn) == str:
- rdn = trdn
- elif type(trdn) == list and len(trdn) != 1:
- raise ldap.UNWILLING_TO_PERFORM("Cannot determine rdn %s from
properties. Too many choices" % (self._rdn_attribute))
- elif type(trdn) == list:
- rdn = trdn[0]
- else:
- raise ldap.UNWILLING_TO_PERFORM("Cannot determine rdn %s from
properties, Invalid type" % type(trdn))
+ # if rdn is None and self._rdn_attribute in properties:
+ # # First see if we can get it from the properties.
+ # trdn = properties.get(self._rdn_attribute)
+ # if type(trdn) == str:
+ # rdn = "%s=%s" % (self._rdn_attribute, trdn)
+ # elif type(trdn) == list and len(trdn) != 1:
+ # raise ldap.UNWILLING_TO_PERFORM("Cannot determine rdn %s from
properties. Too many choices" % (self._rdn_attribute))
+ # elif type(trdn) == list:
+ # rdn = "%s=%s" % (self._rdn_attribute, trdn[0])
+ # else:
+ # raise ldap.UNWILLING_TO_PERFORM("Cannot determine rdn %s from
properties, Invalid type" % type(trdn))
return (rdn, properties)
diff --git a/src/lib389/lib389/idm/account.py b/src/lib389/lib389/idm/account.py
index 9953f28..02c2954 100644
--- a/src/lib389/lib389/idm/account.py
+++ b/src/lib389/lib389/idm/account.py
@@ -7,6 +7,7 @@
# --- END COPYRIGHT BLOCK ---
from lib389._mapped_object import DSLdapObject, DSLdapObjects, _gen_or, _gen_filter,
_term_gen
+from lib389._constants import SER_ROOT_DN, SER_ROOT_PW
class Account(DSLdapObject):
@@ -36,6 +37,31 @@ class Account(DSLdapObject):
self.remove('nsAccountLock', None)
+ # If the account can be bound to, this will attempt to do so. We don't check
+ # for exceptions, just pass them back!
+ def bind(self, password=None, *args, **kwargs):
+ """Open a new connection and bind with the entry.
+ You can pass arguments that will be passed to openConnection.
+
+ :param password: An entry password
+ :type password: str
+ :returns: Connection with a binding as the entry
+ """
+
+ inst_clone = self._instance.clone({SER_ROOT_DN: self.dn, SER_ROOT_PW: password})
+ inst_clone.open(*args, **kwargs)
+ return inst_clone
+
+ def sasl_bind(self, *args, **kwargs):
+ """Open a new connection and bind with the entry via SASL.
+ You can pass arguments that will be pass to clone.
+
+ :return: Connection with a sasl binding to the entry.
+ """
+ inst_clone = self._instance.clone({SER_ROOT_DN: self.dn})
+ inst_clone.open(*args, **kwargs)
+ return inst_clone
+
class Accounts(DSLdapObjects):
"""DSLdapObjects that represents Account entry
diff --git a/src/lib389/lib389/idm/directorymanager.py
b/src/lib389/lib389/idm/directorymanager.py
new file mode 100644
index 0000000..4028fc6
--- /dev/null
+++ b/src/lib389/lib389/idm/directorymanager.py
@@ -0,0 +1,42 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2017, William Brown <william at blackhats.net.au>
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+from lib389.idm.account import Account
+from lib389._constants import DN_DM, PW_DM
+
+
+class DirectoryManager(Account):
+ """
+ The directory manager. This is a convinence class to help with rebinds
+ to the same server, as well as some other DM related specific tasks.
+ """
+
+ def __init__(self, instance, dn=DN_DM):
+ """The Directory Manager instance. Useful for binding in tests.
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: Entry DN
+ :type dn: str
+ """
+ super(DirectoryManager, self).__init__(instance, dn)
+ self._rdn_attribute = 'cn'
+ self._must_attributes = []
+ self._create_objectclasses = None
+ self._protected = True
+
+ def bind(self, password=PW_DM, *args, **kwargs):
+ """Bind as the Directory Manager. We have a default test password
+ that can be overriden.
+
+ :param password: The password to bind as for Directory Manager
+ :type password: str
+ :returns: A new connection bound as directory manager.
+ """
+ return super(DirectoryManager, self).bind(password, *args, **kwargs)
+
diff --git a/src/lib389/lib389/idm/services.py b/src/lib389/lib389/idm/services.py
index 50d8ed4..90b0ba9 100644
--- a/src/lib389/lib389/idm/services.py
+++ b/src/lib389/lib389/idm/services.py
@@ -6,14 +6,15 @@
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
-from lib389._mapped_object import DSLdapObject, DSLdapObjects
+from lib389._mapped_object import DSLdapObjects
+from lib389.idm.account import Account
RDN = 'cn'
MUST_ATTRIBUTES = [
'cn',
]
-class ServiceAccount(DSLdapObject):
+class ServiceAccount(Account):
"""A single instance of Service entry
:param instance: An instance
diff --git a/src/lib389/lib389/idm/user.py b/src/lib389/lib389/idm/user.py
index 176345c..c4d7928 100644
--- a/src/lib389/lib389/idm/user.py
+++ b/src/lib389/lib389/idm/user.py
@@ -60,6 +60,8 @@ class UserAccount(Account):
self._create_objectclasses.append('inetUser')
else:
self._create_objectclasses.append('nsMemberOf')
+ if not ds_is_older('1.4.0'):
+ self._create_objectclasses.append('nsAccount')
user_compare_exclude = [
'nsUniqueId',
'modifyTimestamp',
@@ -75,6 +77,21 @@ class UserAccount(Account):
return super(UserAccount, self)._validate(rdn, properties, basedn)
+ def enroll_certificate(self, der_path):
+ """Enroll a certificate for certmap verification. Because of the
userCertificate
+ attribute, we have to do this on userAccount which has support for it.
+
+ :param der_path: the certificate file in DER format to include.
+ :type der_path: str
+ """
+ if ds_is_older('1.4.0'):
+ raise Exception("This version of DS does not support nsAccount")
+ # Given a cert path, add this to the object as a userCertificate
+ crt = None
+ with open(der_path, 'rb') as f:
+ crt = f.read()
+ self.add('usercertificate;binary', crt)
+
# Add a set password function....
# Can't I actually just set, and it will hash?
diff --git a/src/lib389/lib389/instance/options.py
b/src/lib389/lib389/instance/options.py
index f057fe0..91fcec6 100644
--- a/src/lib389/lib389/instance/options.py
+++ b/src/lib389/lib389/instance/options.py
@@ -187,6 +187,11 @@ class Slapd2Base(Options2):
self._helptext['secure_port'] = "The TCP port that Directory Server
will listen on for TLS secured LDAP connections."
self._example_comment['secure_port'] = True
+ self._options['self_sign_cert'] = True
+ self._type['self_sign_cert'] = bool
+ self._helptext['self_sign_cert'] = "Issue a self signed certificate
during the setup process. This is not suitable for production TLS, but aids simplifying
setup of TLS (you only need to replace a certificate instead)"
+ self._example_comment['self_sign_cert'] = True
+
# In the future, make bin and sbin /usr/[s]bin, but we may need autotools
assistance from Ds
self._options['bin_dir'] = ds_paths.bin_dir
self._type['bin_dir'] = str
diff --git a/src/lib389/lib389/instance/setup.py b/src/lib389/lib389/instance/setup.py
index 90929dd..fa5d99b 100644
--- a/src/lib389/lib389/instance/setup.py
+++ b/src/lib389/lib389/instance/setup.py
@@ -22,6 +22,8 @@ from lib389._constants import *
from lib389.properties import *
from lib389.passwd import password_hash, password_generate
+from lib389.nss_ssl import NssSsl
+
from lib389.configurations import get_config
from lib389.instance.options import General2Base, Slapd2Base
@@ -268,10 +270,9 @@ class SetupDs(object):
assert(slapd['port'] is not None)
assert(socket_check_open('::1', slapd['port']) is False)
- ## This causes some problems in tests :(
- # assert(slapd['secure_port'] is not None)
- if slapd['secure_port'] is not None:
- assert(socket_check_open('::1', slapd['secure_port']) is
False)
+ # We enable secure port by default.
+ assert(slapd['secure_port'] is not None)
+ assert(socket_check_open('::1', slapd['secure_port']) is False)
if self.verbose:
self.log.info("PASSED: network avaliability checking")
@@ -358,7 +359,13 @@ class SetupDs(object):
srcfile = os.path.join(slapd['sysconf_dir'],
'dirsrv/config/slapd-collations.conf')
dstfile = os.path.join(slapd['config_dir'],
'slapd-collations.conf')
shutil.copy2(srcfile, dstfile)
- os.chown(slapd['schema_dir'], slapd['user_uid'],
slapd['group_gid'])
+ os.chown(dstfile, slapd['user_uid'], slapd['group_gid'])
+
+ # Copy in the certmap configuration
+ srcfile = os.path.join(slapd['sysconf_dir'],
'dirsrv/config/certmap.conf')
+ dstfile = os.path.join(slapd['config_dir'], 'certmap.conf')
+ shutil.copy2(srcfile, dstfile)
+ os.chown(dstfile, slapd['user_uid'], slapd['group_gid'])
# If we are on the correct platform settings, systemd
if general['systemd'] and not self.containerised:
@@ -424,9 +431,23 @@ class SetupDs(object):
ds_instance.allocate(args)
# Does this work?
assert(ds_instance.exists())
- # Create the nssdb
- assert(ds_instance.nss_ssl.reinit())
- # Do we want to selfsign a CA and cert?
+
+ # Create a certificate database.
+ tlsdb = NssSsl(dbpath=slapd['cert_dir'])
+ if not tlsdb._db_exists():
+ tlsdb.reinit()
+
+ if slapd['self_sign_cert']:
+ # If it doesn't exist, create a cadb.
+ ssca_path = os.path.join(slapd['sysconf_dir'],
'dirsrv/ssca/')
+ ssca = NssSsl(dbpath=ssca_path)
+ if not ssca._db_exists():
+ ssca.reinit()
+ ssca.create_rsa_ca()
+
+ csr = tlsdb.create_rsa_key_and_csr()
+ (ca, crt) = ssca.rsa_ca_sign_csr(csr)
+ tlsdb.import_rsa_crt(ca, crt)
## LAST CHANCE, FIX PERMISSIONS.
# Selinux fixups?
@@ -444,6 +465,11 @@ class SetupDs(object):
base_config_inst = base_config(ds_instance)
base_config_inst.apply_config(install=True)
+ # Setup TLS with the instance.
+ ds_instance.config.set('nsslapd-secureport', '%s' %
slapd['secure_port'])
+ if slapd['self_sign_cert']:
+ ds_instance.config.set('nsslapd-security', 'on')
+
# Create the backends as listed
# Load example data if needed.
for backend in backends:
@@ -460,7 +486,10 @@ class SetupDs(object):
ds_instance.config.set('nsslapd-rootpw',
ensure_str(slapd['root_password']))
- # In a container build we need to stop DirSrv at the end
if self.containerised:
+ # In a container build we need to stop DirSrv at the end
ds_instance.stop()
+ else:
+ # Restart for changes to take effect - this could be removed later
+ ds_instance.restart(post_open=False)
diff --git a/src/lib389/lib389/nss_ssl.py b/src/lib389/lib389/nss_ssl.py
index 90e78f9..a37c11f 100644
--- a/src/lib389/lib389/nss_ssl.py
+++ b/src/lib389/lib389/nss_ssl.py
@@ -15,6 +15,9 @@ import random
import string
import re
import socket
+import time
+import shutil
+import logging
# from nss import nss
from subprocess import check_call, check_output
from lib389.passwd import password_generate
@@ -27,25 +30,28 @@ CERT_NAME = 'Server-Cert'
USER_PREFIX = 'user-'
PIN_TXT = 'pin.txt'
PWD_TXT = 'pwdfile.txt'
-ISSUER = 'CN=ca.lib389.example.com,O=testing,L=lib389,ST=Queensland,C=AU'
-SELF_ISSUER = 'CN={HOSTNAME},O=testing,L=lib389,ST=Queensland,C=AU'
+CERT_SUFFIX = 'O=testing,L=389ds,ST=Queensland,C=AU'
+ISSUER = 'CN=ssca.389ds.example.com,%s' % CERT_SUFFIX
+SELF_ISSUER = 'CN={HOSTNAME},%s' % CERT_SUFFIX
VALID = 2
+# My logger
+log = logging.getLogger(__name__)
class NssSsl(object):
- def __init__(self, dirsrv, dbpassword=None):
+ def __init__(self, dirsrv=None, dbpassword=None, dbpath=None):
self.dirsrv = dirsrv
- self.log = self.dirsrv.log
+ self._certdb = dbpath
+ if self._certdb is None:
+ self._certdb = self.dirsrv.get_cert_dir()
+ self.log = log
+ if self.dirsrv is not None:
+ self.log = self.dirsrv.log
if dbpassword is None:
self.dbpassword = password_generate()
else:
self.dbpassword = dbpassword
- @property
- def _certdb(self):
- # return "sql:%s" % self.dirsrv.get_cert_dir()
- return self.dirsrv.get_cert_dir()
-
def _generate_noise(self, fpath):
noise = password_generate(256)
with open(fpath, 'w') as f:
@@ -60,38 +66,44 @@ class NssSsl(object):
for f in ('key3.db', 'cert8.db', 'key4.db',
'cert9.db', 'secmod.db', 'pkcs11.txt'):
try:
# Perhaps we should be backing these up instead ...
- os.remove("%s/%s" % (self.dirsrv.get_cert_dir(), f ))
+ os.remove("%s/%s" % (self._certdb, f ))
except:
pass
+ try:
+ os.makedirs(self._certdb)
+ except FileExistsError:
+ pass
+
# In the future we may add the needed option to avoid writing the pin
# files.
# Write the pin.txt, and the pwdfile.txt
- if not os.path.exists('%s/%s' % (self.dirsrv.get_cert_dir(), PIN_TXT)):
- with open('%s/%s' % (self.dirsrv.get_cert_dir(), PIN_TXT),
'w') as f:
+ if not os.path.exists('%s/%s' % (self._certdb, PIN_TXT)):
+ with open('%s/%s' % (self._certdb, PIN_TXT), 'w') as f:
f.write('Internal (Software) Token:%s' % self.dbpassword)
- if not os.path.exists('%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT)):
- with open('%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
'w') as f:
+ if not os.path.exists('%s/%s' % (self._certdb, PWD_TXT)):
+ with open('%s/%s' % (self._certdb, PWD_TXT), 'w') as f:
f.write('%s' % self.dbpassword)
# Init the db.
# 48886; This needs to be sql format ...
- cmd = ['/usr/bin/certutil', '-N', '-d', self._certdb,
'-f', '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT)]
- self.dirsrv.log.debug("nss cmd: %s" % cmd)
- result = check_output(cmd)
- self.dirsrv.log.debug("nss output: %s" % result)
+ cmd = ['/usr/bin/certutil', '-N', '-d', self._certdb,
'-f', '%s/%s' % (self._certdb, PWD_TXT)]
+ self._generate_noise('%s/noise.txt' % self._certdb)
+ self.log.debug("nss cmd: %s" % cmd)
+ result = ensure_str(check_output(cmd))
+ self.log.debug("nss output: %s" % result)
return True
def _db_exists(self):
"""
Check that a nss db exists at the certpath
"""
- key3 = os.path.exists("%s/key3.db" % (self.dirsrv.get_cert_dir()))
- cert8 = os.path.exists("%s/cert8.db" % (self.dirsrv.get_cert_dir()))
- key4 = os.path.exists("%s/key4.db" % (self.dirsrv.get_cert_dir()))
- cert9 = os.path.exists("%s/cert9.db" % (self.dirsrv.get_cert_dir()))
- secmod = os.path.exists("%s/secmod.db" % (self.dirsrv.get_cert_dir()))
- pkcs11 = os.path.exists("%s/pkcs11.txt" %
(self.dirsrv.get_cert_dir()))
+ key3 = os.path.exists("%s/key3.db" % (self._certdb))
+ cert8 = os.path.exists("%s/cert8.db" % (self._certdb))
+ key4 = os.path.exists("%s/key4.db" % (self._certdb))
+ cert9 = os.path.exists("%s/cert9.db" % (self._certdb))
+ secmod = os.path.exists("%s/secmod.db" % (self._certdb))
+ pkcs11 = os.path.exists("%s/pkcs11.txt" % (self._certdb))
if ((key3 and cert8 and secmod) or (key4 and cert9 and pkcs11)):
return True
@@ -102,8 +114,10 @@ class NssSsl(object):
Create a self signed CA.
"""
+ # Wait a second to avoid an NSS bug with serial ids based on time.
+ time.sleep(1)
# Create noise.
- self._generate_noise('%s/noise.txt' % self.dirsrv.get_cert_dir())
+ self._generate_noise('%s/noise.txt' % self._certdb)
# Now run the command. Can we do this with NSS native?
cmd = [
'/usr/bin/certutil',
@@ -122,12 +136,12 @@ class NssSsl(object):
'-d',
self._certdb,
'-z',
- '%s/noise.txt' % self.dirsrv.get_cert_dir(),
+ '%s/noise.txt' % self._certdb,
'-f',
- '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
+ '%s/%s' % (self._certdb, PWD_TXT),
]
- result = check_output(cmd)
- self.dirsrv.log.debug("nss output: %s" % result)
+ result = ensure_str(check_output(cmd))
+ self.log.debug("nss output: %s" % result)
# Now extract the CAcert to a well know place.
# This allows us to point the cacert dir here and it "just works"
cmd = [
@@ -140,10 +154,9 @@ class NssSsl(object):
'-a',
]
certdetails = check_output(cmd)
- with open('%s/ca.crt' % self.dirsrv.get_cert_dir(), 'w') as f:
+ with open('%s/ca.crt' % self._certdb, 'w') as f:
f.write(ensure_str(certdetails))
- if os.path.isfile('/usr/sbin/cacertdir_rehash'):
- check_output(['/usr/sbin/cacertdir_rehash',
self.dirsrv.get_cert_dir()])
+ check_output(['/usr/sbin/cacertdir_rehash', self._certdb])
return True
def _rsa_cert_list(self):
@@ -153,9 +166,9 @@ class NssSsl(object):
'-d',
self._certdb,
'-f',
- '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
+ '%s/%s' % (self._certdb, PWD_TXT),
]
- result = check_output(cmd)
+ result = ensure_str(check_output(cmd))
# We can skip the first few lines. They are junk
# IE ['',
@@ -180,9 +193,9 @@ class NssSsl(object):
'-d',
self._certdb,
'-f',
- '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
+ '%s/%s' % (self._certdb, PWD_TXT),
]
- result = check_output(cmd)
+ result = ensure_str(check_output(cmd))
lines = result.split('\n')[1:-1]
key_list = []
@@ -255,18 +268,20 @@ class NssSsl(object):
if len(alt_names) == 0:
alt_names.append(socket.gethostname())
- if self.dirsrv.host not in alt_names:
+ if self.dirsrv and self.dirsrv.host not in alt_names:
alt_names.append(self.dirsrv.host)
+ # Wait a second to avoid an NSS bug with serial ids based on time.
+ time.sleep(1)
# Create noise.
- self._generate_noise('%s/noise.txt' % self.dirsrv.get_cert_dir())
+ self._generate_noise('%s/noise.txt' % self._certdb)
cmd = [
'/usr/bin/certutil',
'-S',
'-n',
CERT_NAME,
'-s',
- SELF_ISSUER.format(HOSTNAME=self.dirsrv.host),
+ SELF_ISSUER.format(HOSTNAME=alt_names[0]),
# We MUST issue with SANs else ldap wont verify the name.
'-8', ','.join(alt_names),
'-c',
@@ -280,21 +295,123 @@ class NssSsl(object):
'-d',
self._certdb,
'-z',
- '%s/noise.txt' % self.dirsrv.get_cert_dir(),
+ '%s/noise.txt' % self._certdb,
'-f',
- '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
+ '%s/%s' % (self._certdb, PWD_TXT),
]
- result = check_output(cmd)
- self.dirsrv.log.debug("nss output: %s" % result)
+ result = ensure_str(check_output(cmd))
+ self.log.debug("nss output: %s" % result)
return True
+ def create_rsa_key_and_csr(self, alt_names=[]):
+ """Create a new RSA key and the certificate signing request. This
+ request can be submitted to a CA for signing. The returned certifcate
+ can be added with import_rsa_crt.
+ """
+ csr_path = os.path.join(self._certdb, '%s.csr' % CERT_NAME)
+
+ if len(alt_names) == 0:
+ alt_names.append(socket.gethostname())
+ if self.dirsrv and self.dirsrv.host not in alt_names:
+ alt_names.append(self.dirsrv.host)
+
+ # Wait a second to avoid an NSS bug with serial ids based on time.
+ time.sleep(1)
+ # Create noise.
+ self._generate_noise('%s/noise.txt' % self._certdb)
+
+ check_call([
+ '/usr/bin/certutil',
+ '-R',
+ '-s',
+ SELF_ISSUER.format(HOSTNAME=alt_names[0]),
+ # We MUST issue with SANs else ldap wont verify the name.
+ '-8', ','.join(alt_names),
+ '-g',
+ '%s' % KEYBITS,
+ '-v',
+ '%s' % VALID,
+ '-d',
+ self._certdb,
+ '-z',
+ '%s/noise.txt' % self._certdb,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ '-a',
+ '-o', csr_path,
+ ])
+ return csr_path
+
+ def rsa_ca_sign_csr(self, csr_path):
+ """ Given a CSR, sign it with our CA certificate (if present).
This
+ emits a signed certificate which can be imported with import_rsa_crt.
+ """
+ crt_path = 'crt'.join(csr_path.rsplit('csr', 1))
+ ca_path = '%s/ca.crt' % self._certdb
+
+ check_call([
+ '/usr/bin/certutil',
+ '-C',
+ '-d',
+ self._certdb,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ '-a',
+ '-i', csr_path,
+ '-o', crt_path,
+ '-c', CA_NAME,
+ ])
+
+ return (ca_path, crt_path)
+
+ def import_rsa_crt(self, ca, crt):
+ """Given a signed certificate from a ca, import the CA and
certificate
+ to our database.
+ """
+ shutil.copyfile(ca, '%s/ca.crt' % self._certdb)
+ check_output(['/usr/sbin/cacertdir_rehash', self._certdb])
+ check_call([
+ '/usr/bin/certutil',
+ '-A',
+ '-n', CA_NAME,
+ '-t', "CT,,",
+ '-a',
+ '-i', '%s/ca.crt' % self._certdb,
+ '-d', self._certdb,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ])
+ check_call([
+ '/usr/bin/certutil',
+ '-A',
+ '-n', CERT_NAME,
+ '-t', ",,",
+ '-a',
+ '-i', crt,
+ '-d', self._certdb,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ])
+ check_call([
+ '/usr/bin/certutil',
+ '-V',
+ '-d', self._certdb,
+ '-n', CERT_NAME,
+ '-u', 'V'
+ ])
+
def create_rsa_user(self, name):
"""
Create a key and cert for a user to authenticate to the directory.
Name is the uid of the account, and will become the CN of the cert.
"""
+ if self._rsa_user_exists(name):
+ return True
+
+ # Wait a second to avoid an NSS bug with serial ids based on time.
+ time.sleep(1)
cmd = [
'/usr/bin/certutil',
'-S',
@@ -319,20 +436,20 @@ class NssSsl(object):
'-d',
self._certdb,
'-z',
- '%s/noise.txt' % self.dirsrv.get_cert_dir(),
+ '%s/noise.txt' % self._certdb,
'-f',
- '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
+ '%s/%s' % (self._certdb, PWD_TXT),
]
- result = check_output(cmd)
- self.dirsrv.log.debug("nss output: %s" % result)
+ result = ensure_str(check_output(cmd))
+ self.log.debug("nss output: %s" % result)
# Now extract this into PEM files that we can use.
# pk12util -o user-william.p12 -d . -k pwdfile.txt -n user-william -W ''
check_call([
'pk12util',
'-d', self._certdb,
- '-o', '%s/%s%s.p12' % (self.dirsrv.get_cert_dir(),
USER_PREFIX, name),
- '-k', '%s/%s' % (self.dirsrv.get_cert_dir(), PWD_TXT),
+ '-o', '%s/%s%s.p12' % (self._certdb, USER_PREFIX, name),
+ '-k', '%s/%s' % (self._certdb, PWD_TXT),
'-n', '%s%s' % (USER_PREFIX, name),
'-W', '""'
])
@@ -341,9 +458,9 @@ class NssSsl(object):
check_call([
'openssl',
'pkcs12',
- '-in', '%s/%s%s.p12' % (self.dirsrv.get_cert_dir(),
USER_PREFIX, name),
+ '-in', '%s/%s%s.p12' % (self._certdb, USER_PREFIX, name),
'-passin', 'pass:""',
- '-out', '%s/%s%s.key' % (self.dirsrv.get_cert_dir(),
USER_PREFIX, name),
+ '-out', '%s/%s%s.key' % (self._certdb, USER_PREFIX, name),
'-nocerts',
'-nodes'
])
@@ -351,21 +468,32 @@ class NssSsl(object):
check_call([
'openssl',
'pkcs12',
- '-in', '%s/%s%s.p12' % (self.dirsrv.get_cert_dir(),
USER_PREFIX, name),
+ '-in', '%s/%s%s.p12' % (self._certdb, USER_PREFIX, name),
'-passin', 'pass:""',
- '-out', '%s/%s%s.crt' % (self.dirsrv.get_cert_dir(),
USER_PREFIX, name),
+ '-out', '%s/%s%s.crt' % (self._certdb, USER_PREFIX, name),
'-nokeys',
'-clcerts',
'-nodes'
])
+ # Convert the cert for userCertificate attr
+ check_call([
+ 'openssl',
+ 'x509',
+ '-inform', 'PEM',
+ '-outform', 'DER',
+ '-in', '%s/%s%s.crt' % (self._certdb, USER_PREFIX, name),
+ '-out', '%s/%s%s.der' % (self._certdb, USER_PREFIX, name),
+ ])
+
return True
def get_rsa_user(self, name):
"""
Return a dict of information for ca, key and cert paths for the user id
"""
- ca_path = '%s/ca.crt' % self.dirsrv.get_cert_dir()
- key_path = '%s/%s%s.key' % (self.dirsrv.get_cert_dir(), USER_PREFIX,
name)
- crt_path = '%s/%s%s.crt' % (self.dirsrv.get_cert_dir(), USER_PREFIX,
name)
- return {'ca': ca_path, 'key': key_path, 'crt': crt_path}
+ ca_path = '%s/ca.crt' % self._certdb
+ key_path = '%s/%s%s.key' % (self._certdb, USER_PREFIX, name)
+ crt_path = '%s/%s%s.crt' % (self._certdb, USER_PREFIX, name)
+ crt_der_path = '%s/%s%s.der' % (self._certdb, USER_PREFIX, name)
+ return {'ca': ca_path, 'key': key_path, 'crt': crt_path,
'crt_der_path': crt_der_path}
diff --git a/src/lib389/lib389/tests/nss_ssl_test.py
b/src/lib389/lib389/tests/nss_ssl_test.py
index e1867b6..4741da5 100644
--- a/src/lib389/lib389/tests/nss_ssl_test.py
+++ b/src/lib389/lib389/tests/nss_ssl_test.py
@@ -14,6 +14,8 @@ import logging
from lib389.topologies import topology_st as topo
+from lib389.nss_ssl import NssSsl
+
DEBUGGING = os.getenv('DEBUGGING', False)
if DEBUGGING:
@@ -23,52 +25,67 @@ else:
log = logging.getLogger(__name__)
-def test_nss(topo):
+def test_external_ca():
+ """ Test the behaviour of our system ca database.
+
+ :id: 321c85c1-9cf3-413f-9e26-99a8b327509c
+
+ :steps:
+ 1. Create the system CA.
+ 2. Create an nss db
+ 3. Submit a CSR to the system ca
+ 4. Import the crt to the db
+ :expectedresults:
+ 1. It works.
+ 2. It works.
+ 3. It works.
+ 4. It works.
+ """
+ # If it doesn't exist, create a cadb.
+ ssca = NssSsl(dbpath='/tmp/lib389-ssca')
+ ssca.reinit()
+ ssca.create_rsa_ca()
+
+ # Create certificate database.
+ tlsdb = NssSsl(dbpath='/tmp/lib389-tlsdb')
+ tlsdb.reinit()
+
+ csr = tlsdb.create_rsa_key_and_csr()
+ (ca, crt) = ssca.rsa_ca_sign_csr(csr)
+ tlsdb.import_rsa_crt(ca, crt)
+
+def test_nss_ssca_users(topo):
"""
- Build a nss db, create a ca, and check that it is correct.
+ Validate that we can submit user certs to the ds ca for signing.
+
+ :id: a47e47ed-2056-440b-8797-d13fa89098f6
+ :steps:
+ 1. Find the ssca path.
+ 2. Assert it exists
+ 3. Create user certificates from the ssca
+ :expectedresults:
+ 1. It works.
+ 2. It works.
+ 3. It works.
"""
+ ssca = NssSsl(dbpath=topo.standalone.get_ssca_dir())
- standalone = topo.standalone
-
- # This is a trick. The nss db that ships with DS is broken fundamentally.
- # THIS ASSUMES old nss format. SQLite will bite us!
- for f in ('key3.db', 'cert8.db', 'key4.db',
'cert9.db', 'secmod.db', 'pkcs11.txt'):
- try:
- os.remove("%s/%s" % (standalone.confdir, f))
- except:
- pass
-
-
- # Check if the db exists. Should be false.
- assert(standalone.nss_ssl._db_exists() is False)
- # Create it. Should work.
- assert(standalone.nss_ssl.reinit() is True)
- # Check if the db exists. Should be true
- assert(standalone.nss_ssl._db_exists() is True)
-
- # Check if ca exists. Should be false.
- assert(standalone.nss_ssl._rsa_ca_exists() is False)
- # Create it. Should work.
- assert(standalone.nss_ssl.create_rsa_ca() is True)
- # Check if ca exists. Should be true
- assert(standalone.nss_ssl._rsa_ca_exists() is True)
-
- # Check if we have a server cert / key. Should be false.
- assert(standalone.nss_ssl._rsa_key_and_cert_exists() is False)
- # Create it. Should work.
- assert(standalone.nss_ssl.create_rsa_key_and_cert() is True)
- # Check if server cert and key exist. Should be true.
- assert(standalone.nss_ssl._rsa_key_and_cert_exists() is True)
+ if not ssca._rsa_ca_exists():
+ ssca.reinit()
+ ssca.create_rsa_ca()
+
+ # It better exist now!
+ assert(ssca._rsa_ca_exists() is True)
# Check making users certs. They should never conflict
for user in ('william', 'noriko', 'mark'):
- assert(standalone.nss_ssl._rsa_user_exists(user) is False)
# Create the user cert
- assert(standalone.nss_ssl.create_rsa_user(user) is True)
+ assert(ssca.create_rsa_user(user) is True)
# Assert it exists now
- assert(standalone.nss_ssl._rsa_user_exists(user) is True)
+ assert(ssca._rsa_user_exists(user) is True)
if __name__ == "__main__":
CURRENT_FILE = os.path.realpath(__file__)
pytest.main("-s -vv %s" % CURRENT_FILE)
+
diff --git a/src/lib389/lib389/topologies.py b/src/lib389/lib389/topologies.py
index 69c71ee..068a24a 100644
--- a/src/lib389/lib389/topologies.py
+++ b/src/lib389/lib389/topologies.py
@@ -13,10 +13,12 @@ import time
import pytest
from lib389 import DirSrv
+from lib389.nss_ssl import NssSsl
from lib389.utils import generate_ds_params
from lib389.replica import Replicas
from lib389._constants import (args_instance, SER_HOST, SER_PORT, SER_SERVERID_PROP,
SER_CREATION_SUFFIX,
- ReplicaRole, DEFAULT_SUFFIX, REPLICA_ID)
+ SER_SECURE_PORT, ReplicaRole, DEFAULT_SUFFIX, REPLICA_ID,
+ SER_LDAP_URL)
DEBUGGING = os.getenv('DEBUGGING', default=False)
if DEBUGGING:
@@ -61,14 +63,19 @@ def create_topology(topo_dict):
# the instance creation here.
args_instance[SER_HOST] = instance_data[SER_HOST]
args_instance[SER_PORT] = instance_data[SER_PORT]
+ args_instance[SER_SECURE_PORT] = instance_data[SER_SECURE_PORT]
args_instance[SER_SERVERID_PROP] = instance_data[SER_SERVERID_PROP]
args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+
args_copied = args_instance.copy()
instance.allocate(args_copied)
instance_exists = instance.exists()
if instance_exists:
instance.delete()
instance.create()
+ # We set a URL here to force ldap:// only. Once we turn on TLS
+ # we'll flick this to ldaps.
+ instance.use_ldap_uri()
instance.open()
if role == ReplicaRole.STANDALONE:
ins[instance.serverid] = instance
@@ -137,8 +144,10 @@ class TopologyMain(object):
if standalones:
if isinstance(standalones, dict):
self.ins = standalones
+ self.all_insts.update(standalones)
else:
self.standalone = standalones
+ self.all_insts['standalone1'] = standalones
if masters:
self.ms = masters
self.all_insts.update(self.ms)
@@ -149,6 +158,9 @@ class TopologyMain(object):
self.hs = hubs
self.all_insts.update(self.hs)
+ def __iter__(self):
+ return self.all_insts.values().__iter__()
+
def pause_all_replicas(self):
"""Pause all agreements in the class instance"""
@@ -328,6 +340,7 @@ def topology_m1h1c1(request):
instance = DirSrv(verbose=False)
args_instance[SER_HOST] = instance_data[SER_HOST]
args_instance[SER_PORT] = instance_data[SER_PORT]
+ args_instance[SER_SECURE_PORT] = instance_data[SER_SECURE_PORT]
args_instance[SER_SERVERID_PROP] = instance_data[SER_SERVERID_PROP]
args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
args_copied = args_instance.copy()
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.