From 582819dc3156a2543935e1a2dec49f3d6cf1e25b Mon Sep 17 00:00:00 2001
From: Jack Magne <jmagne@dhcp-16-206.sjc.redhat.com>
Date: Wed, 5 Oct 2016 18:16:35 -0700
Subject: [PATCH] Fix for: Add ability to disallow TPS to enroll a single user
 on multiple tokens. #1664

This bug was previously not completely fixed where we left a loophole to allow a user to
end up with 2 active tokens. This fix closes that loophole.

Also:

Fix for: Unable to read an encrypted email using renewed tokens. #2483

This fix provides for a new optional renewal based token policy, that
allows the user to retain or recover old encryption certs for that profile,
that get overwritten by the renewal process.

An example is:

RENEW=YES;RENEW_KEEP_OLD_ENC_CERTS=YES

The second part of the policy is new.

When this is set to "YES", the system will make sure the old enc cert
will remain on the token. If it's missing or "NO", no such attempt will be made.
---
 base/tps/shared/conf/CS.cfg                        |  2 +-
 .../org/dogtagpki/server/tps/TPSTokenPolicy.java   | 10 ++-
 .../org/dogtagpki/server/tps/main/PKCS11Obj.java   |  3 +-
 .../server/tps/processor/EnrolledCertsInfo.java    |  4 +
 .../server/tps/processor/TPSEnrollProcessor.java   | 94 +++++++++++++++++++++-
 5 files changed, 109 insertions(+), 4 deletions(-)

diff --git a/base/tps/shared/conf/CS.cfg b/base/tps/shared/conf/CS.cfg
index a8499a2..cdc45d05 100644
--- a/base/tps/shared/conf/CS.cfg
+++ b/base/tps/shared/conf/CS.cfg
@@ -2130,7 +2130,7 @@ tokendb.bindPassPath=[PKI_INSTANCE_PATH]/conf/password.conf
 tokendb.certBaseDN=ou=Certificates,[TOKENDB_ROOT]
 tokendb.confirmConfigChangesTemplate=confirmConfigChanges.template
 tokendb.confirmDeleteConfigTemplate=confirmDeleteConfig.template
-tokendb.defaultPolicy=RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO
+tokendb.defaultPolicy=RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO;RENEW_KEEP_OLD_ENC_CERTS=NO
 tokendb.deleteResultTemplate=deleteResults.template
 tokendb.deleteTemplate=delete.template
 tokendb.doTokenConfirmTemplate=doTokenConfirm.template
diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java b/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java
index 1a866f7..158815b 100644
--- a/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java
+++ b/base/tps/src/org/dogtagpki/server/tps/TPSTokenPolicy.java
@@ -33,9 +33,10 @@ import com.netscape.certsrv.base.IConfigStore;
 public class TPSTokenPolicy {
     private TPSSubsystem tps;
     private static final String DEFAULT_POLICY_SET_STRING =
-            "RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO";
+            "RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO;RENEW_KEEP_OLD_ENC_CERTS=NO";
     private boolean re_enroll = true;
     private boolean renew = false;
+    private boolean renew_keep_old_enc_certs = false;
     private boolean force_format = false;
     private boolean pin_reset = true;
     private boolean reset_pin_reset_to_no = false;
@@ -85,6 +86,8 @@ public class TPSTokenPolicy {
                 pin_reset = getBool(policy[1], false);
             else if (policy[0].equalsIgnoreCase("RESET_PIN_RESET_TO_NO"))
                 reset_pin_reset_to_no = getBool(policy[1], false);
+            else if (policy[0].equalsIgnoreCase("RENEW_KEEP_OLD_ENC_CERTS"))
+                renew_keep_old_enc_certs = getBool(policy[1],false);
             //else no change, just take the default;
         }
     }
@@ -150,6 +153,11 @@ public class TPSTokenPolicy {
         return re_enroll;
     }
 
+    public boolean isAllowdRenewSaveOldEncCerts(String cuid) {
+        getUpdatedPolicy(cuid);
+        return renew_keep_old_enc_certs;
+    }
+
     public boolean isAllowdTokenRenew(String cuid) {
         getUpdatedPolicy(cuid);
 
diff --git a/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java b/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java
index 2fc62d7..6af39a7 100644
--- a/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java
+++ b/base/tps/src/org/dogtagpki/server/tps/main/PKCS11Obj.java
@@ -655,6 +655,7 @@ public class PKCS11Obj {
             int index = os.getObjectIndex();
 
             if (type == 'C') { //found a certificate
+                CMS.debug("PKS11Obj: getNextFreeCertIdNumber: found cert index "+ index);
                 if (index >= 0 && index < 100) {
                     certTable[index] = 1;
                 }
@@ -669,7 +670,7 @@ public class PKCS11Obj {
             }
         }
 
-        CMS.debug("TPSEnrollProcessor.getNextFreeCertIdNumber: returning free cert id: " + free_cert_id );
+        CMS.debug("PKCS11Obj.getNextFreeCertIdNumber: returning free cert id: " + free_cert_id );
 
         return free_cert_id;
     }
diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java b/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java
index aac1a23..ae9919d 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java
@@ -152,6 +152,10 @@ public class EnrolledCertsInfo {
         certificates.add(x509Cert);
     }
 
+    public void removeCertificate(X509CertImpl x509Cert) {
+        certificates.remove(x509Cert);
+    }
+
     public void setStartProgress(int startP) {
         startProgress = startP;
 
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 9d42546..30bbd59 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Random;
@@ -334,7 +335,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
 
         boolean allowMultiTokens = checkAllowMultiActiveTokensUser(isExternalReg);
 
-        if (isTokenPresent == false && allowMultiTokens == false) {
+        if (allowMultiTokens == false) {
             boolean alreadyHasActiveToken = checkUserAlreadyHasActiveToken(userid);
 
             if (alreadyHasActiveToken == true) {
@@ -1050,8 +1051,10 @@ public class TPSEnrollProcessor extends TPSProcessor {
                         CMS.debug(method + ": There are multiple token entries for user "
                                 + userid);
 
+                        if( checkUserAlreadyHasActiveToken(userid) == false) {
                             isRecover = true;
                             continue; // TODO: or break?
+                        }
                     }
 
                 } else if (tokenRecord.getTokenStatus() == TokenStatus.ACTIVE) {
@@ -1392,10 +1395,14 @@ public class TPSEnrollProcessor extends TPSProcessor {
          */
         Collection<TPSCertRecord> allCerts = tps.tdb.tdbGetCertRecordsByCUID(tokenRecord.getId());
 
+        Collection<TPSCertRecord> oldEncCertsToRecover = new ArrayList<TPSCertRecord>();
+
         certsInfo.setNumCertsToEnroll(keyTypeNum);
 
         CMS.debug(method + ": Number of certs to renew: " + keyTypeNum);
 
+        int numActuallyRenewed = 0;
+
         for (int i = 0; i < keyTypeNum; i++) {
             /*
              * e.g. op.enroll.userKey.renewal.keyType.value.0=signing
@@ -1496,6 +1503,23 @@ public class TPSEnrollProcessor extends TPSProcessor {
                         generateCertificate(certsInfo, channel, aInfo, keyType, TPSEngine.ENROLL_MODES.MODE_RENEWAL,
                                 -1, cEnrollInfo);
 
+                        numActuallyRenewed ++;
+
+
+
+                        if(keyType.equals(TPSEngine.CFG_ENCRYPTION)) {
+                            CMS.debug(method + ": found old encryption cert (just renewed) to attempt to recover back to token, in order to read old emails.");
+                            CMS.debug(method + " adding cert: " + cert);
+                            oldEncCertsToRecover.add(cert);
+
+                        }
+
+                        if(numActuallyRenewed == keyTypeNum) {
+                            CMS.debug(method + " We have already renewed the proper number of certs, bailing from loop.");
+                            status = TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED;
+                            break;
+                        }
+
                         //renewCertificate(cert, certsInfo, channel, aInfo, keyType);
                         status = TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED;
                     } catch (TPSException e) {
@@ -1513,6 +1537,74 @@ public class TPSEnrollProcessor extends TPSProcessor {
             CMS.debug(method + ":" + logMsg);
             throw new TPSException(logMsg + TPSStatus.STATUS_ERROR_RENEWAL_FAILED);
         }
+
+        //Handle recovery of old encryption certs
+
+        //See if policy calls for this feature
+
+        TPSTokenPolicy tokenPolicy = new TPSTokenPolicy(tps);
+
+        boolean recoverOldEncCerts = tokenPolicy.isAllowdRenewSaveOldEncCerts(tokenRecord.getId());
+        CMS.debug(method + " Recover Old Encryption Certs for Renewed Certs: " + recoverOldEncCerts);
+        if (oldEncCertsToRecover.size() > 0 && recoverOldEncCerts == true) {
+            CMS.debug("About to attempt to recover old encryption certs just renewed.");
+
+            Iterator<TPSCertRecord> iterator = oldEncCertsToRecover.iterator();
+
+            // while loop
+            while (iterator.hasNext()) {
+                TPSCertRecord toBeRecovered = iterator.next();
+                String serialToRecover = toBeRecovered.getSerialNumber();
+
+                try {
+
+                    CARetrieveCertResponse certResponse = tps.getEngine().recoverCertificate(toBeRecovered,
+                            serialToRecover, TPSEngine.CFG_ENCRYPTION, getCAConnectorID());
+
+                    String b64cert = certResponse.getCertB64();
+                    CMS.debug("TPSEnrollProcessor.processRecovery: cert blob recovered");
+
+                    KRARecoverKeyResponse keyResponse = tps.getEngine().recoverKey(toBeRecovered.getId(),
+                            toBeRecovered.getUserID(),
+                            channel.getDRMWrappedDesKey(), b64cert, getDRMConnectorID());
+
+
+                    //Try to write recovered cert to token
+
+                    CertEnrollInfo cEnrollInfo = new CertEnrollInfo();
+
+                    cEnrollInfo.setTokenToBeRecovered(tokenRecord);
+                    cEnrollInfo.setRecoveredCertData(certResponse);
+                    cEnrollInfo.setRecoveredKeyData(keyResponse);
+
+
+                    PKCS11Obj pkcs11obj = certsInfo.getPKCS11Obj();
+                    int newCertId = pkcs11obj.getNextFreeCertIdNumber();
+
+                    CMS.debug(method + " newCertId = " + newCertId);
+
+                    CMS.debug(method + "before calling generateCertificate, certsInfo.getCurrentCertIndex() ="
+                            + newCertId);
+                    generateCertificate(certsInfo, channel, aInfo,
+                            "encryption",
+                            TPSEngine.ENROLL_MODES.MODE_RECOVERY,
+                            newCertId, cEnrollInfo);
+
+                    //We don't want this quasi old encryption cert in the official list.
+                    // This cert is on the token ONLY to decrypt old emails after the real cert
+                    // has been renewed. We want to keep the official cert list to contain only the
+                    // legit certs, in order to not confuse other processes such as recovery.
+                    CMS.debug(method + " About to remove old encryption cert recovered from official token db list: ");
+                    certsInfo.removeCertificate(certResponse.getCert());
+
+
+                } catch (TPSException e) {
+                    CMS.debug(method + "Failure to recoverd old encryption certs during renewal operation.");
+
+                }
+            }
+        }
+
         return status;
     }
 
-- 
2.5.0

