You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2016/01/19 09:58:55 UTC
[3/4] mina-sshd git commit: [SSHD-608] Provide PublicKeyAuthenticator
that uses LDAP
[SSHD-608] Provide PublicKeyAuthenticator that uses LDAP
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e6991a73
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e6991a73
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e6991a73
Branch: refs/heads/master
Commit: e6991a73bd7829c327f1d678b3f2e403d673a418
Parents: f374e72
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Jan 19 10:56:55 2016 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Jan 19 10:56:55 2016 +0200
----------------------------------------------------------------------
sshd-ldap/pom.xml | 4 +-
.../auth/pubkey/LdapPublickeyAuthenticator.java | 95 ++++++++++++++++++++
.../sshd/server/auth/BaseAuthenticatorTest.java | 29 ++++--
.../password/LdapPasswordAuthenticatorTest.java | 2 +
.../pubkey/LdapPublickeyAuthenticatorTest.java | 95 ++++++++++++++++++++
sshd-ldap/src/test/resources/auth-users.ldif | 8 +-
6 files changed, 222 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e6991a73/sshd-ldap/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-ldap/pom.xml b/sshd-ldap/pom.xml
index b692860..5921ccb 100644
--- a/sshd-ldap/pom.xml
+++ b/sshd-ldap/pom.xml
@@ -28,9 +28,9 @@
</parent>
<artifactId>sshd-ldap</artifactId>
- <name>Apache Mina SSHD :: Git</name>
+ <name>Apache Mina SSHD :: LDAP</name>
<packaging>jar</packaging>
- <inceptionYear>2008</inceptionYear>
+ <inceptionYear>2016</inceptionYear>
<properties>
<projectRoot>${basedir}/..</projectRoot>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e6991a73/sshd-ldap/src/main/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/main/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticator.java b/sshd-ldap/src/main/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticator.java
new file mode 100644
index 0000000..fa3e929
--- /dev/null
+++ b/sshd-ldap/src/main/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticator.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package org.apache.sshd.server.auth.pubkey;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.naming.NamingException;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.net.LdapNetworkConnector;
+import org.apache.sshd.server.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LdapPublickeyAuthenticator extends LdapNetworkConnector implements PublickeyAuthenticator {
+ public static final String DEFAULT_USERNAME_ATTR_NAME = "uid";
+ public static final String DEFAULT_AUTHENTICATION_MODE = "none";
+ public static final String DEFAULT_SEARCH_FILTER_PATTERN = DEFAULT_USERNAME_ATTR_NAME + "={0}";
+ public static final String DEFAULT_PUBKEY_ATTR_NAME = "sshPublicKey";
+
+ private String keyAttributeName = DEFAULT_PUBKEY_ATTR_NAME;
+
+ public LdapPublickeyAuthenticator() {
+ setAuthenticationMode(DEFAULT_AUTHENTICATION_MODE);
+ setSearchFilterPattern(DEFAULT_SEARCH_FILTER_PATTERN);
+ setRetrievedAttributes(DEFAULT_PUBKEY_ATTR_NAME);
+ }
+
+ /**
+ * @return The LDAP attribute name containing the public key in {@code OpenSSH} format
+ */
+ public String getKeyAttributeName() {
+ return keyAttributeName;
+ }
+
+ public void setKeyAttributeName(String keyAttributeName) {
+ this.keyAttributeName = ValidateUtils.checkNotNullAndNotEmpty(keyAttributeName, "No attribute name");
+ }
+
+ @Override
+ public boolean authenticate(String username, PublicKey key, ServerSession session) {
+ try {
+ Map<String, ?> attrs = resolveAttributes(username, null, session);
+ return authenticate(username, key, session, attrs);
+ } catch (NamingException | GeneralSecurityException | IOException | RuntimeException e) {
+ log.warn("authenticate({}@{}) failed ({}) to query: {}",
+ username, session, e.getClass().getSimpleName(), e.getMessage());
+
+ if (log.isDebugEnabled()) {
+ log.debug("authenticate(" + username + "@" + session + ") query failure details", e);
+ }
+
+ return false;
+ }
+ }
+
+ protected boolean authenticate(String username, PublicKey expected, ServerSession session, Map<String, ?> attrs)
+ throws GeneralSecurityException, IOException {
+ String attrName = getKeyAttributeName();
+ Object keyData = ValidateUtils.checkNotNull(attrs.get(attrName), "No data for attribute=%s", attrName);
+ PublicKey actual = recoverPublicKey(username, expected, session, keyData);
+ return KeyUtils.compareKeys(expected, actual);
+ }
+
+ protected PublicKey recoverPublicKey(String username, PublicKey expected, ServerSession session, Object keyData)
+ throws GeneralSecurityException, IOException {
+ AuthorizedKeyEntry entry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(Objects.toString(keyData, null));
+ return ValidateUtils.checkNotNull(entry, "No key extracted").resolvePublicKey(PublicKeyEntryResolver.FAILING);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e6991a73/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
index 8c0fa47..2b60fb6 100644
--- a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
+++ b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
@@ -127,6 +127,15 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
directoryService.setSystemPartition(systemPartition);
}
+ // Create a new partition for the special extra attributes
+ {
+ JdbmPartition partition = new JdbmPartition();
+ partition.setId("openssh-lpk");
+ partition.setSuffix("cn=openssh-lpk,cn=schema,cn=config");
+ partition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, partition.getId()))));
+ directoryService.addPartition(partition);
+ }
+
// Create a new partition for the users
{
JdbmPartition partition = new JdbmPartition();
@@ -170,12 +179,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
int id = 1;
for (LdifEntry entry : reader) {
if (log.isDebugEnabled()) {
- log.debug("Add LDIF entry={}", entry);
+ log.debug("Process LDIF entry={}", entry);
}
- ChangeType changeType = entry.getChangeType();
- assertEquals("Mismatched change type in users ldif entry=" + entry, ChangeType.Add, changeType);
-
Entry data = entry.getEntry();
EntryAttribute userAttr = data.get("uid");
EntryAttribute passAttr = data.get(credentialName);
@@ -184,10 +190,19 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
ValidateUtils.checkTrue(usersMap.put(username, passAttr.getString()) == null, "Multiple entries for user=%s", username);
}
- InternalAddRequest addRequest = new AddRequestImpl(id++);
- addRequest.setEntry(data);
+ ChangeType changeType = entry.getChangeType();
try {
- session.add(addRequest);
+ switch (changeType) {
+ case Add: {
+ InternalAddRequest addRequest = new AddRequestImpl(id++);
+ addRequest.setEntry(data);
+ session.add(addRequest);
+ break;
+ }
+
+ default:
+ throw new UnsupportedOperationException("Unsupported change type (" + changeType + ") for entry=" + entry);
+ }
} catch (Exception e) {
log.error("Failed (" + e.getClass().getSimpleName() + ") to add entry=" + entry + ": " + e.getMessage(), e);
throw e;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e6991a73/sshd-ldap/src/test/java/org/apache/sshd/server/auth/password/LdapPasswordAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/password/LdapPasswordAuthenticatorTest.java b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/password/LdapPasswordAuthenticatorTest.java
index 2a88342..aa862ca 100644
--- a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/password/LdapPasswordAuthenticatorTest.java
+++ b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/password/LdapPasswordAuthenticatorTest.java
@@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.ldap.LdapServer;
+import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.Pair;
import org.apache.sshd.server.auth.BaseAuthenticatorTest;
import org.apache.sshd.server.session.ServerSession;
@@ -50,6 +51,7 @@ public class LdapPasswordAuthenticatorTest extends BaseAuthenticatorTest {
public static void startApacheDs() throws Exception {
ldapContextHolder.set(startApacheDs(LdapPasswordAuthenticatorTest.class));
usersMap = populateUsers(ldapContextHolder.get().getSecond(), LdapPasswordAuthenticatorTest.class, LdapPasswordAuthenticator.DEFAULT_PASSWORD_ATTR_NAME);
+ assertFalse("No users retrieved", GenericUtils.isEmpty(usersMap));
}
@AfterClass
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e6991a73/sshd-ldap/src/test/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticatorTest.java b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticatorTest.java
new file mode 100644
index 0000000..c697a56
--- /dev/null
+++ b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/pubkey/LdapPublickeyAuthenticatorTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package org.apache.sshd.server.auth.pubkey;
+
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Pair;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.server.auth.BaseAuthenticatorTest;
+import org.apache.sshd.server.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.server.session.ServerSession;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class LdapPublickeyAuthenticatorTest extends BaseAuthenticatorTest {
+ private static final AtomicReference<Pair<LdapServer, DirectoryService>> ldapContextHolder = new AtomicReference<>();
+ private static final Map<String, PublicKey> keysMap = new HashMap<>();
+ // we use this instead of the default since the default requires some extra LDIF manipulation which we don't need
+ private static final String TEST_ATTR_NAME = "description";
+
+ public LdapPublickeyAuthenticatorTest() {
+ super();
+ }
+
+ @BeforeClass
+ public static void startApacheDs() throws Exception {
+ ldapContextHolder.set(startApacheDs(LdapPublickeyAuthenticatorTest.class));
+ Map<String, String> credentials =
+ populateUsers(ldapContextHolder.get().getSecond(), LdapPublickeyAuthenticatorTest.class, TEST_ATTR_NAME);
+ assertFalse("No keys retrieved", GenericUtils.isEmpty(credentials));
+
+ for (Map.Entry<String, String> ce : credentials.entrySet()) {
+ String username = ce.getKey();
+ AuthorizedKeyEntry entry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(ce.getValue());
+ PublicKey key = ValidateUtils.checkNotNull(entry, "No key extracted").resolvePublicKey(PublicKeyEntryResolver.FAILING);
+ keysMap.put(username, key);
+ }
+ }
+
+ @AfterClass
+ public static void stopApacheDs() throws Exception {
+ stopApacheDs(ldapContextHolder.getAndSet(null));
+ }
+
+ @Test
+ public void testPublicKeyComparison() throws Exception {
+ LdapPublickeyAuthenticator auth = new LdapPublickeyAuthenticator();
+ auth.setBaseDN(BASE_DN_TEST);
+ auth.setPort(getPort(ldapContextHolder.get()));
+ auth.setKeyAttributeName(TEST_ATTR_NAME);
+ auth.setRetrievedAttributes(TEST_ATTR_NAME);
+
+ ServerSession session = Mockito.mock(ServerSession.class);
+ for (Map.Entry<String, PublicKey> ke : keysMap.entrySet()) {
+ String username = ke.getKey();
+ PublicKey key = ke.getValue();
+ outputDebugMessage("Authenticate: user=%s, key-type=%s, fingerprint=%s",
+ username, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+ assertTrue("Failed to authenticate user=" + username, auth.authenticate(username, key, session));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e6991a73/sshd-ldap/src/test/resources/auth-users.ldif
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/test/resources/auth-users.ldif b/sshd-ldap/src/test/resources/auth-users.ldif
index 4792099..0fc3b18 100644
--- a/sshd-ldap/src/test/resources/auth-users.ldif
+++ b/sshd-ldap/src/test/resources/auth-users.ldif
@@ -1,31 +1,35 @@
version: 1
+## Add People group definition
dn: ou=People,dc=sshd,dc=apache,dc=org
ou: People
objectClass: top
objectClass: organizationalUnit
description: Parent object of all users accounts
+## Add users
dn: cn=Guillaume Nodet,ou=People,dc=sshd,dc=apache,dc=org
objectClass: top
objectClass: person
objectClass: organizationalPerson
-objectclass: inetOrgPerson
+objectClass: inetOrgPerson
cn: Guillaume Nodet
givenName: Guillaume
sn: Nodet
uid: gnodet
userpassword: gnodet
mail: gnodet@apache.org
+description: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEAGEwosEIS7koPfYFf2qXAqV+gGiuf3wizjYCz8UdYtmSBINRQhWvVtiWAiLOcHiI/nYSTXctIFyzbWJ74BqvgoZS8eujaPUyBO+asF3enfyrnug9uvqrhsGqnsqFSaAYSQG3p4paKThRhxGX1jqMy6wJaTED1d9SxHs9gnGCZLk1yAr0w/mDkF59YmPoAibmyz1aaXGkjPBWa4ac+RBTEuOxt0hz+ykA7rNua5QPMdNx3sXrv80ECDEs78poOiZgkRvBEnA4qCvSK5FBB5DkVn+3cUhd36tEhKz79u8mLAsrUrgi/JKcrdAZSCyS4oPRt9NEm9YZxcYzqDH98RDp sshd@apache.org
dn: cn=Lyor Goldstein,ou=People,dc=sshd,dc=apache,dc=org
objectClass: top
objectClass: person
objectClass: organizationalPerson
-objectclass: inetOrgPerson
+objectClass: inetOrgPerson
cn: Lyor Goldstein
givenName: Lyor
sn: Goldstein
uid: lgoldstein
userpassword: lgoldstein
mail: lgoldstein@apache.org
+description: ssh-dss AAAAB3NzaC1kc3MAAACBAJ6tabphvozXBCnOe2j48OF6pSGmV7+7mRrPxiyPY5mHcHGJXwsE4Rvu81epbTghX3A/s1sme1QtmVVOU3Y3NIxmDBK5UQmSIYS+chXe45GBwC6uJawOn4lPIYw15PJg2ZfjxC1QU5fa6HSQ3vX2MZfYFhEovlBd4mGo2+XY0DqHAAAAFQDeDO2UFNZRBFNN1jEBojpeALh7oQAAAIAI/wYgC1kTjsIFaVvgK3RcGb+lYW0EiuiuG0q37+BzcqtEgjVckvxoCTeghcyf6vhB+ZbrM9we2jH3JvmF4gmtmzjW8UtgYCB2ovRYmd21cSPQW2F9U+sIl2+2sI9rsyrAdyhW1qZ4HVIrKH0RdWkAkjQ+8EGKHKXKo6aEV/HdpAAAAIAv80m0MZoocokStZV7EclLwt3ihvN7/Tjf5aHS9fY8b1ev2Z7eIVm3UHirggEeJMQgIw3MBE/T1ttYyqwnVdOatBJVcVLXYUPuXrq6f9/TYEpYxLqowjOwPaAkZAnecVYo0eU52VSkR38onu7juk4BNzHtLILTvtZppQ6l3loOpA== sshd@apache.org