# Authors: # Fraser Tweedale # # This program 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 of the License. # # 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright (C) 2016 Red Hat, Inc. # All rights reserved. # from __future__ import absolute_import from __future__ import print_function import getopt import ldap import nss.nss as nss import subprocess import sys import getpass import pki.cli class DBCLI(pki.cli.CLI): def __init__(self): super(DBCLI, self).__init__( 'db', 'DB management commands') self.add_module(DBUpgrade()) self.add_module(DBSchemaUpgrade()) class DBSchemaUpgrade(pki.cli.CLI): SCHEMA_PATH = '/usr/share/pki/server/conf/schema.ldif' def __init__(self): super(DBSchemaUpgrade, self).__init__( 'schema-upgrade', 'Upgrade PKI database schema') def usage(self): print('Usage: pki-server db-schema-upgrade [OPTIONS]') print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' -D, --bind-dn Connect DN (default: cn=Directory Manager).') print(' -w, --bind-password Password to connect to DB.') print(' -v, --verbose Run in verbose mode.') print(' --help Show help message.') print() def execute(self, argv): try: opts, _ = getopt.gnu_getopt( argv, 'i:D:w:v', ['instance=', 'bind-dn=', 'bind-password=', 'verbose', 'help']) except getopt.GetoptError as e: print('ERROR: ' + str(e)) self.usage() sys.exit(1) instance_name = 'pki-tomcat' bind_dn = 'cn=Directory Manager' bind_password = None for o, a in opts: if o in ('-i', '--instance'): instance_name = a elif o in ('-D', '--bind-dn'): bind_dn = a elif o in ('-w', '--bind-password'): bind_password = a elif o in ('-v', '--verbose'): self.set_verbose(True) elif o == '--help': self.usage() sys.exit() else: print('ERROR: unknown option ' + o) self.usage() sys.exit(1) instance = pki.server.PKIInstance(instance_name) if not instance.is_valid(): print("ERROR: Invalid instance %s." % instance_name) sys.exit(1) instance.load() if not instance.subsystems: print("ERROR: No subsystem in instance %s." % instance_name) sys.exit(1) if not bind_password: bind_password = getpass.getpass(prompt='Enter password: ') try: self.update_schema(instance.subsystems[0], bind_dn, bind_password) except subprocess.CalledProcessError as e: print("ERROR: " + e.output) sys.exit(e.returncode) self.print_message('Upgrade complete') def update_schema(self, subsystem, bind_dn, bind_password): # TODO(alee) re-implement this using open_database host = subsystem.config['internaldb.ldapconn.host'] port = subsystem.config['internaldb.ldapconn.port'] secure = subsystem.config['internaldb.ldapconn.secureConn'] cmd = ['ldapmodify', '-c', '-D', bind_dn, '-w', bind_password, '-h', host, '-p', port, '-f', self.SCHEMA_PATH ] if secure.lower() == "true": cmd.append('-Z') subprocess.check_output(cmd, stderr=subprocess.STDOUT) class DBUpgrade(pki.cli.CLI): def __init__(self): super(DBUpgrade, self).__init__( 'upgrade', 'Upgrade PKI server database') def usage(self): print('Usage: pki-server db-upgrade [OPTIONS]') print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' -v, --verbose Run in verbose mode.') print(' --help Show help message.') print() def execute(self, argv): try: opts, _ = getopt.gnu_getopt( argv, 'i:v', ['instance=', 'verbose', 'help']) except getopt.GetoptError as e: print('ERROR: ' + str(e)) self.usage() sys.exit(1) instance_name = 'pki-tomcat' for o, a in opts: if o in ('-i', '--instance'): instance_name = a elif o in ('-v', '--verbose'): self.set_verbose(True) elif o == '--help': self.usage() sys.exit() else: print('ERROR: unknown option ' + o) self.usage() sys.exit(1) nss.nss_init_nodb() instance = pki.server.PKIInstance(instance_name) if not instance.is_valid(): print("ERROR: Invalid instance %s." % instance_name) sys.exit(1) instance.load() subsystem = instance.get_subsystem('ca') if not subsystem: print('ERROR: No CA subsystem in instance %s.' + instance_name) sys.exit(1) base_dn = subsystem.config['internaldb.basedn'] conn = subsystem.open_database() try: repo_dn = 'ou=certificateRepository,ou=ca,%s' % base_dn if self.verbose: print('Searching certificates records with missing issuerName in %s' % repo_dn) entries = conn.ldap.search_s( repo_dn, ldap.SCOPE_ONELEVEL, '(&(objectclass=certificaterecord)(|(!(issuername=*))(issuername=)))', None) for entry in entries: self.add_issuer_name(conn, entry) finally: conn.close() self.print_message('Upgrade complete') def add_issuer_name(self, conn, entry): dn, attrs = entry if self.verbose: print('Fixing certificate record %s' % dn) attr_cert = attrs.get('userCertificate;binary') if not attr_cert: return # shouldn't happen, but nothing we can do if it does cert = nss.Certificate(bytearray(attr_cert[0])) issuer_name = str(cert.issuer) try: conn.ldap.modify_s(dn, [(ldap.MOD_REPLACE, 'issuerName', issuer_name)]) except ldap.LDAPError as e: print( 'Failed to add issuerName to certificate {}: {}' .format(attrs.get('cn', [''])[0], e))