Nik,

Updated.

Dan

On Tue, Mar 7, 2017 at 4:11 AM, Nikolai Kondrashov <
nikolai.kondras...@redhat.com> wrote:

> On 03/06/2017 09:34 PM, Dan Lavu wrote:
>
>> Since we last spoken, here is an update patch for review. Adding an
>> optional
>> ssl parameter to the DS class. When enabled, right now the tests fail
>> because SSL is not configured properly on the client, but I'll get to that
>> in my next patch?
>>
>
> Glad to see your progress!
>
> Before I proceed with everything else, why don't you just let the DS
> accept a
> CA parameter as well as an SSL port?
>
> Like this:
>
>     def __init__(self, dir, port, base_dn, admin_rdn, admin_pw,
> ssl_port=None, ca=None):
>         # ...
>         self.ssl_port = ssl_port
>         self.ca = ca
>         # ...
>
> And then perhaps make it set up the SSL socket if the port is passed, in
> *addition*
> to the regular socket.
>
> I.e. have this in the constructor:
>
>     self.ldap_url = "ldap://localhost:"; + str(self.port)
>     if self.ssl_port:
>         self.ldaps_url = "ldaps://localhost:" + str(self.ssl_port)
>
> Nick
> _______________________________________________
> sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
> To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
>
diff --git src/tests/intg/ca.py src/tests/intg/ca.py
new file mode 100644
index 0000000..691c030
--- /dev/null
+++ src/tests/intg/ca.py
@@ -0,0 +1,40 @@
+#
+# Abstract directory server instance class
+#
+# Copyright (c) 2015 Red Hat, Inc.
+# Author: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+class CA:
+    """Abstract CA server instance"""
+
+    def __init__(self, config_dir, subject, country, state, city, organization, unit):
+        """Initialize the instance"""
+        self.config_dir = config_dir
+        self.subject = subject
+        self.country = country
+        self.state = state
+        self.city = city
+        self.organization = organization
+        self.unit = unit
+
+    def setup(self):
+        """Setup the instance"""
+        raise NotImplementedError()
+
+    def teardown(self):
+        """Teardown the instance"""
+        raise NotImplementedError()
diff --git src/tests/intg/ca_openssl.py src/tests/intg/ca_openssl.py
new file mode 100644
index 0000000..b5c51c3
--- /dev/null
+++ src/tests/intg/ca_openssl.py
@@ -0,0 +1,180 @@
+#
+# SSSD LOCAL domain tests
+#
+# Copyright (c) 2016 Red Hat, Inc.
+# Author: Dan Lavu <d...@redhat.com>
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import socket
+import os.path
+
+from os.path import exists
+from OpenSSL import crypto
+from ca import CA
+
+
+class CAOpenSSL(CA):
+    """OpenSSL CA instance"""
+
+    def __init__(self, config_dir, subject, country, state, city, organization, unit):
+        """Initialize the instance"""
+        CA.__init__(self, config_dir, subject, country, state, city, organization, unit)
+        self.config_dir = config_dir
+        self.subject = subject
+        self.country = country
+        self.state = state
+        self.city = city
+        self.organization = organization
+        self.unit = unit
+        self.hostname = socket.gethostname()
+
+        self.ca_cert_file = self.hostname + '-ca.crt'
+        self.ca_cert_key_file = self.hostname + '-ca.key'
+
+        self.csr_dir = self.config_dir + '/newcerts'
+        self.key_dir = self.config_dir + '/private'
+        self.cert_dir = self.config_dir + '/certs'
+
+        self.index = int(1000)
+
+    def setup(self):
+        """Setup OpenSSL CA"""
+        if not exists(os.path.join(self.cert_dir, self.ca_cert_file)) or not \
+                exists(os.path.join(self.key_dir, self.ca_cert_key_file)):
+            ca_key = crypto.PKey()
+            ca_key.generate_key(crypto.TYPE_RSA, 2048)
+
+            ca_cert = crypto.X509()
+            ca_cert.get_subject().C = self.country
+            ca_cert.get_subject().ST = self.state
+            ca_cert.get_subject().L = self.city
+            ca_cert.get_subject().O = self.organization
+            ca_cert.get_subject().OU = self.unit
+            ca_cert.get_subject().CN = self.subject
+            ca_cert.set_serial_number(self.index)
+            ca_cert.gmtime_adj_notBefore(0)
+            ca_cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
+            ca_cert.set_issuer(ca_cert.get_subject())
+            ca_cert.set_pubkey(ca_key)
+            ca_cert.sign(ca_key, 'sha1')
+
+            if not os.path.exists(self.csr_dir):
+                os.makedirs(self.csr_dir)
+            if not os.path.exists(self.key_dir):
+                os.makedirs(self.key_dir)
+            if not os.path.exists(self.cert_dir):
+                os.makedirs(self.cert_dir)
+
+            open(os.path.join(self.cert_dir, self.ca_cert_file), 'wt').\
+                write(crypto.dump_certificate(crypto.FILETYPE_PEM, ca_cert))
+            open(os.path.join(self.key_dir, self.ca_cert_key_file), 'wt').\
+                write(crypto.dump_privatekey(crypto.FILETYPE_PEM, ca_key))
+
+    def teardown(self):
+        """Teardown"""
+
+    def get_ca_cert(self):
+        """Retrieves CA certificate"""
+        ca_cert_file = os.path.join(self.cert_dir, self.ca_cert_file)
+        ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM,
+                                          open(ca_cert_file, 'rt').read())
+        return ca_cert
+
+    def get_cacert_x509(self):
+        """Retrieves CA certificate in x509"""
+        ca_cert_file = os.path.join(self.cert_dir, self.ca_cert_file)
+        ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM,
+                                          open(ca_cert_file, 'rt').read())
+        ca_cert_x509 = crypto.dump_certificate(crypto.FILETYPE_PEM, ca_cert)
+        return ca_cert_x509
+
+    def get_ca_cert_path(self):
+        """Retrieves CA certificate file path"""
+        ca_cert_file = os.path.join(self.cert_dir, self.ca_cert_file)
+
+        return ca_cert_file
+
+    def get_ca_key(self):
+        """Retrieves CA certificate private key"""
+        ca_key_file = os.path.join(self.key_dir, self.ca_cert_key_file)
+        ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM,
+                                        open(ca_key_file, 'rt').read())
+        return ca_key
+
+    def gen_cert(self, csr, hostname):
+        """Generates certificate from csr"""
+        ca_cert_file = os.path.join(self.cert_dir, self.ca_cert_file)
+        ca_key_file = os.path.join(self.key_dir, self.ca_cert_key_file)
+
+        ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM,
+                                          open(ca_cert_file, 'rt').read())
+        ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM,
+                                          open(ca_key_file, 'rt').read())
+
+        cert = crypto.X509()
+        cert.set_subject(csr.get_subject())
+        cert.set_serial_number(self.index + 1)
+        cert.gmtime_adj_notBefore(0)
+        cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
+        cert.set_issuer(ca_cert.get_subject())
+        cert.set_pubkey(csr.get_pubkey())
+        cert.sign(ca_key, 'sha1')
+
+        open(os.path.join(self.cert_dir, hostname + '.crt'), 'wt').\
+            write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
+        return cert
+
+    def get_cert(self, hostname):
+        """Retrieves server certificate in PEM"""
+        cert_file = os.path.join(self.cert_dir, hostname + '.crt')
+        cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(cert_file, 'rt').read())
+        return cert
+
+    def get_cert_x509(self, hostname):
+        """Retrieves server certificate in x509"""
+        cert_file = os.path.join(self.cert_dir, hostname + '.crt')
+        cert_x509 = crypto.load_certificate(crypto.FILETYPE_PEM, open(cert_file, 'rt').read())
+        return cert_x509
+
+    def get_cert_path(self, hostname):
+        """Retrieves server certificate file path"""
+        cert_file = os.path.join(self.cert_dir, hostname + '.crt')
+        return cert_file
+
+    def get_cert_key(self, hostname):
+        """Retrieves server certificate private key"""
+        key_file = os.path.join(self.key_dir, hostname + '.key')
+        key = crypto.load_privatekey(crypto.FILETYPE_PEM, open(key_file, 'rt').read())
+        return key
+
+    def get_cert_key_path(self, hostname):
+        """Retrieves server certificate private key file path"""
+        key_file = os.path.join(self.key_dir, hostname + '.key')
+        return key_file
+
+    def gen_csr(self, hostname):
+        """Generates certificate signing request"""
+        key = crypto.PKey()
+        key.generate_key(crypto.TYPE_RSA, 2048)
+        csr = crypto.X509Req()
+        csr.get_subject().CN = hostname
+        csr.set_pubkey(key)
+        csr.sign(key, 'sha1')
+
+        open(os.path.join(self.key_dir, hostname + '.key'), 'a').\
+            write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
+        open(os.path.join(self.csr_dir, hostname + '.csr'), 'a').\
+            write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, csr))
+        return csr
diff --git src/tests/intg/ds.py src/tests/intg/ds.py
index 66cb887..13c0778 100644
--- src/tests/intg/ds.py
+++ src/tests/intg/ds.py
@@ -23,7 +23,7 @@ import ldap
 class DS:
     """Abstract directory server instance."""
 
-    def __init__(self, dir, port, base_dn, admin_rdn, admin_pw):
+    def __init__(self, dir, port, base_dn, admin_rdn, admin_pw, ssl_port=None, ca=None):
         """
             Initialize the instance.
 
@@ -37,11 +37,17 @@ class DS:
         """
         self.dir = dir
         self.port = port
-        self.ldap_url = "ldap://localhost:"; + str(self.port)
         self.base_dn = base_dn
         self.admin_rdn = admin_rdn
         self.admin_dn = admin_rdn + "," + base_dn
         self.admin_pw = admin_pw
+        self.ssl_port = ssl_port
+        self.ca = ca
+
+        if self.ssl_port and self.ca:
+            self.ldap_url = "ldaps://localhost:" + str(self.ssl_port)
+        else:
+            self.ldap_url = "ldap://localhost:"; + str(self.port)
 
     def setup(self):
         """Setup the instance"""
diff --git src/tests/intg/ds_openldap.py src/tests/intg/ds_openldap.py
index 6a074c9..a5b2152 100644
--- src/tests/intg/ds_openldap.py
+++ src/tests/intg/ds_openldap.py
@@ -25,7 +25,10 @@ import os
 import errno
 import signal
 import shutil
+import socket
+import urllib
 import sys
+import ca_openssl
 from util import *
 from ds import DS
 
@@ -47,7 +50,7 @@ def hash_password(password):
 class DSOpenLDAP(DS):
     """OpenLDAP directory server instance."""
 
-    def __init__(self, dir, port, base_dn, admin_rdn, admin_pw):
+    def __init__(self, dir, port, base_dn, admin_rdn, admin_pw, ssl_port=None, ca=False):
         """
             Initialize the instance.
 
@@ -59,12 +62,14 @@ class DSOpenLDAP(DS):
             admin_rdn   Administrator DN, relative to BASE_DN.
             admin_pw    Administrator password.
         """
-        DS.__init__(self, dir, port, base_dn, admin_rdn, admin_pw)
+        DS.__init__(self, dir, port, base_dn, admin_rdn, admin_pw, ca)
         self.run_dir = self.dir + "/var/run/ldap"
         self.pid_path = self.run_dir + "/slapd.pid"
         self.conf_dir = self.dir + "/etc/ldap"
         self.conf_slapd_d_dir = self.conf_dir + "/slapd.d"
         self.data_dir = self.dir + "/var/lib/ldap"
+        self.ssl_port = ssl_port
+        self.ca = ca
 
     def _setup_config(self):
         """Setup the instance initial configuration."""
@@ -78,9 +83,6 @@ class DSOpenLDAP(DS):
         uid = os.geteuid()
         gid = os.getegid()
 
-        #
-        # Add configuration
-        #
         config = unindent("""
             dn: cn=config
             objectClass: olcGlobal
@@ -223,11 +225,23 @@ class DSOpenLDAP(DS):
                 break
             except ldap.SERVER_DOWN:
                 pass
-            attempt = attempt + 1
+            attempt += 1
             if attempt > 30:
                 raise Exception("Failed to start slapd")
             time.sleep(1)
 
+        if self.ca:
+            self.ca_hostname = socket.gethostname()
+            self.ca_inst = ca_openssl.CAOpenSSL(config.PREFIX, self.ca_hostname,
+                                                "US",
+                                                "NC",
+                                                "Raleigh",
+                                                "Red Hat Inc.",
+                                                "SSSD")
+            self.ca_inst.setup()
+            self.ca_inst.gen_cert(self.ca_inst.gen_csr(self.ca_hostname), self.ca_hostname)
+            self.enable_ssl(self.ca_inst.ca_cert_file, self.ca_inst.ca_cert_file, self.ca_inst.ca_cert_key_file)
+
         #
         # Relax requirement of member attribute presence in groupOfNames
         #
@@ -277,7 +291,7 @@ class DSOpenLDAP(DS):
                 pid_file.close()
             attempt = 0
             while os.path.isfile(self.pid_path):
-                attempt = attempt + 1
+                attempt += 1
                 if attempt > 30:
                     raise Exception("Failed to stop slapd")
                 time.sleep(1)
@@ -287,3 +301,29 @@ class DSOpenLDAP(DS):
 
         for path in (self.conf_slapd_d_dir, self.run_dir, self.data_dir):
             shutil.rmtree(path, True)
+
+        if self.ca:
+            self.ca_inst.teardown()
+
+    def enable_ssl(self, ca_cert, cert, key):
+        """Enable SSL in OpenLDAP"""
+
+        ldapi_socket = self.run_dir + "/ldapi"
+        ldapi_url = "ldapi://" + url_quote(ldapi_socket, "")
+        url_list = ldapi_url + " " + self.ldap_url
+
+        modlist = [
+            (ldap.MOD_ADD, "olcTLSCertificateKeyFile", [key]),
+            (ldap.MOD_ADD, "olcTLSCertificateFile", [cert]),
+            (ldap.MOD_ADD, "olcTLSCACertificatePath", [ca_cert])
+        ]
+        ldap_conn = ldap.initialize(ldapi_url)
+        ldap_conn.simple_bind_s(self.admin_rdn + ",cn=config", self.admin_pw)
+        ldap_conn.modify_s("cn=config", modlist)
+        ldap_conn.unbind_s()
+
+        if subprocess.call(['pkill', 'slapd']) != 0:
+            raise execfile("Failed to stop slapd")
+
+        if subprocess.call(["slapd", "-F", self.conf_slapd_d_dir, "-h", url_list]) != 0:
+            raise Exception("Failed to start slapd")
diff --git src/tests/intg/test_ldap.py src/tests/intg/test_ldap.py
index 848cb41..ea2e05f 100644
--- src/tests/intg/test_ldap.py
+++ src/tests/intg/test_ldap.py
@@ -16,7 +16,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
-import os
+import os.path
 import stat
 import pwd
 import grp
diff --git src/tests/intg/test_sssctl.py src/tests/intg/test_sssctl.py
index 0df5d0b..3c2ba02 100644
--- src/tests/intg/test_sssctl.py
+++ src/tests/intg/test_sssctl.py
@@ -28,6 +28,7 @@ import signal
 import ds_openldap
 import ldap_ent
 import config
+import socket
 from util import unindent
 import sssd_netgroup
 
@@ -38,14 +39,14 @@ LDAP_BASE_DN = "dc=example,dc=com"
 def ds_inst(request):
     """LDAP server instance fixture"""
     ds_inst = ds_openldap.DSOpenLDAP(
-        config.PREFIX, 10389, LDAP_BASE_DN,
-        "cn=admin", "Secret123")
+        config.PREFIX, 10389, LDAP_BASE_DN, "cn=admin", "Secret123")
     try:
         ds_inst.setup()
     except:
         ds_inst.teardown()
         raise
     request.addfinalizer(lambda: ds_inst.teardown())
+
     return ds_inst
 
 
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org

Reply via email to