/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.sshd.server.auth.password; import org.apache.sshd.common.RuntimeSshException; import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.server.auth.AbstractUserAuth; import org.apache.sshd.server.session.ServerSession; /** * TODO Add javadoc * * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a> */ public class UserAuthPassword extends AbstractUserAuth { public static final String NAME = UserAuthPasswordFactory.NAME; public UserAuthPassword() { super(NAME); } @Override public Boolean doAuth(Buffer buffer, boolean init) throws Exception { ValidateUtils.checkTrue(init, "Instance not initialized"); boolean newPassword = buffer.getBoolean(); String password = buffer.getString(); if (newPassword) { return handleClientPasswordChangeRequest(buffer, getServerSession(), getUsername(), password, buffer.getString()); } else { return checkPassword(buffer, getServerSession(), getUsername(), password); } } /** * Invokes the configured {@link PasswordAuthenticator} and returns the result. * If {@link PasswordChangeRequiredException} thrown by the authenticator then * {@link #handleServerPasswordChangeRequest(Buffer, ServerSession, String, String, PasswordChangeRequiredException)} * is invoked * * @param buffer The received {@link Buffer} to be re-used if need to send * a password change request * @param session The {@link ServerSession} through which the request was received * @param username The username * @param password The password * @return The authentication result - if {@code null} then exception was handled * internally and authentication is still in progress * @throws Exception If internal error during authentication (exception for * {@link PasswordChangeRequiredException} which is handled internally) * @see #handleServerPasswordChangeRequest(Buffer, ServerSession, String, String, PasswordChangeRequiredException) */ protected Boolean checkPassword(Buffer buffer, ServerSession session, String username, String password) throws Exception { PasswordAuthenticator auth = session.getPasswordAuthenticator(); if (auth == null) { if (log.isDebugEnabled()) { log.debug("checkPassword({}) no password authenticator", session); } return false; } try { boolean authed; try { authed = auth.authenticate(username, password, session); } catch (Error e) { log.warn("checkPassword({}) failed ({}) to consult authenticator: {}", session, e.getClass().getSimpleName(), e.getMessage()); if (log.isDebugEnabled()) { log.debug("checkPassword(" + session + ") authenticator failure details", e); } throw new RuntimeSshException(e); } if (log.isDebugEnabled()) { log.debug("checkPassword({}) authentication result: {}", session, authed); } return authed; } catch (PasswordChangeRequiredException e) { if (log.isDebugEnabled()) { log.debug("checkPassword({}) password change required: {}", session, e.getMessage()); } return handleServerPasswordChangeRequest(buffer, session, username, password, e); } } /** * Invoked when the client sends a {@code SSH_MSG_USERAUTH_REQUEST} indicating * a password change. Throws {@link UnsupportedOperationException} by default * * @param buffer The {@link Buffer} to re-use in order to respond * @param session The associated {@link ServerSession} * @param username The username * @param oldPassword The old password * @param newPassword The new password * @return Password change and authentication result - {@code null} means * authentication incomplete - i.e., handler has sent some extra query. * @throws Exception If failed to handle the request. */ protected Boolean handleClientPasswordChangeRequest( Buffer buffer, ServerSession session, String username, String oldPassword, String newPassword) throws Exception { throw new UnsupportedOperationException("Password change not supported"); } /** * Invoked by {@link #checkPassword(Buffer, ServerSession, String, String)} * when a {@link PasswordChangeRequiredException} was thrown by the authenticator. * By default it re-throws the original exception. * * @param buffer The received {@link Buffer} to be re-used if need to send * a password change request * @param session The {@link ServerSession} through which the request was received * @param username The username * @param password The (rejected) password * @param e The original thrown exception * @return {@code null} by default to indicate incomplete authentication * @throws Exception If failed to dispatch the message */ protected Boolean handleServerPasswordChangeRequest( Buffer buffer, ServerSession session, String username, String password, PasswordChangeRequiredException e) throws Exception { String prompt = e.getPrompt(); String lang = e.getLanguage(); if (log.isDebugEnabled()) { log.debug("handlePasswordChangeRequest({}) password change required - prompt={}, lang={}", session, prompt, lang); } buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, GenericUtils.length(prompt) + GenericUtils.length(lang) + Integer.SIZE); buffer.putString(prompt); buffer.putString(lang); session.writePacket(buffer); return null; // authentication incomplete } }