mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Fix non-ASCII passwords
This commit is contained in:
committed by
Jeroen van Erp
parent
02b70ef427
commit
d5c045defd
@@ -453,11 +453,14 @@ public class Buffer<T extends Buffer<T>> {
|
|||||||
public T putSensitiveString(char[] str) {
|
public T putSensitiveString(char[] str) {
|
||||||
if (str == null)
|
if (str == null)
|
||||||
return putString("");
|
return putString("");
|
||||||
putUInt32(str.length);
|
// RFC 4252, Section 8 says: passwords should be encoded as UTF-8.
|
||||||
ensureCapacity(str.length);
|
// RFC 4256, Section 3.4 says: keyboard-interactive information responses should be encoded as UTF-8.
|
||||||
for (char c : str)
|
byte[] utf8 = ByteArrayUtils.encodeSensitiveStringToUtf8(str);
|
||||||
data[wpos++] = (byte) c;
|
putUInt32(utf8.length);
|
||||||
Arrays.fill(str, ' ');
|
ensureCapacity(utf8.length);
|
||||||
|
for (byte c : utf8)
|
||||||
|
data[wpos++] = c;
|
||||||
|
Arrays.fill(utf8, (byte) 0);
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.common;
|
package net.schmizz.sshj.common;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetEncoder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Utility functions for byte arrays. */
|
/** Utility functions for byte arrays. */
|
||||||
public class ByteArrayUtils {
|
public class ByteArrayUtils {
|
||||||
|
|
||||||
@@ -124,4 +130,26 @@ public class ByteArrayUtils {
|
|||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
|
throw new IllegalArgumentException("Digit '" + c + "' out of bounds [0-9a-fA-F]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a char-array to UTF-8 byte-array and then blanks out source array and all intermediate arrays.
|
||||||
|
* <p/>
|
||||||
|
* This is useful when a plaintext password needs to be encoded as UTF-8.
|
||||||
|
*
|
||||||
|
* @param str A not-null string as a character array.
|
||||||
|
*
|
||||||
|
* @return UTF-8 bytes of the string
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSensitiveStringToUtf8(char[] str) {
|
||||||
|
CharsetEncoder charsetEncoder = Charset.forName("UTF-8").newEncoder();
|
||||||
|
ByteBuffer utf8Buffer = ByteBuffer.allocate((int) (str.length * charsetEncoder.maxBytesPerChar()));
|
||||||
|
assert utf8Buffer.hasArray();
|
||||||
|
charsetEncoder.encode(CharBuffer.wrap(str), utf8Buffer, true);
|
||||||
|
Arrays.fill(str, ' ');
|
||||||
|
|
||||||
|
byte[] utf8Bytes = new byte[utf8Buffer.position()];
|
||||||
|
System.arraycopy(utf8Buffer.array(), 0, utf8Bytes, 0, utf8Bytes.length);
|
||||||
|
Arrays.fill(utf8Buffer.array(), (byte) 0);
|
||||||
|
return utf8Bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed 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 com.hierynomus.sshj.userauth.method;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.test.SshFixture;
|
||||||
|
import net.schmizz.sshj.SSHClient;
|
||||||
|
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
|
||||||
|
import net.schmizz.sshj.userauth.method.ChallengeResponseProvider;
|
||||||
|
import net.schmizz.sshj.userauth.password.Resource;
|
||||||
|
import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
|
||||||
|
import org.apache.sshd.common.NamedFactory;
|
||||||
|
import org.apache.sshd.server.auth.UserAuth;
|
||||||
|
import org.apache.sshd.server.auth.keyboard.UserAuthKeyboardInteractive;
|
||||||
|
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
|
||||||
|
import org.apache.sshd.server.session.ServerSession;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
public class AuthKeyboardInteractiveTest {
|
||||||
|
@Rule
|
||||||
|
public SshFixture fixture = new SshFixture(false);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setKeyboardInteractiveAuthenticator() throws IOException {
|
||||||
|
fixture.getServer().setUserAuthFactories(Collections.<NamedFactory<UserAuth>>singletonList(new NamedFactory<UserAuth>() {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return UserAuthKeyboardInteractiveFactory.NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserAuth get() {
|
||||||
|
return new UserAuthKeyboardInteractive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserAuth create() {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
fixture.getServer().setPasswordAuthenticator(new PasswordAuthenticator() {
|
||||||
|
@Override
|
||||||
|
public boolean authenticate(String username, String password, ServerSession session) {
|
||||||
|
return password.equals(username);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fixture.getServer().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldEncodePasswordsAsUtf8() throws IOException {
|
||||||
|
SSHClient sshClient = fixture.setupConnectedDefaultClient();
|
||||||
|
final String userAndPassword = "øæå";
|
||||||
|
sshClient.auth(userAndPassword, new AuthKeyboardInteractive(new ChallengeResponseProvider() {
|
||||||
|
@Override
|
||||||
|
public List<String> getSubmethods() {
|
||||||
|
return new ArrayList<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Resource resource, String name, String instruction) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char[] getResponse(String prompt, boolean echo) {
|
||||||
|
return userAndPassword.toCharArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRetry() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
assertThat("Should have been authenticated", sshClient.isAuthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -144,6 +144,14 @@ public class AuthPasswordTest {
|
|||||||
assertThat("Should have been authenticated", sshClient.isAuthenticated());
|
assertThat("Should have been authenticated", sshClient.isAuthenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldEncodePasswordsAsUtf8() throws IOException {
|
||||||
|
SSHClient sshClient = fixture.setupConnectedDefaultClient();
|
||||||
|
String userAndPassword = "øæå";
|
||||||
|
sshClient.authPassword(userAndPassword, userAndPassword);
|
||||||
|
assertThat("Should have been authenticated", sshClient.isAuthenticated());
|
||||||
|
}
|
||||||
|
|
||||||
private static class StaticPasswordUpdateProvider implements PasswordUpdateProvider {
|
private static class StaticPasswordUpdateProvider implements PasswordUpdateProvider {
|
||||||
private Stack<String> newPasswords = new Stack<String>();
|
private Stack<String> newPasswords = new Stack<String>();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user