From 9e3e4ceb38e36ee8d7b4287a60cd92f2b1a66a2a Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 30 Jun 2022 18:12:08 +0200 Subject: [PATCH] SPNEGO working --- .../argeo/cms/servlet/CmsServletContext.java | 6 ++- org.argeo.cms/build.properties | 4 ++ .../src/org/argeo/cms/CmsDeployProperty.java | 2 + .../http/client/SpnegoAuthScheme.java | 6 ++- .../argeo/cms/internal/http/client/jaas.cfg | 1 - .../cms/internal/runtime/CmsUserAdmin.java | 31 +++++++----- .../argeo/cms/internal/runtime/jaas-ipa.cfg | 4 +- .../org/argeo/util/naming/dns/DnsBrowser.java | 47 +++++++++++++------ 8 files changed, 66 insertions(+), 35 deletions(-) diff --git a/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java index cc2bc02d1..9cb48b212 100644 --- a/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java +++ b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java @@ -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; diff --git a/org.argeo.cms/build.properties b/org.argeo.cms/build.properties index 317c9cbb8..8fec5e54b 100644 --- a/org.argeo.cms/build.properties +++ b/org.argeo.cms/build.properties @@ -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 diff --git a/org.argeo.cms/src/org/argeo/cms/CmsDeployProperty.java b/org.argeo.cms/src/org/argeo/cms/CmsDeployProperty.java index ef1edcc1d..243c22851 100644 --- a/org.argeo.cms/src/org/argeo/cms/CmsDeployProperty.java +++ b/org.argeo.cms/src/org/argeo/cms/CmsDeployProperty.java @@ -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 // diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java index 27ed2ec3e..d72e695d5 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java @@ -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() + " "); + 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= " + + SpnegoAuthScheme.class.getName() + " "); System.exit(1); return; } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg b/org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg index 21176b911..4dd0e0d8e 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/jaas.cfg @@ -1,5 +1,4 @@ SINGLE_USER { com.sun.security.auth.module.Krb5LoginModule optional - principal="${user.name}" useTicketCache=true; }; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java index 9ebc42917..9675c2542 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java @@ -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> configs = getUserDirectoryConfigs(); for (Dictionary 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 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 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; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/jaas-ipa.cfg b/org.argeo.cms/src/org/argeo/cms/internal/runtime/jaas-ipa.cfg index c7c804c64..d0928aac0 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/jaas-ipa.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/jaas-ipa.cfg @@ -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; }; diff --git a/org.argeo.util/src/org/argeo/util/naming/dns/DnsBrowser.java b/org.argeo.util/src/org/argeo/util/naming/dns/DnsBrowser.java index 4bd05268c..9ed0b21c6 100644 --- a/org.argeo.util/src/org/argeo/util/naming/dns/DnsBrowser.java +++ b/org.argeo.util/src/org/argeo/util/naming/dns/DnsBrowser.java @@ -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 dnsServerUrls) throws NamingException { + Objects.requireNonNull(dnsServerUrls); Hashtable 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); + } } /** -- 2.30.2