UserAuthKeyboardInteractive.java
/*
* Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jsch;
import java.util.Locale;
class UserAuthKeyboardInteractive extends UserAuth {
@Override
public boolean start(Session session) throws Exception {
super.start(session);
if (userinfo != null && !(userinfo instanceof UIKeyboardInteractive)) {
return false;
}
String dest = username + "@" + session.host;
if (session.port != 22) {
dest += (":" + session.port);
}
byte[] password = session.password;
boolean cancel = false;
byte[] _username = null;
_username = Util.str2byte(username);
while (true) {
if (session.auth_failures >= session.max_auth_tries) {
return false;
}
// send
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name (ISO-10646 UTF-8, as defined in [RFC-2279])
// string service name (US-ASCII) "ssh-userauth" ? "ssh-connection"
// string "keyboard-interactive" (US-ASCII)
// string language tag (as defined in [RFC-3066])
// string submethods (ISO-10646 UTF-8)
packet.reset();
buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
buf.putString(_username);
buf.putString(Util.str2byte("ssh-connection"));
// buf.putString("ssh-userauth".getBytes());
buf.putString(Util.str2byte("keyboard-interactive"));
buf.putString(Util.empty);
buf.putString(Util.empty);
session.write(packet);
boolean firsttime = true;
loop: while (true) {
buf = session.read(buf);
int command = buf.getCommand() & 0xff;
if (command == SSH_MSG_USERAUTH_SUCCESS) {
return true;
}
if (command == SSH_MSG_USERAUTH_BANNER) {
buf.getInt();
buf.getByte();
buf.getByte();
byte[] _message = buf.getString();
byte[] lang = buf.getString();
String message = Util.byte2str(_message);
if (userinfo != null) {
userinfo.showMessage(message);
}
continue loop;
}
if (command == SSH_MSG_USERAUTH_FAILURE) {
buf.getInt();
buf.getByte();
buf.getByte();
byte[] foo = buf.getString();
int partial_success = buf.getByte();
// System.err.println(new String(foo)+
// " partial_success:"+(partial_success!=0));
if (partial_success != 0) {
throw new JSchPartialAuthException(Util.byte2str(foo));
}
if (firsttime) {
return false;
// throw new JSchException("USERAUTH KI is not supported");
// cancel=true; // ??
}
session.auth_failures++;
break;
}
if (command == SSH_MSG_USERAUTH_INFO_REQUEST) {
firsttime = false;
buf.getInt();
buf.getByte();
buf.getByte();
String name = Util.byte2str(buf.getString());
String instruction = Util.byte2str(buf.getString());
String languate_tag = Util.byte2str(buf.getString());
int num = buf.getInt();
String[] prompt = new String[num];
boolean[] echo = new boolean[num];
for (int i = 0; i < num; i++) {
prompt[i] = Util.byte2str(buf.getString());
echo[i] = (buf.getByte() != 0);
}
byte[][] response = null;
if (password != null && prompt.length == 1 && !echo[0]
&& prompt[0].toLowerCase(Locale.ROOT).indexOf("password:") >= 0) {
response = new byte[1][];
response[0] = password;
password = null;
} else if (num > 0 || (name.length() > 0 || instruction.length() > 0)) {
if (userinfo != null) {
UIKeyboardInteractive kbi = (UIKeyboardInteractive) userinfo;
String[] _response =
kbi.promptKeyboardInteractive(dest, name, instruction, prompt, echo);
if (_response != null) {
response = new byte[_response.length][];
for (int i = 0; i < _response.length; i++) {
response[i] = _response[i] != null ? Util.str2byte(_response[i]) : Util.empty;
}
}
}
}
// byte SSH_MSG_USERAUTH_INFO_RESPONSE(61)
// int num-responses
// string response[1] (ISO-10646 UTF-8)
// ...
// string response[num-responses] (ISO-10646 UTF-8)
packet.reset();
buf.putByte((byte) SSH_MSG_USERAUTH_INFO_RESPONSE);
if (num > 0 && (response == null || // cancel
num != response.length)) {
if (response == null) {
// working around the bug in OpenSSH ;-<
buf.putInt(num);
for (int i = 0; i < num; i++) {
buf.putString(Util.empty);
}
} else {
buf.putInt(0);
}
if (response == null)
cancel = true;
} else {
buf.putInt(num);
for (int i = 0; i < num; i++) {
buf.putString(response[i]);
}
}
session.write(packet);
/*
* if(cancel) break;
*/
continue loop;
}
// throw new JSchException("USERAUTH fail ("+command+")");
return false;
}
if (cancel) {
throw new JSchAuthCancelException("keyboard-interactive");
// break;
}
}
// return false;
}
}