001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 package org.crsh.ssh.term; 020 021 import org.apache.sshd.SshServer; 022 import org.apache.sshd.common.Session; 023 import org.apache.sshd.server.PasswordAuthenticator; 024 import org.apache.sshd.server.PublickeyAuthenticator; 025 import org.apache.sshd.server.ServerFactoryManager; 026 import org.apache.sshd.server.session.ServerSession; 027 import org.crsh.plugin.PluginContext; 028 import org.crsh.auth.AuthenticationPlugin; 029 import org.crsh.ssh.SSHPlugin; 030 import org.crsh.ssh.term.scp.SCPCommandFactory; 031 import org.crsh.term.TermLifeCycle; 032 import org.crsh.term.spi.TermIOHandler; 033 import org.crsh.vfs.Resource; 034 035 import java.security.PublicKey; 036 import java.util.logging.Level; 037 import java.util.logging.Logger; 038 039 /** 040 * Interesting stuff here : http://gerrit.googlecode.com/git-history/4b9e5e7fb9380cfadd28d7ffe3dc496dc06f5892/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java 041 */ 042 public class SSHLifeCycle extends TermLifeCycle { 043 044 /** . */ 045 public static final Session.AttributeKey<String> USERNAME = new Session.AttributeKey<java.lang.String>(); 046 047 /** . */ 048 public static final Session.AttributeKey<String> PASSWORD = new Session.AttributeKey<java.lang.String>(); 049 050 /** . */ 051 private final Logger log = Logger.getLogger(SSHLifeCycle.class.getName()); 052 053 /** . */ 054 private SshServer server; 055 056 /** . */ 057 private int port; 058 059 060 /** . */ 061 private int idleTimeout; 062 063 /** . */ 064 private int authTimeout; 065 066 067 /** . */ 068 private Resource key; 069 070 /** . */ 071 private final AuthenticationPlugin authentication; 072 073 /** . */ 074 private Integer localPort; 075 076 public SSHLifeCycle(PluginContext context, AuthenticationPlugin<?> authentication) { 077 super(context); 078 079 // 080 this.authentication = authentication; 081 } 082 083 public int getPort() { 084 return port; 085 } 086 087 public void setPort(int port) { 088 this.port = port; 089 } 090 091 public int getIdleTimeout() { 092 return idleTimeout; 093 } 094 095 public void setIdleTimeout(int idleTimeout) { 096 this.idleTimeout = idleTimeout; 097 } 098 099 public int getAuthTimeout() { 100 return authTimeout; 101 } 102 103 public void setAuthTimeout(int authTimeout) { 104 this.authTimeout = authTimeout; 105 } 106 107 /** 108 * Returns the local part after the ssh server has been succesfully bound or null. This is useful when 109 * the port is chosen at random by the system. 110 * 111 * @return the local port 112 */ 113 public Integer getLocalPort() { 114 return localPort; 115 } 116 117 public Resource getKey() { 118 return key; 119 } 120 121 public void setKey(Resource key) { 122 this.key = key; 123 } 124 125 @Override 126 protected void doInit() { 127 try { 128 129 // 130 TermIOHandler handler = getHandler(); 131 132 // 133 SshServer server = SshServer.setUpDefaultServer(); 134 server.setPort(port); 135 136 if (this.idleTimeout > 0) { 137 server.getProperties().put(ServerFactoryManager.IDLE_TIMEOUT, String.valueOf(this.idleTimeout)); 138 } 139 if (this.authTimeout > 0) { 140 server.getProperties().put(ServerFactoryManager.AUTH_TIMEOUT, String.valueOf(this.authTimeout)); 141 } 142 143 144 server.setShellFactory(new CRaSHCommandFactory(handler)); 145 server.setCommandFactory(new SCPCommandFactory(getContext())); 146 server.setKeyPairProvider(new URLKeyPairProvider(key)); 147 148 // 149 if (authentication.getCredentialType().equals(String.class)) { 150 @SuppressWarnings("unchecked") 151 final AuthenticationPlugin<String> passwordAuthentication = (AuthenticationPlugin<String>)authentication; 152 server.setPasswordAuthenticator(new PasswordAuthenticator() { 153 public boolean authenticate(String _username, String _password, ServerSession session) { 154 boolean auth; 155 try { 156 log.log(Level.FINE, "Using authentication plugin " + authentication + " to authenticate user " + _username); 157 auth = passwordAuthentication.authenticate(_username, _password); 158 } catch (Exception e) { 159 log.log(Level.SEVERE, "Exception authenticating user " + _username + " in authentication plugin: " + authentication, e); 160 return false; 161 } 162 163 // We store username and password in session for later reuse 164 session.setAttribute(USERNAME, _username); 165 session.setAttribute(PASSWORD, _password); 166 167 // 168 return auth; 169 } 170 }); 171 } else if (authentication.getCredentialType().equals(PublicKey.class)) { 172 @SuppressWarnings("unchecked") 173 final AuthenticationPlugin<PublicKey> keyAuthentication = (AuthenticationPlugin<PublicKey>)authentication; 174 server.setPublickeyAuthenticator(new PublickeyAuthenticator() { 175 public boolean authenticate(String username, PublicKey key, ServerSession session) { 176 try { 177 log.log(Level.FINE, "Using authentication plugin " + authentication + " to authenticate user " + username); 178 179 180 return keyAuthentication.authenticate(username, key); 181 } 182 catch (Exception e) { 183 log.log(Level.SEVERE, "Exception authenticating user " + username + " in authentication plugin: " + authentication, e); 184 return false; 185 } 186 } 187 }); 188 } 189 190 // 191 log.log(Level.INFO, "About to start CRaSSHD"); 192 server.start(); 193 localPort = server.getPort(); 194 log.log(Level.INFO, "CRaSSHD started on port " + localPort); 195 196 // 197 this.server = server; 198 } 199 catch (Throwable e) { 200 log.log(Level.SEVERE, "Could not start CRaSSHD", e); 201 } 202 } 203 204 @Override 205 protected void doDestroy() { 206 if (server != null) { 207 try { 208 server.stop(); 209 } 210 catch (InterruptedException e) { 211 log.log(Level.FINE, "Got an interruption when stopping server", e); 212 } 213 } 214 } 215 }