URL: https://github.com/freeipa/freeipa/pull/1254
Author: tiran
 Title: #1254: [WIP] Support sqlite NSSDB
Action: opened

PR body:
"""
Prepare CertDB and NSSDatabase to support sqlite DB format.

https://pagure.io/freeipa/issue/7049

Signed-off-by: Christian Heimes <chei...@redhat.com>
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/1254/head:pr1254
git checkout pr1254
From 4696378ab9c5549d6020285fb4508091be09e35b Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Wed, 8 Nov 2017 12:10:54 +0100
Subject: [PATCH] [WIP] Support sqlite NSSDB

Prepare CertDB and NSSDatabase to support sqlite DB format.

https://pagure.io/freeipa/issue/7049

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 freeipa.spec.in                             |  5 ++
 ipaclient/install/client.py                 | 10 +---
 ipapython/certdb.py                         | 85 ++++++++++++++++++++++-------
 ipaserver/install/certs.py                  | 50 ++++++++---------
 ipaserver/install/ipa_backup.py             |  4 +-
 ipaserver/install/ipa_replica_prepare.py    |  7 +--
 ipaserver/install/ipa_restore.py            |  4 +-
 ipaserver/install/ipa_server_certinstall.py | 19 +++----
 ipaserver/secrets/store.py                  | 34 +++++++-----
 9 files changed, 129 insertions(+), 89 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index cb71fd7aed..cd430b769b 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -1593,9 +1593,14 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
 %dir %attr(0755,root,root) %{_sysconfdir}/ipa/nssdb
+# old dbm format
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/cert8.db
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/key3.db
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/secmod.db
+# new sqlite format
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/cert9.db
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/key4.db
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pkcs11.txt
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pwdfile.txt
 %ghost %config(noreplace) %{_sysconfdir}/pki/ca-trust/source/ipa.p11-kit
 %dir %{_localstatedir}/lib/ipa-client
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 2f89e7eaed..6fa42e3665 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -2304,9 +2304,6 @@ def create_ipa_nssdb():
     db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
     db.create_db(mode=0o755, backup=True)
     os.chmod(db.pwd_file, 0o600)
-    os.chmod(os.path.join(db.secdir, 'cert8.db'), 0o644)
-    os.chmod(os.path.join(db.secdir, 'key3.db'), 0o644)
-    os.chmod(os.path.join(db.secdir, 'secmod.db'), 0o644)
 
 
 def update_ipa_nssdb():
@@ -3067,11 +3064,8 @@ def uninstall(options):
             logger.error("%s failed to stop tracking certificate: %s",
                          cmonger.service_name, e)
 
-    for filename in (os.path.join(ipa_db.secdir, 'cert8.db'),
-                     os.path.join(ipa_db.secdir, 'key3.db'),
-                     os.path.join(ipa_db.secdir, 'secmod.db'),
-                     os.path.join(ipa_db.secdir, 'pwdfile.txt')):
-        remove_file(filename)
+    for filename in certdb.NSS_FILES:
+        remove_file(os.path.join(ipa_db.secdir, filename))
 
     # Remove any special principal names we added to the IPA CA helper
     certmonger.remove_principal_from_cas()
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index 92da7829ac..5f394e19b8 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -53,7 +53,9 @@
 
 CA_NICKNAME_FMT = "%s IPA CA"
 
-NSS_FILES = ("cert8.db", "key3.db", "secmod.db", "pwdfile.txt")
+NSS_DBM_FILES = ("cert8.db", "key3.db", "secmod.db")
+NSS_SQLITE_FILES = ("cert9.db", "key4.db", "pkcs11.txt")
+NSS_FILES = NSS_DBM_FILES + NSS_SQLITE_FILES + ("pwdfile.txt",)
 
 TrustFlags = collections.namedtuple('TrustFlags', 'has_key trusted ca usages')
 
@@ -224,14 +226,46 @@ class NSSDatabase(object):
     # got too tied to IPA server details, killing reusability.
     # BaseCertDB is a class that knows nothing about IPA.
     # Generic NSS DB code should be moved here.
-    def __init__(self, nssdir=None):
+
+    default_dbtype = 'dbm'
+
+    def __init__(self, nssdir=None, dbtype='auto'):
         if nssdir is None:
             self.secdir = tempfile.mkdtemp()
             self._is_temporary = True
+            if dbtype == 'auto':
+                dbtype = self.default_dbtype
+            else:
+                dbtype = dbtype
         else:
             self.secdir = nssdir
             self._is_temporary = False
+            if self.dbtype == 'auto':
+                if os.path.isfile(os.path.join(self.secdir, "cert9.db")):
+                    dbtype = 'sqlite'
+                elif os.path.isfile(os.path.join(self.secdir, "cert8.db")):
+                    dbtype = 'dbm'
+                else:
+                    dbtype = self.default_dbtype
+
         self.pwd_file = os.path.join(self.secdir, 'pwdfile.txt')
+        self.dbtype = dbtype
+        if dbtype == 'dbm':
+            self.certdb = os.path.join(self.secdir, "cert8.db")
+            self.keydb = os.path.join(self.secdir, "key3.db")
+            self.secmod = os.path.join(self.secdir, "secmod.db")
+        elif dbtype == 'sqlite':
+            self.certdb = os.path.join(self.secdir, "cert9.db")
+            self.keydb = os.path.join(self.secdir, "key4.db")
+            self.secmod = os.path.join(self.secdir, "pkcs11.txt")
+        else:
+            raise ValueError(dbtype)
+        self.filenames = [
+            self.certdb,
+            self.keydb,
+            self.secmod,
+            self.pwd_file,
+        ]
 
     def close(self):
         if self._is_temporary:
@@ -244,11 +278,16 @@ def __exit__(self, type, value, tb):
         self.close()
 
     def run_certutil(self, args, stdin=None, **kwargs):
-        new_args = [CERTUTIL, "-d", self.secdir]
-        new_args = new_args + args
+        new_args = [CERTUTIL, "-d", '{}:{}'.format(self.dbtype, self.secdir)]
+        new_args.extend(args)
         new_args.extend(['-f', self.pwd_file])
         return ipautil.run(new_args, stdin, **kwargs)
 
+    def run_pk12util(self, args, stdin=None, **kwargs):
+        new_args = [PK12UTIL, "-d", '{}:{}'.format(self.dbtype, self.secdir)]
+        new_args.extend(args)
+        return ipautil.run(new_args, stdin, **kwargs)
+
     def create_db(self, user=None, group=None, mode=None, backup=False):
         """Create cert DB
 
@@ -257,13 +296,14 @@ def create_db(self, user=None, group=None, mode=None, backup=False):
         :param mode: Mode of the secdir
         :param backup: Backup the sedir files
         """
-        dirmode = 0o750
-        filemode = 0o640
-        pwdfilemode = 0o640
         if mode is not None:
             dirmode = mode
             filemode = mode & 0o666
             pwdfilemode = mode & 0o660
+        else:
+            dirmode = 0o750
+            filemode = 0o640
+            pwdfilemode = 0o640
 
         uid = -1
         gid = -1
@@ -273,7 +313,7 @@ def create_db(self, user=None, group=None, mode=None, backup=False):
             gid = grp.getgrnam(group).gr_gid
 
         if backup:
-            for filename in NSS_FILES:
+            for filename in self.filenames:
                 path = os.path.join(self.secdir, filename)
                 ipautil.backup_file(path)
 
@@ -293,7 +333,7 @@ def create_db(self, user=None, group=None, mode=None, backup=False):
         # Finally fix up perms
         os.chown(self.secdir, uid, gid)
         os.chmod(self.secdir, dirmode)
-        for filename in NSS_FILES:
+        for filename in self.filenames:
             path = os.path.join(self.secdir, filename)
             if os.path.exists(path):
                 os.chown(path, uid, gid)
@@ -304,7 +344,7 @@ def create_db(self, user=None, group=None, mode=None, backup=False):
                 os.chmod(path, new_mode)
 
     def restore(self):
-        for filename in NSS_FILES:
+        for filename in self.filenames:
             path = os.path.join(self.secdir, filename)
             backup_path = path + '.orig'
             save_path = path + '.ipasave'
@@ -367,16 +407,17 @@ def get_trust_chain(self, nickname):
         return root_nicknames
 
     def export_pkcs12(self, nickname, pkcs12_filename, pkcs12_passwd=None):
-        args = [PK12UTIL, "-d", self.secdir,
-                "-o", pkcs12_filename,
-                "-n", nickname,
-                "-k", self.pwd_file]
+        args = [
+            "-o", pkcs12_filename,
+            "-n", nickname,
+            "-k", self.pwd_file
+        ]
         pkcs12_password_file = None
         if pkcs12_passwd is not None:
             pkcs12_password_file = ipautil.write_tmp_file(pkcs12_passwd + '\n')
-            args = args + ["-w", pkcs12_password_file.name]
+            args.extend(["-w", pkcs12_password_file.name])
         try:
-            ipautil.run(args)
+            self.run_pk12util(args)
         except ipautil.CalledProcessError as e:
             if e.returncode == 17:
                 raise RuntimeError("incorrect password for pkcs#12 file %s" %
@@ -391,15 +432,17 @@ def export_pkcs12(self, nickname, pkcs12_filename, pkcs12_passwd=None):
                 pkcs12_password_file.close()
 
     def import_pkcs12(self, pkcs12_filename, pkcs12_passwd=None):
-        args = [PK12UTIL, "-d", self.secdir,
-                "-i", pkcs12_filename,
-                "-k", self.pwd_file, '-v']
+        args = [
+            "-i", pkcs12_filename,
+            "-k", self.pwd_file,
+            "-v"
+        ]
         pkcs12_password_file = None
         if pkcs12_passwd is not None:
             pkcs12_password_file = ipautil.write_tmp_file(pkcs12_passwd + '\n')
-            args = args + ["-w", pkcs12_password_file.name]
+            args.extend(["-w", pkcs12_password_file.name])
         try:
-            ipautil.run(args)
+            self.run_pk12util(args)
         except ipautil.CalledProcessError as e:
             if e.returncode == 17:
                 raise RuntimeError("incorrect password for pkcs#12 file %s" %
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index a40aff5e91..ea0784fba4 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -103,18 +103,20 @@ class CertDB(object):
     # TODO: Remove all selfsign code
     def __init__(self, realm, nssdir, fstore=None,
                  host_name=None, subject_base=None, ca_subject=None,
-                 user=None, group=None, mode=None, create=False):
-        self.nssdb = NSSDatabase(nssdir)
+                 user=None, group=None, mode=None, create=False,
+                 dbtype='auto'):
+        self.nssdb = NSSDatabase(nssdir, dbtype=dbtype)
 
         self.secdir = nssdir
         self.realm = realm
 
-        self.noise_fname = self.secdir + "/noise.txt"
-        self.certdb_fname = self.secdir + "/cert8.db"
-        self.keydb_fname = self.secdir + "/key3.db"
-        self.secmod_fname = self.secdir + "/secmod.db"
-        self.pk12_fname = self.secdir + "/cacert.p12"
-        self.pin_fname = self.secdir + "/pin.txt"
+        self.noise_fname = os.path.join(self.secdir, "noise.txt")
+
+        self.certdb_fname = self.nssdb.cerdb
+        self.keydb_fname = self.nssdb.keydb
+        self.secmod_fname = self.nssdb.secmod
+        self.pk12_fname = os.path.join(self.secdir, "cacert.p12")
+        self.pin_fname = os.path.join(self.secdir + "pin.txt")
         self.reqdir = None
         self.certreq_fname = None
         self.certder_fname = None
@@ -171,15 +173,7 @@ def exists(self):
         """
         Checks whether all NSS database files + our pwd_file exist
         """
-        db_files = (
-            self.secdir,
-            self.certdb_fname,
-            self.keydb_fname,
-            self.secmod_fname,
-            self.nssdb.pwd_file,
-        )
-
-        for f in db_files:
+        for f in self.nssdb.filenames:
             if not os.path.exists(f):
                 return False
         return True
@@ -291,11 +285,12 @@ def export_ca_cert(self, nickname, create_pkcs12=False):
 
         if create_pkcs12:
             ipautil.backup_file(self.pk12_fname)
-            ipautil.run([paths.PK12UTIL, "-d", self.secdir,
-                         "-o", self.pk12_fname,
-                         "-n", self.cacert_name,
-                         "-w", self.passwd_fname,
-                         "-k", self.passwd_fname])
+            self.nssdb.run_pk12util([
+                "-o", self.pk12_fname,
+                "-n", self.cacert_name,
+                "-k", self.passwd_fname,
+                "-w", self.passwd_fname,
+            ])
             self.set_perms(self.pk12_fname)
 
     def load_cacert(self, cacert_fname, trust_flags):
@@ -514,11 +509,12 @@ def export_pkcs12(self, pkcs12_fname, pkcs12_pwd_fname, nickname=None):
         if nickname is None:
             nickname = get_ca_nickname(api.env.realm)
 
-        ipautil.run([paths.PK12UTIL, "-d", self.secdir,
-                     "-o", pkcs12_fname,
-                     "-n", nickname,
-                     "-k", self.passwd_fname,
-                     "-w", pkcs12_pwd_fname])
+        self.nssdb.run_pk12util([
+            "-o", pkcs12_fname,
+            "-n", nickname,
+            "-k", self.passwd_fname,
+            "-w", pkcs12_pwd_fname
+        ])
 
     def create_from_cacert(self):
         cacert_fname = paths.IPA_CA_CRT
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index ac9b0fc1d7..86cb33e0aa 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -38,7 +38,7 @@
 from ipalib import api, errors
 from ipapython import version
 from ipapython.ipautil import run, write_tmp_file
-from ipapython import admintool
+from ipapython import admintool, certdb
 from ipapython.dn import DN
 from ipaserver.install.replication import wait_for_task
 from ipaserver.install import installutils
@@ -192,7 +192,7 @@ class Backup(admintool.AdminTool):
         paths.HOSTS,
     ) + tuple(
         os.path.join(paths.IPA_NSSDB_DIR, file)
-        for file in ('cert8.db', 'key3.db', 'secmod.db')
+        for file in (certdb.NSS_DBM_FILES + certdb.NSS_SQLITE_FILES)
     )
 
     logs=(
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index 1b82218858..e5808746b0 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -45,7 +45,7 @@
 from ipaserver.install.server.replicainstall import install_ca_cert
 from ipaserver.install.bindinstance import (
     add_zone, add_fwd_rr, add_ptr_rr, dns_container_exists)
-from ipapython import ipautil, admintool
+from ipapython import ipautil, admintool, certdb
 from ipapython.dn import DN
 from ipapython import version
 from ipalib import api
@@ -565,9 +565,8 @@ def export_certdb(self, fname, passwd_fname):
                 installutils.remove_file(pkcs12_fname)
                 installutils.remove_file(passwd_fname)
 
-            self.remove_info_file("cert8.db")
-            self.remove_info_file("key3.db")
-            self.remove_info_file("secmod.db")
+            for fname in (certdb.NSS_SQLITE_FILES + certdb.NSS_SQLITE_FILES):
+                self.remove_info_file(fname)
             self.remove_info_file("noise.txt")
 
             orig_filename = passwd_fname + ".orig"
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
index 7bcb0d4924..b19c90e200 100644
--- a/ipaserver/install/ipa_restore.py
+++ b/ipaserver/install/ipa_restore.py
@@ -40,7 +40,7 @@
 from ipalib.constants import FQDN
 from ipapython import version, ipautil
 from ipapython.ipautil import run, user_input
-from ipapython import admintool
+from ipapython import admintool, certdb
 from ipapython.dn import DN
 from ipaserver.install.replication import (wait_for_task, ReplicationManager,
                                            get_cs_replication_manager)
@@ -842,7 +842,7 @@ def cert_restore_prepare(self):
 
         krbinstance.KrbInstance().stop_tracking_certs()
 
-        for basename in ('cert8.db', 'key3.db', 'secmod.db', 'pwdfile.txt'):
+        for basename in certdb.NSS_FILES:
             filename = os.path.join(paths.IPA_NSSDB_DIR, basename)
             try:
                 ipautil.backup_file(filename)
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 36c0a586d6..06f4b61fde 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -30,9 +30,10 @@
 from ipaplatform.constants import constants
 from ipaplatform.paths import paths
 from ipapython import admintool
-from ipapython.certdb import (get_ca_nickname,
-                              NSSDatabase,
-                              verify_kdc_cert_validity)
+from ipapython.certdb import (
+    NSS_DBM_FILES, NSS_SQLITE_FILES, NSSDatabase, get_ca_nickname,
+    verify_kdc_cert_validity
+)
 from ipapython.dn import DN
 from ipalib import api, errors
 from ipaserver.install import certs, dsinstance, installutils, krbinstance
@@ -170,14 +171,12 @@ def install_http_cert(self):
             quotes=False)
 
         # Fix the database permissions
-        os.chmod(os.path.join(dirname, 'cert8.db'), 0o640)
-        os.chmod(os.path.join(dirname, 'key3.db'), 0o640)
-        os.chmod(os.path.join(dirname, 'secmod.db'), 0o640)
-
         pent = pwd.getpwnam(constants.HTTPD_USER)
-        os.chown(os.path.join(dirname, 'cert8.db'), 0, pent.pw_gid)
-        os.chown(os.path.join(dirname, 'key3.db'), 0, pent.pw_gid)
-        os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
+        for filename in (NSS_DBM_FILES + NSS_SQLITE_FILES):
+            absname = os.path.join(dirname, filename)
+            if os.path.isfile(absname):
+                os.chmod(absname, 0o640)
+                os.chown(absname, 0, pent.pw_gid)
 
     def install_kdc_cert(self):
         ca_cert_file = paths.CA_BUNDLE_PEM
diff --git a/ipaserver/secrets/store.py b/ipaserver/secrets/store.py
index e741d3311e..364b748c06 100644
--- a/ipaserver/secrets/store.py
+++ b/ipaserver/secrets/store.py
@@ -6,6 +6,7 @@
 from jwcrypto.common import json_decode, json_encode
 from ipaplatform.paths import paths
 from ipapython import ipautil
+from ipapython.certdb import NSSDatabase
 from ipaserver.secrets.common import iSecLdap
 import ldap
 import os
@@ -80,10 +81,11 @@ def export_key(self):
                 '--wrap-nickname', self.wrap_nick,
                 '--target-nickname', self.target_nick,
                 '-o', wrapped_key_file])
-            ipautil.run([
-                paths.CERTUTIL, '-d', self.nssdb_path,
+            nssdb = NSSDatabase(self.nssdb_path)
+            nssdb.run_certutil([
                 '-L', '-n', self.target_nick,
-                '-a', '-o', certificate_file])
+                '-a', '-o', certificate_file,
+            ])
             with open(wrapped_key_file, 'rb') as f:
                 wrapped_key = f.read()
             with open(certificate_file, 'r') as f:
@@ -120,12 +122,13 @@ def export_key(self):
             with open(pk12pwfile, 'w') as f:
                 f.write(password)
             pk12file = os.path.join(tdir, 'pk12file')
-            ipautil.run([paths.PK12UTIL,
-                         "-d", self.nssdb_path,
-                         "-o", pk12file,
-                         "-n", self.nickname,
-                         "-k", nsspwfile,
-                         "-w", pk12pwfile])
+            nssdb = NSSDatabase(self.nssdb_path)
+            nssdb.run_pk12util([
+                "-o", pk12file,
+                "-n", self.nickname,
+                "-k", nsspwfile,
+                "-w", pk12pwfile,
+            ])
             with open(pk12file, 'rb') as f:
                 data = f.read()
         finally:
@@ -146,12 +149,13 @@ def import_key(self, value):
             pk12file = os.path.join(tdir, 'pk12file')
             with open(pk12file, 'wb') as f:
                 f.write(b64decode(v['pkcs12 data']))
-            ipautil.run([paths.PK12UTIL,
-                         "-d", self.nssdb_path,
-                         "-i", pk12file,
-                         "-n", self.nickname,
-                         "-k", nsspwfile,
-                         "-w", pk12pwfile])
+            nssdb = NSSDatabase(self.nssdb_path)
+            nssdb.run_pk12util([
+                "-i", pk12file,
+                "-n", self.nickname,
+                "-k", nsspwfile,
+                "-w", pk12pwfile,
+            ])
         finally:
             shutil.rmtree(tdir)
 
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org

Reply via email to