[Pki-devel] [PATCH] 0167..0175 external authentication support

Fraser Tweedale ftweedal at redhat.com
Tue Mar 7 01:16:37 UTC 2017


Hi team,

Please review the attached patches, which add support for external
authentication (e.g. GSS-API/SPNEGO).

These patches depend on some other outstanding patches:
0157, 0158, 0165, 0166.

You can review the whole branch (including those commits just
mentioned) on GitHub:
https://github.com/dogtagpki/pki/compare/master...frasertweedale:feature/1359-gssapi

Thanks!
Fraser
-------------- next part --------------
From 13620e08e639e40bfd5eac986f6a692dd6e88ffc Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 29 Nov 2016 17:58:50 +1000
Subject: [PATCH 167/175] Add groups and request attributes to external
 principals

Add the ExternalAuthenticationValve valve, which, if an externally
authenticated principal is available, reads the REMOTE_USER_GROUP
information from the Coyote request and adds the groups ("roles" in
Tomcat terminology) to the principal.

It also saves a complete copy of the request attribute map in the
princpial.  The new class ExternalPrincipal is used to achieve this.

Part of: https://pagure.io/dogtagpki/issue/1359
---
 base/ca/tomcat8/conf/Catalina/localhost/ca.xml     |  2 +
 base/server/tomcat/src/CMakeLists.txt              |  8 +++
 .../cms/tomcat/ExternalAuthenticationValve.java    | 80 ++++++++++++++++++++++
 .../com/netscape/cms/tomcat/ExternalPrincipal.java | 43 ++++++++++++
 4 files changed, 133 insertions(+)
 create mode 100644 base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java
 create mode 100644 base/server/tomcat/src/com/netscape/cms/tomcat/ExternalPrincipal.java

diff --git a/base/ca/tomcat8/conf/Catalina/localhost/ca.xml b/base/ca/tomcat8/conf/Catalina/localhost/ca.xml
index 46f270817a58282b950b75a15bb3bd052f178f0c..0268bc17e055b98198a9a44275319e77217c87fd 100644
--- a/base/ca/tomcat8/conf/Catalina/localhost/ca.xml
+++ b/base/ca/tomcat8/conf/Catalina/localhost/ca.xml
@@ -27,6 +27,8 @@
     <Manager
         secureRandomProvider="Mozilla-JSS" secureRandomAlgorithm="pkcs11prng"/>
 
+    <Valve className="com.netscape.cms.tomcat.ExternalAuthenticationValve" />
+
     <Valve className="com.netscape.cms.tomcat.SSLAuthenticatorWithFallback"
         alwaysUseSession="true"
         secureRandomProvider="Mozilla-JSS"
diff --git a/base/server/tomcat/src/CMakeLists.txt b/base/server/tomcat/src/CMakeLists.txt
index 087bcd9acd7f0be8de274c9716466e3920bc3f52..b9a3b4c5d5aa5f8914851ffed63a40555519f8ef 100644
--- a/base/server/tomcat/src/CMakeLists.txt
+++ b/base/server/tomcat/src/CMakeLists.txt
@@ -104,6 +104,13 @@ find_file(NUXWDOG_JAR
         /usr/share/java
 )
 
+find_file(TOMCAT_COYOTE_JAR
+    NAMES
+        tomcat-coyote.jar
+    PATHS
+        /usr/share/java/tomcat
+)
+
 # build pki-tomcat
 javac(pki-tomcat-classes
     SOURCES
@@ -111,6 +118,7 @@ javac(pki-tomcat-classes
     CLASSPATH
         ${SERVLET_JAR} ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_SCAN_JAR}
 		${NUXWDOG_JAR} ${APACHE_COMMONS_LANG_JAR} ${TOMCATJSS_JAR}
+		${TOMCAT_COYOTE_JAR}
     OUTPUT_DIR
         ${CMAKE_BINARY_DIR}/../../tomcat
     DEPENDS
diff --git a/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java b/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bc570b4692e518a482ec91fc93d51def77dff27
--- /dev/null
+++ b/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java
@@ -0,0 +1,80 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2015 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cms.tomcat;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+
+public class ExternalAuthenticationValve extends ValveBase {
+
+    public void invoke(Request req, Response resp)
+            throws IOException, ServletException {
+        System.out.println("ExternalAuthenticationValve; authType: "
+                + req.getAuthType());
+        System.out.println("ExternalAuthenticationValve; principal: "
+                + req.getUserPrincipal());
+        //System.out.println(req.getCoyoteRequest().getAttributes().toString());
+
+        org.apache.coyote.Request coyoteReq = req.getCoyoteRequest();
+        Principal principal = req.getUserPrincipal();
+
+        if (principal != null) {
+            Integer numGroups = 0;
+            String numGroupsStr = (String)
+                coyoteReq.getAttribute("REMOTE_USER_GROUP_N");
+            if (numGroupsStr != null) {
+                try {
+                    numGroups = new Integer(numGroupsStr);
+                } catch (NumberFormatException e) {
+                    System.out.println("ExternalAuthenticationValve: invalid REMOTE_USER_GROUP_N value: " + e);
+                }
+            }
+
+            ArrayList<String> groups = new ArrayList<>();
+            for (int i = 1; i <= numGroups; i++) {
+                String k = "REMOTE_USER_GROUP_" + i;
+                String s = (String) coyoteReq.getAttribute(k);
+                if (s != null && !s.isEmpty())
+                    groups.add(s);
+                else
+                    System.out.println("ExternalAuthenticationValve: missing or empty attribute: " + k);
+            }
+
+            // replace the principal
+            principal = new ExternalPrincipal(
+                principal.getName(), null, groups, coyoteReq.getAttributes());
+            System.out.println("ExternalAuthenticationValve: setting new principal: " + principal);
+            req.setUserPrincipal(principal);
+
+            // cache principal in session
+            Session session = req.getSessionInternal();
+            session.setAuthType(req.getAuthType());
+            session.setPrincipal(principal);
+        }
+
+        getNext().invoke(req, resp);
+    }
+}
diff --git a/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalPrincipal.java b/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalPrincipal.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7bb0e110382d6b45e66d0c2748b4ac206ce99e2
--- /dev/null
+++ b/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalPrincipal.java
@@ -0,0 +1,43 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cms.tomcat;
+
+import org.apache.catalina.realm.GenericPrincipal;
+
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * Principal that carries additional request attributes.
+ */
+public class ExternalPrincipal extends GenericPrincipal {
+
+    private HashMap<String, Object> attributes;
+
+    public ExternalPrincipal(String name, String password, List<String> roles,
+            HashMap<String, Object> attributes) {
+        super(name, password, roles);
+        this.attributes = attributes;
+    }
+
+    public HashMap<String, Object> getAttributes() {
+        return attributes;
+    }
+
+}
-- 
2.9.3

-------------- next part --------------
From cb05006dd35618f7caa898b7368b6fd54efd96d9 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 29 Nov 2016 18:10:58 +1000
Subject: [PATCH 168/175] Add IAuthToken implementation for external principals

Many parts of Dogtag expect an IAuthToken, which represents the
authenticated user.  The sole implementation, AuthToken, uses some
concepts that do not carry across to externally authenticated
principals, e.g. an external principal does not have an associated
IAuthManager that was used to authenticate the principal.  Therefore
something different is needed.

Implement ExternalAuthToken which wraps a GenericPrincipal and
provides access to the data therein.

Part of: https://pagure.io/dogtagpki/issue/1359
---
 .../certsrv/authentication/ExternalAuthToken.java  | 154 +++++++++++++++++++++
 1 file changed, 154 insertions(+)
 create mode 100644 base/common/src/com/netscape/certsrv/authentication/ExternalAuthToken.java

diff --git a/base/common/src/com/netscape/certsrv/authentication/ExternalAuthToken.java b/base/common/src/com/netscape/certsrv/authentication/ExternalAuthToken.java
new file mode 100644
index 0000000000000000000000000000000000000000..07c09d1404f57e526e742ae2b47e111c5516c475
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/authentication/ExternalAuthToken.java
@@ -0,0 +1,154 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2015 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.certsrv.authentication;
+
+import java.math.BigInteger;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+
+import org.apache.catalina.realm.GenericPrincipal;
+
+import netscape.security.x509.CertificateExtensions;
+import netscape.security.x509.X509CertImpl;
+
+import com.netscape.certsrv.usrgrp.Certificates;
+
+
+/**
+ * Authentication token that wraps an externally authenticated
+ * principal to return.
+ */
+public class ExternalAuthToken implements IAuthToken {
+
+    protected GenericPrincipal principal;
+
+    public ExternalAuthToken(GenericPrincipal principal) {
+        this.principal = principal;
+    }
+
+    public Principal getPrincipal() {
+        return principal;
+    }
+
+    public Enumeration<String> getElements() {
+        ArrayList<String> keys = new ArrayList<>();
+        keys.add(GROUPS);
+        keys.add(TOKEN_AUTHMGR_INST_NAME);
+        keys.add(UID);
+        keys.add(USER_ID);
+        return Collections.enumeration(keys);
+    }
+
+    public Object get(String k) {
+        return null;
+    }
+
+    public boolean set(String k, String v) {
+        return false;
+    }
+
+    public String getInString(String k) {
+        if (k == null)
+            return null;
+        if (k.equals(USER_ID) || k.equals(UID))
+            return principal.getName();
+        if (k.equals(TOKEN_AUTHMGR_INST_NAME))
+            return "external";
+        return null;
+    }
+
+    public boolean set(String k, byte[] v) {
+        return false;
+    }
+
+    public byte[] getInByteArray(String k) {
+        return null;
+    }
+
+    public boolean set(String k, Integer v) {
+        return false;
+    }
+
+    public Integer getInInteger(String k) {
+        return null;
+    }
+
+    public boolean set(String k, BigInteger[] v) {
+        return false;
+    }
+
+    public BigInteger[] getInBigIntegerArray(String k) {
+        return null;
+    }
+
+    public boolean set(String k, Date v) {
+        return false;
+    }
+
+    public Date getInDate(String k) {
+        return null;
+    }
+
+    public boolean set(String k, String[] v) {
+        return false;
+    }
+
+    public String[] getInStringArray(String k) {
+        if (k == null)
+            return null;
+        if (k.equals(GROUPS))
+            return principal.getRoles();
+        return null;
+    }
+
+    public boolean set(String k, X509CertImpl v) {
+        return false;
+    }
+
+    public X509CertImpl getInCert(String k) {
+        return null;
+    }
+
+    public boolean set(String k, CertificateExtensions v) {
+        return false;
+    }
+
+    public CertificateExtensions getInCertExts(String k) {
+        return null;
+    }
+
+    public boolean set(String k, Certificates v) {
+        return false;
+    }
+
+    public Certificates getInCertificates(String k) {
+        return null;
+    }
+
+    public boolean set(String k, byte[][] v) {
+        return false;
+    }
+
+    public byte[][] getInByteArrayArray(String k) {
+        return null;
+    }
+}
-- 
2.9.3

-------------- next part --------------
From 9c38f89b61cbac5293f6358b57b7e1cacd3ffc29 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 29 Nov 2016 18:24:53 +1000
Subject: [PATCH 169/175] Update AuthMethodInterceptor to handle external
 principals

Update AuthMethodInterceptor to handle externally authenticated
principals.  For now, access is unconditionally granted.

Part of: https://pagure.io/dogtagpki/issue/1359
---
 .../server/rest/AuthMethodInterceptor.java         | 27 +++++++++++++---------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java
index ac0b2518cdc42528b7c0e94153f2b02777c26785..8571ad6b1fb241956f8d437e65ff3f1e7169b015 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java
@@ -33,12 +33,14 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.ext.Provider;
 
+import org.apache.catalina.realm.GenericPrincipal;
+
 import org.jboss.resteasy.core.ResourceMethodInvoker;
 import org.jboss.resteasy.spi.Failure;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.authentication.AuthMethodMapping;
-import com.netscape.certsrv.authentication.AuthToken;
+import com.netscape.certsrv.authentication.ExternalAuthToken;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.base.ForbiddenException;
 import com.netscape.cms.realm.PKIPrincipal;
@@ -139,14 +141,11 @@ public class AuthMethodInterceptor implements ContainerRequestFilter {
                 throw new ForbiddenException("Anonymous access not allowed.");
             }
 
-            // If unrecognized principal, reject request.
-            if (!(principal instanceof PKIPrincipal)) {
-                CMS.debug("AuthMethodInterceptor: unknown principal");
-                throw new ForbiddenException("Unknown user principal");
-            }
-
-            PKIPrincipal pkiPrincipal = (PKIPrincipal) principal;
-            IAuthToken authToken = pkiPrincipal.getAuthToken();
+            IAuthToken authToken = null;
+            if (principal instanceof PKIPrincipal)
+                authToken = ((PKIPrincipal) principal).getAuthToken();
+            else if (principal instanceof GenericPrincipal)
+                authToken = new ExternalAuthToken((GenericPrincipal) principal);
 
             // If missing auth token, reject request.
             if (authToken == null) {
@@ -154,7 +153,8 @@ public class AuthMethodInterceptor implements ContainerRequestFilter {
                 throw new ForbiddenException("Missing authentication token.");
             }
 
-            String authManager = (String) authToken.get(AuthToken.TOKEN_AUTHMGR_INST_NAME);
+            String authManager = authToken.getInString(IAuthToken.TOKEN_AUTHMGR_INST_NAME);
+
             CMS.debug("AuthMethodInterceptor: authentication manager: " + authManager);
 
             if (authManager == null) {
@@ -162,7 +162,12 @@ public class AuthMethodInterceptor implements ContainerRequestFilter {
                 throw new ForbiddenException("Missing authentication manager.");
             }
 
-            if (authMethods.isEmpty() || authMethods.contains(authManager) || authMethods.contains("*")) {
+            if (
+                authMethods.isEmpty()
+                || authManager.equals("external")
+                || authMethods.contains(authManager)
+                || authMethods.contains("*")
+            ) {
                 CMS.debug("AuthMethodInterceptor: access granted");
                 return;
             }
-- 
2.9.3

-------------- next part --------------
From 80a5ac922611dfe1a14306b8c1b89c7464c4eeeb Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 29 Nov 2016 18:39:45 +1000
Subject: [PATCH 170/175] Update SessionContextInterceptor to handle external
 principals

Part of: https://pagure.io/dogtagpki/issue/1359
---
 .../server/rest/SessionContextInterceptor.java        | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java
index b6461abfdee36ea4eeba4d07da815482b02712ba..b3b3c3b8ff377ba602060d79c50bbc1d9081fd70 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java
@@ -29,9 +29,11 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.ext.Provider;
 
+import org.apache.catalina.realm.GenericPrincipal;
 import org.jboss.resteasy.core.ResourceMethodInvoker;
 
 import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.ExternalAuthToken;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.base.ForbiddenException;
 import com.netscape.certsrv.base.SessionContext;
@@ -80,14 +82,12 @@ public class SessionContextInterceptor implements ContainerRequestFilter {
 
         CMS.debug("SessionContextInterceptor: principal: " + principal.getName());
 
-        // If unrecognized principal, reject request.
-        if (!(principal instanceof PKIPrincipal)) {
-            CMS.debug("SessionContextInterceptor: Invalid user principal.");
-            throw new ForbiddenException("Invalid user principal.");
-        }
+        IAuthToken authToken = null;
 
-        PKIPrincipal pkiPrincipal = (PKIPrincipal) principal;
-        IAuthToken authToken = pkiPrincipal.getAuthToken();
+        if (principal instanceof PKIPrincipal)
+            authToken = ((PKIPrincipal) principal).getAuthToken();
+        else if (principal instanceof GenericPrincipal)
+            authToken = new ExternalAuthToken((GenericPrincipal) principal);
 
         // If missing auth token, reject request.
         if (authToken == null) {
@@ -104,7 +104,8 @@ public class SessionContextInterceptor implements ContainerRequestFilter {
         context.put(SessionContext.LOCALE, locale);
 
         context.put(SessionContext.AUTH_TOKEN, authToken);
-        context.put(SessionContext.USER_ID, pkiPrincipal.getName());
-        context.put(SessionContext.USER, pkiPrincipal.getUser());
+        context.put(SessionContext.USER_ID, principal.getName());
+        if (principal instanceof PKIPrincipal)
+            context.put(SessionContext.USER, ((PKIPrincipal) principal).getUser());
     }
 }
-- 
2.9.3

-------------- next part --------------
From 5421a7b9fd925018c037a254b7287756ee2f301c Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 29 Nov 2016 18:43:48 +1000
Subject: [PATCH 171/175] Update ACLInterceptor to support external principals

For external principal support, ACLInterceptor must handle
GenericPrincipal instances in addition to PKIPrincipal.
Specifically, if the principal is a GenericPrincipal, the auth token
is set to an ExternalAuthToken, and the authz manager is looked up
by the realm of the principal (it is assumed that the principal name
has the form "id at realm").

Part of: https://pagure.io/dogtagpki/issue/1359
---
 .../org/dogtagpki/server/rest/ACLInterceptor.java  | 41 +++++++++++++++-------
 1 file changed, 29 insertions(+), 12 deletions(-)

diff --git a/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java
index 49001168130831bbb002711120891195b5d54ba5..8e02ec21c438426fe7f0b3e5d571084b7f784a70 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java
@@ -31,14 +31,17 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.ext.Provider;
 
+import org.apache.catalina.realm.GenericPrincipal;
 import org.jboss.resteasy.core.ResourceMethodInvoker;
 import org.jboss.resteasy.spi.Failure;
 
 import com.netscape.certsrv.acls.ACLMapping;
 import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.ExternalAuthToken;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.authorization.AuthzToken;
 import com.netscape.certsrv.authorization.EAuthzAccessDenied;
+import com.netscape.certsrv.authorization.EAuthzUnknownRealm;
 import com.netscape.certsrv.authorization.IAuthzSubsystem;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.base.ForbiddenException;
@@ -140,18 +143,33 @@ public class ACLInterceptor implements ContainerRequestFilter {
         if (principal != null)
             CMS.debug("ACLInterceptor: principal: " + principal.getName());
 
-        // If unrecognized principal, reject request.
-        if (principal != null && !(principal instanceof PKIPrincipal)) {
-            CMS.debug("ACLInterceptor: Invalid user principal.");
-            // audit comment: no Principal, no one to blame here
-            throw new ForbiddenException("Invalid user principal.");
-        }
+        IAuthzSubsystem authzSubsystem =
+            (IAuthzSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTHZ);
 
-        PKIPrincipal pkiPrincipal = null;
         IAuthToken authToken = null;
+        String authzMgrName = null;
         if (principal != null) {
-            pkiPrincipal = (PKIPrincipal) principal;
-            authToken = pkiPrincipal.getAuthToken();
+            if (principal instanceof PKIPrincipal) {
+                authzMgrName = "DirAclAuthz";
+                authToken = ((PKIPrincipal) principal).getAuthToken();
+            }
+            else if (principal instanceof GenericPrincipal) {
+                String realm = null;
+                String[] parts = principal.getName().split("@", 2);
+                if (parts.length == 2) {
+                    realm = parts[1];
+                }
+                try {
+                    authzMgrName = authzSubsystem.getAuthzManagerNameByRealm(realm);
+                } catch (EAuthzUnknownRealm e) {
+                    throw new ForbiddenException(
+                        "Cannot find AuthzManager for external principal " + principal.getName(),
+                        e
+                    );
+                }
+                authToken = new ExternalAuthToken((GenericPrincipal) principal);
+            }
+            CMS.debug("ACLInterceptor: will use authz manager " + authzMgrName);
         }
 
         // If missing auth token, reject request.
@@ -249,9 +267,8 @@ public class ACLInterceptor implements ContainerRequestFilter {
 
         try {
             // Check authorization.
-            IAuthzSubsystem mAuthz = (IAuthzSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTHZ);
-            AuthzToken authzToken = mAuthz.authorize(
-                    "DirAclAuthz",
+            AuthzToken authzToken = authzSubsystem.authorize(
+                    authzMgrName,
                     authToken,
                     values[0], // resource
                     values[1]); // operation
-- 
2.9.3

-------------- next part --------------
From 747bf98090677e2bc3bcfa3bb70ca15c0a04e80e Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 7 Feb 2017 10:36:20 +1000
Subject: [PATCH 172/175] Allow arbitrary user data in cert request

If a certificate request comes with additional data in the
'cert-request' query param, add that to the request.  Profile
components can then use this data.

This is needed to convey the subject principal name to the
ExternalProcessConstraint, when validating FreeIPA certificate
requests after we switch to GSS-API authentication.

Part of: https://pagure.io/dogtagpki/issue/1359
---
 base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java     | 5 +++++
 base/common/src/com/netscape/certsrv/request/IRequest.java           | 5 +++++
 .../cms/src/com/netscape/cms/profile/common/EnrollProfile.java       | 3 +++
 .../cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java   | 5 +++++
 4 files changed, 18 insertions(+)

diff --git a/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java b/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
index 12667120e3d87deecb786965b4abcef492ac556d..34543cb72aba426402bdf6dafe4e7b59f8a4b30e 100644
--- a/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
+++ b/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
@@ -180,6 +180,11 @@ public interface IEnrollProfile extends IProfile {
     public static final String REQUEST_AUTHORITY_ID = "req_authority_id";
 
     /**
+     * Arbitrary user-supplied data.
+     */
+    public static final String REQUEST_USER_DATA = "req_user_data";
+
+    /**
      * Set Default X509CertInfo in the request.
      *
      * @param request profile-based certificate request.
diff --git a/base/common/src/com/netscape/certsrv/request/IRequest.java b/base/common/src/com/netscape/certsrv/request/IRequest.java
index 29b1bbb879220a485388cb38af8a8c5508578752..d929ce24b03b9d712d1c9e3a200f3a57e840b440 100644
--- a/base/common/src/com/netscape/certsrv/request/IRequest.java
+++ b/base/common/src/com/netscape/certsrv/request/IRequest.java
@@ -96,6 +96,11 @@ public interface IRequest extends Serializable {
      */
     public static final String AUTHORITY_ID = "req_authority_id";
 
+    /**
+     * Arbitrary user-supplied data that will be saved in request.
+     */
+    public static final String USER_DATA = "user_data";
+
     public static final String RESULT = "Result"; // service result.
     public static final Integer RES_SUCCESS = Integer.valueOf(1); // result value
     public static final Integer RES_ERROR = Integer.valueOf(2); // result value
diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
index 8d10ec26b3db12f68eb9033473b93615d5a6d824..f03e05d72037c2c997acf6ba08f895d3ece64ddb 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
@@ -194,6 +194,9 @@ public abstract class EnrollProfile extends BasicProfile
 
             // set requested CA
             result[i].setExtData(IRequest.AUTHORITY_ID, ctx.get(REQUEST_AUTHORITY_ID));
+
+            // set user data
+            result[i].setExtData(IRequest.USER_DATA, ctx.get(REQUEST_USER_DATA));
         }
         return result;
     }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
index d394fd30c84a0fb7a0f19b31ba4b6973902ea931..908cbe4aecf96c24e2d356394c7ba1ead2cd3a56 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
@@ -147,6 +147,11 @@ public class EnrollmentProcessor extends CertProcessor {
 
             IProfileContext ctx = profile.createContext();
 
+            // set arbitrary user data into request, if any
+            String userData = request.getParameter("user-data");
+            if (userData != null)
+                ctx.set(IEnrollProfile.REQUEST_USER_DATA, userData);
+
             if (aid != null)
                 ctx.set(IEnrollProfile.REQUEST_AUTHORITY_ID, aid.toString());
 
-- 
2.9.3

-------------- next part --------------
From 24a48dbf79c327d57371b91ae6cc4b1997e1fb00 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Wed, 8 Feb 2017 11:55:13 +1000
Subject: [PATCH 173/175] CertProcessor: set external principal attributes into
 request

When processing a certificate request, if the authenticated
principal is an ExternalPrincipal, add its whole attribute map to
the IRequest.  This provides a way for AJP request attributes to be
propagated through the profile system to profile components like
ExternalProcessConstraint.  One such attribute that is needed for
GSS-API support is "KRB5CCNAME".

Part of: https://pagure.io/dogtagpki/issue/1359
---
 .../netscape/cms/servlet/cert/CertProcessor.java    | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/CertProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/CertProcessor.java
index 47b522208af05486a22abdd6196d8385dd615857..be59e4c0a94b603a6f077352ab7b3117cd266b32 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/CertProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/CertProcessor.java
@@ -18,6 +18,7 @@
 package com.netscape.cms.servlet.cert;
 
 import java.math.BigInteger;
+import java.security.Principal;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -26,6 +27,7 @@ import java.util.Locale;
 import javax.servlet.http.HttpServletRequest;
 
 import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.ExternalAuthToken;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.base.EPropertyNotFound;
@@ -45,6 +47,7 @@ import com.netscape.certsrv.request.RequestId;
 import com.netscape.certsrv.request.RequestStatus;
 import com.netscape.cms.servlet.common.AuthCredentials;
 import com.netscape.cms.servlet.processors.CAProcessor;
+import com.netscape.cms.tomcat.ExternalPrincipal;
 import com.netscape.cmsutil.ldap.LDAPUtil;
 
 public class CertProcessor extends CAProcessor {
@@ -138,6 +141,24 @@ public class CertProcessor extends CAProcessor {
                 }
             }
         }
+
+        // special processing of ExternalAuthToken / ExternalPrincipal
+        if (authToken instanceof ExternalAuthToken) {
+            Principal principal =
+                ((ExternalAuthToken) authToken).getPrincipal();
+            if (principal instanceof ExternalPrincipal) {
+                HashMap<String, Object> m =
+                    ((ExternalPrincipal) principal).getAttributes();
+                for (String k : m.keySet()) {
+                    req.setExtData(
+                        IRequest.AUTH_TOKEN_PREFIX
+                            + "." + "PRINCIPAL"
+                            + "." + k
+                        , m.get(k).toString()
+                    );
+                }
+            }
+        }
     }
 
     /*
-- 
2.9.3

-------------- next part --------------
From 7077e6ada1a88373d4c607b925bdaf3d45741bb3 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Wed, 7 Dec 2016 15:24:07 +1000
Subject: [PATCH 174/175] Add ExternalProcessConstraint for request validation

Add the ExternalProcessConstraint profile policy constraint class.
It can be configured to execute an arbitrary program that performs
additional request validation, rejecting the request if it
terminates with a nonzero exit status.  Information about the
request is conveyed in the subprocess' environment.

Part of: https://pagure.io/dogtagpki/issue/1359
---
 base/ca/shared/conf/registry.cfg                   |   5 +-
 .../constraint/ExternalProcessConstraint.java      | 158 +++++++++++++++++++++
 .../04-AddExternalProcessConstraintToRegistry      |  67 +++++++++
 3 files changed, 229 insertions(+), 1 deletion(-)
 create mode 100644 base/server/cms/src/com/netscape/cms/profile/constraint/ExternalProcessConstraint.java
 create mode 100755 base/server/upgrade/10.4.0/04-AddExternalProcessConstraintToRegistry

diff --git a/base/ca/shared/conf/registry.cfg b/base/ca/shared/conf/registry.cfg
index 280c71388e8f1575a8785c2009b3c728b2824876..2855b7ad7d5ae158838dec0e610a2d061702cb94 100644
--- a/base/ca/shared/conf/registry.cfg
+++ b/base/ca/shared/conf/registry.cfg
@@ -1,5 +1,5 @@
 types=profile,defaultPolicy,constraintPolicy,profileInput,profileOutput,profileUpdater
-constraintPolicy.ids=noConstraintImpl,subjectNameConstraintImpl,uniqueSubjectNameConstraintImpl,userSubjectNameConstraintImpl,validityConstraintImpl,keyUsageExtConstraintImpl,nsCertTypeExtConstraintImpl,extendedKeyUsageExtConstraintImpl,keyConstraintImpl,basicConstraintsExtConstraintImpl,extensionConstraintImpl,signingAlgConstraintImpl,uniqueKeyConstraintImpl,renewGracePeriodConstraintImpl,authzRealmConstraintImpl
+constraintPolicy.ids=noConstraintImpl,subjectNameConstraintImpl,uniqueSubjectNameConstraintImpl,userSubjectNameConstraintImpl,validityConstraintImpl,keyUsageExtConstraintImpl,nsCertTypeExtConstraintImpl,extendedKeyUsageExtConstraintImpl,keyConstraintImpl,basicConstraintsExtConstraintImpl,extensionConstraintImpl,signingAlgConstraintImpl,uniqueKeyConstraintImpl,renewGracePeriodConstraintImpl,authzRealmConstraintImpl,externalProcessConstraintImpl
 constraintPolicy.signingAlgConstraintImpl.class=com.netscape.cms.profile.constraint.SigningAlgConstraint
 constraintPolicy.signingAlgConstraintImpl.desc=Signing Algorithm Constraint
 constraintPolicy.signingAlgConstraintImpl.name=Signing Algorithm Constraint
@@ -45,6 +45,9 @@ constraintPolicy.renewGracePeriodConstraintImpl.name=Renewal Grace Period Constr
 constraintPolicy.uniqueKeyConstraintImpl.class=com.netscape.cms.profile.constraint.UniqueKeyConstraint
 constraintPolicy.uniqueKeyConstraintImpl.desc=Unique Public Key Constraint
 constraintPolicy.uniqueKeyConstraintImpl.name=Unique Public Key Constraint
+constraintPolicy.externalProcessConstraintImpl.class=com.netscape.cms.profile.constraint.ExternalProcessConstraint
+constraintPolicy.externalProcessConstraintImpl.desc=External Process Constraint
+constraintPolicy.externalProcessConstraintImpl.name=External Process Constraint
 defaultPolicy.ids=noDefaultImpl,genericExtDefaultImpl,autoAssignDefaultImpl,subjectNameDefaultImpl,validityDefaultImpl,randomizedValidityDefaultImpl,caValidityDefaultImpl,subjectKeyIdentifierExtDefaultImpl,authorityKeyIdentifierExtDefaultImpl,basicConstraintsExtDefaultImpl,keyUsageExtDefaultImpl,nsCertTypeExtDefaultImpl,extendedKeyUsageExtDefaultImpl,ocspNoCheckExtDefaultImpl,issuerAltNameExtDefaultImpl,subjectAltNameExtDefaultImpl,userSubjectNameDefaultImpl,signingAlgDefaultImpl,userKeyDefaultImpl,userValidityDefaultImpl,userExtensionDefaultImpl,userSigningAlgDefaultImpl,authTokenSubjectNameDefaultImpl,subjectInfoAccessExtDefaultImpl,authInfoAccessExtDefaultImpl,nscCommentExtDefaultImpl,freshestCRLExtDefaultImpl,crlDistributionPointsExtDefaultImpl,policyConstraintsExtDefaultImpl,policyMappingsExtDefaultImpl,nameConstraintsExtDefaultImpl,certificateVersionDefaultImpl,certificatePoliciesExtDefaultImpl,subjectDirAttributesExtDefaultImpl,privateKeyPeriodExtDefaultImpl,inhibitAnyPolicyExtDefaultImpl,imageDefaultImpl,nsTokenDeviceKeySubjectNameDefaultImpl,nsTokenUserKeySubjectNameDefaultImpl,authzRealmDefaultImpl,commonNameToSANDefaultImpl
 defaultPolicy.autoAssignDefaultImpl.class=com.netscape.cms.profile.def.AutoAssignDefault
 defaultPolicy.autoAssignDefaultImpl.desc=Auto Request Assignment Default
diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/ExternalProcessConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/ExternalProcessConstraint.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ee0d46feea7a56f917621d1ce1fd85bc6b36b7c
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/profile/constraint/ExternalProcessConstraint.java
@@ -0,0 +1,158 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2016, 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cms.profile.constraint;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.IOUtils;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthToken;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.profile.EProfileException;
+import com.netscape.certsrv.profile.ERejectException;
+import com.netscape.certsrv.profile.IProfile;
+import com.netscape.certsrv.property.Descriptor;
+import com.netscape.certsrv.property.IDescriptor;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.cms.profile.input.CertReqInput;
+
+import netscape.security.x509.X509CertInfo;
+
+
+public class ExternalProcessConstraint extends EnrollConstraint {
+
+    public static final String CONFIG_EXECUTABLE = "executable";
+
+    public static final long DEFAULT_TIMEOUT = 10;
+
+    /* Map of envvars to include, and the corresponding IRequest keys
+     *
+     * All keys will be prefixed with "DOGTAG_" when added to environment.
+     */
+    protected static final Map<String, String> envVars = new TreeMap<>();
+
+    protected Map<String, String> extraEnvVars = new TreeMap<>();
+
+    static {
+        envVars.put("DOGTAG_CERT_REQUEST", CertReqInput.VAL_CERT_REQUEST);
+        envVars.put("DOGTAG_USER",
+            IRequest.AUTH_TOKEN_PREFIX + "." + IAuthToken.USER_ID);
+        envVars.put("DOGTAG_PROFILE_ID", IRequest.PROFILE_ID);
+        envVars.put("DOGTAG_AUTHORITY_ID", IRequest.AUTHORITY_ID);
+        envVars.put("DOGTAG_USER_DATA", IRequest.USER_DATA);
+    }
+
+    protected String executable;
+
+    public ExternalProcessConstraint() {
+        addConfigName(CONFIG_EXECUTABLE);
+    }
+
+    public void init(IProfile profile, IConfigStore config)
+            throws EProfileException {
+        super.init(profile, config);
+
+        this.executable = getConfig(CONFIG_EXECUTABLE);
+        if (this.executable == null || this.executable.isEmpty()) {
+            throw new EProfileException(
+                "Missing required config param 'executable'");
+        }
+
+        IConfigStore envConfig = config.getSubStore("params.env");
+        Enumeration<String> names = envConfig.getPropertyNames();
+        while (names.hasMoreElements()) {
+            String name = names.nextElement();
+            try {
+                extraEnvVars.put(name, envConfig.getString(name));
+            } catch (EBaseException e) {
+                // shouldn't happen; log and move on
+                CMS.debug(
+                    "ExternalProcessConstraint: caught exception processing "
+                    + "'params.env' config: " + e
+                );
+
+            }
+        }
+    }
+
+    public IDescriptor getConfigDescriptor(Locale locale, String name) {
+        if (name.equals(CONFIG_EXECUTABLE)) {
+            return new Descriptor(
+                IDescriptor.STRING, null, null, "Executable path");
+        } else {
+            return null;
+        }
+    }
+
+    public void validate(IRequest request, X509CertInfo info)
+            throws ERejectException {
+        CMS.debug("About to execute command: " + this.executable);
+        ProcessBuilder pb = new ProcessBuilder(this.executable);
+
+        // set up process environment
+        Map<String, String> env = pb.environment();
+        for (String k : envVars.keySet()) {
+            String v = request.getExtDataInString(envVars.get(k));
+            if (v != null)
+                env.put(k, v);
+        }
+        for (String k : extraEnvVars.keySet()) {
+            String v = request.getExtDataInString(extraEnvVars.get(k));
+            if (v != null)
+                env.put(k, v);
+        }
+
+        Process p;
+        String stdout = "";
+        String stderr = "";
+        boolean timedOut;
+        try {
+            p = pb.start();
+            timedOut = !p.waitFor(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
+            if (timedOut)
+                p.destroyForcibly();
+            else
+                stdout = IOUtils.toString(p.getInputStream());
+                stderr = IOUtils.toString(p.getErrorStream());
+        } catch (Throwable e) {
+            String msg =
+                "Caught exception while executing command: " + this.executable;
+            CMS.debug(msg);
+            CMS.debug(e);
+            throw new ERejectException(msg, e);
+        }
+        if (timedOut)
+            throw new ERejectException("Request validation timed out");
+        int exitValue = p.exitValue();
+        CMS.debug("ExternalProcessConstraint: exit value: " + exitValue);
+        CMS.debug("ExternalProcessConstraint: stdout: " + stdout);
+        CMS.debug("ExternalProcessConstraint: stderr: " + stderr);
+        if (exitValue != 0)
+            throw new ERejectException(stdout);
+    }
+
+}
diff --git a/base/server/upgrade/10.4.0/04-AddExternalProcessConstraintToRegistry b/base/server/upgrade/10.4.0/04-AddExternalProcessConstraintToRegistry
new file mode 100755
index 0000000000000000000000000000000000000000..a9ee00aece2cd73b135fcd30402d81140bbc086e
--- /dev/null
+++ b/base/server/upgrade/10.4.0/04-AddExternalProcessConstraintToRegistry
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+# Authors:
+#     Fraser Tweedale <ftweedal at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2017 Red Hat, Inc.
+# All rights reserved.
+
+from __future__ import absolute_import
+import os.path
+
+import pki
+from pki.server.upgrade import PKIServerUpgradeScriptlet
+
+
+class AddExternalProcessConstraintToRegistry(PKIServerUpgradeScriptlet):
+
+    new_config = {
+        'constraintPolicy.externalProcessConstraintImpl.class':
+            'com.netscape.cms.profile.constraint.ExternalProcessConstraint',
+        'constraintPolicy.externalProcessConstraintImpl.desc':
+            'External Process Constraint',
+        'constraintPolicy.externalProcessConstraintImpl.name':
+            'External Process Constraint',
+    }
+
+    constraint_name = 'externalProcessConstraintImpl'
+
+    def __init__(self):
+        super(AddExternalProcessConstraintToRegistry, self).__init__()
+        self.message = 'Add ExternalProcessConstraint to registry'
+
+    def upgrade_subsystem(self, instance, subsystem):
+        if subsystem.name == 'ca':
+            self.add_new_entries(instance, subsystem)
+
+    def add_new_entries(self, instance, subsystem):  # pylint: disable=W0613
+        filename = os.path.join(subsystem.conf_dir, 'registry.cfg')
+        self.backup(filename)
+
+        properties = pki.PropertyFile(filename)
+        properties.read()
+
+        # add constraint to constraint list
+        constraints = properties.get('constraintPolicy.ids').split(',')
+        if self.constraint_name in constraints:
+            return  # update not required
+
+        constraints.append(self.constraint_name)
+        properties.set('constraintPolicy.ids', ','.join(constraints))
+
+        for k, v in self.new_config.items():
+            properties.set(k, v)
+
+        properties.write()
-- 
2.9.3

-------------- next part --------------
From 0a5f725bd3a44b202ab12010381b5469c3118d13 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Wed, 8 Feb 2017 12:18:03 +1000
Subject: [PATCH 175/175] Add authn manager that reuses auth token from session

To process a cert request immediately (rather than having it queued
as pending), the user must be authenticated *by the profile*; auth
tokens from the main authentication system are not used.

For external authentication support it is possible that the external
authentication is sufficient to authenticate use of a problem;
especially when the profile uses componenets like
ExternalProcessConstraint to perform validation of the cert request
against external sources of information.

To support this use case, add the SessionAuthentication profile
authenticator, which merely reuses the IAuthToken from the session
context, if present.

Part of: https://pagure.io/dogtagpki/issue/1359
---
 base/ca/shared/conf/CS.cfg                         |   2 +
 .../cms/authentication/SessionAuthentication.java  | 167 +++++++++++++++++++++
 .../10.4.0/05-AddSessionAuthenticationPlugin       |  51 +++++++
 3 files changed, 220 insertions(+)
 create mode 100644 base/server/cms/src/com/netscape/cms/authentication/SessionAuthentication.java
 create mode 100755 base/server/upgrade/10.4.0/05-AddSessionAuthenticationPlugin

diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg
index 3beb45c5392427dec411fda0bb12769b9d279f43..e4bbe5f35cf18a4f725713a5a75df591c11bd44f 100644
--- a/base/ca/shared/conf/CS.cfg
+++ b/base/ca/shared/conf/CS.cfg
@@ -175,6 +175,7 @@ auths.impl.UidPwdGroupDirAuth.class=com.netscape.cms.authentication.UidPwdGroupD
 auths.impl.UserPwdDirAuth.class=com.netscape.cms.authentication.UserPwdDirAuthentication
 auths.impl.TokenAuth.class=com.netscape.cms.authentication.TokenAuthentication
 auths.impl.FlatFileAuth.class=com.netscape.cms.authentication.FlatFileAuth
+auths.impl.SessionAuthentication.class=com.netscape.cms.authentication.SessionAuthentication
 auths.instance.TokenAuth.pluginName=TokenAuth
 auths.instance.AgentCertAuth.agentGroup=Certificate Manager Agents
 auths.instance.AgentCertAuth.pluginName=AgentCertAuth
@@ -183,6 +184,7 @@ auths.instance.raCertAuth.pluginName=AgentCertAuth
 auths.instance.flatFileAuth.pluginName=FlatFileAuth
 auths.instance.flatFileAuth.fileName=[PKI_INSTANCE_PATH]/conf/[PKI_SUBSYSTEM_TYPE]/flatfile.txt
 auths.instance.SSLclientCertAuth.pluginName=SSLclientCertAuth
+auths.instance.SessionAuthentication.pluginName=SessionAuthentication
 auths.revocationChecking.bufferSize=50
 auths.revocationChecking.ca=ca
 auths.revocationChecking.enabled=true
diff --git a/base/server/cms/src/com/netscape/cms/authentication/SessionAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/SessionAuthentication.java
new file mode 100644
index 0000000000000000000000000000000000000000..27f08cd9989361709825ec3a632c123a3f7fc0ad
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/authentication/SessionAuthentication.java
@@ -0,0 +1,167 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cms.authentication;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.AuthToken;
+import com.netscape.certsrv.authentication.EMissingCredential;
+import com.netscape.certsrv.authentication.IAuthCredentials;
+import com.netscape.certsrv.authentication.IAuthToken;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.profile.IProfile;
+import com.netscape.certsrv.profile.IProfileAuthenticator;
+import com.netscape.certsrv.property.IDescriptor;
+import com.netscape.certsrv.request.IRequest;
+
+/**
+ * Pull any existing auth token from the session context.
+ *
+ * Use with caution as a profile authenticator; if there is a
+ * session it will unconditionally approve the request
+ * (subject to constraints, etc).
+ */
+public class SessionAuthentication
+        implements IProfileAuthenticator {
+
+    private String instName = null;
+    private String implName = null;
+    private IConfigStore config = null;
+
+    public SessionAuthentication() {
+    }
+
+    public void init(String instName, String implName, IConfigStore config)
+            throws EBaseException {
+        this.instName = instName;
+        this.implName = implName;
+        this.config = config;
+    }
+
+    /**
+     * Gets the name of this authentication manager.
+     */
+    public String getName() {
+        return instName;
+    }
+
+    /**
+     * Gets the plugin name of authentication manager.
+     */
+    public String getImplName() {
+        return implName;
+    }
+
+    public boolean isSSLClientRequired() {
+        return false;
+    }
+
+    /**
+     * Authenticate user.
+     *
+     * @return the auth token from existing session context, if any.
+     * @throws EMissingCredential if no auth token or no session
+     */
+    public IAuthToken authenticate(IAuthCredentials authCred)
+            throws EMissingCredential {
+        SessionContext context = SessionContext.getExistingContext();
+
+        if (context == null)
+            throw new EMissingCredential("SessionAuthentication: no session");
+
+        IAuthToken authToken = (IAuthToken)
+            context.get(SessionContext.AUTH_TOKEN);
+
+        if (authToken == null)
+            throw new EMissingCredential("SessionAuthentication: no auth token");
+
+        return authToken;
+    }
+
+    public String[] getRequiredCreds() {
+        String[] requiredCreds = { };
+        return requiredCreds;
+    }
+
+    public String[] getConfigParams() {
+        return null;
+    }
+
+    /**
+     * prepare this authentication manager for shutdown.
+     */
+    public void shutdown() {
+    }
+
+    /**
+     * gets the configuretion substore used by this authentication
+     * manager
+     *
+     * @return configuration store
+     */
+    public IConfigStore getConfigStore() {
+        return config;
+    }
+
+    // Profile-related methods
+
+    public void init(IProfile profile, IConfigStore config) {
+    }
+
+    /**
+     * Retrieves the localizable name of this policy.
+     */
+    public String getName(Locale locale) {
+        return CMS.getUserMessage(locale, "CMS_AUTHENTICATION_AGENT_NAME");
+    }
+
+    /**
+     * Retrieves the localizable description of this policy.
+     */
+    public String getText(Locale locale) {
+        return CMS.getUserMessage(locale, "CMS_AUTHENTICATION_AGENT_TEXT");
+    }
+
+    /**
+     * Retrieves a list of names of the value parameter.
+     */
+    public Enumeration<String> getValueNames() {
+        return Collections.emptyEnumeration();
+    }
+
+    public boolean isValueWriteable(String name) {
+        return false;
+    }
+
+    /**
+     * Retrieves the descriptor of the given value
+     * parameter by name.
+     */
+    public IDescriptor getValueDescriptor(Locale locale, String name) {
+        return null;
+    }
+
+    public void populate(IAuthToken token, IRequest request) {
+    }
+}
diff --git a/base/server/upgrade/10.4.0/05-AddSessionAuthenticationPlugin b/base/server/upgrade/10.4.0/05-AddSessionAuthenticationPlugin
new file mode 100755
index 0000000000000000000000000000000000000000..62d508ed199f2643bc05bea78c7a80b22188ec4a
--- /dev/null
+++ b/base/server/upgrade/10.4.0/05-AddSessionAuthenticationPlugin
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+# Authors:
+#     Fraser Tweedale <ftweedal at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2017 Red Hat, Inc.
+# All rights reserved.
+
+from __future__ import absolute_import
+import os.path
+
+import pki
+from pki.server.upgrade import PKIServerUpgradeScriptlet
+
+
+class AddSessionAuthenticationPlugin(PKIServerUpgradeScriptlet):
+    def __init__(self):
+        super(AddSessionAuthenticationPlugin, self).__init__()
+        self.message = 'Add SessionAuthentication to CS.cfg'
+
+    def upgrade_subsystem(self, instance, subsystem):
+        if subsystem.name == 'ca':
+            self.add_plugin(instance, subsystem)
+
+    def add_plugin(self, instance, subsystem):  # pylint: disable=W0613
+        filename = os.path.join(subsystem.conf_dir, 'CS.cfg')
+        self.backup(filename)
+
+        properties = pki.PropertyFile(filename)
+        properties.read()
+
+        properties.set(
+            'auths.impl.SessionAuthentication.class',
+            'com.netscape.cms.authentication.SessionAuthentication')
+        properties.set(
+            'auths.instance.SessionAuthentication.pluginName',
+            'SessionAuthentication')
+
+        properties.write()
-- 
2.9.3



More information about the Pki-devel mailing list