SPNEGO working
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 30 Jun 2022 16:12:08 +0000 (18:12 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 30 Jun 2022 16:12:08 +0000 (18:12 +0200)
eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java
org.argeo.cms/build.properties
org.argeo.cms/src/org/argeo/cms/CmsDeployProperty.java
org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java
org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/jaas-ipa.cfg
org.argeo.util/src/org/argeo/util/naming/dns/DnsBrowser.java

index cc2bc02d1978e46bc34e3149f0779baf2728522a..9cb48b212d38b5db6054c6fefac76dc00a9a00e3 100644 (file)
@@ -41,6 +41,8 @@ public class CmsServletContext extends ServletContextHelper {
        public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
                if (log.isTraceEnabled())
                        HttpUtils.logRequestHeaders(log, request);
+               ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
+               Thread.currentThread().setContextClassLoader(CmsServletContext.class.getClassLoader());
                LoginContext lc;
                try {
                        lc = CmsAuth.USER.newLoginContext(
@@ -52,6 +54,8 @@ public class CmsServletContext extends ServletContextHelper {
                                HttpUtils.logResponseHeaders(log, response);
                        if (lc == null)
                                return false;
+               } finally {
+                       Thread.currentThread().setContextClassLoader(currentThreadContextClassLoader);
                }
 
                Subject subject = lc.getSubject();
@@ -79,7 +83,7 @@ public class CmsServletContext extends ServletContextHelper {
                ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
                try {
                        Thread.currentThread().setContextClassLoader(CmsServletContext.class.getClassLoader());
-                       LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
+                       LoginContext lc = CmsAuth.ANONYMOUS.newLoginContext(
                                        new RemoteAuthCallbackHandler(new ServletHttpRequest(request), new ServletHttpResponse(response)));
                        lc.login();
                        return lc;
index 317c9cbb87f7bc98a77f26db34707d2b8fadb68b..8fec5e54bb39dc088fa5f5755bd90116dc35dfa8 100644 (file)
@@ -12,3 +12,7 @@ bin.includes = META-INF/,\
                OSGI-INF/acrContentRepository.xml,\
                OSGI-INF/uuidFactory.xml
 source.. = src/
+additional.bundles = org.argeo.ext.slf4j,\
+                     org.slf4j.commons.logging,\
+                     org.slf4j.api,\
+                     org.apache.commons.codec
index ef1edcc1df31c0133dde07b9462e870b6c13a0c2..243c228513f54c7af549727966fd515507b7e020 100644 (file)
@@ -22,6 +22,8 @@ public enum CmsDeployProperty {
        //
        /** Either a host or an IP address. Restricts all servers to it. */
        HOST("argeo.host"),
+       /** Either a host or an IP address. Restricts all servers to it. */
+       DNS("argeo.dns", 16),
        //
        // HTTP
        //
index 27ed2ec3e6407a34c5635f9bf51762473b35aa68..d72e695d5fb5209b3a2974fa989d0c8c406aa457 100644 (file)
@@ -133,8 +133,10 @@ public class SpnegoAuthScheme implements AuthScheme {
        }
 
        public static void main(String[] args) {
-               if (args.length == 0) {
-                       System.err.println("usage: java " + SpnegoAuthScheme.class.getName() + " <url>");
+               String principal = System.getProperty("javax.security.auth.login.name");
+               if (args.length == 0 || principal == null) {
+                       System.err.println("usage: java -Djavax.security.auth.login.name=<principal@REALM> "
+                                       + SpnegoAuthScheme.class.getName() + " <url>");
                        System.exit(1);
                        return;
                }
index 21176b9117de0598c2e2056b9cf6e578c616807e..4dd0e0d8e9e9f17fe70595be49a57b1f0f794a53 100644 (file)
@@ -1,5 +1,4 @@
 SINGLE_USER {
     com.sun.security.auth.module.Krb5LoginModule optional
-     principal="${user.name}"
      useTicketCache=true;
 };
index 9ebc429178ce0276f7a83ae37eba2ebb083c96cf..9675c25429e1ff3d3274a84e2a9e52960bd4cbd3 100644 (file)
@@ -2,7 +2,6 @@ package org.argeo.cms.internal.runtime;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -81,9 +80,9 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                super.start();
                List<Dictionary<String, Object>> configs = getUserDirectoryConfigs();
                for (Dictionary<String, Object> config : configs) {
-                       UserDirectory userDirectory = enableUserDirectory(config);
-                       if (userDirectory.getRealm().isPresent())
-                               loadIpaJaasConfiguration();
+                       enableUserDirectory(config);
+//                     if (userDirectory.getRealm().isPresent())
+//                             loadIpaJaasConfiguration();
                }
                log.debug(() -> "CMS user admin available");
        }
@@ -276,6 +275,7 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
 
                Optional<String> realm = userDirectory.getRealm();
                if (realm.isPresent()) {
+                       loadIpaJaasConfiguration();
                        if (Files.exists(nodeKeyTab)) {
                                String servicePrincipal = getKerberosServicePrincipal(realm.get());
                                if (servicePrincipal != null) {
@@ -289,7 +289,7 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                                                }
                                        };
                                        try {
-                                               LoginContext nodeLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_NODE, callbackHandler);
+                                               LoginContext nodeLc = CmsAuth.NODE.newLoginContext(callbackHandler);
                                                nodeLc.login();
                                                acceptorCredentials = logInAsAcceptor(nodeLc.getSubject(), servicePrincipal);
                                        } catch (LoginException e) {
@@ -335,16 +335,21 @@ public class CmsUserAdmin extends AggregatingUserAdmin {
                }
        }
 
-       private String getKerberosServicePrincipal(String realm) {
-               String hostname;
-               try (DnsBrowser dnsBrowser = new DnsBrowser()) {
-                       InetAddress localhost = InetAddress.getLocalHost();
-                       hostname = localhost.getHostName();
+       protected String getKerberosServicePrincipal(String realm) {
+               if (!Files.exists(nodeKeyTab))
+                       return null;
+               List<String> dns = CmsStateImpl.getDeployProperties(cmsState, CmsDeployProperty.DNS);
+               String hostname = CmsStateImpl.getDeployProperty(cmsState, CmsDeployProperty.HOST);
+               try (DnsBrowser dnsBrowser = new DnsBrowser(dns)) {
+                       hostname = hostname != null ? hostname : InetAddress.getLocalHost().getHostName();
                        String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
-                       String ipfromDns = dnsBrowser.getRecord(hostname, localhost instanceof Inet6Address ? "AAAA" : "A");
-                       boolean consistentIp = localhost.getHostAddress().equals(ipfromDns);
+                       String ipv4fromDns = dnsBrowser.getRecord(hostname, "A");
+                       String ipv6fromDns = dnsBrowser.getRecord(hostname, "AAAA");
+                       if (ipv4fromDns == null && ipv6fromDns == null)
+                               throw new IllegalStateException("hostname " + hostname + " is not registered in DNS");
+                       // boolean consistentIp = localhost.getHostAddress().equals(ipfromDns);
                        String kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
-                       if (consistentIp && kerberosDomain != null && kerberosDomain.equals(realm) && Files.exists(nodeKeyTab)) {
+                       if (kerberosDomain != null && kerberosDomain.equals(realm)) {
                                return KernelConstants.DEFAULT_KERBEROS_SERVICE + "/" + hostname + "@" + kerberosDomain;
                        } else
                                return null;
index c7c804c649ef13b561e57d3611301c9f9c07faaf..d0928aac0ff1482953ead65fedbf61b628b71239 100644 (file)
@@ -28,10 +28,8 @@ KEYRING {
 
 SINGLE_USER {
     com.sun.security.auth.module.Krb5LoginModule optional
-     principal="${user.name}"
      storeKey=true
-     useTicketCache=true
-     debug=true;
+     useTicketCache=true;
     org.argeo.cms.auth.SingleUserLoginModule requisite;
 };
 
index 4bd05268cc8096ce53d631d4ae351f32dd032d0c..9ed0b21c6ecebb60ae62442964da29691e3b21a3 100644 (file)
@@ -9,11 +9,15 @@ import java.util.Collections;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.SortedSet;
+import java.util.StringJoiner;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
 import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
@@ -25,14 +29,21 @@ public class DnsBrowser implements Closeable {
        private final DirContext initialCtx;
 
        public DnsBrowser() throws NamingException {
-               this(null);
+               this(new ArrayList<>());
        }
 
-       public DnsBrowser(String dnsServerUrls) throws NamingException {
+       public DnsBrowser(List<String> dnsServerUrls) throws NamingException {
+               Objects.requireNonNull(dnsServerUrls);
                Hashtable<String, Object> env = new Hashtable<>();
-               env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
-               if (dnsServerUrls != null)
-                       env.put("java.naming.provider.url", dnsServerUrls);
+               env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
+               if (!dnsServerUrls.isEmpty()) {
+                       StringJoiner providerUrl = new StringJoiner(" ");
+                       for (String dnsUrl : dnsServerUrls) {
+                               if (dnsUrl != null)
+                                       providerUrl.add(dnsUrl);
+                       }
+                       env.put(Context.PROVIDER_URL, providerUrl.toString());
+               }
                initialCtx = new InitialDirContext(env);
        }
 
@@ -54,17 +65,23 @@ public class DnsBrowser implements Closeable {
         * Return a single record (typically A, AAAA, etc. or null if not available.
         * Will fail if multiple records.
         */
-       public String getRecord(String name, String recordType) throws NamingException {
-               Attributes attrs = initialCtx.getAttributes(name, new String[] { recordType });
-               if (attrs.size() == 0)
+       public String getRecord(String name, String recordType) {
+               try {
+                       Attributes attrs = initialCtx.getAttributes(name, new String[] { recordType });
+                       if (attrs.size() == 0)
+                               return null;
+                       Attribute attr = attrs.get(recordType);
+                       if (attr.size() > 1)
+                               throw new IllegalArgumentException("Multiple record type " + recordType);
+                       assert attr.size() != 0;
+                       Object value = attr.get();
+                       assert value != null;
+                       return value.toString();
+               } catch (NameNotFoundException e) {
                        return null;
-               Attribute attr = attrs.get(recordType);
-               if (attr.size() > 1)
-                       throw new IllegalArgumentException("Multiple record type " + recordType);
-               assert attr.size() != 0;
-               Object value = attr.get();
-               assert value != null;
-               return value.toString();
+               } catch (NamingException e) {
+                       throw new IllegalStateException("Cannot get DNS entry " + recordType + " of " + name, e);
+               }
        }
 
        /**