karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jbono...@apache.org
Subject [1/2] karaf git commit: [KARAF-4487] Add support for GSSAPI authentication.
Date Wed, 07 Dec 2016 17:51:47 GMT
Repository: karaf
Updated Branches:
  refs/heads/master d7a2185a7 -> e908c4a9b


[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/43d3aa6d
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/43d3aa6d
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/43d3aa6d

Branch: refs/heads/master
Commit: 43d3aa6deb7bbe400622f6b5da082af098f90225
Parents: d7a2185
Author: alexandre.cartapanis@gmail.com <alexandre.cartapanis@gmail.com>
Authored: Wed Aug 24 12:35:10 2016 +0200
Committer: Jean-Baptiste Onofré <jbonofre@apache.org>
Committed: Wed Dec 7 18:43:39 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     |  75 ++++
 .../src/main/asciidoc/user-guide/security.adoc  |   2 +
 6 files changed, 722 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/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/43d3aa6d/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/43d3aa6d/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/43d3aa6d/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/43d3aa6d/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 b70bcc9..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,81 @@ 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/43d3aa6d/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.
 


Mime
View raw message