/* * This file is part of LCMC written by Rasto Levrinc. * * Copyright (C) 2014, Rastislav Levrinc. * * The LCMC is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your option) * any later version. * * The LCMC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LCMC; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ package lcmc.cluster.service.ssh; import java.io.File; import java.io.IOException; import lcmc.common.domain.Application; import lcmc.host.domain.Host; import lcmc.cluster.ui.SSHGui; import lcmc.logger.Logger; import lcmc.logger.LoggerFactory; import lcmc.common.domain.util.Tools; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @Named public class Authentication { private static final Logger LOG = LoggerFactory.getLogger(Authentication.class); private LastSuccessfulPassword lastSuccessfulPassword; private Host host; private SSHGui sshGui; private boolean enablePublicKey = true; private int publicKeyTry = 3; private final int connectTimeout = Tools.getDefaultInt("SSH.ConnectTimeout"); private final int kexTimeout = Tools.getDefaultInt("SSH.KexTimeout"); private String lastError = null; private boolean authenticated = false; private int passwdTry = 3; private boolean enableKeyboardInteractive = true; @Inject private Application application; @Inject private Provider<PopupHostKeyVerifier> popupHostKeyVerifierProvider; public void init(final LastSuccessfulPassword lastSuccessfulPassword, final Host host, final SSHGui sshGui) { this.lastSuccessfulPassword = lastSuccessfulPassword; this.host = host; this.sshGui = sshGui; } public void authenticate(final SshConnection sshConnection) throws IOException { LOG.debug2("authenticate: start"); final String username = host.getFirstUsername(); while (!sshConnection.isCanceled() && !authenticated) { if (lastSuccessfulPassword.getPassword() == null) { String lastPassword = application.getAutoOptionHost("pw"); if (lastPassword == null) { lastPassword = application.getAutoOptionCluster("pw"); } lastSuccessfulPassword.setPassword(lastPassword); } if (lastSuccessfulPassword.getPassword() == null && enablePublicKey && sshConnection.isAuthMethodAvailable(username, "publickey")) { authenticateWithKey(sshConnection, username); } else if (enableKeyboardInteractive && sshConnection.isAuthMethodAvailable(username, "keyboard-interactive")) { authenticateWithKeyboardInteractive(sshConnection, username); } else if (sshConnection.isAuthMethodAvailable(username, "password")) { authenticateWithPassword(sshConnection, username); } else { throw new IOException("No supported authentication methods available."); } } } private void authenticateWithKey(final SshConnection sshConnection, final String username) throws IOException { final File dsaKey = new File(application.getIdDSAPath()); final File rsaKey = new File(application.getIdRSAPath()); if (dsaKey.exists() || rsaKey.exists()) { String key = ""; if (lastSuccessfulPassword.getDsaKey() != null) { key = lastSuccessfulPassword.getDsaKey(); } else if (lastSuccessfulPassword.getRsaKey() != null) { key = lastSuccessfulPassword.getRsaKey(); } /* Passwordless auth */ if (application.isNoPassphrase() || !"".equals(key)) { if (lastSuccessfulPassword.getRsaKey() == null && dsaKey.exists()) { authenticateWithDsaKey(sshConnection, username, dsaKey, key); if (authenticated) { return; } } if (rsaKey.exists()) { authenticateWithRsaKey(sshConnection, username, rsaKey, key); if (authenticated) { return; } } } key = getKeyFromUser(lastError); if (key == null) { sshConnection.cancel(); sshConnection.disconnectForGood(); return; } if ("".equals(key)) { publicKeyTry = 0; } if (dsaKey.exists()) { authenticateWithDsaKey(sshConnection, username, dsaKey, key); if (authenticated) { return; } } if (rsaKey.exists()) { authenticateWithRsaKey(sshConnection, username, rsaKey, key); if (authenticated) { return; } } lastError = Tools.getString("SSH.Publickey.Authentication.Failed"); } else { publicKeyTry = 0; } publicKeyTry--; if (publicKeyTry <= 0) { // do not try again enablePublicKey = false; publicKeyTry = 3; } } private void authenticateWithRsaKey(final SshConnection sshConnection, final String username, final File rsaKey, final String key) throws IOException { boolean res; try { res = sshConnection.authenticateWithPublicKey(username, rsaKey, key); if (res) { LOG.debug("authenticate: rsa key auth successful"); lastSuccessfulPassword.setDsaKey(null); lastSuccessfulPassword.setRsaKey(key); lastSuccessfulPassword.setPassword(null); authenticated = true; return; } } catch (final IOException e) { lastSuccessfulPassword.setRsaKey(null); LOG.debug("authenticate: rsa key failed"); } sshConnection.close(); final PopupHostKeyVerifier popupHostKeyVerifier = popupHostKeyVerifierProvider.get(); popupHostKeyVerifier.init(sshGui); sshConnection.connect(popupHostKeyVerifier, connectTimeout, kexTimeout); } private void authenticateWithDsaKey(final SshConnection sshConnection, final String username, final File dsaKey, final String key) throws IOException { boolean res; try { res = sshConnection.authenticateWithPublicKey(username, dsaKey, key); if (res) { LOG.debug("authenticate: dsa key auth successful"); lastSuccessfulPassword.setDsaKey(key); lastSuccessfulPassword.setRsaKey(null); lastSuccessfulPassword.setPassword(null); authenticated = true; return; } } catch (final IOException e) { lastSuccessfulPassword.setDsaKey(null); LOG.debug("authenticate: dsa key failed"); } sshConnection.close(); final PopupHostKeyVerifier popupHostKeyVerifier = popupHostKeyVerifierProvider.get(); popupHostKeyVerifier.init(sshGui); sshConnection.connect(popupHostKeyVerifier, connectTimeout, kexTimeout); } private void authenticateWithKeyboardInteractive(final SshConnection sshConnection, final String username) throws IOException { final InteractiveLogic interactiveLogic = new InteractiveLogic(lastError, host, lastSuccessfulPassword, sshGui); final boolean res = sshConnection.authenticateWithKeyboardInteractive(username, interactiveLogic); if (res) { lastSuccessfulPassword.setRsaKey(null); lastSuccessfulPassword.setDsaKey(null); authenticated = true; return; } else { lastSuccessfulPassword.setPassword(null); } if (interactiveLogic.getPromptCount() == 0) { /* "keyboard-interactive" denied */ lastError = Tools.getString("SSH.KeyboardInteractive.DoesNotWork"); /* do not try this again */ enableKeyboardInteractive = false; } else { /* try again, if possible */ lastError = Tools.getString("SSH.KeyboardInteractive.Failed"); } } private void authenticateWithPassword(final SshConnection sshConnection, final String username) throws IOException { final String ans; if (lastSuccessfulPassword.getPassword() == null) { ans = sshGui.enterSomethingDialog(Tools.getString("SSH.PasswordAuthentication"), new String[]{lastError, "<html>" + host.getUserAtHost() + Tools.getString("SSH.Enter.password") + "</html>"}, null, null, true); if (ans == null) { sshConnection.cancel(); return; } } else { ans = lastSuccessfulPassword.getPassword(); } if (ans == null) { throw new IOException("Login aborted by user"); } if ("".equals(ans)) { passwdTry = 0; } final boolean res = sshConnection.authenticateWithPassword(username, ans); if (res) { lastSuccessfulPassword.setPassword(ans); host.setSudoPassword(ans); lastSuccessfulPassword.setRsaKey(null); lastSuccessfulPassword.setDsaKey(null); authenticated = true; return; } else { lastSuccessfulPassword.setPassword(null); } /* try again, if possible */ lastError = Tools.getString("SSH.Password.Authentication.Failed"); passwdTry--; if (passwdTry <= 0) { enablePublicKey = true; passwdTry = 3; } } private String getKeyFromUser(final String lastError) { return sshGui.enterSomethingDialog(Tools.getString("SSH.RSA.DSA.Authentication"), new String[] {lastError, "<html>" + Tools.getString( "SSH.Enter.passphrase") + "</html>", }, "<html>" + Tools.getString("SSH.Enter.passphrase2") + "</html>", Tools.getDefault("SSH.PublicKey"), true); } }