[Pki-devel] [PATCH] 0048-0049 Lightweight CAs: implement deletion
Fraser Tweedale
ftweedal at redhat.com
Thu Oct 1 12:50:20 UTC 2015
On Wed, Sep 30, 2015 at 03:59:55PM -0400, Ade Lee wrote:
> On Wed, 2015-09-30 at 11:26 -0700, Christina Fu wrote:
> See other comments below, but the gist of Christina's comments are:
> 1. We need to look up the parent and revoke the subca signing cert.
> 2. We need to restrict Delete to Leaf CAs. So if you have Host CA ->
> sub ca1 -> sub ca2 -> subca 3,
> you cannot delete sub CA 1 without deleting subca3 and subca2 (in
> that order).
> 3. The mothballed state Christina refers to is what we now call the
> disabled state. In this case,
> we should not be able to issue new certs (including subca certs).
> We should however still be able to
> revoke certs and publish CRLs.
>
Thanks Christina and Ade for the feedback. The updated patch
(attached) addresses #1. #3 is addressed by my patch 0050 (separate
thread).
I will tackle revocation-on-delete next. If you are happy with
what's in the attached patched we could push it and I'll file a
ticket and implement revocation in a separate patch, but the call is
yours.
Cheers,
Fraser
> > Hi Fraser,
> >
> > Ade caught me on irc for some feedback. I have not had chance to
> > look at your patches, but I did get the gist of the subca delete
> > issues from him.
> > Two key suggestions I have:
> > 1. make sure the action is audited to its parent (audit log
> > preserved)
> > 2. make sure revocation is taken cared of of the subca's signing cert
> > (and therefore invalidating all its signed certs)
> > - and make sure the root CA is never deleted, so that the crls
> > could be preserved and referenced to;
> > - and note that ocsp will no longer work for the subca that is
> > deleted, as the signing cert is gone for good
> >
> > Regarding keeping the root CA, we had discussion on possibly keeping
> > it in a "mothballed state"...I'll let Ade add to this.
> >
> > thanks,
> > Christina
> >
> > On 09/30/2015 07:00 AM, Fraser Tweedale wrote:
> > > Updated patch attached. Comments inline.
> > >
> > > On Wed, Sep 30, 2015 at 06:35:57PM +1000, Fraser Tweedale wrote:
> > > > > 3) It would be good to have a "Are you sure?" dialog on the CLI
> > > > > (with
> > > > > relevant override option).
> > > > >
> > > > Will do.
> > > >
> > > Done.
> > >
> > > > > 5) I have been thinking about ways to restrict delete. We
> > > > > should
> > > > > discuss and decide on options. Some ideas:
> > > > >
> > > > > a) Add CS.cfg option to disable deletes (for production
> > > > > say).
> > > > >
> > > > Disagree; don't want more config in flat files. Having the knob
> > > > in
> > > > the database would be better but I prefer a combination of other
> > > > options (see below).
> > > >
> > > > > b) Add optional field (deletable) to the CA entry. This can
> > > > > be
> > > > > set by the creating admin to be True for test
> > > > > environments or
> > > > > cases where we know the environment will be short lived,
> > > > > or
> > > > > False for long lived CAs. Default could be configurable.
> > > > >
> > > > > CAs could still be deleted, but only by doing something
> > > > > out-of-band --like modifying the db entry using pki
> > > > > -server
> > > > > commands or similar.
> > > > >
> > > > > c) Requiring CAs to be disabled before deleting them.
> > > > >
> > > > I'm in favour of this.
> > > >
> > > > > d) Setting a separate ACL for delete, so that it would be
> > > > > easier
> > > > > for admins to set special permissions for delete.
> > > > >
> > > > And in favour of this.
> > > >
> > > > > ... others?
> > > > >
> > > > I like (c) plus (d) plus perhaps a pkispawn knob that controls
> > > > whether the admin-can-delete ACL gets added at the beginning.
> > > >
> > > > Let me know what you think and thanks for your feedback!
> > > >
> > > (c) and (d) are implemented in updated patch. If you agree with
> > > (c)
> > > plus (d) plus pkispawn knob (I guess we'll call that (e)), I'll
> > > file
> > > a ticket for (e).
> > >
> I'm OK with (c) and (d) and others appear to be too. Archiving is
> difficult because it basically won't work with an HSM.
> I suppose this is just equivalent to the controls we have in revoking
> the host CA signing cert.
> I don't think (e) is needed.
>
-------------- next part --------------
From 6494270363b82683136eeda2332207412f29b906 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 29 Sep 2015 11:17:21 -0400
Subject: [PATCH] Lightweight CAs: implement deletion API and CLI
Implement lightweight authority deletion including CLI command. To
be deleted an authority must be disabled and have no sub-CAs.
Fixes: https://fedorahosted.org/pki/ticket/1324
---
base/ca/shared/conf/acl.ldif | 1 +
base/ca/shared/conf/acl.properties | 1 +
.../src/com/netscape/ca/CertificateAuthority.java | 75 ++++++++++++++++++++++
.../dogtagpki/server/ca/rest/AuthorityService.java | 36 +++++++++--
.../certsrv/authority/AuthorityClient.java | 5 ++
.../certsrv/authority/AuthorityResource.java | 8 +++
.../src/com/netscape/certsrv/ca/AuthorityID.java | 4 ++
.../netscape/certsrv/ca/CAEnabledException.java | 15 +++++
.../netscape/certsrv/ca/CANotLeafException.java | 16 +++++
.../netscape/certsrv/ca/ICertificateAuthority.java | 6 ++
.../netscape/cmstools/authority/AuthorityCLI.java | 1 +
.../cmstools/authority/AuthorityRemoveCLI.java | 72 +++++++++++++++++++++
12 files changed, 236 insertions(+), 4 deletions(-)
create mode 100644 base/common/src/com/netscape/certsrv/ca/CAEnabledException.java
create mode 100644 base/common/src/com/netscape/certsrv/ca/CANotLeafException.java
create mode 100644 base/java-tools/src/com/netscape/cmstools/authority/AuthorityRemoveCLI.java
diff --git a/base/ca/shared/conf/acl.ldif b/base/ca/shared/conf/acl.ldif
index 54c9f1d5c64b6578de83f1b7ffdff922a69975f4..27d89a3131e9220b4c03b17aad14b364fe97ff20 100644
--- a/base/ca/shared/conf/acl.ldif
+++ b/base/ca/shared/conf/acl.ldif
@@ -59,3 +59,4 @@ resourceACLS: certServer.ca.selftests:read,execute:allow (read,execute) group="A
resourceACLS: certServer.ca.users:execute:allow (execute) group="Administrators":Admins may execute user operations
resourceACLS: certServer.ca.authorities:list,read:allow (list,read) user="anybody":Anybody may list and read lightweight authorities
resourceACLS: certServer.ca.authorities:create,modify:allow (create,modify) group="Administrators":Administrators may create and modify lightweight authorities
+resourceACLS: certServer.ca.authorities:delete:allow (delete) group="Administrators":Administrators may delete lightweight authorities
diff --git a/base/ca/shared/conf/acl.properties b/base/ca/shared/conf/acl.properties
index f0b5b9f650ad2fc4bde531ade94347a7280d3089..8b3e9d0eea09e5e3ab8271888ab0532d47b69348 100644
--- a/base/ca/shared/conf/acl.properties
+++ b/base/ca/shared/conf/acl.properties
@@ -25,3 +25,4 @@ authorities.create = certServer.ca.authorities,create
authorities.list = certServer.ca.authorities,list
authorities.modify = certServer.ca.authorities,modify
authorities.read = certServer.ca.authorities,read
+authorities.delete = certServer.ca.authorities,delete
diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
index d5523c14cc0132422c971b840324bd95bfa1fda9..5440a380ab8c5627f6305ad882159a7bcf3ef88e 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -50,9 +50,11 @@ import org.mozilla.jss.asn1.INTEGER;
import org.mozilla.jss.asn1.InvalidBERException;
import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
import org.mozilla.jss.asn1.OCTET_STRING;
+import org.mozilla.jss.crypto.CryptoStore;
import org.mozilla.jss.crypto.CryptoToken;
import org.mozilla.jss.crypto.KeyPairAlgorithm;
import org.mozilla.jss.crypto.KeyPairGenerator;
+import org.mozilla.jss.crypto.NoSuchItemOnTokenException;
import org.mozilla.jss.crypto.SignatureAlgorithm;
import org.mozilla.jss.crypto.TokenException;
import org.mozilla.jss.pkix.cert.Extension;
@@ -68,7 +70,9 @@ import com.netscape.certsrv.base.Nonces;
import com.netscape.certsrv.base.PKIException;
import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.ca.CADisabledException;
+import com.netscape.certsrv.ca.CAEnabledException;
import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.CANotLeafException;
import com.netscape.certsrv.ca.CATypeException;
import com.netscape.certsrv.ca.ECAException;
import com.netscape.certsrv.ca.ICRLIssuingPoint;
@@ -2627,4 +2631,75 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori
}
}
+ public void deleteAuthority() throws EBaseException {
+ if (isHostAuthority())
+ throw new CATypeException("Cannot delete the host CA");
+
+ if (authorityEnabled)
+ throw new CAEnabledException("Must disable CA before deletion");
+
+ boolean hasSubCAs = false;
+ for (ICertificateAuthority ca : getCAs()) {
+ AuthorityID parentAID = ca.getAuthorityParentID();
+ if (parentAID != null && parentAID.equals(this.authorityID)) {
+ hasSubCAs = true;
+ break;
+ }
+ }
+ if (hasSubCAs)
+ throw new CANotLeafException("CA with sub-CAs cannot be deleted (delete sub-CAs first)");
+
+ caMap.remove(authorityID);
+ shutdown();
+
+ // delete ldap entry
+ ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory("updateAuthority");
+ dbFactory.init(CMS.getConfigStore().getSubStore("internaldb"));
+ LDAPConnection conn = dbFactory.getConn();
+ String dn = "cn=" + authorityID.toString() + ",ou=authorities,ou="
+ + getId() + "," + getDBSubsystem().getBaseDN();
+ try {
+ conn.delete(dn);
+ } catch (LDAPException e) {
+ throw new ELdapException("Error deleting authority entry '" + dn + "': " + e);
+ } finally {
+ dbFactory.returnConn(conn);
+ dbFactory.reset();
+ }
+
+ CryptoManager cryptoManager;
+ try {
+ cryptoManager = CryptoManager.getInstance();
+ } catch (CryptoManager.NotInitializedException e) {
+ // can't happen
+ throw new ECAException("CryptoManager not initialized");
+ }
+
+ // delete cert
+ CryptoStore cryptoStore =
+ cryptoManager.getInternalKeyStorageToken().getCryptoStore();
+ try {
+ cryptoStore.deleteCert(mCaX509Cert);
+ } catch (NoSuchItemOnTokenException e) {
+ CMS.debug("deleteAuthority: cert is not on token: " + e);
+ // if the cert isn't there, never mind
+ } catch (TokenException e) {
+ CMS.debug("deleteAuthority: TokenExcepetion while deleting cert: " + e);
+ throw new ECAException("TokenException while deleting cert: " + e);
+ }
+
+ // delete key
+ try {
+ cryptoStore.deletePrivateKey(mSigningUnit.getPrivateKey());
+ } catch (NoSuchItemOnTokenException e) {
+ CMS.debug("deleteAuthority: private key is not on token: " + e);
+ // if the key isn't there, never mind
+ } catch (TokenException e) {
+ CMS.debug("deleteAuthority: TokenExcepetion while deleting private key: " + e);
+ // TODO don't know what causes this yet, or how to
+ // prevent it.
+ //throw new ECAException("TokenException while deleting private key: " + e);
+ }
+ }
+
}
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 2aa0e97d966d7f879a9999966fb5942bb54dcf42..8d0cac3ef997d4daab3198be68efcb66b145f991 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java
@@ -43,7 +43,9 @@ import com.netscape.certsrv.base.PKIException;
import com.netscape.certsrv.base.ResourceNotFoundException;
import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.ca.CADisabledException;
+import com.netscape.certsrv.ca.CAEnabledException;
import com.netscape.certsrv.ca.CANotFoundException;
+import com.netscape.certsrv.ca.CANotLeafException;
import com.netscape.certsrv.ca.CATypeException;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.ca.IssuerUnavailableException;
@@ -100,7 +102,7 @@ public class AuthorityService extends PKIService implements AuthorityResource {
try {
aid = new AuthorityID(aidString);
} catch (IllegalArgumentException e) {
- throw new BadRequestException("Bad CA ID: " + aidString);
+ throw new BadRequestException("Bad AuthorityID: " + aidString);
}
ca = hostCA.getCA(aid);
@@ -117,7 +119,7 @@ public class AuthorityService extends PKIService implements AuthorityResource {
try {
aid = new AuthorityID(aidString);
} catch (IllegalArgumentException e) {
- throw new BadRequestException("Bad CA ID: " + aidString);
+ throw new BadRequestException("Bad AuthorityID: " + aidString);
}
ICertificateAuthority ca = hostCA.getCA(aid);
@@ -144,7 +146,7 @@ public class AuthorityService extends PKIService implements AuthorityResource {
try {
aid = new AuthorityID(aidString);
} catch (IllegalArgumentException e) {
- throw new BadRequestException("Bad CA ID: " + aidString);
+ throw new BadRequestException("Bad AuthorityID: " + aidString);
}
ICertificateAuthority ca = hostCA.getCA(aid);
@@ -199,7 +201,7 @@ public class AuthorityService extends PKIService implements AuthorityResource {
try {
aid = new AuthorityID(aidString);
} catch (IllegalArgumentException e) {
- throw new BadRequestException("Bad CA ID: " + aidString);
+ throw new BadRequestException("Bad AuthorityID: " + aidString);
}
ICertificateAuthority ca = hostCA.getCA(aid);
@@ -233,6 +235,32 @@ public class AuthorityService extends PKIService implements AuthorityResource {
new AuthorityData(null, null, null, null, false, null));
}
+ @Override
+ public Response deleteCA(String aidString) {
+ AuthorityID aid = null;
+ try {
+ aid = new AuthorityID(aidString);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Bad AuthorityID: " + aidString);
+ }
+
+ ICertificateAuthority ca = hostCA.getCA(aid);
+ if (ca == null)
+ throw new ResourceNotFoundException("CA \"" + aidString + "\" not found");
+
+ try {
+ ca.deleteAuthority();
+ return createNoContentResponse();
+ } catch (CATypeException e) {
+ throw new ForbiddenException(e.toString());
+ } catch (CAEnabledException | CANotLeafException e) {
+ throw new ConflictingOperationException(e.toString());
+ } catch (EBaseException e) {
+ CMS.debug(e);
+ throw new PKIException("Error modifying authority: " + e.toString());
+ }
+ }
+
private static AuthorityData readAuthorityData(ICertificateAuthority ca)
throws PKIException {
String dn;
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java b/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
index 86de3352e2424211125c146edf759481448a2694..5a80877ca4479058ba88005421c46d092b2df6a6 100644
--- a/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityClient.java
@@ -59,4 +59,9 @@ public class AuthorityClient extends Client {
return client.getEntity(response, AuthorityData.class);
}
+ public void deleteCA(String aidString) {
+ Response response = proxy.deleteCA(aidString);
+ client.getEntity(response, Void.class);
+ }
+
}
diff --git a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
index eaef903db444512dbea6c87b11800130d94a944d..c6dc696247122b5f07802696c38c2f3517341106 100644
--- a/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
+++ b/base/common/src/com/netscape/certsrv/authority/AuthorityResource.java
@@ -1,5 +1,6 @@
package com.netscape.certsrv.authority;
+import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@@ -93,4 +94,11 @@ public interface AuthorityResource {
@ACLMapping("authorities.modify")
public Response disableCA(@PathParam("id") String caIDString);
+ @DELETE
+ @Path("{id}")
+ @ClientResponseType(entityType=Void.class)
+ @AuthMethodMapping("authorities")
+ @ACLMapping("authorities.delete")
+ public Response deleteCA(@PathParam("id") String caIDString);
+
}
diff --git a/base/common/src/com/netscape/certsrv/ca/AuthorityID.java b/base/common/src/com/netscape/certsrv/ca/AuthorityID.java
index daac587b75843f5faf75e556d4d0135c8ffc8fd7..9816f87ad02289739c7ff90be1ffc71e086a2bd9 100644
--- a/base/common/src/com/netscape/certsrv/ca/AuthorityID.java
+++ b/base/common/src/com/netscape/certsrv/ca/AuthorityID.java
@@ -29,6 +29,10 @@ public class AuthorityID implements Comparable<AuthorityID> {
return uuid.toString();
}
+ public boolean equals(AuthorityID aid) {
+ return this.compareTo(aid) == 0;
+ }
+
public int compareTo(AuthorityID aid) {
return uuid.compareTo(aid.uuid);
}
diff --git a/base/common/src/com/netscape/certsrv/ca/CAEnabledException.java b/base/common/src/com/netscape/certsrv/ca/CAEnabledException.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c85276f3bf71bb640a3468f4ed4be4a6aa0180c
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/ca/CAEnabledException.java
@@ -0,0 +1,15 @@
+package com.netscape.certsrv.ca;
+
+/**
+ * Exception to throw when an operation cannot be performed because
+ * the CA to which the operation pertains is enabled.
+ */
+public class CAEnabledException extends ECAException {
+
+ private static final long serialVersionUID = 1056602856006912665L;
+
+ public CAEnabledException(String msgFormat) {
+ super(msgFormat);
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/ca/CANotLeafException.java b/base/common/src/com/netscape/certsrv/ca/CANotLeafException.java
new file mode 100644
index 0000000000000000000000000000000000000000..eabca73641cd0f646167fb6b436aaa736e7139b2
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/ca/CANotLeafException.java
@@ -0,0 +1,16 @@
+package com.netscape.certsrv.ca;
+
+/**
+ * Exception to throw when an operation cannot be performed because
+ * the CA to which the operation pertains is not a leaf CA (ie, has
+ * sub-CAs).
+ */
+public class CANotLeafException extends ECAException {
+
+ private static final long serialVersionUID = -2729093578678941399L;
+
+ public CANotLeafException(String msgFormat) {
+ super(msgFormat);
+ }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
index 31d5c9277a63ca7c916f39651300b0c9a9061c1e..96bc392294f27d57d9795c2b1b793b8cbc001fda 100644
--- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
+++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
@@ -583,4 +583,10 @@ public interface ICertificateAuthority extends ISubsystem {
*/
public void modifyAuthority(Boolean enabled, String desc)
throws EBaseException;
+
+ /**
+ * Delete this lightweight CA.
+ */
+ public void deleteAuthority()
+ throws EBaseException;
}
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 99d38ad1b989e171079df78ddd8b2774817ccb33..4fbcfef760086928b2e0e75fe4fc56f1b249b5fd 100644
--- a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityCLI.java
@@ -17,6 +17,7 @@ public class AuthorityCLI extends CLI {
addModule(new AuthorityCreateCLI(this));
addModule(new AuthorityDisableCLI(this));
addModule(new AuthorityEnableCLI(this));
+ addModule(new AuthorityRemoveCLI(this));
}
public String getFullName() {
diff --git a/base/java-tools/src/com/netscape/cmstools/authority/AuthorityRemoveCLI.java b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityRemoveCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..42265b180ba85b6097ef2354e969fbd0495ac73b
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/authority/AuthorityRemoveCLI.java
@@ -0,0 +1,72 @@
+package com.netscape.cmstools.authority;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class AuthorityRemoveCLI extends CLI {
+
+ public AuthorityCLI authorityCLI;
+
+ public AuthorityRemoveCLI(AuthorityCLI authorityCLI) {
+ super("del", "Delete Authority", authorityCLI);
+ this.authorityCLI = authorityCLI;
+
+ options.addOption(null, "force", false, "Force delete");
+ }
+
+ public void printHelp() {
+ formatter.printHelp(getFullName() + " <dn>", options);
+ }
+
+ public void execute(String[] args) throws Exception {
+ // Always check for "--help" prior to parsing
+ if (Arrays.asList(args).contains("--help")) {
+ // Display usage
+ printHelp();
+ System.exit(0);
+ }
+
+ CommandLine cmd = null;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println("Error: " + e.getMessage());
+ printHelp();
+ System.exit(-1);
+ }
+
+ String[] cmdArgs = cmd.getArgs();
+ if (cmdArgs.length != 1) {
+ if (cmdArgs.length < 1)
+ System.err.println("No ID specified.");
+ else
+ System.err.println("Too many arguments.");
+ printHelp();
+ System.exit(-1);
+ }
+
+ if (!cmd.hasOption("force")) {
+ System.out.print("Are you sure (Y/N)? ");
+ System.out.flush();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ String line = reader.readLine();
+ if (!line.equalsIgnoreCase("Y")) {
+ System.exit(-1);
+ }
+ }
+
+ String aidString = cmdArgs[0];
+ authorityCLI.authorityClient.deleteCA(aidString);
+ MainCLI.printMessage("Deleted authority \"" + aidString + "\"");
+ }
+
+}
--
2.4.3
More information about the Pki-devel
mailing list