From 8f93e60e0057b0706c5d5ad762d7ff7ce20b7b39 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Wed, 16 Mar 2016 16:48:43 +1100 Subject: [PATCH 94/96] Lightweight CAs: indicate when CA does not yet have keys When a lightweight CA is created, clones will initialise a local object when the LDAP replication takes place, however, the signing keys will not yet have been replicated. Therefore, indicate CA readiness in authority data and respond appropriately (HTTP 503) when signing operations are attempted. Part of: https://fedorahosted.org/pki/ticket/1625 --- .../src/com/netscape/ca/CertificateAuthority.java | 24 +++++++++++++++------- .../dogtagpki/server/ca/rest/AuthorityService.java | 12 ++++++++--- .../server/ca/rest/CertRequestService.java | 5 +++++ .../netscape/certsrv/authority/AuthorityData.java | 17 ++++++++++++++- .../certsrv/base/ServiceUnavailableException.java | 17 +++++++++++++++ .../netscape/certsrv/ca/ICertificateAuthority.java | 10 +++++++++ .../netscape/cmstools/authority/AuthorityCLI.java | 1 + .../cmstools/authority/AuthorityCreateCLI.java | 2 +- .../cmstools/authority/AuthorityDisableCLI.java | 2 +- .../cmstools/authority/AuthorityEnableCLI.java | 2 +- .../cms/servlet/cert/RequestProcessor.java | 5 +---- 11 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 base/common/src/com/netscape/certsrv/base/ServiceUnavailableException.java diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java index 60f6b36219fda09a246013707c245a00d89ea84c..d96b8841449f4a19e652cc2512f834fed87f64e5 100644 --- a/base/ca/src/com/netscape/ca/CertificateAuthority.java +++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java @@ -363,9 +363,20 @@ public class CertificateAuthority return hostCA == this; } - private void ensureEnabled() throws CADisabledException { + public void ensureReady() + throws ECAException { if (!authorityEnabled) throw new CADisabledException("Authority is disabled"); + if (!isReady()) { + if (signingUnitException != null) + throw signingUnitException; + else + throw new CAMissingKeyException("Authority does not yet have signing key and cert in local NSSDB"); + } + } + + public boolean isReady() { + return hasKeys; } public boolean getAuthorityEnabled() { @@ -1191,7 +1202,7 @@ public class CertificateAuthority */ public X509CRLImpl sign(X509CRLImpl crl, String algname) throws EBaseException { - ensureEnabled(); + ensureReady(); X509CRLImpl signedcrl = null; IStatsSubsystem statsSub = (IStatsSubsystem) CMS.getSubsystem("stats"); @@ -1264,7 +1275,7 @@ public class CertificateAuthority */ public X509CertImpl sign(X509CertInfo certInfo, String algname) throws EBaseException { - ensureEnabled(); + ensureReady(); X509CertImpl signedcert = null; @@ -1349,7 +1360,7 @@ public class CertificateAuthority */ public byte[] sign(byte[] data, String algname) throws EBaseException { - ensureEnabled(); + ensureReady(); return mSigningUnit.sign(data, algname); } @@ -2261,7 +2272,7 @@ public class CertificateAuthority } private BasicOCSPResponse sign(ResponseData rd) throws EBaseException { - ensureEnabled(); + ensureReady(); try (DerOutputStream out = new DerOutputStream()) { DerOutputStream tmp = new DerOutputStream(); @@ -2490,8 +2501,7 @@ public class CertificateAuthority String subjectDN, String description) throws EBaseException { - if (!authorityEnabled) - throw new CADisabledException("Parent CA is disabled"); + ensureReady(); // check requested DN X500Name subjectX500Name = null; diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java index fa9e1038b7b3ca718a7593052a852c31f48545f7..582248d4cf284fb759c5c483810da87683862c1f 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java @@ -43,9 +43,12 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.ForbiddenException; import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceNotFoundException; +import com.netscape.certsrv.base.ServiceUnavailableException; import com.netscape.certsrv.ca.AuthorityID; import com.netscape.certsrv.ca.CAEnabledException; import com.netscape.certsrv.ca.CADisabledException; +import com.netscape.certsrv.ca.CAMissingCertException; +import com.netscape.certsrv.ca.CAMissingKeyException; import com.netscape.certsrv.ca.CANotFoundException; import com.netscape.certsrv.ca.CANotLeafException; import com.netscape.certsrv.ca.CATypeException; @@ -207,6 +210,8 @@ public class AuthorityService extends PKIService implements AuthorityResource { auditParams.put("exception", e.toString()); audit(ILogger.FAILURE, OpDef.OP_ADD, "", auditParams); throw new ConflictingOperationException(e.toString()); + } catch (CAMissingCertException | CAMissingKeyException e) { + throw new ServiceUnavailableException(e.toString()); } catch (Exception e) { CMS.debug(e); auditParams.put("exception", e.toString()); @@ -261,14 +266,14 @@ public class AuthorityService extends PKIService implements AuthorityResource { public Response enableCA(String aidString) { return modifyCA( aidString, - new AuthorityData(null, null, null, null, true, null)); + new AuthorityData(null, null, null, null, true, null, null)); } @Override public Response disableCA(String aidString) { return modifyCA( aidString, - new AuthorityData(null, null, null, null, false, null)); + new AuthorityData(null, null, null, null, false, null, null)); } @Override @@ -322,7 +327,8 @@ public class AuthorityService extends PKIService implements AuthorityResource { ca.getAuthorityID().toString(), parentAID != null ? parentAID.toString() : null, ca.getAuthorityEnabled(), - ca.getAuthorityDescription() + ca.getAuthorityDescription(), + ca.isReady() ); } diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java index cddbeb1ba47741673ab5eb3d22e2bf7c53c4c33d..80aaf6f7899d92675c15c6f944b7a3a491784145 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/CertRequestService.java @@ -43,9 +43,12 @@ import com.netscape.certsrv.base.ConflictingOperationException; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceNotFoundException; +import com.netscape.certsrv.base.ServiceUnavailableException; import com.netscape.certsrv.base.UnauthorizedException; import com.netscape.certsrv.ca.AuthorityID; import com.netscape.certsrv.ca.CADisabledException; +import com.netscape.certsrv.ca.CAMissingCertException; +import com.netscape.certsrv.ca.CAMissingKeyException; import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.cert.CertEnrollmentRequest; import com.netscape.certsrv.cert.CertRequestInfo; @@ -252,6 +255,8 @@ public class CertRequestService extends PKIService implements CertRequestResourc } catch (CADisabledException e) { CMS.debug("changeRequestState: CA disabled: " + e); throw new ConflictingOperationException(e.toString()); + } catch (CAMissingCertException | CAMissingKeyException e) { + throw new ServiceUnavailableException(e.toString()); } catch (EPropertyException e) { CMS.debug("changeRequestState: execution error " + e); throw new PKIException(CMS.getUserMessage(getLocale(headers), diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java index 2312c39895c09a4b7dbf994d43c2c068eeaec2d4..84679567eb527cbf9fedd21705a72ca9c1a34a93 100644 --- a/base/common/src/com/netscape/certsrv/authority/AuthorityData.java +++ b/base/common/src/com/netscape/certsrv/authority/AuthorityData.java @@ -95,6 +95,19 @@ public class AuthorityData { } + /** + * Whether the CA is ready to perform signing operations. + * + * This is a read-only attribute; it cannot be set by the user. + */ + @XmlAttribute + protected Boolean ready; + + public Boolean getReady() { + return ready; + } + + protected Link link; public Link getLink() { @@ -111,13 +124,15 @@ public class AuthorityData { public AuthorityData( Boolean isHostAuthority, String dn, String id, String parentID, - Boolean enabled, String description) { + Boolean enabled, String description, + Boolean ready) { this.isHostAuthority = isHostAuthority; this.dn = dn; this.id = id; this.parentID = parentID; this.enabled = enabled; this.description = description; + this.ready = ready; } } diff --git a/base/common/src/com/netscape/certsrv/base/ServiceUnavailableException.java b/base/common/src/com/netscape/certsrv/base/ServiceUnavailableException.java new file mode 100644 index 0000000000000000000000000000000000000000..0ee9c8a08d5144bf9cd7e13c1c4bf7d870a7d846 --- /dev/null +++ b/base/common/src/com/netscape/certsrv/base/ServiceUnavailableException.java @@ -0,0 +1,17 @@ +package com.netscape.certsrv.base; + +import javax.ws.rs.core.Response; + +public class ServiceUnavailableException extends PKIException { + + private static final long serialVersionUID = -9160776882517621347L; + + public ServiceUnavailableException(String message) { + super(Response.Status.SERVICE_UNAVAILABLE, message); + } + + public ServiceUnavailableException(String message, Throwable cause) { + super(Response.Status.SERVICE_UNAVAILABLE, message, cause); + } + +} diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java index 6d83e6d07bf7100d03954ac7caec69134dbb5ec1..dd0d1b0851f03b3df8b69f7595a3524e1f9bd9ba 100644 --- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java +++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java @@ -545,6 +545,16 @@ public interface ICertificateAuthority extends ISubsystem { public boolean getAuthorityEnabled(); /** + * Return whether CA is ready to perform signing operations. + */ + public boolean isReady(); + + /** + * Throw an exception if CA is not ready to perform signing operations. + */ + public void ensureReady() throws ECAException; + + /** * Return CA description. May be null. */ public String getAuthorityDescription(); diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java index 4fbcfef760086928b2e0e75fe4fc56f1b249b5fd..ac06ea24ce824ad1b4be29a4176658caa9302e89 100644 --- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java @@ -45,6 +45,7 @@ public class AuthorityCLI extends CLI { if (parentAID != null) System.out.println(" Parent ID: " + data.getParentID()); System.out.println(" Enabled: " + data.getEnabled()); + System.out.println(" Ready to sign: " + data.getReady()); String desc = data.getDescription(); if (desc != null) System.out.println(" Description: " + desc); diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java index d1688fbd1933a567940164d86ac726df1489f7d2..3c36ac756aeedde8d89505be871da3555b548434 100644 --- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCreateCLI.java @@ -81,7 +81,7 @@ public class AuthorityCreateCLI extends CLI { String dn = cmdArgs[0]; AuthorityData data = new AuthorityData( - null, dn, null, parentAIDString, true /* enabled */, desc); + null, dn, null, parentAIDString, true /* enabled */, desc, null); AuthorityData newData = authorityCLI.authorityClient.createCA(data); AuthorityCLI.printAuthorityData(newData); } diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java index fc4cbf30be947233a9289089bb25bef70d532bb6..85b38f0810a6cff3a8c2293feab3153c85e8fee2 100644 --- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityDisableCLI.java @@ -48,7 +48,7 @@ public class AuthorityDisableCLI extends CLI { } AuthorityData data = new AuthorityData( - null, null, cmdArgs[0], null, false, null); + null, null, cmdArgs[0], null, false, null, null); data = authorityCLI.authorityClient.modifyCA(data); AuthorityCLI.printAuthorityData(data); } diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java index f6fdab12f527975d2d0688b65968bfb992b5a97a..936edca599b7d6391370284535584953f0180bc8 100644 --- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityEnableCLI.java @@ -48,7 +48,7 @@ public class AuthorityEnableCLI extends CLI { } AuthorityData data = new AuthorityData( - null, null, cmdArgs[0], null, true, null); + null, null, cmdArgs[0], null, true, null, null); data = authorityCLI.authorityClient.modifyCA(data); AuthorityCLI.printAuthorityData(data); } diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java index 8558ec23f6489407dc5f41951a363d22548851c0..b92ffb1d7527178e38eeaa4e35b83940167e9f4d 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java +++ b/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java @@ -37,7 +37,6 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.ca.AuthorityID; -import com.netscape.certsrv.ca.CADisabledException; import com.netscape.certsrv.ca.CANotFoundException; import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.cert.CertReviewResponse; @@ -350,9 +349,7 @@ public class RequestProcessor extends CertProcessor { if (ca == null) // this shouldn't happen because request was already accepted throw new CANotFoundException("Unknown CA: " + aidString); - if (!ca.getAuthorityEnabled()) - // authority was disabled after request was accepted - throw new CADisabledException("CA '" + aidString + "' is disabled"); + ca.ensureReady(); } /** -- 2.5.5