summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java
diff options
context:
space:
mode:
authorDavid Ostrovsky <david@ostrovsky.org>2014-03-16 18:28:03 +0100
committerJames Moger <james.moger@gitblit.com>2014-04-10 18:58:08 -0400
commit75ebd391b88884581b1139c87c98bb687941a8fe (patch)
tree0050f7a069c483de425e64c5714598a59413893f /src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java
parent282b8fd82c46ba6874fb24c8715af103645f3406 (diff)
downloadgitblit-75ebd391b88884581b1139c87c98bb687941a8fe.tar.gz
gitblit-75ebd391b88884581b1139c87c98bb687941a8fe.zip
Prevent double authentication for the same public key
Openssh client sends two requests, one without a key signature to verify that the public key is acceptable and the second one with the signature after having loaded the private key and signed some data for actual verification. To prevent that the PublickeyAuthenticator#authenticate is called twice cache the authentication status for session and public key. Implement SessionListener to clean up the cache entry when session is destroyed. This is a workaround for SSHD bug [1]. Inspired-By: Guillaume Nodet <gnodet@apache.org> [1] https://issues.apache.org/jira/browse/SSHD-300
Diffstat (limited to 'src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java')
-rw-r--r--src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java b/src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java
new file mode 100644
index 00000000..ee1de591
--- /dev/null
+++ b/src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * 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.
+ */
+package com.gitblit.transport.ssh;
+
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.sshd.common.Session;
+import org.apache.sshd.common.SessionListener;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.models.UserModel;
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ * @author Eric Myrhe
+ *
+ */
+public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator,
+ SessionListener {
+
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+
+ protected final IKeyManager keyManager;
+
+ protected final IAuthenticationManager authManager;
+
+ private final Map<ServerSession, Map<PublicKey, Boolean>> cache =
+ new ConcurrentHashMap<ServerSession, Map<PublicKey, Boolean>>();
+
+ public CachingPublicKeyAuthenticator(IKeyManager keyManager,
+ IAuthenticationManager authManager) {
+ this.keyManager = keyManager;
+ this.authManager = authManager;
+ }
+
+ @Override
+ public boolean authenticate(String username, PublicKey key,
+ ServerSession session) {
+ Map<PublicKey, Boolean> map = cache.get(session);
+ if (map == null) {
+ map = new HashMap<PublicKey, Boolean>();
+ cache.put(session, map);
+ session.addListener(this);
+ }
+ if (map.containsKey(key)) {
+ return map.get(key);
+ }
+ boolean result = doAuthenticate(username, key, session);
+ map.put(key, result);
+ return result;
+ }
+
+ private boolean doAuthenticate(String username, PublicKey suppliedKey,
+ ServerSession session) {
+ SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY);
+ Preconditions.checkState(client.getUser() == null);
+ username = username.toLowerCase(Locale.US);
+ List<PublicKey> keys = keyManager.getKeys(username);
+ if (keys == null || keys.isEmpty()) {
+ log.info("{} has not added any public keys for ssh authentication",
+ username);
+ return false;
+ }
+
+ for (PublicKey key : keys) {
+ if (key.equals(suppliedKey)) {
+ UserModel user = authManager.authenticate(username, key);
+ if (user != null) {
+ client.setUser(user);
+ return true;
+ }
+ }
+ }
+
+ log.warn(
+ "could not authenticate {} for SSH using the supplied public key",
+ username);
+ return false;
+ }
+
+ public IKeyManager getKeyManager() {
+ return keyManager;
+ }
+
+ public void sessionCreated(Session session) {
+ }
+
+ public void sessionEvent(Session sesssion, Event event) {
+ }
+
+ public void sessionClosed(Session session) {
+ cache.remove(session);
+ }
+}