Skip to content

Commit

Permalink
Allow usage of keys from ssh-agent that would otherwise not work beca…
Browse files Browse the repository at this point in the history
…use of missing support for the signature algorithm.
  • Loading branch information
norrisjeremy committed Sep 6, 2021
1 parent 8061e21 commit 6c4c788
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 85 deletions.
150 changes: 75 additions & 75 deletions src/main/java/com/jcraft/jsch/UserAuthPublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

package com.jcraft.jsch;

import java.util.Vector;
import java.util.*;

class UserAuthPublicKey extends UserAuth{

Expand All @@ -44,75 +44,70 @@ public boolean start(Session session) throws Exception{
return false;
}

String pkmethods=session.getConfig("PubkeyAcceptedAlgorithms");
String pkmethodstr=session.getConfig("PubkeyAcceptedAlgorithms");
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
"Before pruning PubkeyAcceptedAlgorithms = " + pkmethods);
"PubkeyAcceptedAlgorithms = " + pkmethodstr);
}

String[] not_available_pks = session.getUnavailableSignatures();
if(not_available_pks!=null && not_available_pks.length>0){
pkmethods=Util.diffString(pkmethods, not_available_pks);
if(pkmethods==null){
throw new JSchException("There are not any available sig algorithm.");
String[] not_available_pka = session.getUnavailableSignatures();
List<String> not_available_pks=(not_available_pka!=null && not_available_pka.length>0 ?
Arrays.asList(not_available_pka) :
Collections.emptyList());
if(!not_available_pks.isEmpty()){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
"Signature algorithms unavailable for non-agent identities = " + not_available_pks);
}
}
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
"After getUnavailableSignatures PubkeyAcceptedAlgorithms = " + pkmethods);
}

String[] pkmethoda=Util.split(pkmethods, ",");
if(pkmethoda.length==0){
List<String> pkmethods=Arrays.asList(Util.split(pkmethodstr, ","));
if(pkmethods.isEmpty()){
return false;
}

String[] server_sig_algs=session.getServerSigAlgs();
if(server_sig_algs!=null && server_sig_algs.length>0){
String _known=null;
String _unknown=null;
for(int i=0; i<pkmethoda.length; i++){
List<String> _known=new ArrayList<>();
List<String> _unknown=new ArrayList<>();
for(String pkmethod : pkmethods){
boolean add=false;
for(int j=0; j<server_sig_algs.length; j++){
if(pkmethoda[i].equals(server_sig_algs[j])){
for(String server_sig_alg : server_sig_algs){
if(pkmethod.equals(server_sig_alg)){
add=true;
break;
}
}

if(add){
if(_known==null) _known=pkmethoda[i];
else _known+=","+pkmethoda[i];
_known.add(pkmethod);
}
else{
if(_unknown==null) _unknown=pkmethoda[i];
else _unknown+=","+pkmethoda[i];
_unknown.add(pkmethod);
}
}

if(_known!=null){
if(!_known.isEmpty()){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
"PubkeyAcceptedAlgorithms in server-sig-algs = " + _known);
}
}

if(_unknown!=null){
if(!_unknown.isEmpty()){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
"PubkeyAcceptedAlgorithms not in server-sig-algs = " + _unknown);
}
}

if(_known!=null && _unknown!=null){
String[] _knowna=Util.split(_known, ",");
boolean success=_start(session, identities, _knowna);
if(!_known.isEmpty() && !_unknown.isEmpty()){
boolean success=_start(session, identities, _known, not_available_pks);
if(success){
return true;
}

String[] _unknowna=Util.split(_unknown, ",");
return _start(session, identities, _unknowna);
return _start(session, identities, _unknown, not_available_pks);
}
}
else{
Expand All @@ -121,77 +116,74 @@ public boolean start(Session session) throws Exception{
}
}

return _start(session, identities, pkmethoda);
return _start(session, identities, pkmethods, not_available_pks);
}
}

private boolean _start(Session session, Vector<Identity> identities, String[] pkmethoda) throws Exception{
private boolean _start(Session session, List<Identity> identities, List<String> pkmethods, List<String> not_available_pks) throws Exception{
if(session.auth_failures >= session.max_auth_tries){
return false;
}

String rsamethods=null;
String nonrsamethods=null;
for(int i=0; i<pkmethoda.length; i++){
if(pkmethoda[i].equals("ssh-rsa") || pkmethoda[i].equals("rsa-sha2-256") || pkmethoda[i].equals("rsa-sha2-512") ||
pkmethoda[i].equals("[email protected]") || pkmethoda[i].equals("[email protected]") ||
pkmethoda[i].equals("[email protected]") || pkmethoda[i].equals("[email protected]")){
if(rsamethods==null) rsamethods=pkmethoda[i];
else rsamethods+=","+pkmethoda[i];
List<String> rsamethods=new ArrayList<>();
List<String> nonrsamethods=new ArrayList<>();
for(String pkmethod : pkmethods){
if(pkmethod.equals("ssh-rsa") || pkmethod.equals("rsa-sha2-256") || pkmethod.equals("rsa-sha2-512") ||
pkmethod.equals("[email protected]") || pkmethod.equals("[email protected]") ||
pkmethod.equals("[email protected]") || pkmethod.equals("[email protected]")){
rsamethods.add(pkmethod);
}
else{
if(nonrsamethods==null) nonrsamethods=pkmethoda[i];
else nonrsamethods+=","+pkmethoda[i];
nonrsamethods.add(pkmethod);
}
}
String[] rsamethoda=Util.split(rsamethods, ",");
String[] nonrsamethoda=Util.split(nonrsamethods, ",");

byte[] _username=Util.str2byte(username);

int command;

iloop:
for(int i=0; i<identities.size(); i++){
for(Identity identity : identities){

if(session.auth_failures >= session.max_auth_tries){
return false;
}

Identity identity=identities.elementAt(i);

//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());
decryptKey(session, identity);
//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted());

String ipkmethod=identity.getAlgName();
String[] ipkmethoda=null;
if(ipkmethod.equals("ssh-rsa")){
ipkmethoda=rsamethoda;
String _ipkmethod=identity.getAlgName();
List<String> ipkmethods=null;
if(_ipkmethod.equals("ssh-rsa")){
ipkmethods=rsamethods;
}
else if(nonrsamethoda!=null && nonrsamethoda.length>0){
for(int j=0; j<nonrsamethoda.length; j++){
if(ipkmethod.equals(nonrsamethoda[j])){
ipkmethoda=new String[]{ipkmethod};
break;
}
}
else if(nonrsamethods.contains(_ipkmethod)){
ipkmethods=Collections.singletonList(_ipkmethod);
}
if(ipkmethoda==null) {
if(ipkmethods==null) {
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
ipkmethod+" cannot be used as public key type for identity "+identity.getName());
_ipkmethod+" cannot be used as public key type for identity "+identity.getName());
}
continue;
}

byte[] pubkeyblob=identity.getPublicKeyBlob();
String[] pkmethodsuccess=null;
List<String> pkmethodsuccesses=null;

if(pubkeyblob!=null){
command=SSH_MSG_USERAUTH_FAILURE;
loop3:
for(int j=0; j<ipkmethoda.length; j++){
for(String ipkmethod : ipkmethods){
if(not_available_pks.contains(ipkmethod) && !(identity instanceof AgentIdentity)){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
ipkmethod+" not available for identity "+identity.getName());
}
continue loop3;
}

// send
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name
Expand All @@ -206,7 +198,7 @@ else if(nonrsamethoda!=null && nonrsamethoda.length>0){
buf.putString(Util.str2byte("ssh-connection"));
buf.putString(Util.str2byte("publickey"));
buf.putByte((byte)0);
buf.putString(Util.str2byte(ipkmethoda[j]));
buf.putString(Util.str2byte(ipkmethod));
buf.putString(pubkeyblob);
session.write(packet);

Expand All @@ -218,15 +210,15 @@ else if(nonrsamethoda!=null && nonrsamethoda.length>0){
if(command==SSH_MSG_USERAUTH_PK_OK){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
ipkmethoda[j] + " preauth success");
ipkmethod + " preauth success");
}
pkmethodsuccess=new String[]{ipkmethoda[j]};
pkmethodsuccesses=Collections.singletonList(ipkmethod);
break loop3;
}
else if(command==SSH_MSG_USERAUTH_FAILURE){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
ipkmethoda[j] + " preauth failure");
ipkmethod + " preauth failure");
}
continue loop3;
}
Expand All @@ -245,7 +237,7 @@ else if(command==SSH_MSG_USERAUTH_BANNER){
//throw new JSchException("USERAUTH fail ("+command+")");
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
ipkmethoda[j] + " preauth failure command (" + command + ")");
ipkmethod + " preauth failure command (" + command + ")");
}
continue loop3;
}
Expand All @@ -265,10 +257,18 @@ else if(command==SSH_MSG_USERAUTH_BANNER){
//System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob);

if(pubkeyblob==null) continue;
if(pkmethodsuccess==null) pkmethodsuccess=ipkmethoda;
if(pkmethodsuccesses==null) pkmethodsuccesses=ipkmethods;

loop4:
for(int j=0; j<pkmethodsuccess.length; j++){
for(String pkmethodsuccess : pkmethodsuccesses){
if(not_available_pks.contains(pkmethodsuccess) && !(identity instanceof AgentIdentity)){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
pkmethodsuccess+" not available for identity "+identity.getName());
}
continue loop4;
}

// send
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name
Expand All @@ -284,7 +284,7 @@ else if(command==SSH_MSG_USERAUTH_BANNER){
buf.putString(Util.str2byte("ssh-connection"));
buf.putString(Util.str2byte("publickey"));
buf.putByte((byte)1);
buf.putString(Util.str2byte(pkmethodsuccess[j]));
buf.putString(Util.str2byte(pkmethodsuccess));
buf.putString(pubkeyblob);

// byte[] tmp=new byte[buf.index-5];
Expand All @@ -300,11 +300,11 @@ else if(command==SSH_MSG_USERAUTH_BANNER){
tmp[3]=(byte)(sidlen);
System.arraycopy(sid, 0, tmp, 4, sidlen);
System.arraycopy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5);
byte[] signature=identity.getSignature(tmp, pkmethodsuccess[j]);
byte[] signature=identity.getSignature(tmp, pkmethodsuccess);
if(signature==null){ // for example, too long key length.
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
pkmethodsuccess[j] + " signature failure");
pkmethodsuccess + " signature failure");
}
continue loop4;
}
Expand All @@ -319,7 +319,7 @@ else if(command==SSH_MSG_USERAUTH_BANNER){
if(command==SSH_MSG_USERAUTH_SUCCESS){
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
pkmethodsuccess[j] + " auth success");
pkmethodsuccess + " auth success");
}
return true;
}
Expand All @@ -345,15 +345,15 @@ else if(command==SSH_MSG_USERAUTH_FAILURE){
session.auth_failures++;
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
pkmethodsuccess[j] + " auth failure");
pkmethodsuccess + " auth failure");
}
break loop2;
}
//System.err.println("USERAUTH fail ("+command+")");
//throw new JSchException("USERAUTH fail ("+command+")");
if(JSch.getLogger().isEnabled(Logger.DEBUG)){
JSch.getLogger().log(Logger.DEBUG,
pkmethodsuccess[j] + " auth failure command (" + command +")");
pkmethodsuccess + " auth failure command (" + command +")");
}
break loop2;
}
Expand Down
6 changes: 2 additions & 4 deletions src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,14 @@ public void testServerSigAlgs() throws Exception {
String expectedExtInfo = "SSH_MSG_EXT_INFO received";
String expectedServerSigAlgs = "server-sig-algs=<ssh-ed25519,ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>";
String expectedOpenSSH74Bug = "OpenSSH 7.4 detected: adding rsa-sha2-256 & rsa-sha2-512 to server-sig-algs";
String expectedPubkeysBefore = String.format("After getUnavailableSignatures PubkeyAcceptedAlgorithms = %s", algos);
String expectedPubkeysKnown = "PubkeyAcceptedAlgorithms in server-sig-algs = rsa-sha2-512,rsa-sha2-256,ssh-rsa";
String expectedPubkeysUnknown = "PubkeyAcceptedAlgorithms not in server-sig-algs = [email protected],[email protected],[email protected],[email protected]";
String expectedPubkeysKnown = "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]";
String expectedPubkeysUnknown = "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[[email protected], [email protected], [email protected], [email protected]\\]";
String expectedPreauth = "rsa-sha2-512 preauth success";
String expectedAuth = "rsa-sha2-512 auth success";
checkLogs(expectedKex);
checkLogs(expectedExtInfo);
checkLogs(expectedServerSigAlgs);
checkLogs(expectedOpenSSH74Bug);
checkLogs(expectedPubkeysBefore);
checkLogs(expectedPubkeysKnown);
checkLogs(expectedPubkeysUnknown);
checkLogs(expectedPreauth);
Expand Down
8 changes: 2 additions & 6 deletions src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,13 @@ public void testServerSigAlgs() throws Exception {
String expectedKex = "kex: host key algorithm: rsa-sha2-512";
String expectedExtInfo = "SSH_MSG_EXT_INFO received";
String expectedServerSigAlgs = "server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>";
String expectedPubkeysBefore = String.format("After getUnavailableSignatures PubkeyAcceptedAlgorithms = %s", algos);
String expectedPubkeysKnown = "PubkeyAcceptedAlgorithms in server-sig-algs = rsa-sha2-512,rsa-sha2-256,ssh-rsa";
String expectedPubkeysUnknown = "PubkeyAcceptedAlgorithms not in server-sig-algs = [email protected],[email protected],[email protected],[email protected]";
String expectedPubkeysKnown = "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]";
String expectedPubkeysUnknown = "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[[email protected], [email protected], [email protected], [email protected]\\]";
String expectedPreauth = "rsa-sha2-512 preauth success";
String expectedAuth = "rsa-sha2-512 auth success";
checkLogs(expectedKex);
checkLogs(expectedExtInfo);
checkLogs(expectedServerSigAlgs);
checkLogs(expectedPubkeysBefore);
checkLogs(expectedPubkeysKnown);
checkLogs(expectedPubkeysUnknown);
checkLogs(expectedPreauth);
Expand All @@ -145,7 +143,6 @@ public void testNoServerSigAlgs() throws Exception {
doSftp(session, true);

String expectedKex = "kex: host key algorithm: rsa-sha2-512";
String expectedPubkeysBefore = String.format("After getUnavailableSignatures PubkeyAcceptedAlgorithms = %s", algos);
String expectedPubkeysNoServerSigs = String.format("No server-sig-algs found, using PubkeyAcceptedAlgorithms = %s", algos);
String expectedPreauthFail1 = "[email protected] preauth failure";
String expectedPreauthFail2 = "[email protected] preauth failure";
Expand All @@ -154,7 +151,6 @@ public void testNoServerSigAlgs() throws Exception {
String expectedPreauth = "rsa-sha2-512 preauth success";
String expectedAuth = "rsa-sha2-512 auth success";
checkLogs(expectedKex);
checkLogs(expectedPubkeysBefore);
checkLogs(expectedPreauthFail1);
checkLogs(expectedPreauthFail2);
checkLogs(expectedPreauthFail3);
Expand Down

0 comments on commit 6c4c788

Please sign in to comment.