URL: https://github.com/freeipa/freeipa/pull/428
Author: MartinBasti
 Title: #428: [WIP] [Py3] ipa-server-install
Action: opened

PR body:
"""
This PR allows to install ipa server using PY3 (CA, no DNS, no KRA, no CA-less, 
no external CA) and uninstall it (ipa-server-install --uninstall)

Functionality depends on #427
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/428/head:pr428
git checkout pr428
From 2f86d65d64bf3034d2fd91623f4b7c3bdfdc00be Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 24 Jan 2017 17:49:06 +0100
Subject: [PATCH 01/17] py3: base64 encoding/decoding returns always bytes
 don't mix it

Using unicode(bytes) call causes undesired side effect that is inserting
`b` character to result. This obviously causes issues with binary base64 data

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/plugins/baseldap.py | 4 ++--
 ipaserver/plugins/ca.py       | 2 +-
 ipaserver/plugins/cert.py     | 2 +-
 ipaserver/secrets/client.py   | 6 ++++--
 4 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py
index e7bf43c..2f7889b 100644
--- a/ipaserver/plugins/baseldap.py
+++ b/ipaserver/plugins/baseldap.py
@@ -1036,8 +1036,8 @@ def process_attr_options(self, entry_attrs, dn, keys, options):
                     except ValueError:
                         if isinstance(delval, bytes):
                             # This is a Binary value, base64 encode it
-                            delval = unicode(base64.b64encode(delval))
-                        raise errors.AttrValueNotFound(attr=attr, value=delval)
+                            delval = base64.b64encode(delval).decode('ascii')
+                            raise errors.AttrValueNotFound(attr=attr, value=delval)
 
         # normalize all values
         changedattrs = setattrs | addattrs | delattrs
diff --git a/ipaserver/plugins/ca.py b/ipaserver/plugins/ca.py
index 4f24278..ac9f68e 100644
--- a/ipaserver/plugins/ca.py
+++ b/ipaserver/plugins/ca.py
@@ -176,7 +176,7 @@ def set_certificate_attrs(entry, options, want_cert=True):
     with api.Backend.ra_lightweight_ca as ca_api:
         if want_cert or full:
             der = ca_api.read_ca_cert(ca_id)
-            entry['certificate'] = six.text_type(base64.b64encode(der))
+            entry['certificate'] = base64.b64encode(der).decode('ascii')
 
         if want_chain or full:
             pkcs7_der = ca_api.read_ca_chain(ca_id)
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 5bf4cfb..6bf5c03 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -1260,7 +1260,7 @@ def _get_cert_key(self, cert):
         return (DN(cert_obj.issuer), cert_obj.serial)
 
     def _get_cert_obj(self, cert, all, raw, pkey_only):
-        obj = {'certificate': unicode(base64.b64encode(cert))}
+        obj = {'certificate': base64.b64encode(cert).decode('ascii')}
 
         full = not pkey_only and all
         if not raw:
diff --git a/ipaserver/secrets/client.py b/ipaserver/secrets/client.py
index a04b9a6..a945e01 100644
--- a/ipaserver/secrets/client.py
+++ b/ipaserver/secrets/client.py
@@ -70,7 +70,8 @@ def init_creds(self):
         name = gssapi.Name(self.client_service,
                            gssapi.NameType.hostbased_service)
         store = {'client_keytab': self.keytab,
-                 'ccache': 'MEMORY:Custodia_%s' % b64encode(os.urandom(8))}
+                 'ccache': 'MEMORY:Custodia_%s' % b64encode(
+                     os.urandom(8)).decode('ascii')}
         return gssapi.Credentials(name=name, store=store, usage='initiate')
 
     def _auth_header(self):
@@ -78,7 +79,8 @@ def _auth_header(self):
             self.creds = self.init_creds()
         ctx = gssapi.SecurityContext(name=self.service_name, creds=self.creds)
         authtok = ctx.step()
-        return {'Authorization': 'Negotiate %s' % b64encode(authtok)}
+        return {'Authorization': 'Negotiate %s' % b64encode(
+            authtok).decode('ascii')}
 
     def fetch_key(self, keyname, store=True):
 

From df975b68875628940ce50c5470d34f30cc716762 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 24 Jan 2017 18:31:50 +0100
Subject: [PATCH 02/17] py3: base64.b64encode requires bytes as param

Decimal must be changed to string first and then encoded to bytes

https://fedorahosted.org/freeipa/ticket/4985
---
 ipalib/rpc.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index fb739f8..a3642a6 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -308,7 +308,7 @@ def json_encode_binary(val, version):
             encoded = encoded.decode('ascii')
         return {'__base64__': encoded}
     elif isinstance(val, Decimal):
-        return {'__base64__': base64.b64encode(str(val))}
+        return {'__base64__': base64.b64encode(str(val).encode('ascii'))}
     elif isinstance(val, DN):
         return str(val)
     elif isinstance(val, datetime.datetime):

From 6669afc9ccec975607fbe0da597f81ed1f6dd64d Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 25 Jan 2017 14:56:07 +0100
Subject: [PATCH 03/17] py3: remove_entry_from_group: attribute name must be
 string

Do not encode attribute names

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/plugins/ldap2.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py
index 71c095d..e671ecb 100644
--- a/ipaserver/plugins/ldap2.py
+++ b/ipaserver/plugins/ldap2.py
@@ -442,7 +442,7 @@ def remove_entry_from_group(self, dn, group_dn, member_attr='member'):
         # update group entry
         try:
             with self.error_handler():
-                modlist = [(a, self.encode(b), self.encode(c))
+                modlist = [(a, b, self.encode(c))
                            for a, b, c in modlist]
                 self.conn.modify_s(str(group_dn), modlist)
         except errors.MidairCollision:

From d02b38e92b0e48e70cfa7bd810636e5fa4944f58 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 25 Jan 2017 15:38:03 +0100
Subject: [PATCH 04/17] py3: _ptrrecord_precallaback: use bytes with labels

DNS labels are bytes so bytes must be used for comparison

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/plugins/dns.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
index 08f9c6d..0838161 100644
--- a/ipaserver/plugins/dns.py
+++ b/ipaserver/plugins/dns.py
@@ -3098,7 +3098,7 @@ def _ptrrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
 
         # Classless zones (0/25.0.0.10.in-addr.arpa.) -> skip check
         # zone has to be checked without reverse domain suffix (in-addr.arpa.)
-        for sign in ('/', '-'):
+        for sign in (b'/', b'-'):
             for name in (zone, addr):
                 for label in name.labels:
                     if sign in label:

From ce2f44b848b0109cc1abb6ae7ccfc3081c8c9d52 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Thu, 26 Jan 2017 10:25:46 +0100
Subject: [PATCH 05/17] py3: DNS: get_record_entry_attrs: do not modify dict
 during iteration

In py3 keys() doesn't return list but iterator so it must be transformed
to tuple otherwise iterator will be broken.

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/plugins/dns.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
index 0838161..97f6527 100644
--- a/ipaserver/plugins/dns.py
+++ b/ipaserver/plugins/dns.py
@@ -3195,7 +3195,7 @@ def get_dns_masters(self):
 
     def get_record_entry_attrs(self, entry_attrs):
         entry_attrs = entry_attrs.copy()
-        for attr in entry_attrs.keys():
+        for attr in tuple(entry_attrs.keys()):
             if attr not in self.params or self.params[attr].primary_key:
                 del entry_attrs[attr]
         return entry_attrs

From ac1a38a1ef3f16e761a78c0449b2a6f7acfd5eff Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 27 Jan 2017 10:48:32 +0100
Subject: [PATCH 06/17] py3: entry_to_dict: input can be both bytes and unicode

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/plugins/baseldap.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py
index 2f7889b..803c478 100644
--- a/ipaserver/plugins/baseldap.py
+++ b/ipaserver/plugins/baseldap.py
@@ -255,7 +255,8 @@ def entry_to_dict(entry, **options):
                 value = list(entry.raw[attr])
                 for (i, v) in enumerate(value):
                     try:
-                        value[i] = v.decode('utf-8')
+                        if isinstance(v, bytes):
+                            value[i] = v.decode('utf-8')
                     except UnicodeDecodeError:
                         pass
             result[attr] = value

From f14a85426c9c5ab5e4198b900f03b3b05d81bb26 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 27 Jan 2017 12:06:54 +0100
Subject: [PATCH 07/17] py3: _convert_to_idna: fix bytes/unicode mistmatch

ToASCII() returns bytes, it must be decoded to unicode

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/plugins/dns.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
index 97f6527..40c9b51 100644
--- a/ipaserver/plugins/dns.py
+++ b/ipaserver/plugins/dns.py
@@ -1620,8 +1620,9 @@ def _convert_to_idna(value):
         idna_val = encodings.idna.nameprep(idna_val)
         idna_val = re.split(r'(?<!\\)\.', idna_val)
         idna_val = u'%s%s%s' % (start_dot,
-                                u'.'.join(encodings.idna.ToASCII(x)
-                                          for x in idna_val),
+                                u'.'.join(
+                                    encodings.idna.ToASCII(x).decode('ascii')
+                                    for x in idna_val),
                                 end_dot)
         return idna_val
     except Exception:

From 4a7754a499297e8a072eb136ed89873ad751166b Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 27 Jan 2017 17:00:42 +0100
Subject: [PATCH 08/17] py3: modify_s: attribute nmae must be str not bytes

https://fedorahosted.org/freeipa/ticket/4985
---
 ipapython/ipaldap.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py
index 497b947..2b8bfec 100644
--- a/ipapython/ipaldap.py
+++ b/ipapython/ipaldap.py
@@ -727,7 +727,7 @@ def modify_s(self, dn, modlist):
         # FIXME: for backwards compatibility only
         assert isinstance(dn, DN)
         dn = str(dn)
-        modlist = [(a, self.encode(b), self.encode(c)) for a, b, c in modlist]
+        modlist = [(a, b, self.encode(c)) for a, b, c in modlist]
         return self.conn.modify_s(dn, modlist)
 
     @property

From a44c979e6045b4452e2e2bd9bfe7870221d3fa01 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 27 Jan 2017 17:33:44 +0100
Subject: [PATCH 09/17] py3: configparser: use raw keyword

configparser.get() changed in python3 and `raw` is now a keyword attribute.

Also it must be set to True, otherwise InterpolationSyntaxError is raised

'''
InterpolationSyntaxError: '%' must be followed by '%' or '(', found:
'%2fvar%2frun%2fslapd-EXAMPLE-COM.socket'
'''

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/secrets/kem.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/secrets/kem.py b/ipaserver/secrets/kem.py
index 143caaf..3577975 100644
--- a/ipaserver/secrets/kem.py
+++ b/ipaserver/secrets/kem.py
@@ -181,7 +181,7 @@ def __init__(self, config=None, ipaconf=paths.IPA_DEFAULT_CONF):
         self.realm = conf.get('global', 'realm')
         self.ldap_uri = config.get('ldap_uri', None)
         if self.ldap_uri is None:
-            self.ldap_uri = conf.get('global', 'ldap_uri', None)
+            self.ldap_uri = conf.get('global', 'ldap_uri', raw=True)
         self._server_keys = None
 
     def find_key(self, kid, usage):

From 27e5c044908bb8cfb85dd75098249b5a43363dd0 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 27 Jan 2017 18:10:37 +0100
Subject: [PATCH 10/17] py3: custodia: basedn must be unicode

basedn in custodia related modules has type bytes, that causes issues in
Py3 when strings were concatenated with bytes

```
malformed RDN string = "cn=custodia,cn=ipa,cn=etc,b'dc=example,dc=com'"
```

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/secrets/common.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/secrets/common.py b/ipaserver/secrets/common.py
index 2b906b6..f3dc320 100644
--- a/ipaserver/secrets/common.py
+++ b/ipaserver/secrets/common.py
@@ -23,7 +23,7 @@ def basedn(self):
         if self._basedn is None:
             conn = self.connect()
             r = conn.search_s('', ldap.SCOPE_BASE)
-            self._basedn = r[0][1]['defaultnamingcontext'][0]
+            self._basedn = r[0][1]['defaultnamingcontext'][0].decode('utf-8')
         return self._basedn
 
     def connect(self):

From c97a0810936a62611e5216de7f4bb401a5cdb38e Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 18:11:42 +0100
Subject: [PATCH 11/17] py3: kem.py: user bytes with ldap values

python ldap requires bytes as values

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/secrets/kem.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/ipaserver/secrets/kem.py b/ipaserver/secrets/kem.py
index 3577975..5d784b7 100644
--- a/ipaserver/secrets/kem.py
+++ b/ipaserver/secrets/kem.py
@@ -130,13 +130,13 @@ def set_key(self, usage, principal, key):
         service_rdn = ('cn', servicename) if servicename != 'host' else DN()
         dn = str(DN(('cn', name), service_rdn, self.keysbase))
         try:
-            mods = [('objectClass', ['nsContainer',
-                                     'ipaKeyPolicy',
-                                     'ipaPublicKeyObject',
-                                     'groupOfPrincipals']),
-                    ('cn', name),
-                    ('ipaKeyUsage', RFC5280_USAGE_MAP[usage]),
-                    ('memberPrincipal', principal),
+            mods = [('objectClass', [b'nsContainer',
+                                     b'ipaKeyPolicy',
+                                     b'ipaPublicKeyObject',
+                                     b'groupOfPrincipals']),
+                    ('cn', name.encode('utf-8')),
+                    ('ipaKeyUsage', RFC5280_USAGE_MAP[usage].encode('utf-8')),
+                    ('memberPrincipal', principal.encode('utf-8')),
                     ('ipaPublicKey', public_key)]
             conn.add_s(dn, mods)
         except Exception:  # pylint: disable=broad-except

From fb74dc50787595e3967012904741fd753981cf21 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 18:14:33 +0100
Subject: [PATCH 12/17] custodia: kem.set_keys: replace too-broad exception

Exception is too brod and may hide various issues that show up later. If
the code expects that entry may exist, then ldap.ALREADY_EXISTS
exception should be used
---
 ipaserver/secrets/kem.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/ipaserver/secrets/kem.py b/ipaserver/secrets/kem.py
index 5d784b7..28fb4d3 100644
--- a/ipaserver/secrets/kem.py
+++ b/ipaserver/secrets/kem.py
@@ -139,8 +139,7 @@ def set_key(self, usage, principal, key):
                     ('memberPrincipal', principal.encode('utf-8')),
                     ('ipaPublicKey', public_key)]
             conn.add_s(dn, mods)
-        except Exception:  # pylint: disable=broad-except
-            # This may fail if the entry already exists
+        except ldap.ALREADY_EXISTS:
             mods = [(ldap.MOD_REPLACE, 'ipaPublicKey', public_key)]
             conn.modify_s(dn, mods)
 

From 015ab52f5a5ba75f8b9dc23a6268a988b86c4418 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 18:36:24 +0100
Subject: [PATCH 13/17] py3: upgradeinstance: open dse.ldif in textual mode

ldap ldif parser requires to have input file opened in textual mode

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/install/upgradeinstance.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
index 8e28436..b244898 100644
--- a/ipaserver/install/upgradeinstance.py
+++ b/ipaserver/install/upgradeinstance.py
@@ -124,7 +124,7 @@ def create_instance(self):
 
     def __save_config(self):
         shutil.copy2(self.filename, self.savefilename)
-        with open(self.filename, "rb") as in_file:
+        with open(self.filename, "r") as in_file:
             parser = GetEntryFromLDIF(in_file, entries_dn=["cn=config"])
             parser.parse()
             try:
@@ -156,8 +156,8 @@ def __save_config(self):
 
     def __enable_ds_global_write_lock(self):
         ldif_outfile = "%s.modified.out" % self.filename
-        with open(ldif_outfile, "wb") as out_file:
-            with open(self.filename, "rb") as in_file:
+        with open(ldif_outfile, "w") as out_file:
+            with open(self.filename, "r") as in_file:
                 parser = installutils.ModifyLDIF(in_file, out_file)
 
                 parser.replace_value(
@@ -172,8 +172,8 @@ def __restore_config(self):
         global_lock = self.restore_state('nsslapd-global-backend-lock')
 
         ldif_outfile = "%s.modified.out" % self.filename
-        with open(ldif_outfile, "wb") as out_file:
-            with open(self.filename, "rb") as in_file:
+        with open(ldif_outfile, "w") as out_file:
+            with open(self.filename, "r") as in_file:
                 parser = installutils.ModifyLDIF(in_file, out_file)
 
                 if port is not None:
@@ -194,8 +194,8 @@ def __restore_config(self):
 
     def __disable_listeners(self):
         ldif_outfile = "%s.modified.out" % self.filename
-        with open(ldif_outfile, "wb") as out_file:
-            with open(self.filename, "rb") as in_file:
+        with open(ldif_outfile, "w") as out_file:
+            with open(self.filename, "r") as in_file:
                 parser = installutils.ModifyLDIF(in_file, out_file)
                 parser.replace_value("cn=config", "nsslapd-port", ["0"])
                 parser.replace_value("cn=config", "nsslapd-security", ["off"])

From b94dddf7086a08f5ae7cadc14131bc4fe88fbd50 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 18:53:47 +0100
Subject: [PATCH 14/17] py3: upgradeinstance: decode data before storing them
 as backup...

...and vice versa
backup requires string not bytes, but ldap provide bytes thus data must
be decoded and encoded from restore

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/install/upgradeinstance.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
index b244898..8e6c87d 100644
--- a/ipaserver/install/upgradeinstance.py
+++ b/ipaserver/install/upgradeinstance.py
@@ -134,21 +134,22 @@ def __save_config(self):
                                    self.filename)
 
             try:
-                port = config_entry['nsslapd-port'][0]
+                port = config_entry['nsslapd-port'][0].decode('utf-8')
             except KeyError:
                 pass
             else:
                 self.backup_state('nsslapd-port', port)
 
             try:
-                security = config_entry['nsslapd-security'][0]
+                security = config_entry['nsslapd-security'][0].decode('utf-8')
             except KeyError:
                 pass
             else:
                 self.backup_state('nsslapd-security', security)
 
             try:
-                global_lock = config_entry['nsslapd-global-backend-lock'][0]
+                global_lock = config_entry[
+                    'nsslapd-global-backend-lock'][0].decode('utf-8')
             except KeyError:
                 pass
             else:
@@ -177,16 +178,17 @@ def __restore_config(self):
                 parser = installutils.ModifyLDIF(in_file, out_file)
 
                 if port is not None:
-                    parser.replace_value("cn=config", "nsslapd-port", [port])
+                    parser.replace_value(
+                        "cn=config", "nsslapd-port", [port.encode('utf-8')])
                 if security is not None:
                     parser.replace_value("cn=config", "nsslapd-security",
-                                         [security])
+                                         [security.encode('utf-8')])
 
                 # disable global lock by default
                 parser.remove_value("cn=config", "nsslapd-global-backend-lock")
                 if global_lock is not None:
                     parser.add_value("cn=config", "nsslapd-global-backend-lock",
-                                     [global_lock])
+                                     [global_lock.encode('utf-8')])
 
                 parser.parse()
 

From 3bd4cee6eeaf5db1a1ac108d162fd6529cce3832 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 19:23:39 +0100
Subject: [PATCH 15/17] py3: upgradeinstance: use bytes literals with LDIF
 operations

python ldif support only bytes as values, literals must be bytes

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/install/upgradeinstance.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
index 8e6c87d..fca4226 100644
--- a/ipaserver/install/upgradeinstance.py
+++ b/ipaserver/install/upgradeinstance.py
@@ -162,7 +162,7 @@ def __enable_ds_global_write_lock(self):
                 parser = installutils.ModifyLDIF(in_file, out_file)
 
                 parser.replace_value(
-                    "cn=config", "nsslapd-global-backend-lock", ["on"])
+                    "cn=config", "nsslapd-global-backend-lock", [b"on"])
                 parser.parse()
 
         shutil.copy2(ldif_outfile, self.filename)
@@ -199,8 +199,8 @@ def __disable_listeners(self):
         with open(ldif_outfile, "w") as out_file:
             with open(self.filename, "r") as in_file:
                 parser = installutils.ModifyLDIF(in_file, out_file)
-                parser.replace_value("cn=config", "nsslapd-port", ["0"])
-                parser.replace_value("cn=config", "nsslapd-security", ["off"])
+                parser.replace_value("cn=config", "nsslapd-port", [b"0"])
+                parser.replace_value("cn=config", "nsslapd-security", [b"off"])
                 parser.remove_value("cn=config", "nsslapd-ldapientrysearchbase")
                 parser.parse()
 

From 841e14477f72e428631b5e6114764f0801a90102 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 19:59:31 +0100
Subject: [PATCH 16/17] py3: create DNS zonefile: use textual mode

Also code was rewritten to use NamedTemporaryFile with context

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/install/bindinstance.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index e24249a..10a37b9 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -668,10 +668,13 @@ def create_file_with_system_records(self):
                 system_records.get_base_records()
             )
         )
-        [fd, name] = tempfile.mkstemp(".db","ipa.system.records.")
-        os.write(fd, text)
-        os.close(fd)
-        print("Please add records in this file to your DNS system:", name)
+        with tempfile.NamedTemporaryFile(
+                mode="w", prefix="ipa.system.records.",
+                suffix=".db", delete=False
+        ) as f:
+            f.write(text)
+            print("Please add records in this file to your DNS system:",
+                  f.name)
 
     def create_instance(self):
 

From 0769eeef325b1777257bf5fa86b3d8d6a0daafbd Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 31 Jan 2017 20:27:11 +0100
Subject: [PATCH 17/17] py3: change_admin_password: use textual mode

Convert function to NamedTemporaryFile with textual mode, because
passwords are text. Using `with` and NamedTemporaryFile gives more
security agains leaking password from tempfiles.

https://fedorahosted.org/freeipa/ticket/4985
---
 ipaserver/install/dsinstance.py | 24 ++++++++----------------
 1 file changed, 8 insertions(+), 16 deletions(-)

diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index ceb7bf3..ceb3693 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -951,21 +951,19 @@ def add_hbac(self):
 
     def change_admin_password(self, password):
         root_logger.debug("Changing admin password")
-        dmpwdfile = ""
-        admpwdfile = ""
 
-        try:
-            (dmpwdfd, dmpwdfile) = tempfile.mkstemp(dir=paths.VAR_LIB_IPA)
-            os.write(dmpwdfd, self.dm_password)
-            os.close(dmpwdfd)
+        NTF = tempfile.NamedTemporaryFile
+        dir = paths.VAR_LIB_IPA
+        with NTF("w", dir=dir) as dmpwdfile, NTF("w", dir=dir) as admpwdfile:
+            dmpwdfile.write(self.dm_password)
+            dmpwdfile.flush()
 
-            (admpwdfd, admpwdfile) = tempfile.mkstemp(dir=paths.VAR_LIB_IPA)
-            os.write(admpwdfd, password)
-            os.close(admpwdfd)
+            admpwdfile.write(password)
+            admpwdfile.flush()
 
             args = [paths.LDAPPASSWD, "-h", self.fqdn,
                     "-ZZ", "-x", "-D", str(DN(('cn', 'Directory Manager'))),
-                    "-y", dmpwdfile, "-T", admpwdfile,
+                    "-y", dmpwdfile.name, "-T", admpwdfile.name,
                     str(DN(('uid', 'admin'), ('cn', 'users'), ('cn', 'accounts'), self.suffix))]
             try:
                 env = {'LDAPTLS_CACERTDIR': os.path.dirname(paths.IPA_CA_CRT),
@@ -976,12 +974,6 @@ def change_admin_password(self, password):
                 print("Unable to set admin password", e)
                 root_logger.debug("Unable to set admin password %s" % e)
 
-        finally:
-            if os.path.isfile(dmpwdfile):
-                os.remove(dmpwdfile)
-            if os.path.isfile(admpwdfile):
-                os.remove(admpwdfile)
-
     def uninstall(self):
         if self.is_configured():
             self.print_msg("Unconfiguring directory server")
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to