>From 7c3f4bdcb06890889afa52e23a59ec96c54960de Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 11 Feb 2015 16:28:50 -0500
Subject: [PATCH 241/241] Add granularity to token termination in TPS

BZ 1163987.  Added revocation checks to optionally revoke
expired certs, and handle cases where certs are shared on  multiple
tokens.
---
 .../src/org/dogtagpki/server/tps/TPSTokendb.java   | 149 ++++++++++++++++++---
 .../server/tps/processor/TPSEnrollProcessor.java   |  11 +-
 .../server/tps/processor/TPSProcessor.java         |   2 +-
 .../dogtagpki/server/tps/rest/TokenService.java    |  23 ++--
 4 files changed, 150 insertions(+), 35 deletions(-)

diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java
index bf86b19edeef2ad129ac0f4594242a5e82b47f62..03de7aa7ba6efabf1da1621fd99c234e2f3d99fa 100644
--- a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java
+++ b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java
@@ -20,6 +20,7 @@ package org.dogtagpki.server.tps;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -29,6 +30,7 @@ import netscape.security.x509.RevocationReason;
 
 import org.dogtagpki.server.tps.cms.CARemoteRequestHandler;
 import org.dogtagpki.server.tps.cms.CARevokeCertResponse;
+import org.dogtagpki.server.tps.dbs.ActivityDatabase;
 import org.dogtagpki.server.tps.dbs.TPSCertRecord;
 import org.dogtagpki.server.tps.dbs.TokenRecord;
 import org.dogtagpki.tps.main.TPSException;
@@ -258,7 +260,7 @@ public class TPSTokendb {
      * @param cuid the cuid of the token
      * @return ArrayList of the cert records
      */
-    public ArrayList<TPSCertRecord> tdbGetCertificatesByCUID(String cuid)
+    public ArrayList<TPSCertRecord> tdbGetCertRecordsByCUID(String cuid)
             throws TPSException {
         if (cuid == null)
             throw new TPSException("TPSTokendb.tdbGetCertificatesByCUID: cuid null");
@@ -281,6 +283,32 @@ public class TPSTokendb {
         return certRecords;
     }
 
+    public ArrayList<TPSCertRecord> tdbGetCertRecordsByCert(String serial, String issuer)
+            throws TPSException {
+        if (serial == null)
+            throw new TPSException("TPSTokendb.tdbGetCertificatesBySerial: serial null");
+
+        Map<String, String> attributes = new HashMap<String, String>();
+        attributes.put("serialNumber", serial);
+        attributes.put("issuedBy", issuer);
+
+        ArrayList<TPSCertRecord> certRecords = new ArrayList<TPSCertRecord>();
+        Iterator<TPSCertRecord> records;
+        try {
+            records = tps.certDatabase.findRecords(null, attributes).iterator();
+        } catch (Exception e) {
+            CMS.debug("TPSTokendb.tdbGetCertificatesByCUID:" + e);
+            throw new TPSException(e.getMessage());
+        }
+
+        while (records.hasNext()) {
+            TPSCertRecord certRecord = records.next();
+            certRecords.add(certRecord);
+        }
+
+        return certRecords;
+    }
+
     public void tdbRemoveCertificatesByCUID(String cuid)
         throws Exception {
         String method = "TPSTokendb.tdbRemoveCertificatesByCUID";
@@ -306,20 +334,99 @@ public class TPSTokendb {
         }
     }
 
-    public void revokeCertsByCUID(String cuid, String tokenReason) throws Exception {
+    public void revokeCertsByCUID(String cuid, String tokenReason, String ipAddress, String remoteUser)
+            throws Exception {
         String method = "TPStokendb.revokeCertsByCUID";
         CMS.debug(method + ": called");
         if (cuid == null)
             throw new TPSException(method + ": cuid null");
-        revokeCertsByCUID(true, cuid, tokenReason);
+        revokeCertsByCUID(true, cuid, tokenReason, ipAddress, remoteUser);
     }
 
-    public void unRevokeCertsByCUID(String cuid) throws Exception {
+    public void unRevokeCertsByCUID(String cuid, String ipAddress, String remoteUser) throws Exception {
         String method = "TPStokendb.unRevokeCertsByCUID";
         CMS.debug(method + ": called");
         if (cuid == null)
             throw new TPSException(method + ": cuid null");
-        revokeCertsByCUID(false, cuid, null /* null for unrevoke*/);
+        revokeCertsByCUID(false, cuid, null /* null for unrevoke*/, ipAddress, remoteUser);
+    }
+
+    private boolean isLastActiveSharedCert(String serial, String issuer, String cuid) throws TPSException {
+        ArrayList<TPSCertRecord> certRecords = tps.getTokendb().tdbGetCertRecordsByCert(serial, issuer);
+        for (TPSCertRecord cert : certRecords) {
+            // exclude current token
+            if (cert.getTokenID().equals(cuid))
+                continue;
+
+            if (cert.getStatus().equals("active"))
+                return false;
+        }
+
+        return true;
+    }
+
+    private boolean shouldRevoke(TPSCertRecord cert, String cuid, String tokenReason,
+            String ipAddress, String remoteUser) throws Exception {
+        IConfigStore configStore = CMS.getConfigStore();
+        String method = "TPStokendb.shouldRevoke";
+        String activityMsg;
+
+        if (cert == null) {
+            throw new TPSException(method + ": cert null");
+        }
+
+        String tokenType = cert.getType();
+        String keyType = cert.getKeyType();
+
+        // check if certificate revocation is enabled
+        String config = "op.enroll." + tokenType + ".keyGen." + keyType +
+                ".recovery." + tokenReason + ".revokeCert";
+        boolean revokeCerts = configStore.getBoolean(config, true);
+        if (!revokeCerts) {
+            activityMsg = "certificate revocation (serial " + cert.getSerialNumber() +
+                    ") not enabled for tokenType: " + tokenType +
+                    ", keyType: " + keyType +
+                    ", state: " + tokenReason;
+
+            tdbActivity(ActivityDatabase.OP_DO_TOKEN, tdbGetTokenEntry(cuid),
+                    ipAddress, activityMsg, "success", remoteUser);
+
+            return false;
+        }
+
+        // check if expired certificates should be revoked.
+        config = "op.enroll." + tokenType + ".keyGen." + keyType + ".recovery." +
+                tokenReason + ".revokeExpiredCerts";
+        boolean revokeExpiredCerts = configStore.getBoolean(config, true);
+        if (!revokeExpiredCerts) {
+            Date notBefore = cert.getValidNotBefore();
+            Date notAfter = cert.getValidNotAfter();
+            Date now = new Date();
+            if (now.after(notAfter) || now.before(notBefore)) {
+                activityMsg = "revocation not enabled for expired cert: " + cert.getSerialNumber();
+                tdbActivity(ActivityDatabase.OP_DO_TOKEN, tdbGetTokenEntry(cuid),
+                        ipAddress, activityMsg, "success", remoteUser);
+                return false;
+            }
+        }
+
+        // check if certs on multiple tokens should be revoked
+        config = "op.enroll." + tokenType + ".keyGen." + keyType + ".recovery." +
+                tokenReason + ".holdRevocationUntilLastCredential";
+        boolean holdRevocation = configStore.getBoolean(config, false);
+        if (holdRevocation) {
+            if (!isLastActiveSharedCert(cert.getSerialNumber(), cert.getIssuedBy(), cuid)) {
+                activityMsg = "revocation not permitted as certificate " + cert.getSerialNumber() +
+                        " is shared by anothr active token";
+
+                tdbActivity(ActivityDatabase.OP_DO_TOKEN, tdbGetTokenEntry(cuid),
+                        ipAddress, activityMsg, "success", remoteUser);
+
+                return false;
+            }
+        }
+
+        return true;
     }
 
     /*
@@ -328,17 +435,19 @@ public class TPSTokendb {
      * @param cuid cuid of token to revoke/unrevoke
      * @param onHold true if revocation is to put onHold; false if to really revoke
      */
-    private void revokeCertsByCUID(boolean isRevoke, String cuid, String tokenReason) throws Exception {
+    private void revokeCertsByCUID(boolean isRevoke, String cuid, String tokenReason,
+            String ipAddress, String remoteUser) throws Exception {
         String method = "TPSTokendb.revokeCertsByCUID";
         if (cuid == null)
             throw new TPSException(method + ": cuid null");
         String auditMsg;
         IConfigStore configStore = CMS.getConfigStore();
-        ArrayList<TPSCertRecord> certRecords = tps.getTokendb().tdbGetCertificatesByCUID(cuid);
+        ArrayList<TPSCertRecord> certRecords = tps.getTokendb().tdbGetCertRecordsByCUID(cuid);
         if (tokenReason != null) {
             if (!tokenReason.equalsIgnoreCase("onHold") &&
                     !tokenReason.equalsIgnoreCase("destroyed") &&
-                    !tokenReason.equalsIgnoreCase("keyCompromise")) {
+                    !tokenReason.equalsIgnoreCase("keyCompromise") &&
+                    !tokenReason.equalsIgnoreCase("terminated")) {
                 auditMsg = "unknown tokenRecord lost reason:" + tokenReason;
                 CMS.debug(method + ":" + auditMsg);
                 throw new Exception(method + ":" + auditMsg);
@@ -355,13 +464,8 @@ public class TPSTokendb {
             if (isRevoke) {
                 auditMsg = "called to revoke";
                 CMS.debug(method + ":" + auditMsg);
-                boolean revokeCert = false;
+                boolean revokeCert = shouldRevoke(cert, cuid, tokenReason, ipAddress, remoteUser);
 
-                // get revoke or not
-                config = "op.enroll." + cert.getType() + ".keyGen." + cert.getKeyType() +
-                        ".recovery." + tokenReason + ".revokeCert";
-                //TODO: temporaryToken doesn't have all params; default to false if not found for now
-                revokeCert = configStore.getBoolean(config, false); // default to false
                 if (!revokeCert) {
                     auditMsg = "cert not to be revoked:" + cert.getSerialNumber();
                     CMS.debug(method + ":" + auditMsg);
@@ -407,20 +511,29 @@ public class TPSTokendb {
             // update certificate status
             if (isRevoke) {
                 if (revokeReason == RevocationReason.CERTIFICATE_HOLD) {
-                    cert.setStatus("revoked_on_hold");
+                    updateCertsStatus(cert.getSerialNumber(), cert.getIssuedBy(), "revoked_on_hold");
                 } else {
-                    cert.setStatus("revoked");
+                    updateCertsStatus(cert.getSerialNumber(), cert.getIssuedBy(), "revoked");
                 }
             } else {
-                cert.setStatus("active");
+                updateCertsStatus(cert.getSerialNumber(), cert.getIssuedBy(), "active");
             }
-            tps.certDatabase.updateRecord(cert.getId(), cert);
+
             auditMsg = "cert (un)revoked:" + cert.getSerialNumber();
             CMS.debug(method + ":" + auditMsg);
             //TODO: tdbActivity
         }
     }
 
+    public void updateCertsStatus(String serial, String issuer, String status) throws Exception {
+        ArrayList<TPSCertRecord> certRecords = tps.getTokendb().tdbGetCertRecordsByCert(serial, issuer);
+
+        for (TPSCertRecord certRecord : certRecords) {
+            certRecord.setStatus(status);
+            tps.certDatabase.updateRecord(certRecord.getId(), certRecord);
+        }
+    }
+
     public void tdbAddCertEntry(TPSCertRecord certRecord, String status)
             throws Exception {
         certRecord.setStatus(status);
diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
index 482236bc322ea92644dbc38ba67428fd4a7c47fd..c74fe2cb6800d2f9ba267e7db35ec92ee942500c 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
@@ -1004,7 +1004,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
          * Get certs from the tokendb for this token to find out about
          * renewal possibility
          */
-        ArrayList<TPSCertRecord> allCerts = tps.tdb.tdbGetCertificatesByCUID(tokenRecord.getId());
+        ArrayList<TPSCertRecord> allCerts = tps.tdb.tdbGetCertRecordsByCUID(tokenRecord.getId());
 
         certsInfo.setNumCertsToEnroll(keyTypeNum);
 
@@ -1366,7 +1366,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
                     actualCertIndex++;
                 }
 
-                ArrayList<TPSCertRecord> certs = tps.tdb.tdbGetCertificatesByCUID(toBeRecovered.getId());
+                ArrayList<TPSCertRecord> certs = tps.tdb.tdbGetCertRecordsByCUID(toBeRecovered.getId());
 
                 String serialToRecover = null;
                 TPSCertRecord certToRecover = null;
@@ -1441,10 +1441,11 @@ public class TPSEnrollProcessor extends TPSProcessor {
                         }
                     }
 
-                    // set cert status to active
-                    certToRecover.setStatus("active");
                     try {
-                        tps.tdb.tdbUpdateCertEntry(certToRecover);
+                        // set cert status to active
+                        tps.tdb.updateCertsStatus(certToRecover.getSerialNumber(),
+                                                  certToRecover.getIssuedBy(),
+                                                  "active");
                     } catch (Exception e) {
                         auditMsg = "failed tdbUpdateCertEntry";
                         CMS.debug(method + ":" + auditMsg);
diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
index 1a87f3e3cbabfc8b55ce73894771c85f9b801a83..8958939243b59ded6c78287eda0cac18721e3028 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
@@ -1195,7 +1195,7 @@ public class TPSProcessor {
             throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
         }
         //find all certs belonging to the token
-        ArrayList<TPSCertRecord> certRecords = tps.tdb.tdbGetCertificatesByCUID(cuid);
+        ArrayList<TPSCertRecord> certRecords = tps.tdb.tdbGetCertRecordsByCUID(cuid);
 
         CMS.debug(method + ": found " + certRecords.size() + " certs");
 
diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java b/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java
index fc04759beab0c1c8ccb91e0fb47506096fd4c593..b67922e4802c5dddde018c15340d588d2717005e 100644
--- a/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java
+++ b/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java
@@ -121,7 +121,8 @@ public class TokenService extends PKIService implements TokenResource {
         return TokenStatus.PERM_LOST;
     }
 
-    public void setTokenStatus(TokenRecord tokenRecord, TokenStatus tokenState) throws Exception {
+    public void setTokenStatus(TokenRecord tokenRecord, TokenStatus tokenState, String ipAddress, String remoteUser)
+            throws Exception {
         TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
 
         switch (tokenState) {
@@ -135,7 +136,7 @@ public class TokenService extends PKIService implements TokenResource {
             if (origStatus.equalsIgnoreCase("lost") &&
                     origReason.equalsIgnoreCase("onHold")) {
                 //unrevoke certs
-                tps.tdb.unRevokeCertsByCUID(tokenRecord.getId());
+                tps.tdb.unRevokeCertsByCUID(tokenRecord.getId(), ipAddress, remoteUser);
             }
 
             tokenRecord.setStatus("active");
@@ -147,14 +148,14 @@ public class TokenService extends PKIService implements TokenResource {
             tokenRecord.setReason("keyCompromise");
 
             //revoke certs
-            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "keyCompromise");
+            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "keyCompromise", ipAddress, remoteUser);
             break;
         case DAMAGED:
             tokenRecord.setStatus("lost");
             tokenRecord.setReason("destroyed");
 
             //revoke certs
-            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "destroyed");
+            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "destroyed", ipAddress, remoteUser);
 
             break;
         case TEMP_LOST:
@@ -162,10 +163,10 @@ public class TokenService extends PKIService implements TokenResource {
             tokenRecord.setReason("onHold");
 
             // put certs onHold
-            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "onHold");
+            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), "onHold", ipAddress, remoteUser);
             break;
         case TERMINATED:
-            String reason = "keyCompromise";
+            String reason = "terminated";
             String origStatus2 = tokenRecord.getStatus();
             String origReason2 = tokenRecord.getReason();
             // temp token looks at "onHold"
@@ -177,7 +178,7 @@ public class TokenService extends PKIService implements TokenResource {
             tokenRecord.setReason(reason);
 
             //revoke certs
-            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), reason) ;
+            tps.tdb.revokeCertsByCUID(tokenRecord.getId(), reason, ipAddress, remoteUser) ;
             break;
         default:
             throw new PKIException("Unsupported token state: " + tokenState);
@@ -213,13 +214,13 @@ public class TokenService extends PKIService implements TokenResource {
         return tokenData;
     }
 
-    public TokenRecord createTokenRecord(TokenData tokenData) throws Exception {
+    public TokenRecord createTokenRecord(TokenData tokenData, String ipAddress, String remoteUser) throws Exception {
 
         TokenRecord tokenRecord = new TokenRecord();
         tokenRecord.setId(tokenData.getID());
         tokenRecord.setUserID(tokenData.getUserID());
         tokenRecord.setType(tokenData.getType());
-        setTokenStatus(tokenRecord, tokenData.getStatus());
+        setTokenStatus(tokenRecord, tokenData.getStatus(), ipAddress, remoteUser);
         tokenRecord.setAppletID(tokenData.getAppletID());
         tokenRecord.setKeyInfo(tokenData.getKeyInfo());
         tokenRecord.setPolicy(tokenData.getPolicy());
@@ -320,7 +321,7 @@ public class TokenService extends PKIService implements TokenResource {
             // new tokens are uninitialized when created
             tokenData.setStatus(TokenStatus.UNINITIALIZED);
 
-            tokenRecord = createTokenRecord(tokenData);
+            tokenRecord = createTokenRecord(tokenData, ipAddress, remoteUser);
             tokenRecord.setId(tokenID);
             database.addRecord(tokenID, tokenRecord);
             subsystem.tdb.tdbActivity(ActivityDatabase.OP_ADD, tokenRecord,
@@ -506,7 +507,7 @@ public class TokenService extends PKIService implements TokenResource {
             }
 
             CMS.debug("TokenService.changeTokenStatus(): next status allowed: " + tokenStatus);
-            setTokenStatus(tokenRecord, tokenStatus);
+            setTokenStatus(tokenRecord, tokenStatus, ipAddress, remoteUser);
             database.updateRecord(tokenID, tokenRecord);
             subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
                 ipAddress, msg, "success",
-- 
1.9.3

