From 5485da49b04bb139d28d42fe8f3d371915e79a3d Mon Sep 17 00:00:00 2001
From: Fabrice Bacchella <fbacchella@spamcop.net>
Date: Mon, 04 May 2015 05:52:12 -0400
Subject: [PATCH] Adding Kerberos5/GSS authentication to ssh Adding the possibility to define authentication method order for ssh

---
 src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java |    1 
 src/test/java/com/gitblit/tests/SshUnitTest.java                  |    8 ++++
 src/main/java/com/gitblit/transport/ssh/SshDaemon.java            |   58 ++++++++++++++++++++++++++++
 src/main/distrib/data/defaults.properties                         |   22 +++++++++++
 src/test/config/test-gitblit.properties                           |    2 +
 5 files changed, 90 insertions(+), 1 deletions(-)

diff --git a/src/main/distrib/data/defaults.properties b/src/main/distrib/data/defaults.properties
index 0857ccf..ee97cb6 100644
--- a/src/main/distrib/data/defaults.properties
+++ b/src/main/distrib/data/defaults.properties
@@ -126,6 +126,28 @@
 # SINCE 1.5.0
 git.sshKeysFolder= ${baseFolder}/ssh
 
+# Use kerberos5 (GSS) authentication
+#
+# SINCE 1.7.0
+git.sshWithKrb5 = "false"
+
+# The path to a kerberos 5 keytab.
+#
+# SINCE 1.7.0
+git.sshKrb5Keytab = ""
+
+# The service principal name to be used for Kerberos5.  The default is host/hostname.
+#
+# SINCE 1.7.0
+git.sshKrb5ServicePrincipalName = ""
+
+# A comma-separated list of authentication method. They will be tried in
+# the given order. Possible values are 
+# "gssapi-with-mic", "publickey", "keyboard-interactive" or "password"
+#
+# SINCE 1.7.0
+git.sshAuthenticatorsOrder = "password,keyboard-interactive,publickey"
+
 # SSH backend NIO2|MINA.
 #
 # The Apache Mina project recommends using the NIO2 backend.
diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
index 9667154..ec7d7c3 100644
--- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
+++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
@@ -23,15 +23,25 @@
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.sshd.SshServer;
+import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.io.IoServiceFactoryFactory;
 import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory;
 import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.server.auth.CachingPublicKeyAuthenticator;
+import org.apache.sshd.server.UserAuth;
+import org.apache.sshd.server.auth.UserAuthKeyboardInteractive;
+import org.apache.sshd.server.auth.UserAuthPassword;
+import org.apache.sshd.server.auth.UserAuthPublicKey;
+import org.apache.sshd.server.auth.gss.GSSAuthenticator;
+import org.apache.sshd.server.auth.gss.UserAuthGSS;
 import org.bouncycastle.openssl.PEMWriter;
 import org.eclipse.jgit.internal.JGitText;
 import org.slf4j.Logger;
@@ -120,7 +130,49 @@
 		} else {
 			addr = new InetSocketAddress(bindInterface, port);
 		}
-
+		
+		//Will do GSS ?
+		GSSAuthenticator gssAuthenticator = null;
+		if(settings.getBoolean(Keys.git.sshWithKrb5, false)) {
+			gssAuthenticator = new GSSAuthenticator();
+			String keytabString = settings.getString(Keys.git.sshKrb5Keytab,
+					"");
+			if(! keytabString.isEmpty()) {
+				gssAuthenticator.setKeytabFile(keytabString);
+			}
+			String servicePrincipalName = settings.getString(Keys.git.sshKrb5ServicePrincipalName,
+					"");
+			if(! servicePrincipalName.isEmpty()) {
+				gssAuthenticator.setServicePrincipalName(servicePrincipalName);
+			}			
+		}
+		
+		//Sort the authenticators for sshd
+		List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<>();
+		String sshAuthenticatorsOrderString = settings.getString(Keys.git.sshAuthenticatorsOrder,
+				"password,keyboard-interactive,publickey");
+		for(String authenticator: sshAuthenticatorsOrderString.split(",")) {
+			String authenticatorName = authenticator.trim().toLowerCase(Locale.US);
+			switch (authenticatorName) {
+			case "gssapi-with-mic":
+				if(gssAuthenticator != null) {
+					userAuthFactories.add(new UserAuthGSS.Factory());					
+				}
+				break;
+			case "publickey":
+				userAuthFactories.add(new UserAuthPublicKey.Factory());
+				break;
+			case "password":
+				userAuthFactories.add(new UserAuthPassword.Factory());
+				break;
+			case "keyboard-interactive":
+				userAuthFactories.add(new UserAuthKeyboardInteractive.Factory());
+				break;
+			default:
+				log.error("Unknown ssh authenticator: '{}'", authenticatorName);
+			}
+		}
+		
 		// Create the SSH server
 		sshd = SshServer.setUpDefaultServer();
 		sshd.setPort(addr.getPort());
@@ -128,6 +180,10 @@
 		sshd.setKeyPairProvider(hostKeyPairProvider);
 		sshd.setPublickeyAuthenticator(new CachingPublicKeyAuthenticator(keyAuthenticator));
 		sshd.setPasswordAuthenticator(new UsernamePasswordAuthenticator(gitblit));
+		if(gssAuthenticator != null) {
+			sshd.setGSSAuthenticator(gssAuthenticator);
+		}
+		sshd.setUserAuthFactories(userAuthFactories);
 		sshd.setSessionFactory(new SshServerSessionFactory());
 		sshd.setFileSystemFactory(new DisabledFilesystemFactory());
 		sshd.setTcpipForwardingFilter(new NonForwardingFilter());
diff --git a/src/test/config/test-gitblit.properties b/src/test/config/test-gitblit.properties
index 78e9ab9..398047c 100644
--- a/src/test/config/test-gitblit.properties
+++ b/src/test/config/test-gitblit.properties
@@ -9,6 +9,8 @@
 git.daemonPort = 8300
 git.sshPort = 29418
 git.sshKeysManager = com.gitblit.transport.ssh.MemoryKeyManager
+git.sshWithKrb5 = true
+git.sshAuthenticatorsOrder = password, publickey,gssapi-with-mic,invalid
 groovy.scriptsFolder = src/main/distrib/data/groovy
 groovy.preReceiveScripts = blockpush
 groovy.postReceiveScripts = sendmail
diff --git a/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java b/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java
index 5d24b40..421f336 100644
--- a/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java
+++ b/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java
@@ -21,6 +21,7 @@
     @Override
     protected void configure(OpenSshConfig.Host host, Session session) {
         session.setConfig("StrictHostKeyChecking", "no");
+        session.setConfig("PreferredAuthentications", "password");
     }
 
     @Override
diff --git a/src/test/java/com/gitblit/tests/SshUnitTest.java b/src/test/java/com/gitblit/tests/SshUnitTest.java
index 43b51b7..3def700 100644
--- a/src/test/java/com/gitblit/tests/SshUnitTest.java
+++ b/src/test/java/com/gitblit/tests/SshUnitTest.java
@@ -24,13 +24,18 @@
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.sshd.ClientChannel;
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.SshClient;
 import org.apache.sshd.client.ServerKeyVerifier;
+import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.client.UserAuth;
+import org.apache.sshd.client.auth.UserAuthPublicKey;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -102,6 +107,9 @@
 				return true;
 			}
 		});
+		List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<>();
+		userAuthFactories.add(new UserAuthPublicKey.Factory());
+		client.setUserAuthFactories(userAuthFactories);
 		client.start();
 		return client;
 	}

--
Gitblit v1.9.1