diff --git a/0002-Fix-renewal-profile-approval-process.patch b/0002-Fix-renewal-profile-approval-process.patch new file mode 100644 index 0000000..a85c833 --- /dev/null +++ b/0002-Fix-renewal-profile-approval-process.patch @@ -0,0 +1,170 @@ +From 34c492c30f49ee9feaa1332db91b56e55efc7994 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 13 Jan 2021 18:27:46 +1100 +Subject: [PATCH] Fix renewal profile approval process + +Due to a recent change in PKI CLI, the CLI now passes along user +authentication with submissions to the renewal endpoint. Unlike the EE +pages, the REST API has passed along this authentication for a while. +Due to a bug in the RenewalProcessor, requests with credentials against +profiles with no authentication method and no ACLs result in the +certificiate automatically being approved. This occurs because, when +an earlier commit (cb9eb967b5e24f5fde8bbf8ae87aa615b7033db7) modified +the code to allow Light-Weight SubCAs to issue certificates, validation +wasn't done on the passed principal, to see if it was a trusted agent. +Because profiles requring Agent approval have an empty ACL list (as, no +user should be able to submit a certificate request and have it +automatically signed without agent approval), authorize allows any user +to approve this request and thus accepts the AuthToken. + +Critical analysis: the RenewalProcessor code interprets (authToken +!= null) as evidence that the authenticated user is /authorized/ to +immediately issue the certificate. This mismatch of concerns (authn +vs authz) resulted in a misunderstanding of system behaviour. The +"latent" AuthToken (from the HTTP request) was assigned to authToken +without realising that authorization needed to be performed. + +We fix this by splitting the logic on whether the profile defines an +authenticator. If so, we (re)authenticate and authorize the user +according to the profile configuration. + +If the profile does not define an authenticator but there is a +principal in the HTTP request, if (and only if) the user has +permission to approve certificate requests *and* the requested +renewal profile is caManualRenewal (which is hardcoded to be used +for LWCA renewal), then we issue the certificate immediately. This +special case ensures that LWCA renewal keeps working. + +Otherwise, if there is no principal in the HTTP request or the +principal does not have permission to approve certificate requests, +we leave the authToken unset. The resulting renewal request will be +created with status PENDING, i.e. enqueued for agent review. + +Signed-off-by: Fraser Tweedale +Signed-off-by: Alexander Scheel +--- + .../com/netscape/ca/CertificateAuthority.java | 10 +++ + .../cms/servlet/cert/RenewalProcessor.java | 75 +++++++++++++++++-- + 2 files changed, 79 insertions(+), 6 deletions(-) + +diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java +index 560507168..431ce9ff7 100644 +--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java ++++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java +@@ -1929,6 +1929,16 @@ public class CertificateAuthority + } + + ProfileSubsystem ps = engine.getProfileSubsystem(); ++ /* NOTE: hard-coding the profile to use for Lightweight CA renewal ++ * might be OK, but caManualRenewal was not the right one to use. ++ * As a consequence, we have an undesirable special case in ++ * RenewalProcessor.processRenewal(). ++ * ++ * We should introduce a new profile specifically for LWCA renewal, ++ * with an authenticator and ACLs to match the authz requirements ++ * for the renewAuthority REST resource itself. Then we can use ++ * it here, and remove the workaround from RenewalProcessor. ++ */ + Profile profile = ps.getProfile("caManualRenewal"); + CertEnrollmentRequest req = CertEnrollmentRequestFactory.create( + new ArgBlock(), profile, httpReq.getLocale()); +diff --git a/base/ca/src/com/netscape/cms/servlet/cert/RenewalProcessor.java b/base/ca/src/com/netscape/cms/servlet/cert/RenewalProcessor.java +index 4293cdd06..fd20f4826 100644 +--- a/base/ca/src/com/netscape/cms/servlet/cert/RenewalProcessor.java ++++ b/base/ca/src/com/netscape/cms/servlet/cert/RenewalProcessor.java +@@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletRequest; + + import org.apache.commons.lang3.StringUtils; + import org.dogtagpki.server.ca.CAEngine; ++import org.dogtagpki.server.authorization.AuthzToken; + import org.mozilla.jss.netscape.security.x509.BasicConstraintsExtension; + import org.mozilla.jss.netscape.security.x509.X509CertImpl; + +@@ -267,16 +268,78 @@ public class RenewalProcessor extends CertProcessor { + + // before creating the request, authenticate the request + IAuthToken authToken = null; +- Principal principal = request.getUserPrincipal(); +- if (principal instanceof PKIPrincipal) +- authToken = ((PKIPrincipal) principal).getAuthToken(); +- if (authToken == null && authenticator != null) { +- authToken = authenticate(request, origReq, authenticator, context, true, credentials); ++ ++ if (authenticator != null) { ++ /* The profile specifies an authenticator. Use it to ++ * authenticate the user. Ignore the "latent" session ++ * principal (if any). ++ */ ++ authToken = authenticate( ++ request, ++ origReq, ++ authenticator, ++ context, ++ true /* isRenewal */, ++ credentials); ++ } else { ++ /* When authenticator is null, we expect manual agent ++ * review (leave authToken as null). ++ * ++ * But as a special case to ensure Lightweight CA (LWCA) ++ * renewal works, if there is a latent user in the HTTP ++ * request, we use that user (i.e. set authToken to the ++ * principal's IAuthToken) if and only if: ++ * ++ * - The renewal profile is caManualRenewal (LWCA renewal ++ * is hardcoded to use this profile); AND ++ * ++ * - The latent user is authorized to "execute" ++ * certificate requests (i.e. agent approval) ++ * ++ * See also CertificateAuthority.renewAuthority(). ++ */ ++ ++ Principal principal = request.getUserPrincipal(); ++ if ( ++ renewProfileId.equals("caManualRenewal") ++ && principal instanceof PKIPrincipal ++ ) { ++ IAuthToken latentToken = ((PKIPrincipal) principal).getAuthToken(); ++ AuthzToken authzToken = authorize( ++ "DirAclAuthz", latentToken, "certServer.ca.certrequests", "execute"); ++ if (authzToken != null) { ++ // Success (no exception); user is authorized to approve ++ // cert requests. Set the authToken. ++ // ++ // NOTE: This authz does not replace or subsume the ++ // profile-specific authz check below. ++ authToken = latentToken; ++ } else { ++ // leave authToken as null to enqueue a pending request. ++ } ++ } else { ++ // not caManualRenewal or no latent principal; ++ // leave authToken as null to enqueue a pending request. ++ } + } + +- // authentication success, now authorize ++ /* Authorize the request. ++ * ++ * If authToken != null, it will be checked against ACLs specified ++ * in the profile (if any). If ACLs are defined and authToken does ++ * not match, throws an authorization exception. ++ * ++ * If authToken == null, no check is performed (even if the profile ++ * defines ACLs). This is fine, because null authToken will cause ++ * the request status to be 'pending' [agent approval]. ++ */ + authorize(profileId, renewProfile, authToken); + ++ /* At this point, the request will be created. If authToken ++ * is non-null, then the certificate will be issued ++ * immediately. Otherwise the request will be pending. */ ++ ++ + /////////////////////////////////////////////// + // create and populate requests + /////////////////////////////////////////////// +-- +2.29.2 + diff --git a/pki-core.spec b/pki-core.spec index 924c153..981ed00 100644 --- a/pki-core.spec +++ b/pki-core.spec @@ -13,7 +13,7 @@ License: GPLv2 and LGPLv2 # For development (i.e. unsupported) releases, use x.y.z-0.n.. # For official (i.e. supported) releases, use x.y.z-r where r >=1. Version: 10.10.5 -Release: 4%{?_timestamp}%{?_commit_id}%{?dist} +Release: 6%{?_timestamp}%{?_commit_id}%{?dist} #global _phase -beta1 # To create a tarball from a version tag: @@ -31,6 +31,15 @@ Source: https://github.com/dogtagpki/pki/archive/v%{version}%{?_phase}/pki-%{ver # > pki-VERSION-RELEASE.patch # Patch: pki-VERSION-RELEASE.patch Patch1: 0001-remove-jakarta-commons-httpclient.patch +Patch2: 0002-Fix-renewal-profile-approval-process.patch + +# md2man isn't available on i686. Additionally, we aren't generally multi-lib +# compatible (https://fedoraproject.org/wiki/Packaging:Java) +# so dropping i686 everywhere but RHEL-8 (which we've already shipped) seems +# safest. +%if ! 0%{?rhel} || 0%{?rhel} > 8 +ExcludeArch: i686 +%endif ################################################################################ # NSS @@ -158,8 +167,6 @@ fi; ################################################################################ # Build Dependencies ################################################################################ -# Exclude i686 architectures as neither 389-ds nor IPA are supported there -ExcludeArch: i686 # autosetup BuildRequires: git @@ -1382,6 +1389,13 @@ fi ################################################################################ %changelog +* Fri Mar 12 2021 Dogtag PKI Team - 10.10.5-4 - Use JDK 11 for ELN and RHEL 9 builds