diff options
| author | Gunnar Wagenknecht | 2012-03-03 13:12:16 +0000 |
|---|---|---|
| committer | Lazar Kirchev | 2012-03-03 13:14:08 +0000 |
| commit | f8ceac0dbadd22dc7e587d0bc1cb0505e9fc4a93 (patch) | |
| tree | d546501161ea42c398071540ae940582f9d784b5 | |
| parent | 7dcad19d3d9008f024ed6c247b67675ca56f3c7c (diff) | |
| download | rt.equinox.bundles-f8ceac0dbadd22dc7e587d0bc1cb0505e9fc4a93.tar.gz rt.equinox.bundles-f8ceac0dbadd22dc7e587d0bc1cb0505e9fc4a93.tar.xz rt.equinox.bundles-f8ceac0dbadd22dc7e587d0bc1cb0505e9fc4a93.zip | |
Bug 366188 - Add support for 'authorized_keys' file to SSH consolev20120303-1314
allow custom public key authentication by using OSGi services
Any bundle (with sufficient permission) may provide an
authenticator by registering an OSGi service. If no specific
authorized_keys file is configured (via system property) the OSGi
service registry will be searched for available authenticators.
The authorized_keys file authenticator is now a separate class.
3 files changed, 115 insertions, 38 deletions
diff --git a/bundles/org.eclipse.equinox.console.ssh/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.console.ssh/META-INF/MANIFEST.MF index 9de3d86c6..ebd036df1 100644 --- a/bundles/org.eclipse.equinox.console.ssh/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.console.ssh/META-INF/MANIFEST.MF @@ -26,4 +26,7 @@ Import-Package: javax.security.auth;resolution:=optional, org.osgi.framework;version="1.7.0", org.osgi.service.cm;resolution:=optional, org.osgi.util.tracker -Export-Package: org.eclipse.equinox.console.jaas +Export-Package: org.eclipse.equinox.console.internal.ssh;x-internal:=true, + org.eclipse.equinox.console.jaas, + org.eclipse.equinox.console.ssh;x-internal:=true, + org.eclipse.equinox.console.storage;x-internal:=true diff --git a/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/internal/ssh/AuthorizedKeysFileAuthenticator.java b/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/internal/ssh/AuthorizedKeysFileAuthenticator.java new file mode 100644 index 000000000..5abd2be74 --- /dev/null +++ b/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/internal/ssh/AuthorizedKeysFileAuthenticator.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2012 Gunnar Wagenknecht and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Gunnar Wagenknecht - initial API and implementation + */ +package org.eclipse.equinox.console.internal.ssh; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.PublicKey; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPublicKey; + +import org.apache.sshd.server.PublickeyAuthenticator; +import org.apache.sshd.server.session.ServerSession; + +/** + * {@link PublickeyAuthenticator} which authenticates using a specified + * {@link #setAuthorizedKeysFile(String) authorized_keys} file. + */ +public class AuthorizedKeysFileAuthenticator implements PublickeyAuthenticator { + private String authorizedKeysFile; + + public String getAuthorizedKeysFile() { + return authorizedKeysFile; + } + + public void setAuthorizedKeysFile(String authorizedKeysFile) { + this.authorizedKeysFile = authorizedKeysFile; + } + + public boolean authenticate(String username, PublicKey key, ServerSession session) { + String authorizedKeysFile = getAuthorizedKeysFile(); + if(null == authorizedKeysFile) { + // TODO should use better logging than System.err? + System.err.println("No authorized_keys file configured!"); + return false; + } + try { + // dynamically read key file at each login attempt + AuthorizedKeys keys = new AuthorizedKeys(authorizedKeysFile); + for (PublicKey authorizedKey : keys.getKeys()) { + if (isSameKey(authorizedKey, key)) { + return true; + } + } + } catch (FileNotFoundException e) { + // TODO should use better logging than System.err? + System.err.println("Configured authorized_keys file not found! " + e.getMessage()); + } catch (IOException e) { + // TODO should use better logging than System.err? + System.err.println("Please check authorized_keys file! " + e.getMessage()); + } + return false; + } + + private boolean isSameKey(PublicKey k1, PublicKey k2) throws IOException { + if ((k1 instanceof DSAPublicKey) && (k2 instanceof DSAPublicKey)) { + return isSameDSAKey((DSAPublicKey) k1, (DSAPublicKey) k2); + } else if ((k1 instanceof RSAPublicKey) && (k2 instanceof RSAPublicKey)) { + return isSameRSAKey((RSAPublicKey) k1, (RSAPublicKey) k2); + } else { + throw new IOException("Unsupported key types detected!"); + } + } + + private boolean isSameRSAKey(RSAPublicKey k1, RSAPublicKey k2) { + return k1.getPublicExponent().equals(k2.getPublicExponent()) && k1.getModulus().equals(k2.getModulus()); + } + + private boolean isSameDSAKey(DSAPublicKey k1, DSAPublicKey k2) { + return k1.getY().equals(k2.getY()) && k1.getParams().getG().equals(k2.getParams().getG()) && k1.getParams().getP().equals(k2.getParams().getP()) && k1.getParams().getQ().equals(k2.getParams().getQ()); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/ssh/SshServ.java b/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/ssh/SshServ.java index 09276511c..696cb8d70 100644 --- a/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/ssh/SshServ.java +++ b/bundles/org.eclipse.equinox.console.ssh/src/org/eclipse/equinox/console/ssh/SshServ.java @@ -11,16 +11,15 @@ package org.eclipse.equinox.console.ssh; -import java.io.FileNotFoundException; import java.io.IOException; import java.security.PublicKey; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.RSAPublicKey; import java.util.List; -import org.eclipse.equinox.console.internal.ssh.AuthorizedKeys; +import org.eclipse.equinox.console.internal.ssh.AuthorizedKeysFileAuthenticator; import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; import org.apache.felix.service.command.CommandProcessor; import org.apache.sshd.SshServer; @@ -35,8 +34,10 @@ import org.apache.sshd.server.session.ServerSession; * */ public class SshServ extends Thread { - private int port; - private String host; + + private final BundleContext context; + private final int port; + private final String host; private SshServer sshServer = null; private SshShellFactory shellFactory = null; @@ -46,7 +47,8 @@ public class SshServ extends Thread { private static final String EQUINOX_CONSOLE_DOMAIN = "equinox_console"; public SshServ(List<CommandProcessor> processors, BundleContext context, String host, int port) { - this.host = host; + this.context = context; + this.host = host; this.port = port; shellFactory = new SshShellFactory(processors, context); } @@ -92,46 +94,38 @@ public class SshServ extends Thread { } private PublickeyAuthenticator createSimpleAuthorizedKeysAuthenticator() { - // check if property is set + // use authorized keys file if property is set final String authorizedKeysFile = System.getProperty(SSH_AUTHORIZED_KEYS_FILE_PROP); - if (null == authorizedKeysFile) - return null; + if (null != authorizedKeysFile) { + AuthorizedKeysFileAuthenticator authenticator = new AuthorizedKeysFileAuthenticator(); + authenticator.setAuthorizedKeysFile(authorizedKeysFile); + return authenticator; + } - // dynamically read key file at each login attempt + // fall back to dynamic provider based on available OSGi services return new PublickeyAuthenticator() { + + @Override public boolean authenticate(String username, PublicKey key, ServerSession session) { + // find available services try { - AuthorizedKeys keys = new AuthorizedKeys(authorizedKeysFile); - for (PublicKey authorizedKey : keys.getKeys()) { - if (isSameKey(authorizedKey, key)) { - return true; + for (ServiceReference<PublickeyAuthenticator> reference : context.getServiceReferences(PublickeyAuthenticator.class, null)) { + PublickeyAuthenticator authenticator = null; + try { + authenticator = context.getService(reference); + // first positive match wins; continue looking otherwise + if(authenticator.authenticate(username, key, session)) + return true; + } finally { + if(null != authenticator) + context.ungetService(reference); } } - } catch (FileNotFoundException e) { - System.err.println("Configured authorized_keys file not found! " + e.getMessage()); - } catch (IOException e) { - System.err.println("Please check authorized_keys file! " + e.getMessage()); + } catch (InvalidSyntaxException e) { + // no filter is used } return false; } - - private boolean isSameKey(PublicKey k1, PublicKey k2) throws IOException { - if ((k1 instanceof DSAPublicKey) && (k2 instanceof DSAPublicKey)) { - return isSameDSAKey((DSAPublicKey) k1, (DSAPublicKey) k2); - } else if ((k1 instanceof RSAPublicKey) && (k2 instanceof RSAPublicKey)) { - return isSameRSAKey((RSAPublicKey) k1, (RSAPublicKey) k2); - } else { - throw new IOException("Unsupported key types detected!"); - } - } - - private boolean isSameRSAKey(RSAPublicKey k1, RSAPublicKey k2) { - return k1.getPublicExponent().equals(k2.getPublicExponent()) && k1.getModulus().equals(k2.getModulus()); - } - - private boolean isSameDSAKey(DSAPublicKey k1, DSAPublicKey k2) { - return k1.getY().equals(k2.getY()) && k1.getParams().getG().equals(k2.getParams().getG()) && k1.getParams().getP().equals(k2.getParams().getP()) && k1.getParams().getQ().equals(k2.getParams().getQ()); - } }; } } |
