You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2016/12/07 17:54:17 UTC
karaf git commit: [KARAF-4487] Add support for GSSAPI authentication.
Repository: karaf
Updated Branches:
refs/heads/karaf-4.0.x 74cc54778 -> 3ea2830ea
[KARAF-4487] Add support for GSSAPI authentication.
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/3ea2830e
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/3ea2830e
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/3ea2830e
Branch: refs/heads/karaf-4.0.x
Commit: 3ea2830ead794ea065dab39090c5a8cbea85fb07
Parents: 74cc547
Author: alexandre.cartapanis@gmail.com <al...@gmail.com>
Authored: Wed Aug 24 12:35:10 2016 +0200
Committer: Jean-Baptiste Onofr� <jb...@apache.org>
Committed: Wed Dec 7 18:53:55 2016 +0100
----------------------------------------------------------------------
.../modules/ldap/GSSAPILdapLoginModule.java | 151 +++++++
.../modules/ldap/GSSAPILdapLoginModuleTest.java | 431 +++++++++++++++++++
.../jaas/modules/ldap/gssapi.ldap.properties | 37 ++
.../karaf/jaas/modules/ldap/gssapi.login.config | 26 ++
.../developer-guide/security-framework.adoc | 74 ++++
.../src/main/asciidoc/user-guide/security.adoc | 2 +
6 files changed, 721 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/3ea2830e/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java
----------------------------------------------------------------------
diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java
new file mode 100644
index 0000000..ba6ed5b
--- /dev/null
+++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+package org.apache.karaf.jaas.modules.ldap;
+
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.apache.karaf.jaas.boot.principal.UserPrincipal;
+import org.apache.karaf.jaas.modules.AbstractKarafLoginModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.IOException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Specific LDAPLoginModule to be used with GSSAPI. Uses the specified realm as login context.
+ */
+public class GSSAPILdapLoginModule extends AbstractKarafLoginModule {
+
+ private static Logger logger = LoggerFactory.getLogger(LDAPLoginModule.class);
+
+ public static final String REALM_PROPERTY = "gssapiRealm";
+
+ private LoginContext context;
+
+ @Override
+ public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
+ super.initialize(subject, callbackHandler, options);
+ }
+
+ @Override
+ public boolean login() throws LoginException {
+ if (!options.containsKey(REALM_PROPERTY)) {
+ logger.warn(REALM_PROPERTY + " is not set");
+ throw new LoginException("cannot authenticate through the delegating realm");
+ }
+
+ context = new LoginContext((String) options.get(REALM_PROPERTY), this.subject, this.callbackHandler);
+ context.login();
+
+ try {
+ return Subject.doAs(context.getSubject(), (PrivilegedExceptionAction<Boolean>) () -> doLogin());
+ } catch (PrivilegedActionException pExcp) {
+ logger.error("error with delegated authentication", pExcp);
+ throw new LoginException(pExcp.getMessage());
+ }
+ }
+
+ protected boolean doLogin() throws LoginException {
+
+ //force GSSAPI for login
+ Map<String, Object> opts = new HashMap<>(this.options);
+ opts.put(LDAPOptions.AUTHENTICATION, "GSSAPI");
+
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ try {
+ LDAPOptions lOptions = new LDAPOptions(opts);
+
+ NameCallback[] callbacks = new NameCallback[1];
+ callbacks[0] = new NameCallback("Username: ");
+
+ try {
+ callbackHandler.handle(callbacks);
+ } catch (IOException ioException) {
+ logger.error("error with callback handler", ioException);
+ throw new LoginException(ioException.getMessage());
+ } catch (UnsupportedCallbackException unsupportedCallbackException) {
+ logger.error("error with callback handler", unsupportedCallbackException);
+ throw new LoginException(unsupportedCallbackException.getMessage() + " not available to obtain information from user.");
+ }
+
+ user = callbacks[0].getName();
+
+ principals = new HashSet<>();
+
+ String[] userDnAndNamespace;
+ try (LDAPCache cache = LDAPCache.getCache(lOptions)) {
+
+ try {
+ logger.debug("Get the user DN.");
+ userDnAndNamespace = cache.getUserDnAndNamespace(user);
+
+ } catch (Exception e) {
+ logger.warn("Can't connect to the LDAP server: {}", e.getMessage(), e);
+ throw new LoginException("Can't connect to the LDAP server: " + e.getMessage());
+ }
+
+ if (userDnAndNamespace == null) {
+ return false;
+ }
+
+ principals.add(new UserPrincipal(user));
+
+ try {
+ String[] roles = cache.getUserRoles(user, userDnAndNamespace[0], userDnAndNamespace[1]);
+ for (String role : roles) {
+ principals.add(new RolePrincipal(role));
+ }
+ } catch (Exception e) {
+ throw new LoginException("Can't get user " + user + " roles: " + e.getMessage());
+ }
+
+ return true;
+ }
+ } finally {
+ ManagedSSLSocketFactory.setSocketFactory(null);
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+ }
+
+ @Override
+ public boolean abort() throws LoginException {
+ return true;
+ }
+
+ @Override
+ public boolean commit() throws LoginException {
+ boolean ret = super.commit();
+ principals.addAll(subject.getPrincipals(KerberosPrincipal.class));
+ return ret;
+ }
+
+ @Override
+ public boolean logout() throws LoginException {
+ subject.getPrincipals().removeAll(principals);
+ principals.clear();
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3ea2830e/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java
new file mode 100644
index 0000000..9e34749
--- /dev/null
+++ b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java
@@ -0,0 +1,431 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+package org.apache.karaf.jaas.modules.ldap;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.SystemUtils;
+import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.util.Strings;
+import org.apache.directory.server.annotations.CreateKdcServer;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.annotations.SaslMechanism;
+import org.apache.directory.server.core.annotations.ApplyLdifs;
+import org.apache.directory.server.core.annotations.ContextEntry;
+import org.apache.directory.server.core.annotations.CreateDS;
+import org.apache.directory.server.core.annotations.CreateIndex;
+import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.kerberos.kdc.AbstractKerberosITest;
+import org.apache.directory.server.kerberos.kdc.KerberosTestUtils;
+import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler;
+import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler;
+import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler;
+import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler;
+import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler;
+import org.apache.directory.server.protocol.shared.transport.TcpTransport;
+import org.apache.directory.server.protocol.shared.transport.Transport;
+import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
+import org.apache.directory.shared.kerberos.crypto.checksum.ChecksumType;
+import org.apache.felix.utils.properties.Properties;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.apache.karaf.jaas.boot.principal.UserPrincipal;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(FrameworkRunner.class)
+@CreateDS(name = "GSSAPILdapLoginModuleTest-class",
+ partitions =
+ {
+ @CreatePartition(
+ name = "example",
+ suffix = "dc=example,dc=com",
+ contextEntry = @ContextEntry(
+ entryLdif =
+ "dn: dc=example,dc=com\n" +
+ "dc: example\n" +
+ "objectClass: top\n" +
+ "objectClass: domain\n\n"),
+ indexes =
+ {
+ @CreateIndex(attribute = "objectClass"),
+ @CreateIndex(attribute = "dc"),
+ @CreateIndex(attribute = "ou")
+ })
+ },
+ additionalInterceptors =
+ {
+ KeyDerivationInterceptor.class
+ })
+@CreateLdapServer(
+ transports =
+ {
+ @CreateTransport(protocol = "LDAP")
+ },
+ saslHost = "localhost",
+ saslPrincipal = "ldap/localhost@EXAMPLE.COM",
+ saslMechanisms =
+ {
+ @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class),
+ @SaslMechanism(name = SupportedSaslMechanisms.CRAM_MD5, implClass = CramMd5MechanismHandler.class),
+ @SaslMechanism(name = SupportedSaslMechanisms.DIGEST_MD5, implClass = DigestMd5MechanismHandler.class),
+ @SaslMechanism(name = SupportedSaslMechanisms.GSSAPI, implClass = GssapiMechanismHandler.class),
+ @SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class),
+ @SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class)
+ })
+@CreateKdcServer(
+ transports =
+ {
+ @CreateTransport(protocol = "UDP", port = 6088),
+ @CreateTransport(protocol = "TCP", port = 6088)
+ })
+@ApplyLdifs({
+ "dn: ou=users,dc=example,dc=com",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: users",
+
+ "dn: ou=groups,dc=example,dc=com",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: groups",
+
+ "dn: cn=admin,ou=groups,dc=example,dc=com",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: admin",
+ "member: uid=hnelson,ou=users,dc=example,dc=com"
+})
+public class GSSAPILdapLoginModuleTest extends AbstractKerberosITest {
+
+ private static boolean loginConfigUpdated;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Set up a partition for EXAMPLE.COM and add user and service principals to test authentication with.
+ KerberosTestUtils.fixServicePrincipalName(
+ "ldap/" + KerberosTestUtils.getHostName() + "@EXAMPLE.COM", null, getLdapServer());
+ setupEnv(TcpTransport.class,
+ EncryptionType.AES128_CTS_HMAC_SHA1_96, ChecksumType.HMAC_SHA1_96_AES128);
+
+ kdcServer.getConfig().setPaEncTimestampRequired(false);
+
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+ File config = new File(basedir + "/target/test-classes/org/apache/karaf/jaas/modules/ldap/gssapi.login.config");
+
+ System.setProperty("java.security.auth.login.config", config.toString());
+
+ updatePort();
+ }
+
+ public void updatePort() throws Exception {
+ if (!loginConfigUpdated) {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ // Read in ldap.properties and substitute in the correct port
+ File f = new File(basedir + "/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties");
+
+ FileInputStream inputStream = new FileInputStream(f);
+ String content = IOUtils.toString(inputStream, "UTF-8");
+ inputStream.close();
+ content = content.replaceAll("portno", "" + super.getLdapServer().getPort());
+ content = content.replaceAll("address", KerberosTestUtils.getHostName());
+
+ File f2 = new File(basedir + "/target/test-classes/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties");
+ FileOutputStream outputStream = new FileOutputStream(f2);
+ IOUtils.write(content, outputStream, "UTF-8");
+ outputStream.close();
+ loginConfigUpdated = true;
+ }
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LDAPCache.clear();
+ super.tearDown();
+ }
+
+ @Test
+ public void testSuccess() throws Exception {
+
+ Properties options = ldapLoginModuleOptions();
+ GSSAPILdapLoginModule module = new GSSAPILdapLoginModule();
+
+ CallbackHandler cb = new CallbackHandler() {
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof NameCallback) {
+ ((NameCallback) cb).setName("hnelson");
+ } else if (cb instanceof PasswordCallback) {
+ ((PasswordCallback) cb).setPassword("secret".toCharArray());
+ }
+ }
+ }
+ };
+ Subject subject = new Subject();
+ module.initialize(subject, cb, null, options);
+
+ assertEquals("Precondition", 0, subject.getPrincipals().size());
+ assertTrue(module.login());
+ assertTrue(module.commit());
+
+ assertEquals(3, subject.getPrincipals().size());
+
+ boolean foundKrb5User = false;
+ boolean foundUser = false;
+ boolean foundRole = false;
+ boolean foundTicket = false;
+
+ for (Principal pr : subject.getPrincipals()) {
+ if (pr instanceof KerberosPrincipal) {
+ assertEquals("hnelson@EXAMPLE.COM", pr.getName());
+ foundKrb5User = true;
+ } else if (pr instanceof UserPrincipal) {
+ assertEquals("hnelson", pr.getName());
+ foundUser = true;
+ } else if (pr instanceof RolePrincipal) {
+ assertEquals("admin", pr.getName());
+ foundRole = true;
+ }
+ }
+ for (Object crd : subject.getPrivateCredentials()) {
+ if (crd instanceof KerberosTicket) {
+ assertEquals("hnelson@EXAMPLE.COM", ((KerberosTicket) crd).getClient().getName());
+ assertEquals("krbtgt/EXAMPLE.COM@EXAMPLE.COM", ((KerberosTicket) crd).getServer().getName());
+ foundTicket = true;
+ break;
+ }
+ }
+
+ assertTrue("Principals should contains kerberos user", foundKrb5User);
+ assertTrue("Principals should contains ldap user", foundUser);
+ assertTrue("Principals should contains ldap role", foundRole);
+ assertTrue("PricatePrincipals should contains kerberos ticket", foundTicket);
+
+ assertTrue(module.logout());
+ assertEquals("Principals should be gone as the user has logged out", 0, subject.getPrincipals().size());
+ }
+
+ @Test(expected = LoginException.class)
+ public void testUsernameFailure() throws Exception {
+
+ Properties options = ldapLoginModuleOptions();
+ GSSAPILdapLoginModule module = new GSSAPILdapLoginModule();
+
+ CallbackHandler cb = new CallbackHandler() {
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof NameCallback) {
+ ((NameCallback) cb).setName("hnelson0");
+ } else if (cb instanceof PasswordCallback) {
+ ((PasswordCallback) cb).setPassword("secret".toCharArray());
+ }
+ }
+ }
+ };
+ Subject subject = new Subject();
+ module.initialize(subject, cb, null, options);
+
+ assertEquals("Precondition", 0, subject.getPrincipals().size());
+ assertTrue(module.login()); // should throw LoginException
+ }
+
+ @Test(expected = LoginException.class)
+ public void testPasswordFailure() throws Exception {
+
+ Properties options = ldapLoginModuleOptions();
+ GSSAPILdapLoginModule module = new GSSAPILdapLoginModule();
+
+ CallbackHandler cb = new CallbackHandler() {
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof NameCallback) {
+ ((NameCallback) cb).setName("hnelson");
+ } else if (cb instanceof PasswordCallback) {
+ ((PasswordCallback) cb).setPassword("secret0".toCharArray());
+ }
+ }
+ }
+ };
+ Subject subject = new Subject();
+ module.initialize(subject, cb, null, options);
+
+ assertEquals("Precondition", 0, subject.getPrincipals().size());
+ assertTrue(module.login());
+ }
+
+ @Test(expected = LoginException.class)
+ public void testUserNotFound() throws Exception {
+
+ Properties options = ldapLoginModuleOptions();
+ GSSAPILdapLoginModule module = new GSSAPILdapLoginModule();
+
+ CallbackHandler cb = new CallbackHandler() {
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof NameCallback) {
+ ((NameCallback) cb).setName("test");
+ } else if (cb instanceof PasswordCallback) {
+ ((PasswordCallback) cb).setPassword("test".toCharArray());
+ }
+ }
+ }
+ };
+ Subject subject = new Subject();
+ module.initialize(subject, cb, null, options);
+
+ assertEquals("Precondition", 0, subject.getPrincipals().size());
+ assertFalse(module.login());
+ }
+
+ @Test(expected = LoginException.class)
+ public void testNoRealm() throws Exception {
+
+ Properties options = ldapLoginModuleOptions();
+ options.remove(GSSAPILdapLoginModule.REALM_PROPERTY);
+ GSSAPILdapLoginModule module = new GSSAPILdapLoginModule();
+
+ CallbackHandler cb = new CallbackHandler() {
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof NameCallback) {
+ ((NameCallback) cb).setName("hnelson0");
+ } else if (cb instanceof PasswordCallback) {
+ ((PasswordCallback) cb).setPassword("secret".toCharArray());
+ }
+ }
+ }
+ };
+ Subject subject = new Subject();
+ module.initialize(subject, cb, null, options);
+
+ assertEquals("Precondition", 0, subject.getPrincipals().size());
+ assertTrue(module.login()); // should throw LoginException
+ }
+
+ protected void setupEnv(Class<? extends Transport> transport, EncryptionType encryptionType,
+ ChecksumType checksumType)
+ throws Exception {
+ // create krb5.conf with proper encryption type
+ String krb5confPath = createKrb5Conf(checksumType, encryptionType, transport == TcpTransport.class);
+ System.setProperty("java.security.krb5.conf", krb5confPath);
+
+ // change encryption type in KDC
+ kdcServer.getConfig().setEncryptionTypes(Collections.singleton(encryptionType));
+
+ // create principals
+ createPrincipal("uid=" + USER_UID, "Last", "admin",
+ USER_UID, USER_PASSWORD, USER_UID + "@" + REALM);
+
+ createPrincipal("uid=krbtgt", "KDC Service", "KDC Service",
+ "krbtgt", "secret", "krbtgt/" + REALM + "@" + REALM);
+
+ String servicePrincipal = LDAP_SERVICE_NAME + "/" + HOSTNAME + "@" + REALM;
+ createPrincipal("uid=ldap", "Service", "LDAP Service",
+ "ldap", "randall", servicePrincipal);
+ }
+
+ private String createKrb5Conf(ChecksumType checksumType, EncryptionType encryptionType, boolean isTcp) throws IOException {
+ File file = folder.newFile("krb5.conf");
+
+ String data = "";
+
+ data += "[libdefaults]" + SystemUtils.LINE_SEPARATOR;
+ data += "default_realm = " + REALM + SystemUtils.LINE_SEPARATOR;
+ data += "default_tkt_enctypes = " + encryptionType.getName() + SystemUtils.LINE_SEPARATOR;
+ data += "default_tgs_enctypes = " + encryptionType.getName() + SystemUtils.LINE_SEPARATOR;
+ data += "permitted_enctypes = " + encryptionType.getName() + SystemUtils.LINE_SEPARATOR;
+ // data += "default_checksum = " + checksumType.getName() + SystemUtils.LINE_SEPARATOR;
+ // data += "ap_req_checksum_type = " + checksumType.getName() + SystemUtils.LINE_SEPARATOR;
+ data += "default-checksum_type = " + checksumType.getName() + SystemUtils.LINE_SEPARATOR;
+
+ if (isTcp) {
+ data += "udp_preference_limit = 1" + SystemUtils.LINE_SEPARATOR;
+ }
+
+
+ data += "[realms]" + SystemUtils.LINE_SEPARATOR;
+ data += REALM + " = {" + SystemUtils.LINE_SEPARATOR;
+ data += "kdc = " + HOSTNAME + ":" + kdcServer.getTransports()[0].getPort() + SystemUtils.LINE_SEPARATOR;
+ data += "}" + SystemUtils.LINE_SEPARATOR;
+
+ data += "[domain_realm]" + SystemUtils.LINE_SEPARATOR;
+ data += "." + Strings.lowerCaseAscii(REALM) + " = " + REALM + SystemUtils.LINE_SEPARATOR;
+ data += Strings.lowerCaseAscii(REALM) + " = " + REALM + SystemUtils.LINE_SEPARATOR;
+
+ FileUtils.writeStringToFile(file, data);
+
+ return file.getAbsolutePath();
+ }
+
+ private void createPrincipal(String rdn, String sn, String cn,
+ String uid, String userPassword, String principalName) throws LdapException {
+ Entry entry = new DefaultEntry();
+ entry.setDn(rdn + "," + USERS_DN);
+ entry.add("objectClass", "top", "person", "inetOrgPerson", "krb5principal", "krb5kdcentry");
+ entry.add("cn", cn);
+ entry.add("sn", sn);
+ entry.add("uid", uid);
+ entry.add("userPassword", userPassword);
+ entry.add("krb5PrincipalName", principalName);
+ entry.add("krb5KeyVersionNumber", "0");
+ conn.add(entry);
+ }
+
+ protected Properties ldapLoginModuleOptions() throws IOException {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+ File file = new File(basedir + "/target/test-classes/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties");
+ return new Properties(file);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3ea2830e/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties
----------------------------------------------------------------------
diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties
new file mode 100644
index 0000000..8186a73
--- /dev/null
+++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties
@@ -0,0 +1,37 @@
+################################################################################
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+
+debug=true
+connection.url=ldap://address:portno
+connection.username=hnelson
+connection.password=secret
+connection.protocol=
+authentication=GSSAPI
+gssapiRealm=krb5TestRealm
+
+user.base.dn=ou=users,dc=example,dc=com
+user.filter=(uid=%u)
+user.search.subtree=true
+
+role.base.dn=ou=groups,dc=example,dc=com
+role.name.attribute=cn
+role.filter=(member=%fqdn)
+role.search.subtree=true
+
+initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
http://git-wip-us.apache.org/repos/asf/karaf/blob/3ea2830e/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config
----------------------------------------------------------------------
diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config
new file mode 100644
index 0000000..84635f9
--- /dev/null
+++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config
@@ -0,0 +1,26 @@
+// ################################################################################
+// #
+// # Licensed to the Apache Software Foundation (ASF) under one or more
+// # contributor license agreements. See the NOTICE file distributed with
+// # this work for additional information regarding copyright ownership.
+// # The ASF licenses this file to You under the Apache License, Version 2.0
+// # (the "License"); you may not use this file except in compliance with
+// # the License. You may obtain a copy of the License at
+// #
+// # http://www.apache.org/licenses/LICENSE-2.0
+// #
+// # Unless required by applicable law or agreed to in writing, software
+// # distributed under the License is distributed on an "AS IS" BASIS,
+// # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// # See the License for the specific language governing permissions and
+// # limitations under the License.
+// #
+// ################################################################################
+krb5TestRealm {
+ org.apache.karaf.jaas.modules.krb5.Krb5LoginModule REQUIRED
+ refreshKrb5Config = true
+ password-stacking = storePass
+ doNotPrompt = false
+ useTicketCache = true
+ debug=true;
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/3ea2830e/manual/src/main/asciidoc/developer-guide/security-framework.adoc
----------------------------------------------------------------------
diff --git a/manual/src/main/asciidoc/developer-guide/security-framework.adoc b/manual/src/main/asciidoc/developer-guide/security-framework.adoc
index 41d9ed7..962bd75 100644
--- a/manual/src/main/asciidoc/developer-guide/security-framework.adoc
+++ b/manual/src/main/asciidoc/developer-guide/security-framework.adoc
@@ -516,6 +516,80 @@ performed directly on the LDAP backend.
The Kerberos login module uses the Oracle JVM Krb5 internal login module.
+Here is a simple configuration :
+----
+<jaas:config name="krb5" rank="1">
+ <jaas:module className="org.apache.karaf.jaas.modules.krb5.Krb5LoginModule">
+ refreshKrb5Config = true
+ password-stacking = storePass
+ doNotPrompt = false
+ useTicketCache = true
+ </jaas:module>
+</jaas:config>
+----
+
+You must specify a krb5 configuration file through the "java.security.krb5.conf" system property.
+Here is a simple example of a krb5 configuration file :
+----
+[libdefaults]
+ default_realm = EXAMPLE.COM
+ dns_lookup_realm = false
+ dns_lookup_kdc = false
+ ticket_lifetime = 24h
+ renew_lifetime = 365d
+ forwardable = true
+
+[realms]
+
+ EXAMPLE.COM = {
+ kdc = kdc.example.com
+ admin_server = kdc.example.com
+ default_domain = example.com
+ }
+
+[domain_realm]
+ .example.com = EXAMPLE.COM
+ example.com = EXAMPLE.COM
+----
+
+===== GSSAPILdapLoginModule
+
+|===
+|LoginModule |BackendEngineFactory
+
+|org.apache.karaf.jaas.modules.ldap.GSSAPILdapLoginModule
+|
+|===
+
+The GSSAPI module uses the GSSAPI mechanism to handle authentication to a LDAP server.
+Typical use is using this and a Kerberos Login Module to connect to an ActiveDirectory Server, or any other LDAP server that needs a Kerberos tickets for authentication.
+
+Here is a simple configuration, that use as Kerberos login module as authentication backend :
+----
+<jaas:config name="ldap" rank="1">
+ <jaas:module className="org.apache.karaf.jaas.modules.ldap.GSSAPILdapLoginModule"flags="required">
+ gssapiRealm=krb5
+ initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
+ connection.url=ldap://activedirectory_host:389
+ user.base.dn=ou=Users,ou=there,DC=local
+ user.filter=(sAMAccountName=%u)
+ user.search.subtree=true
+ role.base.dn=ou=Groups,ou=there,DC=local
+ role.name.attribute=cn
+ role.filter=(member=%fqdn)
+ role.search.subtree=true
+ </jaas:module>
+</jaas:config>
+<jaas:config name="krb5" rank="1">
+ <jaas:module className="org.apache.karaf.jaas.modules.krb5.Krb5LoginModule">
+ refreshKrb5Config = true
+ password-stacking = storePass
+ doNotPrompt = false
+ useTicketCache = true
+ </jaas:module>
+</jaas:config>
+----
+Note the 'gssapiRealm' property of the LDAP login module that match the name of the Kerberos Configuration.
===== SyncopeLoginModule
http://git-wip-us.apache.org/repos/asf/karaf/blob/3ea2830e/manual/src/main/asciidoc/user-guide/security.adoc
----------------------------------------------------------------------
diff --git a/manual/src/main/asciidoc/user-guide/security.adoc b/manual/src/main/asciidoc/user-guide/security.adoc
index afeba11..4d6e293 100644
--- a/manual/src/main/asciidoc/user-guide/security.adoc
+++ b/manual/src/main/asciidoc/user-guide/security.adoc
@@ -59,6 +59,8 @@ Apache Karaf provides additional login modules (see the developer guide for deta
* LDAPLoginModule uses a LDAP server as backend
* SyncopeLoginModule uses Apache Syncope as backend
* OsgiConfigLoginModule uses a configuration as backend
+* Krb5LoginModule uses a Kerberos Server as backend
+* GSSAPILdapLoginModule uses a LDAP server as backend but delegate LDAP server authentication to an other backend (typically Krb5LoginModule)
You can manage an existing realm, login module, or create your own realm using the `jaas:realm-manage` command.