From 3f1078ad0329b87b62dd1503fc82dc6f2f227366 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Thu, 28 Nov 2024 19:51:14 +0100 Subject: [PATCH] [RHCS-5445] Implement new SSN legacy2 generator for CA The legacy id generator can introduce gaps when new ranges are allocated because of some conversion errors between hex and decimal. A new serial id generator has been introduced to avoid gap problems. The old and new serial generator are described in the following pages: - SSNv1: https://github.com/dogtagpki/pki/wiki/Sequential-Serial-Numbers-v1 - SSNv2://github.com/dogtagpki/pki/wiki/Sequential-Serial-Numbers-v2 Instances using SSNv1 can migrate to the SSNv2 using the commands described here: - https://github.com/dogtagpki/pki/wiki/Migrating-to-Sequential-Serial-Numbers-v2 If there is no need to avoid gaps it is better to avoid the migration. NOTE: the links above provide documentation to the newer version of dogtag. The porting to this release has some limitations to consider: - the new generator is available only to the CA ids; - some of the parameter are not customisable with `pkispawn` (e.g. increment, transfer, etc..) only the initial range limits. Additionally, during the migration of a clone it is required to provide new range limits even they will be overwritten with values coming from master. --- base/ca/shared/conf/db.ldif | 10 - .../org/dogtagpki/server/ca/cli/CACLI.java | 1 + .../org/dogtagpki/server/ca/cli/CADBCLI.java | 3 +- .../dogtagpki/server/ca/cli/CADBInitCLI.java | 31 + .../org/dogtagpki/server/ca/cli/CAIdCLI.java | 19 + .../server/ca/cli/CAIdGeneratorCLI.java | 21 + .../server/ca/cli/CAIdGeneratorUpdateCLI.java | 45 ++ .../certsrv/dbs/repository/IRepository.java | 50 +- base/server/etc/default.cfg | 4 + base/server/python/pki/server/cli/ca.py | 2 + base/server/python/pki/server/cli/id.py | 193 +++++++ .../deployment/scriptlets/configuration.py | 20 + base/server/python/pki/server/subsystem.py | 22 + .../cms/servlet/csadmin/LDAPConfigurator.java | 8 + .../servlet/csadmin/UpdateNumberRange.java | 42 +- .../java/com/netscape/cmscore/apps/CMS.java | 4 + .../netscape/cmscore/apps/DatabaseConfig.java | 7 + .../netscape/cmscore/apps/ServerConfig.java | 98 ++++ .../cmscore/dbs/CertificateRepository.java | 81 ++- .../com/netscape/cmscore/dbs/DBSubsystem.java | 2 + .../com/netscape/cmscore/dbs/Repository.java | 51 +- .../cmscore/request/RequestRepository.java | 20 +- .../server/cli/SubsystemDBInitCLI.java | 79 +++ .../cli/SubsystemIdGeneratorUpdateCLI.java | 546 ++++++++++++++++++ .../server/cli/SubsystemRangeUpdateCLI.java | 134 ++++- 25 files changed, 1445 insertions(+), 48 deletions(-) create mode 100644 base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBInitCLI.java create mode 100644 base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdCLI.java create mode 100644 base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorCLI.java create mode 100644 base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorUpdateCLI.java create mode 100644 base/server/python/pki/server/cli/id.py create mode 100644 base/server/src/main/java/com/netscape/cmscore/apps/ServerConfig.java create mode 100644 base/server/src/main/java/org/dogtagpki/server/cli/SubsystemIdGeneratorUpdateCLI.java diff --git a/base/ca/shared/conf/db.ldif b/base/ca/shared/conf/db.ldif index 704b8d11be7..6da245266ef 100644 --- a/base/ca/shared/conf/db.ldif +++ b/base/ca/shared/conf/db.ldif @@ -150,16 +150,6 @@ objectClass: top objectClass: organizationalUnit ou: replica -dn: ou=requests, ou=ranges,{rootSuffix} -objectClass: top -objectClass: organizationalUnit -ou: requests - -dn: ou=certificateRepository, ou=ranges,{rootSuffix} -objectClass: top -objectClass: organizationalUnit -ou: certificateRepository - dn: ou=certificateProfiles,ou=ca,{rootSuffix} objectClass: top objectClass: organizationalUnit diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CACLI.java b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CACLI.java index 71593bac5ec..a3340636f8c 100644 --- a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CACLI.java +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CACLI.java @@ -37,6 +37,7 @@ public CACLI(CLI parent) { addModule(new SubsystemGroupCLI(this)); addModule(new CAProfileCLI(this)); addModule(new SubsystemRangeCLI(this)); + addModule(new CAIdCLI(this)); addModule(new SubsystemUserCLI(this)); addModule(new SDCLI(this)); } diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBCLI.java b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBCLI.java index 8c8aa18ccba..b0842bcb521 100644 --- a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBCLI.java +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBCLI.java @@ -21,7 +21,6 @@ import org.dogtagpki.cli.CLI; import org.dogtagpki.server.cli.SubsystemDBEmptyCLI; import org.dogtagpki.server.cli.SubsystemDBInfoCLI; -import org.dogtagpki.server.cli.SubsystemDBInitCLI; import org.dogtagpki.server.cli.SubsystemDBRemoveCLI; import org.dogtagpki.server.cli.SubsystemDBVLVCLI; @@ -34,7 +33,7 @@ public CADBCLI(CLI parent) { super("db", "CA database management commands", parent); addModule(new SubsystemDBInfoCLI(this)); - addModule(new SubsystemDBInitCLI(this)); + addModule(new CADBInitCLI(this)); addModule(new SubsystemDBEmptyCLI(this)); addModule(new SubsystemDBRemoveCLI(this)); addModule(new CADBUpgradeCLI(this)); diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBInitCLI.java b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBInitCLI.java new file mode 100644 index 00000000000..678df2c8ff2 --- /dev/null +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CADBInitCLI.java @@ -0,0 +1,31 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.ca.cli; +import org.dogtagpki.cli.CLI; +import org.dogtagpki.server.cli.SubsystemDBInitCLI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netscape.certsrv.dbs.repository.IRepository.IDGenerator; +import com.netscape.cmscore.apps.DatabaseConfig; +import com.netscape.cmscore.dbs.CertificateRepository; +/** + * @author Endi S. Dewata + */ +public class CADBInitCLI extends SubsystemDBInitCLI { + public static Logger logger = LoggerFactory.getLogger(CADBInitCLI.class); + public CADBInitCLI(CLI parent) { + super("init", "Initialize CA database", parent); + } + @Override + public void init(DatabaseConfig dbConfig) throws Exception { + super.init(dbConfig); + String value = dbConfig.getString( + CertificateRepository.PROP_CERT_ID_GENERATOR, + CertificateRepository.DEFAULT_CERT_ID_GENERATOR); + serialIDGenerator = IDGenerator.fromString(value); + } +} \ No newline at end of file diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdCLI.java b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdCLI.java new file mode 100644 index 00000000000..b376679057c --- /dev/null +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdCLI.java @@ -0,0 +1,19 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.ca.cli; + +import org.dogtagpki.cli.CLI; + +/** + * @author Marco Fargetta {@literal } + */ +public class CAIdCLI extends CLI { + public CAIdCLI(CLI parent) { + super("id", "CA id generator management commands", parent); + + addModule(new CAIdGeneratorCLI(this)); + } +} \ No newline at end of file diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorCLI.java b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorCLI.java new file mode 100644 index 00000000000..1fea9cd57a4 --- /dev/null +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorCLI.java @@ -0,0 +1,21 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.ca.cli; + +import org.dogtagpki.cli.CLI; + +/** + * @author Marco Fargetta {@literal } + */ +public class CAIdGeneratorCLI extends CLI { + + public CAIdGeneratorCLI(CLI parent) { + super("generator", "CA id generator commands", parent); + + addModule(new CAIdGeneratorUpdateCLI(this)); + } + +} \ No newline at end of file diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorUpdateCLI.java b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorUpdateCLI.java new file mode 100644 index 00000000000..d1a6a55c067 --- /dev/null +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/cli/CAIdGeneratorUpdateCLI.java @@ -0,0 +1,45 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.ca.cli; + +import org.dogtagpki.cli.CLI; +import org.dogtagpki.server.cli.SubsystemIdGeneratorUpdateCLI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netscape.certsrv.dbs.repository.IRepository.IDGenerator; +import com.netscape.cmscore.apps.DatabaseConfig; +import com.netscape.cmscore.dbs.CertificateRepository; +import com.netscape.cmscore.ldapconn.LdapBoundConnection; + +/** + * @author Marco Fargetta {@literal } + */ +public class CAIdGeneratorUpdateCLI extends SubsystemIdGeneratorUpdateCLI { + private static final Logger logger = LoggerFactory.getLogger(CAIdGeneratorUpdateCLI.class); + + public CAIdGeneratorUpdateCLI(CLI parent) { + super(parent); + } + + @Override + protected void updateSerialNumberRangeGenerator(LdapBoundConnection conn, + DatabaseConfig dbConfig, String baseDN, String newRangesName, + IDGenerator newGenerator, String hostName, String securePort) throws Exception { + String value = dbConfig.getString( + CertificateRepository.PROP_CERT_ID_GENERATOR, + null); + if (value == null) { + idGenerator = IDGenerator.LEGACY; + } else { + idGenerator = IDGenerator.fromString(value); + } + if (newGenerator == IDGenerator.LEGACY_2 && idGenerator == IDGenerator.LEGACY) { + dbConfig.put(CertificateRepository.PROP_CERT_ID_GENERATOR, newGenerator.toString()); + } + super.updateSerialNumberRangeGenerator(conn, dbConfig, baseDN, newRangesName, newGenerator, hostName, securePort); + } +} \ No newline at end of file diff --git a/base/common/src/main/java/com/netscape/certsrv/dbs/repository/IRepository.java b/base/common/src/main/java/com/netscape/certsrv/dbs/repository/IRepository.java index 5b0f01d339a..7f5aab8f530 100644 --- a/base/common/src/main/java/com/netscape/certsrv/dbs/repository/IRepository.java +++ b/base/common/src/main/java/com/netscape/certsrv/dbs/repository/IRepository.java @@ -29,7 +29,27 @@ * @version $Revision$, $Date$ */ public interface IRepository { - + /** + * Base type for the serial generator + */ + public enum IDGenerator { + LEGACY("legacy"), + LEGACY_2("legacy2"); + private String name; + private IDGenerator(String name) { + this.name = name; + } + @Override + public String toString() { + return name; + } + public static IDGenerator fromString(String name) { + for (IDGenerator idGenerator : values()) { + if (idGenerator.name.equals(name)) return idGenerator; + } + throw new IllegalArgumentException("Invalid ID generator: " + name); + } + } /** * Retrieves the next serial number, and also increase the * serial number by one. @@ -80,4 +100,30 @@ public interface IRepository { */ public void setEnableSerialMgmt(boolean value) throws EBaseException; -} + + /** + * Gets the id generator associated with the repository instance + */ + public IDGenerator getIDGenerator(); + + /** + * Sets the id generator associated with the repository instance + * + * @param the generator + */ + public void setIDGenerator(IDGenerator idGenerator); + + /** + * Sets the id generator associated with the repository instance + * + * @param the generator name + */ + public void setIDGenerator(String idGenerator); + + /** + * Gets the entry containing the nextRange attribute + * + * @return entry DN + */ + public String getNextRangeDN(); +} \ No newline at end of file diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index d6c63c7201b..4c210220d4d 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -450,6 +450,10 @@ pki_request_number_range_end= pki_replica_number_range_start= pki_replica_number_range_end= +# Cert cert ID generator: legacy, legacy2 +pki_cert_id_generator=legacy +# Cert request ID generator: legacy, legacy2 +pki_request_id_generator=legacy ############################################################################### ## KRA Configuration: ## diff --git a/base/server/python/pki/server/cli/ca.py b/base/server/python/pki/server/cli/ca.py index 07df631bccf..a81e54ed00c 100644 --- a/base/server/python/pki/server/cli/ca.py +++ b/base/server/python/pki/server/cli/ca.py @@ -34,6 +34,7 @@ import pki.server.cli.config import pki.server.cli.db import pki.server.cli.group +import pki.server.cli.id import pki.server.cli.range import pki.server.cli.subsystem import pki.server.cli.user @@ -58,6 +59,7 @@ def __init__(self): self.add_module(pki.server.cli.group.GroupCLI(self)) self.add_module(CAProfileCLI()) self.add_module(pki.server.cli.range.RangeCLI(self)) + self.add_module(pki.server.cli.id.IdCLI(self)) self.add_module(pki.server.cli.user.UserCLI(self)) diff --git a/base/server/python/pki/server/cli/id.py b/base/server/python/pki/server/cli/id.py new file mode 100644 index 00000000000..29a5945d86d --- /dev/null +++ b/base/server/python/pki/server/cli/id.py @@ -0,0 +1,193 @@ +# +# Copyright Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +from __future__ import absolute_import +from __future__ import print_function +import getopt +import logging +import sys + +import pki.cli + +logger = logging.getLogger(__name__) + + +class IdCLI(pki.cli.CLI): + + def __init__(self, parent): + super().__init__( + 'id', + '%s id configuration management commands' % parent.name.upper()) + + self.parent = parent + self.add_module(IdGeneratorCLI(self)) + + +class IdGeneratorCLI(pki.cli.CLI): + + def __init__(self, parent): + super().__init__('generator', + '%s id generator configuration' % parent.parent.name.upper()) + + self.parent = parent + self.add_module(IdGeneratorShowCLI(self)) + self.add_module(IdGeneratorUpdateCLI(self)) + + +class IdGeneratorShowCLI(pki.cli.CLI): + + def __init__(self, parent): + super().__init__('show', 'Display %s id generator' % parent.parent.parent.name.upper()) + + self.parent = parent + + def print_help(self): + print('Usage: pki-server %s-id-generator-show [OPTIONS]' % + self.parent.parent.parent.name) + print() + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' -v, --verbose Run in verbose mode.') + print(' --debug Run in debug mode.') + print(' --help Show help message.') + print() + + def execute(self, argv): + try: + opts, _ = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', + 'verbose', 'debug', 'help']) + + except getopt.GetoptError as e: + logger.error(e) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + subsystem_name = self.parent.parent.parent.name + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o in ('-v', '--verbose'): + logging.getLogger().setLevel(logging.INFO) + + elif o == '--debug': + logging.getLogger().setLevel(logging.DEBUG) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + logger.error('Invalid option: %s', o) + self.print_help() + sys.exit(1) + + instance = pki.server.instance.PKIServerFactory.create(instance_name) + if not instance.exists(): + logger.error('Invalid instance: %s', instance_name) + sys.exit(1) + + instance.load() + + subsystem = instance.get_subsystem(subsystem_name) + + if not subsystem: + logger.error('No %s subsystem in instance %s', + subsystem_name.upper(), instance_name) + sys.exit(1) + + print(' Request ID generator: %s' % subsystem.config.get('dbs.request.id.generator', 'legacy')) + print(' Cert ID generator: %s' % subsystem.config.get('dbs.cert.id.generator', 'legacy')) + + +class IdGeneratorUpdateCLI(pki.cli.CLI): + + def __init__(self, parent): + super().__init__('update', 'Update %s id generator' % parent.parent.parent.name.upper()) + + self.parent = parent + + def print_help(self): + print('Usage: pki-server %s-id-generator-update [OPTIONS] ' % + self.parent.parent.parent.name) + print() + print(' Element to apply the generator (e.g. cert).') + print(' -t, --type Type of generator to use (e.g. random).') + print(' -r, --range Name for the new range tree if needed.') + print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' -v, --verbose Run in verbose mode.') + print(' --debug Run in debug mode.') + print(' --help Show help message.') + print() + + def execute(self, argv): + try: + opts, args = getopt.gnu_getopt(argv, 'i:t:r:v', [ + 'instance=', 'type=', 'range=', + 'verbose', 'debug', 'help']) + + except getopt.GetoptError as e: + logger.error(e) + self.print_help() + sys.exit(1) + + if len(args) != 1: + logger.error('Missing object for generator') + self.print_help() + sys.exit(1) + + generator_object = args[0] + instance_name = 'pki-tomcat' + subsystem_name = self.parent.parent.parent.name + generator = None + range_object = None + + for o, a in opts: + if o in ('-t', '--type'): + generator = a + + elif o in ('-r', '--range'): + range_object = a + + elif o in ('-i', '--instance'): + instance_name = a + + elif o in ('-v', '--verbose'): + logging.getLogger().setLevel(logging.INFO) + + elif o == '--debug': + logging.getLogger().setLevel(logging.DEBUG) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + logger.error('Invalid option: %s', o) + self.print_help() + sys.exit(1) + + if not generator: + logger.error('No specified') + self.print_help() + sys.exit(1) + + instance = pki.server.instance.PKIServerFactory.create(instance_name) + if not instance.exists(): + logger.error('Invalid instance: %s', instance_name) + sys.exit(1) + + instance.load() + + subsystem = instance.get_subsystem(subsystem_name) + + if not subsystem: + logger.error('No %s subsystem in instance %s', + subsystem_name.upper(), instance_name) + sys.exit(1) + + subsystem.update_id_generator(generator, generator_object, range_object) diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index e94778b4176..59d8712fc44 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -229,6 +229,10 @@ def spawn(self, deployer): subsystem.config['ca.defaultOcspUri'] = ocsp_uri if subsystem.name == 'ca': + cert_id_generator = deployer.mdict.get('pki_cert_id_generator') + if cert_id_generator: + subsystem.config['dbs.cert.id.generator'] = cert_id_generator + serial_number_range_start = deployer.mdict.get('pki_serial_number_range_start') if serial_number_range_start: subsystem.config['dbs.beginSerialNumber'] = serial_number_range_start @@ -237,6 +241,21 @@ def spawn(self, deployer): if serial_number_range_end: subsystem.config['dbs.endSerialNumber'] = serial_number_range_end + if cert_id_generator == 'legacy2': + if not serial_number_range_start.startswith('0x'): + raise Exception('pki_serial_number_range_start format not valid, expecting 0x...') + if not serial_number_range_end.startswith('0x'): + raise Exception('pki_serial_number_range_end format not valid, expecting 0x...') + subsystem.config['dbs.serialIncrement'] = '0x10000000' + subsystem.config['dbs.serialLowWaterMark'] = '0x2000000' + subsystem.config['dbs.serialCloneTransferNumber'] = '0x10000' + subsystem.config['dbs.requestRangeDN'] = 'ou=requests,ou=ranges_v2' + subsystem.config['dbs.serialRangeDN'] = 'ou=certificateRepository,ou=ranges_v2' + + request_id_generator = deployer.mdict.get('pki_request_id_generator') + if request_id_generator: + subsystem.config['dbs.request.id.generator'] = request_id_generator + request_number_range_start = deployer.mdict.get('pki_request_number_range_start') if request_number_range_start: subsystem.config['dbs.beginRequestNumber'] = request_number_range_start @@ -253,6 +272,7 @@ def spawn(self, deployer): if replica_number_range_end: subsystem.config['dbs.endReplicaNumber'] = replica_number_range_end + if subsystem.name == 'kra': if config.str2bool(deployer.mdict['pki_kra_ephemeral_requests']): logger.debug('Setting ephemeral requests to true') diff --git a/base/server/python/pki/server/subsystem.py b/base/server/python/pki/server/subsystem.py index c5f773ef9d9..7a8317e9d0b 100644 --- a/base/server/python/pki/server/subsystem.py +++ b/base/server/python/pki/server/subsystem.py @@ -1151,6 +1151,28 @@ def update_ranges(self, as_current_user=False): self.run(cmd, as_current_user=as_current_user) + def update_id_generator( + self, generator, generator_object, + range_object=None, as_current_user=False): + + cmd = [self.name + '-id-generator-update'] + + if logger.isEnabledFor(logging.DEBUG): + cmd.append('--debug') + + elif logger.isEnabledFor(logging.INFO): + cmd.append('--verbose') + + if range_object: + cmd.append('--range') + cmd.append(range_object) + + cmd.append('--type') + cmd.append(generator) + cmd.append(generator_object) + + self.run(cmd, as_current_user=as_current_user) + def retrieve_config(self, master_url, names, substores, session_id=None, install_token=None): tmpdir = tempfile.mkdtemp() diff --git a/base/server/src/main/java/com/netscape/cms/servlet/csadmin/LDAPConfigurator.java b/base/server/src/main/java/com/netscape/cms/servlet/csadmin/LDAPConfigurator.java index 1e0364cfea1..8d18214d5e0 100644 --- a/base/server/src/main/java/com/netscape/cms/servlet/csadmin/LDAPConfigurator.java +++ b/base/server/src/main/java/com/netscape/cms/servlet/csadmin/LDAPConfigurator.java @@ -222,6 +222,14 @@ public LDAPEntry getEntry(String dn) throws Exception { } } + public void createEntry(String dn, String[] objectClasses) throws Exception { + logger.info("Adding " + dn); + LDAPAttributeSet attrs = new LDAPAttributeSet(); + attrs.add(new LDAPAttribute("objectClass", objectClasses)); + LDAPEntry entry = new LDAPEntry(dn, attrs); + connection.add(entry); + } + public void validateDatabaseOwnership(String database, String baseDN) throws Exception { logger.info("Validating database " + database + " is owned by " + baseDN); diff --git a/base/server/src/main/java/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java b/base/server/src/main/java/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java index 41a4b873be5..311e1a8e65c 100644 --- a/base/server/src/main/java/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java +++ b/base/server/src/main/java/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java @@ -160,12 +160,19 @@ protected void process(CMSRequest cmsReq) throws EBaseException { * cases this is done by a scheduled task). */ - String endNumStr = dbConfig.getString(endNumConfig); - BigInteger endNum = new BigInteger(endNumStr, radix); + BigInteger endNum; + BigInteger transferSize; + if (repo.getIDGenerator() == IRepository.IDGenerator.LEGACY_2) { + endNum = dbConfig.getBigInteger(endNumConfig); + transferSize = dbConfig.getBigInteger(cloneNumConfig); + } else { + String endNumStr = dbConfig.getString(endNumConfig); + endNum = new BigInteger(endNumStr, radix); + + String transferSizeStr = dbConfig.getString(cloneNumConfig, ""); + transferSize = new BigInteger(transferSizeStr, radix); + } logger.info("UpdateNumberRange: dbs." + endNumConfig + ": " + endNum); - - String transferSizeStr = dbConfig.getString(cloneNumConfig, ""); - BigInteger transferSize = new BigInteger(transferSizeStr, radix); logger.info("UpdateNumberRange: dbs." + cloneNumConfig + ": " + transferSize); // transferred range will start at beginNum @@ -223,7 +230,11 @@ protected void process(CMSRequest cmsReq) throws EBaseException { * this scenario is unlikely to arise. Furthermore, * recovery is automatic thanks to the scheduled tasks. */ - endNum = new BigInteger(dbConfig.getString(nextEndConfig, ""), radix); + if (repo.getIDGenerator() == IRepository.IDGenerator.LEGACY_2) { + endNum = dbConfig.getBigInteger(nextEndConfig); + } else { + endNum = new BigInteger(dbConfig.getString(nextEndConfig, ""), radix); + } BigInteger newEndNum = endNum.subtract(transferSize); logger.info("UpdateNumberRange: Transferring from the end of next range"); @@ -231,7 +242,11 @@ protected void process(CMSRequest cmsReq) throws EBaseException { logger.info("UpdateNumberRange: Next range new end: " + newEndNum); repo.setNextMaxSerial(newEndNum); - dbConfig.putString(nextEndConfig, newEndNum.toString(radix)); + String strNewEndNum = newEndNum.toString(radix); + if (repo.getIDGenerator() == IRepository.IDGenerator.LEGACY_2 && radix == 16) { + strNewEndNum = "0x" + strNewEndNum; + } + dbConfig.putString(nextEndConfig, strNewEndNum); beginNum = newEndNum.add(BigInteger.ONE); } else { @@ -241,8 +256,10 @@ protected void process(CMSRequest cmsReq) throws EBaseException { BigInteger newEndNum = beginNum.subtract(BigInteger.ONE); repo.setMaxSerial(newEndNum); String newValStr = newEndNum.toString(radix); + if (repo.getIDGenerator() == IRepository.IDGenerator.LEGACY_2 && radix == 16) { + newValStr = "0x" + newValStr; + } dbConfig.putString(endNumConfig, newValStr); - logger.info("UpdateNumberRange: New current range: " + nextSerial + ".." + newEndNum); } @@ -273,8 +290,13 @@ protected void process(CMSRequest cmsReq) throws EBaseException { Node root = xmlObj.createRoot("XMLResponse"); xmlObj.addItemToContainer(root, "Status", SUCCESS); - xmlObj.addItemToContainer(root, "beginNumber", beginNum.toString(radix)); - xmlObj.addItemToContainer(root, "endNumber", endNum.toString(radix)); + if(repo.getIDGenerator() == IRepository.IDGenerator.LEGACY_2 && radix == 16) { + xmlObj.addItemToContainer(root, "beginNumber", "0x" + beginNum.toString(radix)); + xmlObj.addItemToContainer(root, "endNumber", "0x" + endNum.toString(radix)); + } else { + xmlObj.addItemToContainer(root, "beginNumber", beginNum.toString(radix)); + xmlObj.addItemToContainer(root, "endNumber", endNum.toString(radix)); + } byte[] cb = xmlObj.toByteArray(); outputResult(httpResp, "application/xml", cb); diff --git a/base/server/src/main/java/com/netscape/cmscore/apps/CMS.java b/base/server/src/main/java/com/netscape/cmscore/apps/CMS.java index a5b8a1be6f3..379f575e002 100644 --- a/base/server/src/main/java/com/netscape/cmscore/apps/CMS.java +++ b/base/server/src/main/java/com/netscape/cmscore/apps/CMS.java @@ -80,6 +80,10 @@ public static String getProductVersion() { return System.getenv("PKI_VERSION"); // defined in tomcat.conf } + public static String getInstanceDir() { + return System.getProperty("catalina.base"); // defined by Tomcat + } + /** * Retrieves the localized user message from UserMessages.properties. * diff --git a/base/server/src/main/java/com/netscape/cmscore/apps/DatabaseConfig.java b/base/server/src/main/java/com/netscape/cmscore/apps/DatabaseConfig.java index 8befbecb73b..db0222b27d9 100644 --- a/base/server/src/main/java/com/netscape/cmscore/apps/DatabaseConfig.java +++ b/base/server/src/main/java/com/netscape/cmscore/apps/DatabaseConfig.java @@ -69,6 +69,13 @@ public void setSerialRangeDN(String serialRangeDN) { putString(DBSubsystem.PROP_SERIAL_RANGE_DN, serialRangeDN); } + public String getSerialCloneTransferNumber() throws EBaseException { + return getString(DBSubsystem.PROP_SERIAL_CLONE_TRANSFER_NUMBER, ""); + } + + public void setSerialCloneTransferNumber(String serialCloneTransferNumber) { + putString(DBSubsystem.PROP_SERIAL_CLONE_TRANSFER_NUMBER, serialCloneTransferNumber); + } public String getBeginSerialNumber() throws EBaseException { return getString(DBSubsystem.PROP_MIN_SERIAL_NUMBER, "0"); } diff --git a/base/server/src/main/java/com/netscape/cmscore/apps/ServerConfig.java b/base/server/src/main/java/com/netscape/cmscore/apps/ServerConfig.java new file mode 100644 index 00000000000..3e351de49b8 --- /dev/null +++ b/base/server/src/main/java/com/netscape/cmscore/apps/ServerConfig.java @@ -0,0 +1,98 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// 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. +// +// (C) 2019 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmscore.apps; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class ServerConfig { + + public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ServerConfig.class); + + String unsecurePort; + String securePort; + + public static ServerConfig load(String filename) throws Exception { + + logger.debug("ServerConfig: Parsing " + filename); + + ServerConfig serverConfig = new ServerConfig(); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(filename); + + XPathFactory xpathFactory = XPathFactory.newInstance(); + XPath xpath = xpathFactory.newXPath(); + + NodeList connectors = (NodeList) xpath.evaluate( + "/Server/Service[@name='Catalina']/Connector", + document, + XPathConstants.NODESET); + + int length = connectors.getLength(); + for (int i = 0; i < length; i++) { + Element connector = (Element) connectors.item(i); + + String protocol = connector.getAttribute("protocol"); + if (protocol.startsWith("AJP/")) { + continue; + } + + // HTTP/1.1 connector + + String scheme = connector.getAttribute("scheme"); + String port = connector.getAttribute("port"); + + if (scheme != null && scheme.equals("https")) { + logger.debug("ServerConfig: - secure port: " + port); + serverConfig.setSecurePort(port); + + } else { + logger.debug("ServerConfig: - unsecure port: " + port); + serverConfig.setUnsecurePort(port); + } + } + + return serverConfig; + } + + public String getUnsecurePort() { + return unsecurePort; + } + + public void setUnsecurePort(String unsecurePort) { + this.unsecurePort = unsecurePort; + } + + public String getSecurePort() { + return securePort; + } + + public void setSecurePort(String securePort) { + this.securePort = securePort; + } +} \ No newline at end of file diff --git a/base/server/src/main/java/com/netscape/cmscore/dbs/CertificateRepository.java b/base/server/src/main/java/com/netscape/cmscore/dbs/CertificateRepository.java index 70981387c7c..75f57efa424 100644 --- a/base/server/src/main/java/com/netscape/cmscore/dbs/CertificateRepository.java +++ b/base/server/src/main/java/com/netscape/cmscore/dbs/CertificateRepository.java @@ -81,6 +81,9 @@ public class CertificateRepository extends Repository { private static final String PROP_MINIMUM_RANDOM_BITS = "minimumRandomBits"; private static final BigInteger BI_MINUS_ONE = (BigInteger.ZERO).subtract(BigInteger.ONE); + public static final String PROP_CERT_ID_GENERATOR = "cert.id.generator"; + public static final String DEFAULT_CERT_ID_GENERATOR = "legacy"; + private boolean mConsistencyCheck = false; private int mBitLength = 0; @@ -102,25 +105,81 @@ public CertificateRepository(DBSubsystem dbSubsystem) throws EBaseException { mDBConfig = dbSubsystem.getDBConfigStore(); + String value = mDBConfig.getString(PROP_CERT_ID_GENERATOR, null); + logger.debug("CertificateRepository: - cert ID generator: " + value); + if (value != null) { + setIDGenerator(value); + } + + if (idGenerator == IDGenerator.LEGACY_2) { + initLegacy2Generator(); + } else { + initLegacyGenerator(); + } + } + + protected void initLegacy2Generator() throws EBaseException { + mBaseDN = mDBConfig.getSerialDN() + "," + dbSubsystem.getBaseDN(); logger.info("CertificateRepository: - base DN: " + mBaseDN); rangeDN = mDBConfig.getSerialRangeDN() + "," + dbSubsystem.getBaseDN(); - logger.info("CertificateRepository: - range DN: " + rangeDN); + logger.debug("CertificateRepository: - range DN: " + rangeDN); + + minSerialName = DBSubsystem.PROP_MIN_SERIAL_NUMBER; + mMinSerialNo = mDBConfig.getBigInteger(DBSubsystem.PROP_MIN_SERIAL_NUMBER, null); + logger.debug("CertificateRepository: - min serial: " + mMinSerialNo); + + maxSerialName = DBSubsystem.PROP_MAX_SERIAL_NUMBER; + mMaxSerialNo = mDBConfig.getBigInteger(DBSubsystem.PROP_MAX_SERIAL_NUMBER, null); + logger.debug("CertificateRepository: - max serial: " + mMaxSerialNo); + + nextMinSerialName = DBSubsystem.PROP_NEXT_MIN_SERIAL_NUMBER; + String nextMinSerial = mDBConfig.getNextBeginSerialNumber(); + if (nextMinSerial == null || nextMinSerial.equals("-1")) { + mNextMinSerialNo = null; + } else { + mNextMinSerialNo = mDBConfig.getBigInteger(DBSubsystem.PROP_NEXT_MIN_SERIAL_NUMBER, null); + } + logger.debug("CertificateRepository: - next min serial: " + mNextMinSerialNo); + + nextMaxSerialName = DBSubsystem.PROP_NEXT_MAX_SERIAL_NUMBER; + String nextMaxSerial = mDBConfig.getNextEndSerialNumber(); + if (nextMaxSerial == null || nextMaxSerial.equals("-1")) { + mNextMaxSerialNo = null; + } else { + mNextMaxSerialNo = mDBConfig.getBigInteger(DBSubsystem.PROP_NEXT_MAX_SERIAL_NUMBER, null); + } + logger.debug("CertificateRepository: - next max serial: " + mNextMaxSerialNo); + + mLowWaterMarkNo = mDBConfig.getBigInteger(DBSubsystem.PROP_SERIAL_LOW_WATER_MARK, null); + logger.debug("CertificateRepository: - low water mark serial: " + mNextMaxSerialNo); + + mIncrementNo = mDBConfig.getBigInteger(DBSubsystem.PROP_SERIAL_INCREMENT, null); + logger.debug("CertificateRepository: - increment serial: " + mIncrementNo); + } + + public void initLegacyGenerator() throws EBaseException { + + mBaseDN = mDBConfig.getSerialDN() + "," + dbSubsystem.getBaseDN(); + logger.info("CertificateRepository: - base DN: " + mBaseDN); + + rangeDN = mDBConfig.getSerialRangeDN() + "," + dbSubsystem.getBaseDN(); + logger.debug("CertificateRepository: - range DN: " + rangeDN); minSerialName = DBSubsystem.PROP_MIN_SERIAL_NUMBER; String minSerial = mDBConfig.getBeginSerialNumber(); if (minSerial != null) { mMinSerialNo = new BigInteger(minSerial, mRadix); } - logger.info("CertificateRepository: - min serial: " + mMinSerialNo); + logger.debug("CertificateRepository: - min serial: " + mMinSerialNo); maxSerialName = DBSubsystem.PROP_MAX_SERIAL_NUMBER; String maxSerial = mDBConfig.getEndSerialNumber(); if (maxSerial != null) { mMaxSerialNo = new BigInteger(maxSerial, mRadix); } - logger.info("CertificateRepository: - max serial: " + mMaxSerialNo); + logger.debug("CertificateRepository: - max serial: " + mMaxSerialNo); nextMinSerialName = DBSubsystem.PROP_NEXT_MIN_SERIAL_NUMBER; String nextMinSerial = mDBConfig.getNextBeginSerialNumber(); @@ -129,7 +188,7 @@ public CertificateRepository(DBSubsystem dbSubsystem) throws EBaseException { } else { mNextMinSerialNo = new BigInteger(nextMinSerial, mRadix); } - logger.info("CertificateRepository: - next min serial: " + mNextMinSerialNo); + logger.debug("CertificateRepository: - next min serial: " + mNextMinSerialNo); nextMaxSerialName = DBSubsystem.PROP_NEXT_MAX_SERIAL_NUMBER; String nextMaxSerial = mDBConfig.getNextEndSerialNumber(); @@ -138,7 +197,7 @@ public CertificateRepository(DBSubsystem dbSubsystem) throws EBaseException { } else { mNextMaxSerialNo = new BigInteger(nextMaxSerial, mRadix); } - logger.info("CertificateRepository: - next max serial: " + mNextMaxSerialNo); + logger.debug("CertificateRepository: - next max serial: " + mNextMaxSerialNo); String lowWaterMark = mDBConfig.getSerialLowWaterMark(); if (lowWaterMark != null) { @@ -294,6 +353,18 @@ private BigInteger checkSerialNumbers(BigInteger randomNumber, BigInteger serial return nextSerialNumber; } + @Override + public String getNextRangeDN() { + + if (idGenerator == IDGenerator.LEGACY_2) { + // store nextRange in range subtree for SSNv2 + return rangeDN; + } + + // store nextRange in repository subtree for SSNv1 + return super.getNextRangeDN(); + } + /** * Retrieves the next certificate serial number, and also increases * the serial number by one. diff --git a/base/server/src/main/java/com/netscape/cmscore/dbs/DBSubsystem.java b/base/server/src/main/java/com/netscape/cmscore/dbs/DBSubsystem.java index 81aa193122d..b43b78f853f 100644 --- a/base/server/src/main/java/com/netscape/cmscore/dbs/DBSubsystem.java +++ b/base/server/src/main/java/com/netscape/cmscore/dbs/DBSubsystem.java @@ -89,6 +89,7 @@ public class DBSubsystem { public static final String PROP_SERIAL_INCREMENT = "serialIncrement"; public static final String PROP_SERIAL_BASEDN = "serialDN"; public static final String PROP_SERIAL_RANGE_DN = "serialRangeDN"; + public static final String PROP_SERIAL_CLONE_TRANSFER_NUMBER = "serialCloneTransferNumber"; public static final String PROP_MIN_REQUEST_NUMBER = "beginRequestNumber"; public static final String PROP_MAX_REQUEST_NUMBER = "endRequestNumber"; @@ -98,6 +99,7 @@ public class DBSubsystem { public static final String PROP_REQUEST_INCREMENT = "requestIncrement"; public static final String PROP_REQUEST_BASEDN = "requestDN"; public static final String PROP_REQUEST_RANGE_DN = "requestRangeDN"; + public static final String PROP_REQUEST_CLONE_TRANSFER_NUMBER = "requestCloneTransferNumber"; public static final String PROP_MIN_REPLICA_NUMBER = "beginReplicaNumber"; public static final String PROP_MAX_REPLICA_NUMBER = "endReplicaNumber"; diff --git a/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java b/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java index 58493ab7459..a180f7c574d 100644 --- a/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java +++ b/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java @@ -83,6 +83,7 @@ public abstract class Repository implements IRepository { protected Hashtable repositoryConfig = new Hashtable<>(); private BigInteger mLastSerialNo = null; + protected IDGenerator idGenerator = IDGenerator.LEGACY; /** * Constructs a repository. @@ -164,6 +165,18 @@ protected void setLastSerialNo(BigInteger lastSN) { mLastSerialNo = lastSN; } + public IDGenerator getIDGenerator() { + return idGenerator; + } + + public void setIDGenerator(IDGenerator idGenerator) { + this.idGenerator = idGenerator; + } + + public void setIDGenerator(String idGenerator) { + this.idGenerator = IDGenerator.fromString(idGenerator); + } + /** * init serial number cache */ @@ -362,6 +375,9 @@ public void setMinSerialConfig() throws EBaseException { DatabaseConfig dbConfig = dbSubsystem.getDBConfigStore(); String serial = mMinSerialNo.toString(mRadix); + if (mRadix == 16 && idGenerator == IDGenerator.LEGACY_2) { + serial = "0x" + serial; + } logger.debug("Repository: Setting min serial number: " + serial); dbConfig.putString(minSerialName, serial); @@ -380,6 +396,9 @@ public void setMaxSerialConfig() throws EBaseException { DatabaseConfig dbConfig = dbSubsystem.getDBConfigStore(); String serial = mMaxSerialNo.toString(mRadix); + if (mRadix == 16 && idGenerator == IDGenerator.LEGACY_2) { + serial = "0x" + serial; + } logger.debug("Repository: Setting max serial number: " + serial); dbConfig.putString(maxSerialName, serial); @@ -402,6 +421,9 @@ public void setNextMinSerialConfig() throws EBaseException { dbConfig.remove(nextMinSerialName); } else { String serial = mNextMinSerialNo.toString(mRadix); + if (mRadix == 16 && idGenerator == IDGenerator.LEGACY_2) { + serial = "0x" + serial; + } logger.debug("Repository: Setting next min number: " + serial); dbConfig.putString(nextMinSerialName, serial); } @@ -425,6 +447,9 @@ public void setNextMaxSerialConfig() throws EBaseException { dbConfig.remove(nextMaxSerialName); } else { String serial = mNextMaxSerialNo.toString(mRadix); + if (mRadix == 16 && idGenerator == IDGenerator.LEGACY_2) { + serial = "0x" + serial; + } logger.debug("Repository: Setting next max number: " + serial); dbConfig.putString(nextMaxSerialName, serial); } @@ -451,6 +476,11 @@ private void switchToNextRange() throws EBaseException { setNextMaxSerialConfig(); } + public String getNextRangeDN() { + // store nextRange in repository subtree for SSNv1 + return mBaseDN; + } + /** * Gets start of next range from database. * Increments the nextRange attribute and allocates @@ -467,9 +497,9 @@ public String getNextRange() throws EBaseException { try { LDAPConnection conn = session.getConnection(); - - logger.info("Repository: Reading entry " + mBaseDN); - LDAPEntry entry = conn.read(mBaseDN); + String nextRangeDN = getNextRangeDN(); + logger.info("Repository: Reading entry " + nextRangeDN); + LDAPEntry entry = conn.read(nextRangeDN); LDAPAttribute attr = entry.getAttribute(DBSubsystem.PROP_NEXT_RANGE); if (attr == null) { @@ -493,8 +523,8 @@ public String getNextRange() throws EBaseException { new LDAPModification(LDAPModification.ADD, attrNextRange) }; - logger.info("Repository: Modifying entry " + mBaseDN); - conn.modify(mBaseDN, mods); + logger.info("Repository: Modifying entry " + nextRangeDN); + conn.modify(nextRangeDN, mods); // Add new range object @@ -549,9 +579,12 @@ public boolean hasRangeConflict() throws EBaseException { logger.info("Repository: Searching for conflicting entries"); + String minSerial = idGenerator == IDGenerator.LEGACY_2 ? + mMinSerialNo.toString() : mMinSerialNo.toString(mRadix); + String filter = "(&(nsds5ReplConflict=*)(objectClass=pkiRange)(host= " + cs.getHostname() + ")(SecurePort=" + engine.getEESSLPort() + - ")(beginRange=" + mMinSerialNo.toString(mRadix) + "))"; + ")(beginRange=" + minSerial + "))"; LDAPSearchResults results = conn.search(rangeDN, LDAPv3.SCOPE_SUB, filter, null, false); @@ -627,7 +660,11 @@ public void checkRanges() throws EBaseException { String nextRange = getNextRange(); logger.debug("Repository: next range: " + nextRange); - mNextMinSerialNo = new BigInteger(nextRange, mRadix); + if (idGenerator == IDGenerator.LEGACY_2) { + mNextMinSerialNo = new BigInteger(nextRange); + } else { + mNextMinSerialNo = new BigInteger(nextRange, mRadix); + } if (mNextMinSerialNo == null) { logger.debug("Repository: Next range not available"); } else { diff --git a/base/server/src/main/java/com/netscape/cmscore/request/RequestRepository.java b/base/server/src/main/java/com/netscape/cmscore/request/RequestRepository.java index dab97a4a883..3232d81ee59 100644 --- a/base/server/src/main/java/com/netscape/cmscore/request/RequestRepository.java +++ b/base/server/src/main/java/com/netscape/cmscore/request/RequestRepository.java @@ -53,7 +53,8 @@ public class RequestRepository extends Repository { public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(RequestRepository.class); - + public static final String PROP_REQUEST_ID_GENERATOR = "request.id.generator"; + public static final String DEFAULT_REQUEST_ID_GENERATOR = "legacy"; protected String filter; /** @@ -74,6 +75,12 @@ public RequestRepository(DBSubsystem dbSubsystem, String filter) throws EBaseExc DatabaseConfig dbConfig = dbSubsystem.getDBConfigStore(); + String value = dbConfig.getString(PROP_REQUEST_ID_GENERATOR, null); + logger.debug("CertificateRepository: - cert ID generator: " + value); + if (value != null) { + setIDGenerator(value); + } + mBaseDN = dbConfig.getRequestDN() + "," + dbSubsystem.getBaseDN(); logger.info("RequestRepository: - base DN: " + mBaseDN); @@ -127,6 +134,17 @@ public RequestRepository(DBSubsystem dbSubsystem, String filter) throws EBaseExc RequestRecord.register(dbSubsystem); } + public String getNextRangeDN() { + + if (idGenerator == IDGenerator.LEGACY_2) { + // store nextRange in range subtree for SSNv2 + return rangeDN; + } + + // store nextRange in repository subtree for SSNv1 + return super.getNextRangeDN(); + } + public RequestRepository( DBSubsystem dbSubsystem, String filter, diff --git a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemDBInitCLI.java b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemDBInitCLI.java index e5eadd6b01f..8d1421fb7ce 100644 --- a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemDBInitCLI.java +++ b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemDBInitCLI.java @@ -9,6 +9,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; +import org.apache.commons.lang3.StringUtils; import org.apache.tomcat.util.net.jss.TomcatJSS; import org.dogtagpki.cli.CLI; import org.dogtagpki.cli.CommandCLI; @@ -16,6 +17,7 @@ import org.slf4j.LoggerFactory; import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.dbs.repository.IRepository.IDGenerator; import com.netscape.cms.servlet.csadmin.LDAPConfigurator; import com.netscape.cmscore.apps.CMS; import com.netscape.cmscore.apps.DatabaseConfig; @@ -33,6 +35,7 @@ import com.netscape.cmscore.ldapconn.LdapConnInfo; import com.netscape.cmscore.ldapconn.PKISocketConfig; import com.netscape.cmscore.ldapconn.PKISocketFactory; +import com.netscape.cmscore.request.RequestRepository; import com.netscape.cmsutil.ldap.LDAPUtil; import com.netscape.cmsutil.password.IPasswordStore; import com.netscape.cmsutil.password.PasswordStoreConfig; @@ -45,11 +48,17 @@ public class SubsystemDBInitCLI extends CommandCLI { public static Logger logger = LoggerFactory.getLogger(SubsystemDBInitCLI.class); + protected IDGenerator requestIDGenerator; + protected IDGenerator serialIDGenerator; public SubsystemDBInitCLI(CLI parent) { super("init", "Initialize " + parent.getParent().getName().toUpperCase() + " database", parent); } + public SubsystemDBInitCLI(String name, String description, CLI parent) { + super(name, description, parent); + } + @Override public void createOptions() { @@ -76,6 +85,71 @@ public void createOptions() { options.addOption(null, "help", false, "Show help message."); } + public void init(DatabaseConfig dbConfig) throws Exception { + String value = dbConfig.getString( + RequestRepository.PROP_REQUEST_ID_GENERATOR, + RequestRepository.DEFAULT_REQUEST_ID_GENERATOR); + requestIDGenerator = IDGenerator.fromString(value); + } + public void createRangesSubtree( + LDAPConfig ldapConfig, + LDAPConfigurator ldapConfigurator) throws Exception { + if (requestIDGenerator == IDGenerator.LEGACY_2 || + serialIDGenerator == IDGenerator.LEGACY_2) { + // create ou=ranges_v2 for SSNv2 + ldapConfigurator.createEntry( + "ou=ranges_v2," + ldapConfig.getBaseDN(), + new String[] { "organizationalUnit" }); + return; + } + // ou=ranges for SSNv1 is defined in create.ldif so it will + // be created automatically + } + + public void createRequestRangesSubtree( + LDAPConfig ldapConfig, + DatabaseConfig dbConfig, + LDAPConfigurator ldapConfigurator) throws Exception { + String requestRangeRDN = dbConfig.getRequestRangeDN(); + if (StringUtils.isEmpty(requestRangeRDN)) { + // dbs.requestRangeDN only exists in CA and KRA + return; + } + // create ou=requests,ou=ranges for SSNv1 or + // ou=requests,ou=ranges_v2 for SSNv2 + if (requestIDGenerator == IDGenerator.LEGACY_2) { + ldapConfigurator.createEntry( + requestRangeRDN + "," + ldapConfig.getBaseDN(), + new String[] { "repository" }); + return; + } + ldapConfigurator.createEntry( + requestRangeRDN + "," + ldapConfig.getBaseDN(), + new String[] { "organizationalUnit" }); + } + + public void createSerialRangesSubtree( + LDAPConfig ldapConfig, + DatabaseConfig dbConfig, + LDAPConfigurator ldapConfigurator) throws Exception { + String serialRangeRDN = dbConfig.getSerialRangeDN(); + if (StringUtils.isEmpty(serialRangeRDN)) { + // dbs.serialRangeDN only exists in CA and KRA + return; + } + // create ou=certificateRepository,ou=ranges for SSNv1 or + // ou=certificateRepository,ou=ranges_v2 for SSNv2 + if (serialIDGenerator == IDGenerator.LEGACY_2) { + ldapConfigurator.createEntry( + serialRangeRDN + "," + ldapConfig.getBaseDN(), + new String[] { "repository" }); + return; + } + ldapConfigurator.createEntry( + serialRangeRDN + "," + ldapConfig.getBaseDN(), + new String[] { "organizationalUnit" }); + } + @Override public void execute(CommandLine cmd) throws Exception { @@ -130,6 +204,8 @@ public void execute(CommandLine cmd) throws Exception { LdapBoundConnection conn = new LdapBoundConnection(socketFactory, connInfo, authInfo); LDAPConfigurator ldapConfigurator = new LDAPConfigurator(conn, ldapConfig, instanceId); + DatabaseConfig dbConfig = cs.getDatabaseConfig(); + init(dbConfig); try { ldapConfigurator.initDatabase(); @@ -152,6 +228,9 @@ public void execute(CommandLine cmd) throws Exception { if (cmd.hasOption("create-containers")) { ldapConfigurator.createContainers(subsystem); + createRangesSubtree(ldapConfig, ldapConfigurator); + createRequestRangesSubtree(ldapConfig, dbConfig, ldapConfigurator); + createSerialRangesSubtree(ldapConfig, dbConfig, ldapConfigurator); ldapConfigurator.setupACL(subsystem); } diff --git a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemIdGeneratorUpdateCLI.java b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemIdGeneratorUpdateCLI.java new file mode 100644 index 00000000000..67c230b157a --- /dev/null +++ b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemIdGeneratorUpdateCLI.java @@ -0,0 +1,546 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.cli; + +import java.io.File; +import java.math.BigInteger; + +import org.apache.commons.cli.CommandLine; +import org.apache.tomcat.util.net.jss.TomcatJSS; +import org.dogtagpki.cli.CLI; +import org.dogtagpki.cli.CommandCLI; +import org.dogtagpki.util.logging.PKILogger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.dbs.repository.IRepository.IDGenerator; +import com.netscape.cmscore.apps.CMS; +import com.netscape.cmscore.apps.DatabaseConfig; +import com.netscape.cmscore.apps.EngineConfig; +import com.netscape.cmscore.apps.ServerConfig; +import com.netscape.cmscore.base.ConfigStorage; +import com.netscape.cmscore.base.FileConfigStore; +import com.netscape.cmscore.dbs.DBSubsystem; +import com.netscape.cmscore.ldapconn.LDAPAuthenticationConfig; +import com.netscape.cmscore.ldapconn.LDAPConfig; +import com.netscape.cmscore.ldapconn.LDAPConnectionConfig; +import com.netscape.cmscore.ldapconn.LdapAuthInfo; +import com.netscape.cmscore.ldapconn.LdapBoundConnection; +import com.netscape.cmscore.ldapconn.LdapConnInfo; +import com.netscape.cmscore.ldapconn.PKISocketConfig; +import com.netscape.cmscore.ldapconn.PKISocketFactory; +import com.netscape.cmscore.request.RequestRepository; +import com.netscape.cmsutil.password.IPasswordStore; +import com.netscape.cmsutil.password.PasswordStoreConfig; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv3; + +/** + * @author Marco Fargetta {@literal } + */ +public abstract class SubsystemIdGeneratorUpdateCLI extends CommandCLI { + private static final Logger logger = LoggerFactory.getLogger(SubsystemIdGeneratorUpdateCLI.class); + protected IDGenerator idGenerator; + private static final String SERVER_XML = "server.xml"; + + public SubsystemIdGeneratorUpdateCLI(CLI parent) { + super("update", "Update " + parent.getParent().getParent().getName().toUpperCase() + " range generator", parent); + } + @Override + public void createOptions() { + options.addOption("t", "type", true, "Generator type to update."); + options.addOption("r", "range", true, "Name of the ranges entry in DS."); + } + + @Override + public void execute(CommandLine cmd) throws Exception { + if (!cmd.hasOption("type")) { + throw new Exception("Missing generator type."); + } + IDGenerator generator = IDGenerator.fromString(cmd.getOptionValue("type")); + + String newRangesName = generator == IDGenerator.LEGACY_2 ? "ranges_v2" : "ranges_new"; + if (cmd.hasOption("range")) { + newRangesName = cmd.getOptionValue("range"); + } + + if (cmd.hasOption("debug")) { + PKILogger.setLevel(PKILogger.Level.DEBUG); + + } else if (cmd.hasOption("verbose")) { + PKILogger.setLevel(PKILogger.Level.INFO); + } + + String[] cmdArgs = cmd.getArgs(); + + if (cmdArgs.length != 1) { + throw new Exception("Missing generator"); + } + String generatorAtttirbute = cmdArgs[0]; + + TomcatJSS tomcatjss = TomcatJSS.getInstance(); + tomcatjss.loadConfig(); + tomcatjss.init(); + + String subsystem = parent.getParent().getParent().getName(); + EngineConfig cs = getEngineConfig(subsystem); + cs.load(); + + LDAPConfig ldapConfig = cs.getInternalDBConfig(); + String baseDN = ldapConfig.getBaseDN(); + + PasswordStoreConfig psc = cs.getPasswordStoreConfig(); + IPasswordStore passwordStore = IPasswordStore.create(psc); + + LDAPConnectionConfig connConfig = ldapConfig.getConnectionConfig(); + + LdapConnInfo connInfo = new LdapConnInfo(connConfig); + LdapAuthInfo authInfo = getAuthInfo(passwordStore, connInfo, ldapConfig); + + PKISocketConfig socketConfig = cs.getSocketConfig(); + + PKISocketFactory socketFactory; + if (authInfo.getAuthType() == LdapAuthInfo.LDAP_AUTHTYPE_SSLCLIENTAUTH) { + socketFactory = new PKISocketFactory(authInfo.getClientCertNickname()); + } else { + socketFactory = new PKISocketFactory(connInfo.getSecure()); + } + socketFactory.init(socketConfig); + DatabaseConfig dbConfig = cs.getDatabaseConfig(); + LdapBoundConnection conn = new LdapBoundConnection(socketFactory, connInfo, authInfo); + try { + if (generatorAtttirbute.equals("cert")){ + updateSerialNumberRangeGenerator( + conn, + dbConfig, + baseDN, + newRangesName, + generator, + cs.getHostname(), + getSecurePort(cs)); + cs.commit(false); + } else if (generatorAtttirbute.equals("request")) { + updateRequestNumberRangeGenerator( + conn, + dbConfig, + baseDN, + newRangesName, + generator, + cs.getHostname(), + getSecurePort(cs)); + cs.commit(false); + } else { + throw new EBaseException("Generator for " + generatorAtttirbute + " not supported."); + } + } finally { + conn.disconnect(); + } + + } + + protected void updateSerialNumberRangeGenerator(LdapBoundConnection conn, + DatabaseConfig dbConfig, String baseDN, String newRangesName, + IDGenerator newGenerator, String hostName, String securePort) throws Exception { + + if (newGenerator == IDGenerator.LEGACY_2 && idGenerator == IDGenerator.LEGACY) { + logger.debug("SubsystemIdGeneratorUpdateCLI: Updating ranges entry to hex format"); + + String rangeDN = dbConfig.getSerialRangeDN() + "," + baseDN; + String newRangeDN = createRangesEntry(conn, "certificateRepository", newRangesName, baseDN); + dbConfig.setSerialRangeDN(newRangeDN); + newRangeDN = newRangeDN + "," + baseDN; + + String serialIncrement = dbConfig.getSerialIncrement(); + dbConfig.setSerialIncrement("0x" + serialIncrement); + BigInteger increment = new BigInteger(serialIncrement, 16); + + String serialLowWaterMark = dbConfig.getSerialLowWaterMark(); + dbConfig.setSerialLowWaterMark("0x" + serialLowWaterMark); + + String serialCloneTransfer = dbConfig.getSerialCloneTransferNumber(); + dbConfig.setSerialCloneTransferNumber("0x" + serialCloneTransfer); + + String beginSerialNumber = dbConfig.getBeginSerialNumber(); + dbConfig.setBeginSerialNumber("0x" + beginSerialNumber); + BigInteger beginSerialNo = new BigInteger(beginSerialNumber, 16); + String endSerialNumber = dbConfig.getEndSerialNumber(); + BigInteger endSerialNo = new BigInteger(endSerialNumber, 16); + if (endSerialNo.equals(beginSerialNo.add(increment).subtract(BigInteger.ONE))){ + try { + LDAPEntry entrySerial = conn.read("cn=" + beginSerialNumber+"," + rangeDN); + LDAPAttribute attrEnd = entrySerial.getAttribute("endRange"); + if (attrEnd != null) { + endSerialNumber = attrEnd.getStringValues().nextElement(); + } + } catch (LDAPException ldae) { + if (ldae.getLDAPResultCode() == 32) { + logger.debug("No range available, using config values"); + } else { + logger.error("LDAP error: " + ldae.getMessage(), ldae); + return; + } + + } + } + dbConfig.setEndSerialNumber("0x" + endSerialNumber); + + String nextBeginSerial = dbConfig.getNextBeginSerialNumber(); + String nextEndSerial = dbConfig.getNextEndSerialNumber(); + if (nextBeginSerial != null && !nextBeginSerial.equals("-1")) { + BigInteger nextBeginSerialNo = new BigInteger(nextBeginSerial, 16); + dbConfig.setNextBeginSerialNumber("0x" + nextBeginSerial); + BigInteger nextEndSerialNo = new BigInteger(nextEndSerial, 16); + if (nextEndSerialNo.equals(nextBeginSerialNo.add(increment).subtract(BigInteger.ONE))) { + try { + LDAPEntry entryNextSerial = conn.read("cn=" + nextBeginSerial + "," + rangeDN); + LDAPAttribute attrNextEnd = entryNextSerial.getAttribute("endRange"); + if (attrNextEnd != null) { + nextEndSerial = attrNextEnd.getStringValues().nextElement(); + } + } catch (LDAPException ldae) { + if (ldae.getLDAPResultCode() == 32) { + logger.debug("No range available, using config vaules"); + } else { + logger.error("LDAP error", ldae); + return; + } + } + } + dbConfig.setNextEndSerialNumber("0x" + nextEndSerial); + endSerialNumber = nextEndSerial; + } + + updateSerialRanges(conn, rangeDN, newRangeDN, endSerialNumber, hostName, securePort); + return; + } + throw new EBaseException("Update to " + newGenerator + " not supported"); + } + + protected void updateRequestNumberRangeGenerator(LdapBoundConnection conn, + DatabaseConfig dbConfig, String baseDN, String newRangesName, IDGenerator newGenerator, + String hostName, String securePort) throws Exception { + + String value = dbConfig.getString( + RequestRepository.PROP_REQUEST_ID_GENERATOR, + null); + if (value == null) { + idGenerator = IDGenerator.LEGACY; + } else { + idGenerator = IDGenerator.fromString(value); + } + if (newGenerator == IDGenerator.LEGACY_2 && idGenerator == IDGenerator.LEGACY) { + + logger.info("Updating request range configuration"); + + dbConfig.put(RequestRepository.PROP_REQUEST_ID_GENERATOR, newGenerator.toString()); + + String rangeDN = dbConfig.getRequestRangeDN() + "," + baseDN; + String newRangeDN = createRangesEntry(conn, "requests", newRangesName, baseDN); + dbConfig.setRequestRangeDN(newRangeDN); + newRangeDN = newRangeDN + "," + baseDN; + + String requestIncrement = dbConfig.getRequestIncrement(); + BigInteger increment = new BigInteger(requestIncrement); + + String beginRequestNumber = dbConfig.getBeginRequestNumber(); + BigInteger beginRequestNo = new BigInteger(beginRequestNumber); + + String endRequestNumber = dbConfig.getEndRequestNumber(); + BigInteger endRequestNo = new BigInteger(endRequestNumber); + + if (endRequestNo.equals(beginRequestNo.add(increment).subtract(BigInteger.ONE))) { + try { + LDAPEntry entryRequest = conn.read("cn=" + beginRequestNumber+"," + rangeDN); + LDAPAttribute attrEnd = entryRequest.getAttribute("endRange"); + if (attrEnd != null) { + endRequestNumber = attrEnd.getStringValues().nextElement(); + } + } catch (LDAPException ldae) { + if (ldae.getLDAPResultCode() == 32) { + logger.info("No range available, using config values"); + } else { + logger.error("LDAP error: " + ldae.getMessage(), ldae); + return; + } + + } + } + + dbConfig.setEndRequestNumber(endRequestNumber); + + String nextBeginRequest = dbConfig.getNextBeginRequestNumber(); + String nextEndRequest = dbConfig.getNextEndRequestNumber(); + + if (nextBeginRequest != null && !nextBeginRequest.equals("-1")) { + try { + LDAPEntry entryNextRequest = conn.read("cn=" + nextBeginRequest + "," + rangeDN); + LDAPAttribute attrNextEnd = entryNextRequest.getAttribute("endRange"); + if (attrNextEnd != null) { + nextEndRequest = attrNextEnd.getStringValues().nextElement(); + } + } catch (LDAPException ldae) { + if (ldae.getLDAPResultCode() == 32) { + logger.info("No range available, using config values"); + } else { + logger.error("LDAP error: " + ldae.getMessage(), ldae); + return; + } + + } + dbConfig.setNextEndRequestNumber(nextEndRequest); + endRequestNumber = nextEndRequest; + } + + updateRequestRanges(conn, rangeDN, newRangeDN, endRequestNumber, hostName, securePort); + return; + } + throw new EBaseException("Update to " + newGenerator + " not supported"); + } + + private void updateSerialRanges( + LdapBoundConnection conn, + String rangeDN, + String newRangeDN, + String configEndSerialNumber, + String hostName, + String securePort) throws Exception{ + + LDAPSearchResults instanceRanges = conn.search(rangeDN, LDAPv3.SCOPE_SUB, "(&(objectClass=pkiRange)(host= " + + hostName + ")(SecurePort=" + securePort + "))", null, false); + + // update all ranges associated to the CA to update to decimal + while (instanceRanges.hasMoreElements()) { + LDAPEntry entry = instanceRanges.next(); + String beginRange = entry.getAttribute("beginRange").getStringValues().nextElement(); + BigInteger beginRangeNo = new BigInteger(beginRange, 16); + String endRange = entry.getAttribute("endRange").getStringValues().nextElement(); + BigInteger endRangeNo = new BigInteger(endRange, 16); + + String dn = "cn=" + beginRangeNo.toString() + "," + newRangeDN; + logger.info("Creating serial range " + dn); + + LDAPAttributeSet attrs = new LDAPAttributeSet(); + attrs.add(new LDAPAttribute("objectClass", "top")); + attrs.add(new LDAPAttribute("objectClass", "pkiRange")); + + // store beginRange as decimal + logger.info("- begin range: " + beginRangeNo.toString() + " (0x" + beginRangeNo.toString(16) + ")"); + attrs.add(new LDAPAttribute("beginRange", beginRangeNo.toString())); + + // store endRange as decimal + logger.info("- end range: " + endRangeNo.toString() + " (0x" + endRangeNo.toString(16) + ")"); + attrs.add(new LDAPAttribute("endRange", endRangeNo.toString())); + + attrs.add(new LDAPAttribute("cn", beginRangeNo.toString())); + attrs.add(new LDAPAttribute("host", hostName)); + attrs.add(new LDAPAttribute("securePort", securePort)); + + LDAPEntry rangeEntry = new LDAPEntry(dn, attrs); + conn.add(rangeEntry); + } + + LDAPSearchResults ranges = conn.search(newRangeDN, LDAPv3.SCOPE_SUB, "(objectClass=pkiRange)", null, false); + + BigInteger lastUsedSerial = BigInteger.ZERO; + boolean nextRangeToUpdate = true; + // Search for the last range entry. If it is associated to the CA to update or ranges are not defined + // then the nextRange is + while (ranges.hasMoreElements()) { + LDAPEntry entry = ranges.next(); + String endRange = entry.getAttribute("endRange").getStringValues().nextElement(); + String host = entry.getAttribute("host").getStringValues().nextElement(); + String port = entry.getAttribute("securePort").getStringValues().nextElement(); + BigInteger next = new BigInteger(endRange); + if (lastUsedSerial.compareTo(next) < 0) { + lastUsedSerial = next; + nextRangeToUpdate = host.equals(hostName) && port.equals(securePort); + } + } + + if (nextRangeToUpdate) { + + logger.info("Updating serial next range in " + newRangeDN); + + // nextRange is updated using last range entry or, if no ranges, the configured endSerialNumber + if (lastUsedSerial == BigInteger.ZERO) { + lastUsedSerial = new BigInteger(configEndSerialNumber, 16); + } + BigInteger nextSerialNumber = lastUsedSerial.add(BigInteger.ONE); + + // store nextRange as decimal + logger.info("- next range: " + nextSerialNumber.toString() + " (0x" + nextSerialNumber.toString(16) + ")"); + LDAPAttribute attrSerialNextRange = new LDAPAttribute(DBSubsystem.PROP_NEXT_RANGE, nextSerialNumber.toString()); + + LDAPModification serialmod = new LDAPModification(LDAPModification.REPLACE, attrSerialNextRange); + + conn.modify(newRangeDN, serialmod); + } + } + + private void updateRequestRanges( + LdapBoundConnection conn, + String rangeDN, + String newRangeDN, + String configEndRequestNumber, + String hostName, + String securePort) throws Exception{ + + LDAPSearchResults instanceRanges = conn.search(rangeDN, LDAPv3.SCOPE_SUB, "(&(objectClass=pkiRange)(host= " + + hostName + ")(SecurePort=" + securePort + "))", null, false); + + // update all ranges associated to the CA to update to decimal + while (instanceRanges.hasMoreElements()) { + LDAPEntry entry = instanceRanges.next(); + String beginRange = entry.getAttribute("beginRange").getStringValues().nextElement(); + String endRange = entry.getAttribute("endRange").getStringValues().nextElement(); + + String dn = "cn=" + beginRange + "," + newRangeDN; + logger.info("Creating request range " + dn); + + LDAPAttributeSet attrs = new LDAPAttributeSet(); + attrs.add(new LDAPAttribute("objectClass", "top")); + attrs.add(new LDAPAttribute("objectClass", "pkiRange")); + + // store beginRange as decimal + logger.info("- begin range: " + beginRange); + attrs.add(new LDAPAttribute("beginRange", beginRange)); + + // store endRange as decimal + logger.info("- end range: " + endRange); + attrs.add(new LDAPAttribute("endRange", endRange)); + + attrs.add(new LDAPAttribute("cn", beginRange)); + attrs.add(new LDAPAttribute("host", hostName)); + attrs.add(new LDAPAttribute("securePort", securePort)); + + LDAPEntry rangeEntry = new LDAPEntry(dn, attrs); + conn.add(rangeEntry); + } + + LDAPSearchResults ranges = conn.search(newRangeDN, LDAPv3.SCOPE_SUB, "(objectClass=pkiRange)", null, false); + + BigInteger lastUsedNumber = BigInteger.ZERO; + boolean nextRangeToUpdate = true; + + // Search for the last range entry. If it is associated to the CA to update or ranges are not defined + // then the nextRange is + while (ranges.hasMoreElements()) { + LDAPEntry entry = ranges.next(); + + String endRange = entry.getAttribute("endRange").getStringValues().nextElement(); + String host = entry.getAttribute("host").getStringValues().nextElement(); + String port = entry.getAttribute("securePort").getStringValues().nextElement(); + BigInteger next = new BigInteger(endRange); + + if (lastUsedNumber.compareTo(next) < 0) { + lastUsedNumber = next; + nextRangeToUpdate = host.equals(hostName) && port.equals(securePort); + } + } + + if (nextRangeToUpdate) { + + logger.info("Updating request next range in " + newRangeDN); + + // nextRange is updated using last range entry or, if no ranges, the configured endRequestNumber + if (lastUsedNumber == BigInteger.ZERO) { + lastUsedNumber = new BigInteger(configEndRequestNumber); + } + BigInteger nextRequestNumber = lastUsedNumber.add(BigInteger.ONE); + + // store nextRange as decimal + logger.info("- next range: " + nextRequestNumber.toString()); + LDAPAttribute attrRequestNextRange = new LDAPAttribute(DBSubsystem.PROP_NEXT_RANGE, nextRequestNumber.toString()); + + LDAPModification mods = new LDAPModification(LDAPModification.REPLACE, attrRequestNextRange); + + conn.modify(newRangeDN, mods); + } + } + + private String createRangesEntry(LdapBoundConnection conn, String newRangeObject, String ranges, String baseDN) throws Exception { + + String baseRanges = "ou=" + ranges; + String baseRangesDN = baseRanges + "," + baseDN; + try { + logger.debug("SubsystemRangeGeneratorUpdateCLI: Create ranges entry: {}", baseRangesDN); + LDAPAttributeSet attrs = new LDAPAttributeSet(); + attrs.add(new LDAPAttribute("objectClass", "top")); + attrs.add(new LDAPAttribute("objectClass", "organizationalUnit")); + attrs.add(new LDAPAttribute("ou", ranges)); + LDAPEntry rangesEntry = new LDAPEntry(baseRangesDN, attrs); + conn.add(rangesEntry); + } catch (LDAPException ldae) { + if (ldae.getLDAPResultCode() != 68) { + throw new EBaseException("Impossible create ranges object: " + ldae.getMessage(), ldae); + } + logger.debug("SubsystemRangeGeneratorUpdateCLI: entry {} already exist", baseRangesDN); + } + + String newRangeEntry = "ou=" + newRangeObject + "," + baseRanges; + String newRangeEntryDN = newRangeEntry + "," + baseDN; + logger.debug("SubsystemRangeGeneratorUpdateCLI: Create range entry: {}", newRangeEntryDN); + try { + LDAPAttributeSet attrs = new LDAPAttributeSet(); + attrs.add(new LDAPAttribute("objectClass", "top")); + attrs.add(new LDAPAttribute("objectClass", "repository")); + attrs.add(new LDAPAttribute("ou", newRangeObject)); + LDAPEntry rangeEntry = new LDAPEntry(newRangeEntryDN, attrs); + conn.add(rangeEntry); + } catch (LDAPException ldae) { + if (ldae.getLDAPResultCode() != 68) { + throw new EBaseException("Impossible access object in ranges: " + ldae.getMessage(), ldae); + } + logger.debug("SubsystemRangeGeneratorUpdateCLI: entry {} already exist", baseRangesDN); + } + return newRangeEntry; + } + + private EngineConfig getEngineConfig(String subsystem) throws Exception { + + // use subsystem conf folder: /var/lib/pki//conf/ + String confDir = CMS.getInstanceDir() + File.separator + "conf" + File.separator + subsystem; + String configFile = confDir + File.separator + CMS.CONFIG_FILE; + logger.debug("{}: Loading {}", getClass().getSimpleName(), configFile); + + ConfigStorage storage = new FileConfigStore(configFile); + return new EngineConfig(storage); + } + + private LdapAuthInfo getAuthInfo(IPasswordStore passwordStore, LdapConnInfo connInfo, LDAPConfig ldapConfig) + throws EBaseException { + LDAPAuthenticationConfig authConfig = ldapConfig.getAuthenticationConfig(); + LdapAuthInfo authInfo = new LdapAuthInfo(); + authInfo.setPasswordStore(passwordStore); + authInfo.init( + authConfig, + connInfo.getHost(), + connInfo.getPort(), + connInfo.getSecure()); + return authInfo; + } + + private String getSecurePort(EngineConfig config) throws Exception { + + String path = CMS.getInstanceDir() + File.separator + "conf" + File.separator + SERVER_XML; + + ServerConfig serverConfig = ServerConfig.load(path); + String securePort = serverConfig.getSecurePort(); + + String port = config.getString("proxy.securePort", ""); + if (!port.equals("")) { + securePort = port; + } + return securePort; + } +} \ No newline at end of file diff --git a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java index e97d8ee99ef..e931a4693cb 100644 --- a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java +++ b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java @@ -16,11 +16,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.netscape.certsrv.dbs.repository.IRepository.IDGenerator; import com.netscape.cmscore.apps.CMS; import com.netscape.cmscore.apps.DatabaseConfig; import com.netscape.cmscore.apps.EngineConfig; import com.netscape.cmscore.base.ConfigStorage; import com.netscape.cmscore.base.FileConfigStore; +import com.netscape.cmscore.dbs.DBSubsystem; import com.netscape.cmscore.ldapconn.LDAPAuthenticationConfig; import com.netscape.cmscore.ldapconn.LDAPConfig; import com.netscape.cmscore.ldapconn.LDAPConnectionConfig; @@ -29,6 +31,7 @@ import com.netscape.cmscore.ldapconn.LdapConnInfo; import com.netscape.cmscore.ldapconn.PKISocketConfig; import com.netscape.cmscore.ldapconn.PKISocketFactory; +import com.netscape.cmscore.request.RequestRepository; import com.netscape.cmsutil.password.IPasswordStore; import com.netscape.cmsutil.password.PasswordStoreConfig; @@ -42,6 +45,9 @@ public class SubsystemRangeUpdateCLI extends CommandCLI { public static Logger logger = LoggerFactory.getLogger(SubsystemRangeUpdateCLI.class); + protected IDGenerator serialIDGenerator = IDGenerator.LEGACY; + protected IDGenerator requestIDGenerator = IDGenerator.LEGACY; + public SubsystemRangeUpdateCLI(CLI parent) { super("update", "Update " + parent.getParent().getName().toUpperCase() + " ranges", parent); } @@ -58,6 +64,50 @@ public void createOptions() { options.addOption(option); } + public void init(DatabaseConfig dbConfig) throws Exception { + + String value = dbConfig.getString( + RequestRepository.PROP_REQUEST_ID_GENERATOR, + null); + if (value != null) { + requestIDGenerator = IDGenerator.fromString(value); + } + + value = dbConfig.getString( + "cert.id.generator", + null); + if (value != null) { + serialIDGenerator = IDGenerator.fromString(value); + } + } + + + public String getRequestNextRangeDN( + LDAPConfig ldapConfig, + DatabaseConfig dbConfig) throws Exception { + + if (requestIDGenerator == IDGenerator.LEGACY_2) { + // the request nextRange is stored in request repository's range DN + return dbConfig.getRequestRangeDN() + "," + ldapConfig.getBaseDN(); + } + + // the request nextRange is stored in request repository's base DN + return dbConfig.getRequestDN() + "," + ldapConfig.getBaseDN(); + } + + public String getSerialNextRangeDN( + LDAPConfig ldapConfig, + DatabaseConfig dbConfig) throws Exception { + + if (serialIDGenerator == IDGenerator.LEGACY_2) { + // the cert/key nextRange is stored in cert/key repository's range DN + return dbConfig.getSerialRangeDN() + "," + ldapConfig.getBaseDN(); + } + + // the cert/key nextRange is stored in cert/key repository's base DN + return dbConfig.getSerialDN() + "," + ldapConfig.getBaseDN(); + } + @Override public void execute(CommandLine cmd) throws Exception { @@ -108,29 +158,91 @@ public void execute(CommandLine cmd) throws Exception { LdapBoundConnection conn = new LdapBoundConnection(socketFactory, connInfo, authInfo); DatabaseConfig dbConfig = cs.getDatabaseConfig(); + init(dbConfig); + + updateSerialNumberRange( + socketFactory, + connInfo, + authInfo, + ldapConfig, + dbConfig, + baseDN); + + updateRequestNumberRange( + socketFactory, + connInfo, + authInfo, + ldapConfig, + dbConfig, + baseDN); + } - try { - logger.info("Updating serial number range"); + public void updateSerialNumberRange( + PKISocketFactory socketFactory, + LdapConnInfo connInfo, + LdapAuthInfo authInfo, + LDAPConfig ldapConfig, + DatabaseConfig dbConfig, + String baseDN) throws Exception { + + LdapBoundConnection conn = new LdapBoundConnection(socketFactory, connInfo, authInfo); - BigInteger endSerialNumber = new BigInteger(dbConfig.getEndSerialNumber()); + try { + String nextRangeDN = getSerialNextRangeDN(ldapConfig, dbConfig); + logger.info("Updating serial next range in " + nextRangeDN); + + BigInteger endSerialNumber; + if (serialIDGenerator == IDGenerator.LEGACY_2) { + endSerialNumber = dbConfig.getBigInteger(DBSubsystem.PROP_MAX_SERIAL_NUMBER); + } else { + // parse the end of current cert range as decimal + // NOTE: this is a bug, cert range is stored as hex in CS.cfg + endSerialNumber = new BigInteger(dbConfig.getEndSerialNumber()); + } BigInteger nextSerialNumber = endSerialNumber.add(BigInteger.ONE); - String serialDN = dbConfig.getSerialDN() + "," + baseDN; - LDAPAttribute attrSerialNextRange = new LDAPAttribute("nextRange", nextSerialNumber.toString()); + // store nextRange as decimal + logger.info("- next range: " + nextSerialNumber.toString() + " (0x" + nextSerialNumber.toString(16) + ")"); + LDAPAttribute attrSerialNextRange = new LDAPAttribute(DBSubsystem.PROP_NEXT_RANGE, nextSerialNumber.toString()); + LDAPModification serialmod = new LDAPModification(LDAPModification.REPLACE, attrSerialNextRange); - conn.modify(serialDN, serialmod); + conn.modify(nextRangeDN, serialmod); + + } finally { + conn.disconnect(); + } + } - logger.info("Updating request number range"); + public void updateRequestNumberRange( + PKISocketFactory socketFactory, + LdapConnInfo connInfo, + LdapAuthInfo authInfo, + LDAPConfig ldapConfig, + DatabaseConfig dbConfig, + String baseDN) throws Exception { - BigInteger endRequestNumber = new BigInteger(dbConfig.getEndRequestNumber()); + LdapBoundConnection conn = new LdapBoundConnection(socketFactory, connInfo, authInfo); + + try { + String nextRangeDN = getRequestNextRangeDN(ldapConfig, dbConfig); + logger.info("Updating request ID next range in " + nextRangeDN); + + BigInteger endRequestNumber; + if (requestIDGenerator == IDGenerator.LEGACY_2) { + endRequestNumber = dbConfig.getBigInteger(DBSubsystem.PROP_MAX_REQUEST_NUMBER); + } else { + // parse the end of current range as decimal + endRequestNumber = new BigInteger(dbConfig.getEndRequestNumber()); + } BigInteger nextRequestNumber = endRequestNumber.add(BigInteger.ONE); - String requestDN = dbConfig.getRequestDN() + "," + baseDN; - LDAPAttribute attrRequestNextRange = new LDAPAttribute("nextRange", nextRequestNumber.toString()); + // store nextRange as decimal + LDAPAttribute attrRequestNextRange = new LDAPAttribute(DBSubsystem.PROP_NEXT_RANGE, nextRequestNumber.toString()); + LDAPModification requestmod = new LDAPModification(LDAPModification.REPLACE, attrRequestNextRange); - conn.modify(requestDN, requestmod); + conn.modify(nextRangeDN, requestmod); } finally { conn.disconnect();