From dcf1135e8d55ba8bcfc6df07883aa3704b20a81f Mon Sep 17 00:00:00 2001 From: Christina Fu Date: Thu, 1 Jul 2021 14:58:31 -0700 Subject: [PATCH 1/5] Bug1958277 PKCS10Client Attribute Encoding PKCS10Client has an option "-k" which allows for individual DN attributes to be encoded differently and separately. For example: PKCS10Client -p -d . -k true -o req.txt -n 'cn=UTF8String:aa,ou=BMPString:bb,o=cc' This option might have been accidentally disabled. In this patch, the attribute encoding code is moved to CryptoUtil.java with some refactoring, and calls to getJssName() is re-enabled for subjectName in PKCS10Client; fixes https://bugzilla.redhat.com/show_bug.cgi?id=1958277 (cherry picked from commit 22008c96bf943e575c254cbd0e8414a478481263) --- .../src/com/netscape/cmstools/PKCS10Client.java | 151 +--------------- .../com/netscape/cmsutil/crypto/CryptoUtil.java | 196 ++++++++++++++++++++- 2 files changed, 196 insertions(+), 151 deletions(-) diff --git a/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java b/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java index 137049e..4c002c2 100644 --- a/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java +++ b/base/java-tools/src/com/netscape/cmstools/PKCS10Client.java @@ -24,18 +24,11 @@ import java.io.PrintStream; import java.security.KeyPair; import org.mozilla.jss.CryptoManager; -import org.mozilla.jss.asn1.BMPString; import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; -import org.mozilla.jss.asn1.PrintableString; -import org.mozilla.jss.asn1.TeletexString; -import org.mozilla.jss.asn1.UTF8String; -import org.mozilla.jss.asn1.UniversalString; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.KeyPairAlgorithm; import org.mozilla.jss.crypto.KeyPairGenerator; import org.mozilla.jss.crypto.PrivateKey; -import org.mozilla.jss.pkix.primitive.AVA; -import org.mozilla.jss.pkix.primitive.Name; import org.mozilla.jss.util.Password; import com.netscape.cmsutil.crypto.CryptoUtil; @@ -138,6 +131,7 @@ public class PKCS10Client { enable_encoding = true; else enable_encoding = false; + System.out.println("PKCS10Client: enable_encoding =" + enable_encoding); } else if (name.equals("-s")) { String ec_sensitive_s = args[i+1]; ec_sensitive = Integer.parseInt(ec_sensitive_s); @@ -289,7 +283,7 @@ public class PKCS10Client { PKCS10 certReq = CryptoUtil.createCertificationRequest( - subjectName, pair, extns); + subjectName, enable_encoding, pair, extns); if (certReq == null) { System.out.println("PKCS10Client: cert request null"); @@ -333,145 +327,4 @@ public class PKCS10Client { } } - static boolean isEncoded (String elementValue) { - boolean encoded = false; - - if (elementValue != null && ((elementValue.startsWith("UTF8String:")) || - (elementValue.startsWith("PrintableString:")) || - (elementValue.startsWith("BMPString:")) || - (elementValue.startsWith("TeletexString:")) || - (elementValue.startsWith("UniversalString:")))) { - encoded = true; - } - return encoded; - } - - static Name addNameElement (Name name, OBJECT_IDENTIFIER oid, int n, String elementValue) { - try { - String encodingType = (n > 0)? elementValue.substring(0, n): null; - String nameValue = (n > 0)? elementValue.substring(n+1): null; - if (encodingType != null && encodingType.length() > 0 && - nameValue != null && nameValue.length() > 0) { - if (encodingType.equals("UTF8String")) { - name.addElement( new AVA(oid, new UTF8String(nameValue))); - } else if (encodingType.equals("PrintableString")) { - name.addElement( new AVA(oid, new PrintableString(nameValue))); - } else if (encodingType.equals("BMPString")) { - name.addElement( new AVA(oid, new BMPString(nameValue))); - } else if (encodingType.equals("TeletexString")) { - name.addElement( new AVA(oid, new TeletexString(nameValue))); - } else if (encodingType.equals("UniversalString")) { - name.addElement( new AVA(oid, new UniversalString(nameValue))); - } - } - } catch (Exception e) { - System.out.println("PKCS10Client: Error adding name element: " + elementValue + " Error: " + e.toString()); - } - return name; - } - - static Name getJssName(boolean enable_encoding, String dn) { - - X500Name x5Name = null; - - try { - x5Name = new X500Name(dn); - } catch (IOException e) { - - System.out.println("PKCS10Client: Illegal Subject Name: " + dn + " Error: " + e.toString()); - System.out.println("PKCS10Client: Filling in default Subject Name......"); - return null; - } - - Name ret = new Name(); - netscape.security.x509.RDN[] names = null; - names = x5Name.getNames(); - int nameLen = x5Name.getNamesLength(); - - netscape.security.x509.RDN cur = null; - - for (int i = 0; i < nameLen; i++) { - cur = names[i]; - String rdnStr = cur.toString(); - String[] split = rdnStr.split("="); - - if (split.length != 2) - continue; - int n = split[1].indexOf(':'); - - try { - if (split[0].equals("UID")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement(ret, new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), - n, split[1]); - } else { - ret.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), - new PrintableString(split[1]))); - } - // System.out.println("UID found : " + split[1]); - } - - if (split[0].equals("C")) { - ret.addCountryName(split[1]); - // System.out.println("C found : " + split[1]); - continue; - } - - if (split[0].equals("CN")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.commonName, n, split[1]); - } else { - ret.addCommonName(split[1]); - } - // System.out.println("CN found : " + split[1]); - continue; - } - - if (split[0].equals("L")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.localityName, n, split[1]); - } else { - ret.addLocalityName(split[1]); - } - // System.out.println("L found : " + split[1]); - continue; - } - - if (split[0].equals("O")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.organizationName, n, split[1]); - } else { - ret.addOrganizationName(split[1]); - } - // System.out.println("O found : " + split[1]); - continue; - } - - if (split[0].equals("ST")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.stateOrProvinceName, n, split[1]); - } else { - ret.addStateOrProvinceName(split[1]); - } - // System.out.println("ST found : " + split[1]); - continue; - } - - if (split[0].equals("OU")) { - if (enable_encoding && isEncoded(split[1])) { - ret = addNameElement (ret, Name.organizationalUnitName, n, split[1]); - } else { - ret.addOrganizationalUnitName(split[1]); - } - // System.out.println("OU found : " + split[1]); - continue; - } - } catch (Exception e) { - System.out.println("PKCS10Client: Error constructing RDN: " + rdnStr + " Error: " + e.toString()); - continue; - } - } - - return ret; - } } diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java index 2fe4757..befceed 100644 --- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java +++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java @@ -65,6 +65,11 @@ import org.mozilla.jss.asn1.ANY; import org.mozilla.jss.asn1.ASN1Value; import org.mozilla.jss.asn1.BIT_STRING; import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.BMPString; +import org.mozilla.jss.asn1.PrintableString; +import org.mozilla.jss.asn1.TeletexString; +import org.mozilla.jss.asn1.UTF8String; +import org.mozilla.jss.asn1.UniversalString; import org.mozilla.jss.asn1.InvalidBERException; import org.mozilla.jss.asn1.NULL; import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; @@ -114,6 +119,7 @@ import org.mozilla.jss.pkix.crmf.EncryptedKey; import org.mozilla.jss.pkix.crmf.EncryptedValue; import org.mozilla.jss.pkix.crmf.PKIArchiveOptions; import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.AVA; import org.mozilla.jss.pkix.primitive.Name; import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; import org.mozilla.jss.ssl.SSLSocket; @@ -1691,6 +1697,14 @@ public class CryptoUtil { throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, IOException, CertificateException, SignatureException { + return createCertificationRequest(subjectName, false, keyPair, exts); + } + // encodeSubj works with PKCS10Client "-k" option + public static PKCS10 createCertificationRequest(String subjectName, + boolean encodeSubj, KeyPair keyPair, Extensions exts) + throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, IOException, CertificateException, + SignatureException { String method = "CryptoUtil: createCertificationRequest: "; String alg = "SHA256withRSA"; @@ -1705,7 +1719,7 @@ public class CryptoUtil { } return createCertificationRequest( - subjectName, key, (org.mozilla.jss.crypto.PrivateKey) keyPair.getPrivate(), + subjectName, encodeSubj, key, (org.mozilla.jss.crypto.PrivateKey) keyPair.getPrivate(), alg, exts); } @@ -1714,6 +1728,14 @@ public class CryptoUtil { throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, IOException, CertificateException, SignatureException { + return createCertificationRequest(subjectName, false, pubk, prik, alg, null); + } + public static PKCS10 createCertificationRequest(String subjectName, + boolean encodeSubj, + X509Key pubk, PrivateKey prik, String alg, Extensions exts) + throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, IOException, CertificateException, + SignatureException { X509Key key = pubk; java.security.Signature sig = java.security.Signature.getInstance(alg, "Mozilla-JSS"); @@ -1734,13 +1756,182 @@ public class CryptoUtil { } else { pkcs10 = new PKCS10(key); } - X500Name name = new X500Name(subjectName); + + Name n = getJssName(encodeSubj, subjectName); + ByteArrayOutputStream subjectEncStream = new ByteArrayOutputStream(); + n.encode(subjectEncStream); + byte[] b = subjectEncStream.toByteArray(); + X500Name name = new X500Name(b); X500Signer signer = new X500Signer(sig, name); pkcs10.encodeAndSign(signer); return pkcs10; } + static boolean isEncoded (String elementValue) { + boolean encoded = false; + + //System.out.println("CryptoUtil: isEncoded: elementValue =" + + // elementValue); + if (elementValue != null && ((elementValue.startsWith("UTF8String:")) || + (elementValue.startsWith("PrintableString:")) || + (elementValue.startsWith("BMPString:")) || + (elementValue.startsWith("TeletexString:")) || + (elementValue.startsWith("UniversalString:")))) { + encoded = true; + } + return encoded; + } + + static Name addNameElement (Name name, OBJECT_IDENTIFIER oid, int n, String elementValue) { + // System.out.println("CryptoUtil: addNameElement: elementValue =" + + // elementValue); + try { + String encodingType = (n > 0)? elementValue.substring(0, n): null; + // System.out.println("CryptoUtil: addNameElement: encodingType =" + + // encodingType); + String nameValue = (n > 0)? elementValue.substring(n+1): null; + // System.out.println("CryptoUtil: addNameElement: nameValue =" + + // nameValue); + if (encodingType != null && encodingType.length() > 0 && + nameValue != null && nameValue.length() > 0) { + if (encodingType.equals("UTF8String")) { + // System.out.println("CryptoUtil: addNameElement: UTF8String"); + name.addElement( new AVA(oid, new UTF8String(nameValue))); + } else if (encodingType.equals("PrintableString")) { + // System.out.println("CryptoUtil: addNameElement: PrintableString"); + name.addElement( new AVA(oid, new PrintableString(nameValue))); + } else if (encodingType.equals("BMPString")) { + // System.out.println("CryptoUtil: addNameElement: BMPString"); + name.addElement( new AVA(oid, new BMPString(nameValue))); + } else if (encodingType.equals("TeletexString")) { + // System.out.println("CryptoUtil: addNameElement: TeletexString"); + name.addElement( new AVA(oid, new TeletexString(nameValue))); + } else if (encodingType.equals("UniversalString")) { + // System.out.println("CryptoUtil: addNameElement: UniversalString"); + name.addElement( new AVA(oid, new UniversalString(nameValue))); + } + } + } catch (Exception e) { + System.out.println("CryptoUtil: Error adding name element: " + elementValue + " Error: " + e.toString()); + } + return name; + } + + static Name getJssName(boolean enable_encoding, String dn) { + + X500Name x5Name = null; + + //System.out.println("CryptoUtil: getJssName: dn= " + dn); + try { + x5Name = new X500Name(dn); + } catch (IOException e) { + + System.out.println("CryptoUtil: Illegal Subject Name: " + dn + " Error: " + e.toString()); + System.out.println("CryptoUtil: Filling in default Subject Name......"); + return null; + } + + Name ret = new Name(); + netscape.security.x509.RDN[] names = x5Name.getNames(); + int nameLen = x5Name.getNamesLength(); + + netscape.security.x509.RDN cur = null; + + for (int i = 0; i < nameLen; i++) { + cur = names[i]; + String rdnStr = cur.toString(); + String[] split = rdnStr.split("="); + + if (split.length != 2) + continue; + // System.out.println(" getJssName: split[0] =" + split[0]); + // System.out.println(" getJssName: split[1] =" + split[1]); + int n = split[1].indexOf(':'); + + try { + if (split[0].equals("UID")) { + if (enable_encoding && isEncoded(split[1])) { + // System.out.println(" getJssName: encoded UID"); + ret = addNameElement(ret, new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), + n, split[1]); + } else { + // System.out.println(" getJssName: not encoded UID"); + ret.addElement(new AVA(new OBJECT_IDENTIFIER("0.9.2342.19200300.100.1.1"), + new PrintableString(split[1]))); + } + // System.out.println("UID found : " + split[1]); + } + + if (split[0].equals("C")) { + ret.addCountryName(split[1]); + // System.out.println("C found : " + split[1]); + continue; + } + + if (split[0].equals("CN")) { + if (enable_encoding && isEncoded(split[1])) { + // System.out.println(" getJssName: encoded CN"); + ret = addNameElement (ret, Name.commonName, n, split[1]); + } else { + // System.out.println(" getJssName: not encoded CN"); + ret.addCommonName(split[1]); + } + // System.out.println("CN found : " + split[1]); + continue; + } + + if (split[0].equals("L")) { + if (enable_encoding && isEncoded(split[1])) { + ret = addNameElement (ret, Name.localityName, n, split[1]); + } else { + ret.addLocalityName(split[1]); + } + // System.out.println("L found : " + split[1]); + continue; + } + + if (split[0].equals("O")) { + if (enable_encoding && isEncoded(split[1])) { + // System.out.println(" getJssName: encoded O"); + ret = addNameElement (ret, Name.organizationName, n, split[1]); + } else { + // System.out.println(" getJssName: not encoded O"); + ret.addOrganizationName(split[1]); + } + // System.out.println("O found : " + split[1]); + continue; + } + + if (split[0].equals("ST")) { + if (enable_encoding && isEncoded(split[1])) { + ret = addNameElement (ret, Name.stateOrProvinceName, n, split[1]); + } else { + ret.addStateOrProvinceName(split[1]); + } + // System.out.println("ST found : " + split[1]); + continue; + } + + if (split[0].equals("OU")) { + if (enable_encoding && isEncoded(split[1])) { + // System.out.println(" getJssName: encoded OU"); + ret = addNameElement (ret, Name.organizationalUnitName, n, split[1]); + } else { + // System.out.println(" getJssName: not encoded OU"); + ret.addOrganizationalUnitName(split[1]); + } + // System.out.println("OU found : " + split[1]); + continue; + } + } catch (Exception e) { + System.out.println("CryptoUtil: Error constructing RDN: " + rdnStr + " Error: " + e.toString()); + continue; + } + } + + return ret; + } public static KeyIdentifier createKeyIdentifier(KeyPair keypair) throws NoSuchAlgorithmException, InvalidKeyException { String method = "CryptoUtil: createKeyIdentifier: "; @@ -1848,6 +2039,7 @@ public class CryptoUtil { PKCS10 pkcs10 = new PKCS10(key); X500Name name = new X500Name(subjectName); + X500Signer signer = new X500Signer(sig, name); pkcs10.encodeAndSign(signer); -- 1.8.3.1 From b974f1d9daf393efc19308bac42b955c601090b7 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Thu, 15 Jul 2021 13:24:26 -0500 Subject: [PATCH 2/5] Add GitLab synchronization job The .gitlab-ci.yml has been added to define a job to synchronize a branch from an upstream repository to a GitLab repository. (cherry picked from commit 27912b9e4311d4f12499f9f1b59e0b4bf4c5bac5) --- .gitlab-ci.yml | 22 +++++++++++ docs/development/Synchronizing-GitLab-Branch.adoc | 48 +++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 .gitlab-ci.yml create mode 100644 docs/development/Synchronizing-GitLab-Branch.adoc diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..249e240 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,22 @@ +image: fedora + +sync: + + script: + - echo "Synchronizing $CI_COMMIT_BRANCH branch from $UPSTREAM_URL to $CI_PROJECT_URL" + - dnf install -y git + - git remote set-url origin https://sync:$ACCESS_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git + - git remote remove upstream || true + - git remote add upstream $UPSTREAM_URL + - git remote -v + - git fetch upstream $CI_COMMIT_BRANCH + - git checkout upstream/$CI_COMMIT_BRANCH + - git log origin/$CI_COMMIT_BRANCH..upstream/$CI_COMMIT_BRANCH --oneline + - GIT_SSL_NO_VERIFY=true git push origin HEAD:$CI_COMMIT_BRANCH + + rules: + - if: $SYNC == "true" + + tags: + # Use shared runners. + - shared diff --git a/docs/development/Synchronizing-GitLab-Branch.adoc b/docs/development/Synchronizing-GitLab-Branch.adoc new file mode 100644 index 0000000..b0937f2 --- /dev/null +++ b/docs/development/Synchronizing-GitLab-Branch.adoc @@ -0,0 +1,48 @@ += Synchronizing GitLab Branch = + +== Overview == + +This page describes the procedure to synchronize a branch from an upstream repository +to a GitLab repository. + +== Creating Access Token == + +In the GitLab repository create a project access token with a **write_repository** permission. + +See link:https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#creating-a-project-access-token[Creating a project access token]. + +== Configuring Synchronization == + +In the GitLab repository create the following variables: + +* `UPSTREAM_URL`: The URL of the upstream repository. +** Unselect **Protect variable** to synchronize unprotected branches. +* `ACCESS_TOKEN`: The value of the access token. +** Unselect **Protect variable** to synchronize unprotected branches. +** Select **Mask variable** to keep the access token hidden. + +See link:https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project[Add a CI/CD variable to a project]. + +== Running Synchronization Manually == + +In the GitLab repository run a pipeline with the following parameters: + +* **Run for branch name or tag**: The branch to be synchronized. +* **Variables**: +** `SYNC`: `true` + +See link:https://docs.gitlab.com/ee/ci/pipelines/#run-a-pipeline-manually[Run a pipeline manually]. + +== Scheduling Automatic Synchronization == + +In the GitLab repository create a schedule with the following parameters: + +* **Interval Pattern**: The frequency of synchronization. +** To synchronize every hour, enter: `0 * * * *` +* **Target Branch**: The branch to be synchronized. +* **Variables**: +** `SYNC`: `true` + +Additional schedules can be created for synchronizing other branches. + +See link:https://docs.gitlab.com/ee/ci/pipelines/schedules.html#configuring-pipeline-schedules[Configuring pipeline schedules]. -- 1.8.3.1 From 32fcaab4585e893742018855c142d01716430cdb Mon Sep 17 00:00:00 2001 From: Christina Fu Date: Wed, 28 Jul 2021 16:21:27 -0700 Subject: [PATCH 3/5] Bug1959937 - TPS Allowing Token Transactions while the CA is Down This patch propagates the exception thrown when revocation/unrevocation fails so that the token record is not updated on TPS; This allows the TPS token to be consistent with the certs on the CA. fixes https://bugzilla.redhat.com/show_bug.cgi?id=1959937 (cherry picked from commit 2f7ed836ab20988386e651c1000f4e12eff6c0af) --- base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java index b58c24f..147f346 100644 --- a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java +++ b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java @@ -616,7 +616,7 @@ public class TPSTokendb { } private void revokeCert(TokenRecord tokenRecord, TPSCertRecord cert, String tokenReason, - String ipAddress, String remoteUser) { + String ipAddress, String remoteUser) throws Exception { String method = "TPSTokendb.revokeCert"; String logMsg; @@ -678,12 +678,15 @@ public class TPSTokendb { tdbActivity(ActivityDatabase.OP_CERT_REVOCATION, tokenRecord, ipAddress, e.getMessage(), "failure", remoteUser); - // continue revoking the next certificate + // bail out if revocation failed; This will allow the token + // status info to be consistent with that of the certs on the + // CA + throw e; } } private void unrevokeCert(TokenRecord tokenRecord, TPSCertRecord cert, String tokenReason, - String ipAddress, String remoteUser) { + String ipAddress, String remoteUser) throws Exception { String method = "TPSTokendb.unrevokeCert"; String logMsg; @@ -733,7 +736,10 @@ public class TPSTokendb { tdbActivity(ActivityDatabase.OP_CERT_RESTORATION, tokenRecord, ipAddress, e.getMessage(), "failure", remoteUser); - // continue unrevoking the next certificate + // bail out if revocation failed; This will allow the token + // status info to be consistent with that of the certs on the + // CA + throw e; } } -- 1.8.3.1 From d413394b2673e94e21dd645e588e934cc05c932b Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 30 May 2019 19:42:42 +1000 Subject: [PATCH 4/5] AuthorityService.getCert/Chain: avoid NPE if CA is not ready If a LWCA is not ready (i.e. key replication and signing unit initialisation has not completed), asking for its certificate (or chain) results in a NullPointerException. Update AuthorityService.getCert() and .getChain() to raise ResourceNotFoundException instead. Part of: https://pagure.io/dogtagpki/issue/3102 (cherry picked from commit a491bb99f273a3bd2f8c9540c8c18b2604adc035) --- .../src/org/dogtagpki/server/ca/rest/AuthorityService.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 36ddc6f..12388c9 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java @@ -140,8 +140,13 @@ public class AuthorityService extends SubsystemService implements AuthorityResou if (ca == null) throw new ResourceNotFoundException("CA \"" + aidString + "\" not found"); + org.mozilla.jss.crypto.X509Certificate cert = ca.getCaX509Cert(); + if (cert == null) + throw new ResourceNotFoundException( + "Certificate for CA \"" + aidString + "\" not available"); + try { - return Response.ok(ca.getCaX509Cert().getEncoded()).build(); + return Response.ok(cert.getEncoded()).build(); } catch (CertificateEncodingException e) { // this really is a 500 Internal Server Error throw new PKIException("Error encoding certificate: " + e); @@ -167,9 +172,14 @@ public class AuthorityService extends SubsystemService implements AuthorityResou if (ca == null) throw new ResourceNotFoundException("CA \"" + aidString + "\" not found"); + netscape.security.x509.CertificateChain chain = ca.getCACertChain(); + if (chain == null) + throw new ResourceNotFoundException( + "Certificate chain for CA \"" + aidString + "\" not available"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); try { - ca.getCACertChain().encode(out); + chain.encode(out); } catch (IOException e) { throw new PKIException("Error encoding certificate chain: " + e); } -- 1.8.3.1 From dae038b021e8623b920df8abf3abd5d48ab0636c Mon Sep 17 00:00:00 2001 From: Christina Fu Date: Wed, 14 Jul 2021 17:24:59 -0700 Subject: [PATCH 5/5] Bug1979710-TPS: separate config actions by profile permission list This patch addresses the issue that TPS agent operations on tokens, activities, and profiles are not limited by the types (profiles) permmtted to the agent (as described in the documentation). This is a regression from 8.x. The affected operations are: - findProfiles - getProfiles - updateProfile - changeStatus (of a profile) - retrieveTokens - getToken - modifyToken - changeTokenStatus - retrieveActivities - getActivity Note that some operations that seem like should be affected are not due to the fact that they are TPS admin operations and are shielded from entering the TPS service at the activity level. For example, deleting a token would be such a case. The authorization enforcement added in this patch should affect both access from the web UI as well as access from PKI CLI. Reference: https://github.com/dogtagpki/pki/wiki/PKI-TPS-CLI Another note: the VLV complicates the resulting page. If the returned entries on the page are all restricted then nothing would be shown. To add a bit more clarity, an entry is added to reflect such effect so that it would be less confusing to the role user. The entries are left with the epoch date. This would affect both WEB UI and PKI CLI. Also, a list minute addition to address an issue with 1911472 in CertService.java where the subject DN of the CA signing cert should be used instead of the issuer. fixes https://bugzilla.redhat.com/show_bug.cgi?id=1979710 (cherry picked from commit eea6184452505f1755b7e5b9d12b0fb765742fec) --- .../org/dogtagpki/server/ca/rest/CertService.java | 2 +- base/tps/shared/conf/CS.cfg | 2 +- .../dogtagpki/server/tps/rest/ActivityService.java | 188 ++++++++++++++-- .../dogtagpki/server/tps/rest/ProfileService.java | 125 ++++++++--- .../dogtagpki/server/tps/rest/TokenService.java | 249 ++++++++++++++++----- 5 files changed, 463 insertions(+), 103 deletions(-) diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CertService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CertService.java index 74d3a5d..f577992 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/CertService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/CertService.java @@ -193,7 +193,7 @@ public class CertService extends PKIService implements CertResource { processor.setAuthority(authority); - caX500DN = (X500Name) authority.getCACert().getIssuerDN(); + caX500DN = (X500Name) authority.getCACert().getSubjectDN(); } catch (EBaseException e) { throw new PKIException(e.getMessage()); diff --git a/base/tps/shared/conf/CS.cfg b/base/tps/shared/conf/CS.cfg index 4bd4bb7..2e5d499 100644 --- a/base/tps/shared/conf/CS.cfg +++ b/base/tps/shared/conf/CS.cfg @@ -2361,7 +2361,7 @@ target.Profile_Mappings.displayname=Token Profile Mapping Resolvers target.Profile_Mappings.list=enrollProfileMappingResolver,formatProfileMappingResolver,pinResetProfileMappingResolver target.Profile_Mappings.pattern=mappingResolver\.$name\.mapping\..* target.Profiles.displayname=Token Profile -target.Profiles.list=userKey,soKey,soCleanUserToken,soUserKey,cleanToken,soCleanSoToken,tokenKey +target.Profiles.list=userKey,soKey,soCleanUserToken,soUserKey,cleanToken,soCleanSoToken,tokenKey,externalRegISEtoken,externalRegAddToToken,delegateISEtoken,delegateIEtoken target.Profiles.pattern=op\..*\.$name\..* target.Subsystem_Connections.displayname=Subsystem Connection target.Subsystem_Connections.list= diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java index 37a3083..4f07be7 100644 --- a/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java +++ b/base/tps/src/org/dogtagpki/server/tps/rest/ActivityService.java @@ -21,15 +21,20 @@ package org.dogtagpki.server.tps.rest; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; +import java.util.Date; import java.util.Iterator; +import java.util.List; import javax.ws.rs.core.Response; import org.dogtagpki.server.tps.TPSSubsystem; import org.dogtagpki.server.tps.dbs.ActivityDatabase; import org.dogtagpki.server.tps.dbs.ActivityRecord; +import org.dogtagpki.server.tps.dbs.TokenDatabase; +import org.dogtagpki.server.tps.dbs.TokenRecord; import org.jboss.resteasy.plugins.providers.atom.Link; +import com.netscape.cms.realm.PKIPrincipal; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.BadRequestException; import com.netscape.certsrv.base.PKIException; @@ -38,6 +43,9 @@ import com.netscape.certsrv.logging.ActivityCollection; import com.netscape.certsrv.logging.ActivityData; import com.netscape.certsrv.logging.ActivityResource; import com.netscape.cms.servlet.base.PKIService; +import com.netscape.certsrv.user.UserResource; +import com.netscape.certsrv.usrgrp.IUGSubsystem; +import com.netscape.certsrv.usrgrp.IUser; /** * @author Endi S. Dewata @@ -74,6 +82,21 @@ public class ActivityService extends PKIService implements ActivityResource { return activityData; } + public ActivityData createRestrictedActivityData() { + + ActivityData activityData = new ActivityData(); + activityData.setID(""); + activityData.setTokenID(""); + activityData.setUserID(""); + activityData.setIP(""); + activityData.setOperation(""); + activityData.setResult(""); + activityData.setMessage(""); + activityData.setDate(new Date(0L)); + + return activityData; + } + public ActivityRecord createActivityRecord(ActivityData activityData) { ActivityRecord activityRecord = new ActivityRecord(); @@ -91,8 +114,8 @@ public class ActivityService extends PKIService implements ActivityResource { @Override public Response findActivities(String filter, Integer start, Integer size) { - - CMS.debug("ActivityService.findActivities()"); + String method = "ActivityService.findActivities: "; + CMS.debug(method); if (filter != null && filter.length() < MIN_FILTER_LENGTH) { throw new BadRequestException("Filter is too short."); @@ -136,24 +159,65 @@ public class ActivityService extends PKIService implements ActivityResource { Integer size, ActivityCollection response) throws Exception { + String method = "ActivityService.retrieveActivitiesWithVLV: "; + CMS.debug(method); // search with VLV sorted by date in reverse order IDBVirtualList list = database.findRecords( null, null, new String[] { "-date" }, size); + List authorizedProfiles = getAuthorizedProfiles(); + int total = list.getSize(); + CMS.debug(method + "total: " + total); + int retTotal = 0; // debugging only // return entries in the requested page - for (int i = start; i < start + size && i < total; i++) { - ActivityRecord record = list.getElementAt(i); - - if (record == null) { - CMS.debug("ActivityService: Activity record not found"); - throw new PKIException("Activity record not found"); + if (authorizedProfiles != null) { + if (authorizedProfiles.contains(UserResource.ALL_PROFILES)) { + for (int i = start; i < start + size && i < total; i++) { + ActivityRecord record = list.getElementAt(i); + + response.addEntry(createActivityData(record)); + retTotal++; + } + } else { // not authorized for all profiles + for (int i = start; i < start + size && i < total; i++) { + ActivityRecord record = list.getElementAt(i); + + //CMS.debug(method + "record.Id="+ record.getId()); + // On some rare occasions, some activities don't have + // their token type filled in. It is therefore necessary + // to get it from the token record directly. + String type = record.getType(); + //CMS.debug(method + "record.tokenType="+ type); + if ((type == null) || type.isEmpty()) { + CMS.debug(method + "record.tokenType null...getting from token record"); + String tokenID = record.getTokenID(); + if ((tokenID != null) && !tokenID.isEmpty()) { + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TokenDatabase t_database = subsystem.getTokenDatabase(); + TokenRecord t_record = t_database.getRecord(tokenID); + if (t_record != null) + type = t_record.getType(); + } + } + + //CMS.debug(method + "type="+ type); + if ((type == null) || type.isEmpty() || authorizedProfiles.contains(type)) { + //CMS.debug(method + "token type allowed"); + retTotal++; + response.addEntry(createActivityData(record)); + } else { + CMS.debug(method + "token type restricted; adding 'restricted' record"); + response.addEntry(createRestrictedActivityData()); + } + } //for } - - response.addEntry(createActivityData(record)); + } else { //authorizedProfiles null; no permission + CMS.debug(method + "authorized profiles is null"); } + CMS.debug(method + "retTotal = " + retTotal); response.setTotal(total); } @@ -164,44 +228,120 @@ public class ActivityService extends PKIService implements ActivityResource { Integer size, ActivityCollection response) throws Exception { + String method = "ActivityService.retrieveActivitiesWithoutVLV: "; // search without VLV - Iterator activities = database.findRecords(filter).iterator(); + List activities = (List) database.findRecords(filter); + int total = activities.size(); + CMS.debug(method + "total: " + total); - // TODO: sort results by date in reverse order + List authorizedProfiles = getAuthorizedProfiles(); + int retTotal = 0; // debugging only int i = 0; - // skip to the start of the page - for (; i < start && activities.hasNext(); i++) - activities.next(); - // return entries in the requested page - for (; i < start + size && activities.hasNext(); i++) { - ActivityRecord record = activities.next(); - response.addEntry(createActivityData(record)); + if (authorizedProfiles != null) { + if (authorizedProfiles.contains(UserResource.ALL_PROFILES)) { + for (i= start; i < start + size && i < total; i++) { + ActivityRecord record = activities.get(i); + + //CMS.debug(method + "record.tokenType="+ record.getType()); + response.addEntry(createActivityData(record)); + retTotal++; + } + } else { // not authorized for all profiles + for (i= start; i < start + size && i < total; i++) { + ActivityRecord record = activities.get(i); + //CMS.debug(method + "record.ID="+ record.getId()); + // On some rare occasions, some activities don't have + // their token type filled in. It is therefore necessary + // to get it from the token record directly. + String type = record.getType(); + //CMS.debug(method + "record.tokenType="+ type); + if ((type == null) || type.isEmpty()) { + CMS.debug(method + "record.tokenType null...getting from token record"); + String tokenID = record.getTokenID(); + if ((tokenID != null) && !tokenID.isEmpty()) { + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TokenDatabase t_database = subsystem.getTokenDatabase(); + TokenRecord t_record = t_database.getRecord(tokenID); + if (t_record != null) + type = t_record.getType(); + } + } + //CMS.debug(method + "type="+ type); + + if ((type == null) || type.isEmpty() || authorizedProfiles.contains(type)) { + retTotal++; + response.addEntry(createActivityData(record)); + } else { + //CMS.debug(method + "token type not allowed: " + type + + // "; adding 'restricted' record"); + response.addEntry(createRestrictedActivityData()); + } + } + } + } else { //authorizedProfiles null; no permission + CMS.debug(method + "authorized profiles is null"); } - // count the total entries - for (; activities.hasNext(); i++) activities.next(); - response.setTotal(i); + CMS.debug(method + "retTotal = " + retTotal); + response.setTotal(total); } @Override public Response getActivity(String activityID) { + String method = "ActivityService.getActivity: "; + String msg = ""; if (activityID == null) throw new BadRequestException("Activity ID is null."); - CMS.debug("ActivityService.getActivity(\"" + activityID + "\")"); + CMS.debug(method + "(\"" + activityID + "\")"); try { + List authorizedProfiles = getAuthorizedProfiles(); + if (authorizedProfiles == null) { + msg = "authorizedProfiles null"; + CMS.debug(method + msg); + throw new PKIException(method + msg); + } + TPSSubsystem subsystem = (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID); ActivityDatabase database = subsystem.getActivityDatabase(); + ActivityRecord record = database.getRecord(activityID); + if (record == null) { + CMS.debug(method + "record not found"); + throw new PKIException(method + "record not found"); + } + String type = record.getType(); - return createOKResponse(createActivityData(database.getRecord(activityID))); + if ((type != null) && !type.isEmpty() && !authorizedProfiles.contains(UserResource.ALL_PROFILES) && !authorizedProfiles.contains(type)) { + msg = "token type restricted: " + type; + CMS.debug(method + msg); + throw new PKIException(msg); + } + return createOKResponse(createActivityData(record)); } catch (Exception e) { CMS.debug(e); throw new PKIException(e.getMessage()); } } + + /* + * returns a list of TPS profiles allowed for the current user + */ + List getAuthorizedProfiles() + throws Exception { + String method = "ActivityService.getAuthorizedProfiles: "; + /* + String userID = servletRequest.getUserPrincipal().getName(); + CMS.debug(method + "principal name: " + userID); + IUGSubsystem userGroupManager = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG); + IUser user = userGroupManager.getUser(userID); + */ + PKIPrincipal pkiPrincipal = (PKIPrincipal) servletRequest.getUserPrincipal(); + IUser user = pkiPrincipal.getUser(); + return user.getTpsProfiles(); + } } diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java index 71bf9ad..de2691c 100644 --- a/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java +++ b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java @@ -22,8 +22,11 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -35,16 +38,21 @@ import org.dogtagpki.server.tps.config.ProfileDatabase; import org.dogtagpki.server.tps.config.ProfileRecord; import org.jboss.resteasy.plugins.providers.atom.Link; +import com.netscape.cms.realm.PKIPrincipal; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.BadRequestException; import com.netscape.certsrv.base.ForbiddenException; import com.netscape.certsrv.base.PKIException; +import com.netscape.certsrv.base.UserNotFoundException; import com.netscape.certsrv.common.Constants; import com.netscape.certsrv.logging.AuditEvent; import com.netscape.certsrv.logging.ILogger; import com.netscape.certsrv.tps.profile.ProfileCollection; import com.netscape.certsrv.tps.profile.ProfileData; import com.netscape.certsrv.tps.profile.ProfileResource; +import com.netscape.certsrv.usrgrp.IUGSubsystem; +import com.netscape.certsrv.usrgrp.IUser; +import com.netscape.certsrv.user.UserResource; import com.netscape.cms.servlet.base.SubsystemService; /** @@ -94,30 +102,51 @@ public class ProfileService extends SubsystemService implements ProfileResource throw new BadRequestException("Filter is too short."); } - start = start == null ? 0 : start; - size = size == null ? DEFAULT_SIZE : size; - + CMS.debug("ProfileService.j.findProfiles filter: " + filter); try { + List authorizedProfiles = getAuthorizedProfiles(); + + start = start == null ? 0 : start; + size = size == null ? DEFAULT_SIZE : size; + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); ProfileDatabase database = subsystem.getProfileDatabase(); - Iterator profiles = database.findRecords(filter).iterator(); + Collection profiles = new ArrayList<>(); + if (authorizedProfiles != null) { + + Collection filteredProfiles = database.findRecords(filter); + + if (authorizedProfiles.contains(UserResource.ALL_PROFILES)) { + CMS.debug("ProfileService: User allowed to access all profiles"); + profiles.addAll(filteredProfiles); + + } else { + for (ProfileRecord profile : filteredProfiles) { + if (authorizedProfiles.contains(profile.getID())) { + CMS.debug("ProfileService: User allowed to access profile " + profile.getID()); + profiles.add(profile); + } + } + } + } + Iterator profileIterator = profiles.iterator(); ProfileCollection response = new ProfileCollection(); int i = 0; // skip to the start of the page - for (; i < start && profiles.hasNext(); i++) - profiles.next(); + for (; i < start && profileIterator.hasNext(); i++) + profileIterator.next(); // return entries up to the page size - for (; i < start + size && profiles.hasNext(); i++) { - response.addEntry(createProfileData(profiles.next())); + for (; i < start + size && profileIterator.hasNext(); i++) { + response.addEntry(createProfileData(profileIterator.next())); } // count the total entries - for (; profiles.hasNext(); i++) - profiles.next(); + for (; profileIterator.hasNext(); i++) + profileIterator.next(); response.setTotal(i); if (start > 0) { @@ -145,23 +174,33 @@ public class ProfileService extends SubsystemService implements ProfileResource @Override public Response getProfile(String profileID) { + String method = "ProfileService.getProfile: "; + String msg = ""; if (profileID == null) throw new BadRequestException("Profile ID is null."); - CMS.debug("ProfileService.getProfile(\"" + profileID + "\")"); + CMS.debug(method + "(\"" + profileID + "\")"); + ProfileRecord profileRecord = null; try { + List authorizedProfiles = getAuthorizedProfiles(); + if ((authorizedProfiles== null) || ((authorizedProfiles != null) && !authorizedProfiles.contains(UserResource.ALL_PROFILES) && !authorizedProfiles.contains(profileID))) { + msg = "profile record restricted for profileID:" + profileID; + CMS.debug(method + msg); + + throw new PKIException(msg); + } TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); ProfileDatabase database = subsystem.getProfileDatabase(); - - return createOKResponse(createProfileData(database.getRecord(profileID))); + profileRecord = database.getRecord(profileID); + return createOKResponse(createProfileData(profileRecord)); } catch (PKIException e) { - CMS.debug("ProfileService: " + e); + CMS.debug(method + e); throw e; } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); throw new PKIException(e); } } @@ -231,6 +270,7 @@ public class ProfileService extends SubsystemService implements ProfileResource @Override public Response updateProfile(String profileID, ProfileData profileData) { String method = "ProfileService.updateProfile"; + String msg = ""; if (profileID == null) { auditConfigTokenGeneral(ILogger.FAILURE, method, null, @@ -244,7 +284,7 @@ public class ProfileService extends SubsystemService implements ProfileResource throw new BadRequestException("Profile data is null."); } - CMS.debug("ProfileService.updateProfile(\"" + profileID + "\")"); + CMS.debug(method + "(\"" + profileID + "\")"); Map properties = profileData.getProperties(); for (String name : properties.keySet()) { @@ -254,6 +294,14 @@ public class ProfileService extends SubsystemService implements ProfileResource } try { + List authorizedProfiles = getAuthorizedProfiles(); + if ((authorizedProfiles== null) || ((authorizedProfiles != null) && !authorizedProfiles.contains(UserResource.ALL_PROFILES) && !authorizedProfiles.contains(profileID))) { + msg = "profile record restricted for profileID:" + profileID; + CMS.debug(method + msg); + + throw new PKIException(msg); + } + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); ProfileDatabase database = subsystem.getProfileDatabase(); @@ -306,12 +354,12 @@ public class ProfileService extends SubsystemService implements ProfileResource return createOKResponse(profileData); } catch (PKIException e) { - CMS.debug("ProfileService: " + e); + CMS.debug(method + e); auditTPSProfileChange(ILogger.FAILURE, method, profileID, profileData.getProperties(), e.toString()); throw e; } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); auditTPSProfileChange(ILogger.FAILURE, method, profileID, profileData.getProperties(), e.toString()); throw new PKIException(e); } @@ -319,7 +367,8 @@ public class ProfileService extends SubsystemService implements ProfileResource @Override public Response changeStatus(String profileID, String action) { - String method = "ProfileService.changeStatus"; + String method = "ProfileService.changeStatus: "; + String msg = ""; Map auditModParams = new HashMap(); if (profileID == null) { @@ -336,9 +385,17 @@ public class ProfileService extends SubsystemService implements ProfileResource } auditModParams.put("Action", action); - CMS.debug("ProfileService.changeStatus(\"" + profileID + "\", \"" + action + "\")"); + CMS.debug(method + "(\"" + profileID + "\", \"" + action + "\")"); try { + List authorizedProfiles = getAuthorizedProfiles(); + if ((authorizedProfiles== null) || ((authorizedProfiles!= null) && (!authorizedProfiles.contains(UserResource.ALL_PROFILES) && !authorizedProfiles.contains(profileID)))) { + msg = "profile record restricted for profileID:" + profileID; + CMS.debug(method + msg); + + throw new PKIException(msg); + } + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); ProfileDatabase database = subsystem.getProfileDatabase(); @@ -424,13 +481,13 @@ public class ProfileService extends SubsystemService implements ProfileResource return createOKResponse(profileData); } catch (PKIException e) { - CMS.debug("ProfileService: " + e); + CMS.debug(method + e); auditConfigTokenGeneral(ILogger.FAILURE, method, auditModParams, e.toString()); throw e; } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); auditConfigTokenGeneral(ILogger.FAILURE, method, auditModParams, e.toString()); throw new PKIException(e); @@ -439,7 +496,8 @@ public class ProfileService extends SubsystemService implements ProfileResource @Override public Response removeProfile(String profileID) { - String method = "ProfileService.removeProfile"; + String method = "ProfileService.removeProfile: "; + String msg = ""; Map auditModParams = new HashMap(); if (profileID == null) { @@ -449,9 +507,10 @@ public class ProfileService extends SubsystemService implements ProfileResource } auditModParams.put("profileID", profileID); - CMS.debug("ProfileService.removeProfile(\"" + profileID + "\")"); + CMS.debug(method + "(\"" + profileID + "\")"); try { + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); ProfileDatabase database = subsystem.getProfileDatabase(); @@ -471,13 +530,13 @@ public class ProfileService extends SubsystemService implements ProfileResource return createNoContentResponse(); } catch (PKIException e) { - CMS.debug("ProfileService: " + e); + CMS.debug(method + e); auditTPSProfileChange(ILogger.FAILURE, method, profileID, auditModParams, e.toString()); throw e; } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); auditTPSProfileChange(ILogger.FAILURE, method, profileID, auditModParams, e.toString()); throw new PKIException(e); @@ -485,6 +544,19 @@ public class ProfileService extends SubsystemService implements ProfileResource } /* + * returns a list of TPS profiles allowed for the current user + */ + List getAuthorizedProfiles() + throws Exception { + String method = "ProfileService.getAuthorizedProfiles: "; + + PKIPrincipal pkiPrincipal = (PKIPrincipal) servletRequest.getUserPrincipal(); + IUser user = pkiPrincipal.getUser(); + + return user.getTpsProfiles(); + } + + /* * Service can be any of the methods offered */ public void auditTPSProfileChange(String status, String service, String profileID, Map params, @@ -498,6 +570,7 @@ public class ProfileService extends SubsystemService implements ProfileResource profileID, auditor.getParamString(params), info); + // CMS.debug("auditTPSProfileChange: " + msg); signedAuditLogger.log(msg); } 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 9dd3ce1..a7a6022 100644 --- a/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java +++ b/base/tps/src/org/dogtagpki/server/tps/rest/TokenService.java @@ -23,8 +23,10 @@ import java.net.URI; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -39,6 +41,7 @@ import org.dogtagpki.server.tps.dbs.TokenRecord; import org.dogtagpki.server.tps.engine.TPSEngine; import org.jboss.resteasy.plugins.providers.atom.Link; +import com.netscape.cms.realm.PKIPrincipal; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.BadRequestException; import com.netscape.certsrv.base.IConfigStore; @@ -53,6 +56,9 @@ import com.netscape.certsrv.tps.token.TokenData; import com.netscape.certsrv.tps.token.TokenData.TokenStatusData; import com.netscape.certsrv.tps.token.TokenResource; import com.netscape.certsrv.tps.token.TokenStatus; +import com.netscape.certsrv.user.UserResource; +import com.netscape.certsrv.usrgrp.IUGSubsystem; +import com.netscape.certsrv.usrgrp.IUser; import com.netscape.cms.servlet.base.SubsystemService; import netscape.ldap.LDAPException; @@ -229,6 +235,28 @@ public class TokenService extends SubsystemService implements TokenResource { return tokenData; } + public TokenData createRestrictedTokenData() throws Exception { + + TokenData tokenData = new TokenData(); + tokenData.setID(""); + tokenData.setTokenID(""); + tokenData.setUserID(""); + tokenData.setType(""); + + TokenStatusData statusData = new TokenStatusData(); + statusData.name = TokenStatus.valueOf(null); + statusData.label = ""; + tokenData.setStatus(statusData); + + tokenData.setAppletID(""); + tokenData.setKeyInfo(""); + tokenData.setPolicy(""); + tokenData.setCreateTimestamp(new Date(0L)); + tokenData.setModifyTimestamp(new Date(0L)); + + return tokenData; + } + @Override public Response findTokens( String filter, @@ -311,24 +339,48 @@ public class TokenService extends SubsystemService implements TokenResource { Integer size, TokenCollection response) throws Exception { + String method = "TokenService.retrieveTokensWithVLV: "; // search with VLV sorted by date in reverse order IDBVirtualList list = database.findRecords( null, null, new String[] { "-modifyTimestamp", "-createTimestamp" }, size); + List authorizedProfiles = getAuthorizedProfiles(); + int total = list.getSize(); + int retTotal = 0; //debugging only // return entries in the requested page - for (int i = start; i < start + size && i < total; i++) { - TokenRecord record = list.getElementAt(i); + if (authorizedProfiles != null) { + if (authorizedProfiles.contains(UserResource.ALL_PROFILES)) { + for (int i = start; i < start + size && i < total; i++) { + TokenRecord record = list.getElementAt(i); - if (record == null) { - CMS.debug("TokenService: Token record not found"); - throw new PKIException("Token record not found"); + response.addEntry(createTokenData(record)); + retTotal++; + } + } else { // not authorized for all profiles + for (int i = start; i < start + size && i < total; i++) { + TokenRecord record = list.getElementAt(i); + //CMS.debug(method + "record.ID="+ record.getId()); + + String type = record.getType(); + //CMS.debug(method + "record.tokenType="+ type; + if ((type == null) || type.isEmpty() || authorizedProfiles.contains(type)) { + //CMS.debug(method + "token type allowed"); + retTotal++; + response.addEntry(createTokenData(record)); + } else { + //CMS.debug(method + "token type restricted: " + type + + // "; adding 'restricted' record"); + response.addEntry(createRestrictedTokenData()); + } + } //for } - - response.addEntry(createTokenData(record)); + } else { //authorizedProfiles null; no permission + CMS.debug(method + "authorized profiles is null"); } + CMS.debug(method + "retTotal = " + retTotal); response.setTotal(total); } @@ -340,44 +392,84 @@ public class TokenService extends SubsystemService implements TokenResource { Integer size, TokenCollection response) throws Exception { - // search without VLV - Iterator tokens = database.findRecords(filter, attributes).iterator(); + String method = "TokenService.retrieveTokensWithoutVLV: "; - // TODO: sort results by date in reverse order + List tokens = (List) database.findRecords(filter); + int total = tokens.size(); + CMS.debug(method + "total: " + total); - int i = 0; + List authorizedProfiles = getAuthorizedProfiles(); - // skip to the start of the page - for (; i < start && tokens.hasNext(); i++) - tokens.next(); + int retTotal = 0; //debugging only + int i = 0; // return entries in the requested page - for (; i < start + size && tokens.hasNext(); i++) { - TokenRecord record = tokens.next(); - - response.addEntry(createTokenData(record)); + if (authorizedProfiles != null) { + if (authorizedProfiles.contains(UserResource.ALL_PROFILES)) { + for (i=start; i < start + size && i < total; i++) { + TokenRecord record = tokens.get(i); + + //CMS.debug(method + "record.tokenType="+ record.getType()); + response.addEntry(createTokenData(record)); + retTotal++; + } + } else { // not authorized for all profiles + for (i=start; i < start + size && i < total; i++) { + TokenRecord record = tokens.get(i); + //CMS.debug(method + "record.ID="+ record.getId()); + String type = record.getType(); + //CMS.debug(method + "record.tokenType="+ type; + if ((type == null) || type.isEmpty() || authorizedProfiles.contains(type)) { + //CMS.debug(method + "token type allowed"); + retTotal++; + response.addEntry(createTokenData(record)); + } else { + //CMS.debug(method + "token type not allowed: " + type + + // "; adding 'restricted' record"); + response.addEntry(createRestrictedTokenData()); + } + } + } + } else { //authorizedProfiles null; no permission + CMS.debug(method + "authorized profiles is null"); } - // count the total entries - for (; tokens.hasNext(); i++) - tokens.next(); + CMS.debug(method + "retTotal = " + retTotal); - response.setTotal(i); + response.setTotal(total); } @Override public Response getToken(String tokenID) { - + String method = "TokenService.getToken: "; + String msg = ""; if (tokenID == null) throw new BadRequestException("Token ID is null."); - CMS.debug("TokenService.getToken(\"" + tokenID + "\")"); + CMS.debug(method + "(\"" + tokenID + "\")"); try { + List authorizedProfiles = getAuthorizedProfiles(); + if (authorizedProfiles == null) { + msg = "authorizedProfiles null"; + CMS.debug(method + msg); + throw new PKIException(method + msg); + } + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); TokenDatabase database = subsystem.getTokenDatabase(); + TokenRecord record = database.getRecord(tokenID); + if (record == null) { + msg = "Token record not found"; + CMS.debug(method + msg); + throw new PKIException(method + msg); + } + String type = record.getType(); + if ((type == null) || type.isEmpty() || authorizedProfiles.contains(UserResource.ALL_PROFILES) || authorizedProfiles.contains(type)) - return createOKResponse(createTokenData(database.getRecord(tokenID))); + return createOKResponse(createTokenData(record)); + else + throw new PKIException(method + "Token record restricted"); } catch (EDBException e) { Throwable t = e.getCause(); @@ -397,7 +489,7 @@ public class TokenService extends SubsystemService implements TokenResource { @Override public Response addToken(TokenData tokenData) { - String method = "TokenService.addToken"; + String method = "TokenService.addToken: "; Map auditModParams = new HashMap(); if (tokenData == null) { @@ -410,7 +502,7 @@ public class TokenService extends SubsystemService implements TokenResource { String tokenID = tokenData.getTokenID(); auditModParams.put("tokenID", tokenID); - CMS.debug("TokenService.addToken(\"" + tokenID + "\")"); + CMS.debug(method + "(\"" + tokenID + "\")"); String remoteUser = servletRequest.getRemoteUser(); String ipAddress = servletRequest.getRemoteAddr(); @@ -451,7 +543,7 @@ public class TokenService extends SubsystemService implements TokenResource { return createCreatedResponse(tokenData, tokenData.getLink().getHref()); } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); msg = msg + ": " + e.getMessage(); subsystem.tdb.tdbActivity(ActivityDatabase.OP_ADD, tokenRecord, @@ -481,7 +573,7 @@ public class TokenService extends SubsystemService implements TokenResource { @Override public Response replaceToken(String tokenID, TokenData tokenData) { - String method = "TokenService.replaceToken"; + String method = "TokenService.replaceToken: "; Map auditModParams = new HashMap(); if (tokenID == null) { @@ -495,7 +587,7 @@ public class TokenService extends SubsystemService implements TokenResource { throw new BadRequestException("Token data is null."); } - CMS.debug("TokenService.replaceToken(\"" + tokenID + "\")"); + CMS.debug(method +"(\"" + tokenID + "\")"); String remoteUser = servletRequest.getRemoteUser(); String ipAddress = servletRequest.getRemoteAddr(); @@ -528,7 +620,7 @@ public class TokenService extends SubsystemService implements TokenResource { return createOKResponse(tokenData); } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); msg = msg + ": " + e.getMessage(); subsystem.tdb.tdbActivity(ActivityDatabase.OP_TOKEN_MODIFY, tokenRecord, @@ -559,7 +651,7 @@ public class TokenService extends SubsystemService implements TokenResource { @Override public Response modifyToken(String tokenID, TokenData tokenData) { - String method = "TokenService.modifyToken"; + String method = "TokenService.modifyToken: "; Map auditModParams = new HashMap(); if (tokenID == null) { @@ -575,7 +667,7 @@ public class TokenService extends SubsystemService implements TokenResource { throw e; } - CMS.debug("TokenService.modifyToken(\"" + tokenID + "\")"); + CMS.debug(method + "(\"" + tokenID + "\")"); String remoteUser = servletRequest.getRemoteUser(); String ipAddress = servletRequest.getRemoteAddr(); @@ -584,11 +676,29 @@ public class TokenService extends SubsystemService implements TokenResource { TokenRecord tokenRecord = null; String msg = "modify token"; try { + List authorizedProfiles = getAuthorizedProfiles(); + if (authorizedProfiles == null) { + msg = "authorizedProfiles null"; + CMS.debug(method + msg); + throw new PKIException(method + msg); + } + TokenDatabase database = subsystem.getTokenDatabase(); // get existing record tokenRecord = database.getRecord(tokenID); + if (tokenRecord == null) { + CMS.debug(method + "Token record not found"); + throw new PKIException(method + "Token record not found"); + } + String type = tokenRecord.getType(); + if ((type != null) && !type.isEmpty() && !authorizedProfiles.contains(UserResource.ALL_PROFILES) && !authorizedProfiles.contains(type)) { + CMS.debug(method + "token record restricted"); + + throw new PKIException("token record restricted"); + } + // update user ID if specified String userID = tokenData.getUserID(); if (userID != null) { @@ -622,7 +732,7 @@ public class TokenService extends SubsystemService implements TokenResource { return createOKResponse(tokenData); } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); msg = msg + ": " + e.getMessage(); subsystem.tdb.tdbActivity(ActivityDatabase.OP_TOKEN_MODIFY, tokenRecord, @@ -653,7 +763,7 @@ public class TokenService extends SubsystemService implements TokenResource { @Override public Response changeTokenStatus(String tokenID, TokenStatus tokenStatus) { - String method = "TokenService.changeTokenStatus"; + String method = "TokenService.changeTokenStatus: "; CMS.debug(method + "begins: with tokenStatus=" + tokenStatus.getName()); Map auditModParams = new HashMap(); @@ -662,8 +772,12 @@ public class TokenService extends SubsystemService implements TokenResource { "Token ID is null."); throw new BadRequestException("Token ID is null."); } - auditModParams.put("tokenID", tokenID); + + TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); + TokenDatabase database = null; + TokenRecord tokenRecord = null; + if (tokenStatus == null) { auditConfigTokenGeneral(ILogger.FAILURE, method, null, "Token state is null."); @@ -671,39 +785,55 @@ public class TokenService extends SubsystemService implements TokenResource { } auditModParams.put("tokenStatus", tokenStatus.toString()); - CMS.debug("TokenService.changeTokenStatus(\"" + tokenID + "\", \"" + tokenStatus + "\")"); + CMS.debug(method + "(\"" + tokenID + "\", \"" + tokenStatus + "\")"); String remoteUser = servletRequest.getRemoteUser(); String ipAddress = servletRequest.getRemoteAddr(); - TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID); // for auditing TokenStatus oldStatus = null; String oldReason = null; TokenStatus newStatus = null; String newReason = null; - TokenRecord tokenRecord = null; String msg = "change token status"; try { - TokenDatabase database = subsystem.getTokenDatabase(); + List authorizedProfiles = getAuthorizedProfiles(); + if (authorizedProfiles == null) { + msg = "authorizedProfiles null"; + CMS.debug(method + msg); + throw new PKIException(method + msg); + } + + database = subsystem.getTokenDatabase(); tokenRecord = database.getRecord(tokenID); + if (tokenRecord == null) { + CMS.debug(method + "Token record not found"); + throw new PKIException(method + "Token record not found"); + } + String type = tokenRecord.getType(); + if ((type != null) && !type.isEmpty() && !authorizedProfiles.contains(UserResource.ALL_PROFILES) && !authorizedProfiles.contains(type)) { + CMS.debug(method + "token record restricted: " + type); + + throw new PKIException("token record restricted"); + } TokenStatus currentTokenStatus = tokenRecord.getTokenStatus(); - CMS.debug("TokenService.changeTokenStatus(): current status: " + currentTokenStatus); + CMS.debug(method + " current status: " + currentTokenStatus); oldStatus = tokenRecord.getTokenStatus(); oldReason = tokenRecord.getReason(); newStatus = tokenStatus; if (currentTokenStatus == tokenStatus) { - CMS.debug("TokenService.changeTokenStatus(): no status change, no activity log generated"); + CMS.debug(method + " no status change, no activity log generated"); TokenData tokenData = createTokenData(tokenRecord); return createOKResponse(tokenData); } msg = msg + " from " + currentTokenStatus + " to " + tokenStatus; + CMS.debug(method + msg); // Check for invalid current status if(!oldStatus.isValid()) { @@ -717,7 +847,7 @@ public class TokenService extends SubsystemService implements TokenResource { // make sure transition is allowed if (!subsystem.isUITransitionAllowed(tokenRecord, tokenStatus)) { - CMS.debug("TokenService.changeTokenStatus(): next status not allowed: " + tokenStatus); + CMS.debug(method + " next status not allowed: " + tokenStatus); Exception ex = new BadRequestException("Invalid token status transition"); auditTokenStateChange(ILogger.FAILURE, oldStatus, newStatus, oldReason, newReason, @@ -725,7 +855,7 @@ public class TokenService extends SubsystemService implements TokenResource { throw ex; } - CMS.debug("TokenService.changeTokenStatus(): next status allowed: " + tokenStatus); + CMS.debug(method + " next status allowed: " + tokenStatus); // audit in setTokenStatus() setTokenStatus(tokenRecord, tokenStatus, ipAddress, remoteUser, auditModParams); database.updateRecord(tokenID, tokenRecord); @@ -738,7 +868,7 @@ public class TokenService extends SubsystemService implements TokenResource { return createOKResponse(tokenData); } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); msg = msg + ": " + e.getMessage(); subsystem.tdb.tdbActivity(ActivityDatabase.OP_TOKEN_STATUS_CHANGE, tokenRecord, @@ -772,7 +902,7 @@ public class TokenService extends SubsystemService implements TokenResource { @Override public Response removeToken(String tokenID) { - String method = "TokenService.removeToken"; + String method = "TokenService.removeToken: "; Map auditModParams = new HashMap(); if (tokenID == null) { @@ -782,7 +912,7 @@ public class TokenService extends SubsystemService implements TokenResource { throw ex; } - CMS.debug("TokenService.removeToken(\"" + tokenID + "\")"); + CMS.debug(method + "(\"" + tokenID + "\")"); String remoteUser = servletRequest.getRemoteUser(); String ipAddress = servletRequest.getRemoteAddr(); @@ -795,7 +925,7 @@ public class TokenService extends SubsystemService implements TokenResource { tokenRecord = database.getRecord(tokenID); //delete all certs associated with this token - CMS.debug("TokenService.removeToken: about to remove all certificates associated with the token first"); + CMS.debug(method + "about to remove all certificates associated with the token first"); subsystem.tdb.tdbRemoveCertificatesByCUID(tokenRecord.getId()); database.removeRecord(tokenID); @@ -807,7 +937,7 @@ public class TokenService extends SubsystemService implements TokenResource { return createNoContentResponse(); } catch (Exception e) { - CMS.debug(e); + CMS.debug(method + e); msg = msg + ": " + e.getMessage(); subsystem.tdb.tdbActivity(ActivityDatabase.OP_DELETE, tokenRecord, @@ -837,11 +967,25 @@ public class TokenService extends SubsystemService implements TokenResource { } /* + * returns a list of TPS profiles allowed for the current user + */ + List getAuthorizedProfiles() + throws Exception { + String method = "TokenService.getAuthorizedProfiles: "; + + PKIPrincipal pkiPrincipal = (PKIPrincipal) servletRequest.getUserPrincipal(); + IUser user = pkiPrincipal.getUser(); + + return user.getTpsProfiles(); + } + + /* * Service can be any of the methods offered */ public void auditConfigTokenRecord(String status, String service, String tokenID, Map params, String info) { + //CMS.debug("auditTokenStateChange1: "); String msg = CMS.getLogMessage( AuditEvent.CONFIG_TOKEN_RECORD, servletRequest.getUserPrincipal().getName(), @@ -850,6 +994,7 @@ public class TokenService extends SubsystemService implements TokenResource { tokenID, auditor.getParamString(params), info); + //CMS.debug("auditConfigTokenRecord: " + msg); signedAuditLogger.log(msg); } @@ -859,16 +1004,18 @@ public class TokenService extends SubsystemService implements TokenResource { public void auditTokenStateChange(String status, TokenStatus oldState, TokenStatus newState, String oldReason, String newReason, Map params, String info) { + //CMS.debug("auditTokenStateChange2: "); String msg = CMS.getLogMessage( AuditEvent.TOKEN_STATE_CHANGE, servletRequest.getUserPrincipal().getName(), status, - oldState.toString(), + (oldState==null)? "":oldState.toString(), oldReason, - newState.toString(), + (newState==null)? "":newState.toString(), newReason, auditor.getParamString(params), info); + //CMS.debug("auditTokenStateChange: " + msg); signedAuditLogger.log(msg); } } -- 1.8.3.1