blob: 297b456807dccbd1b83d6ed90d336f06a4c6e897 [file] [log] [blame]
/*
* Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.internal.transport.sshd;
import java.io.IOException;
import java.security.PublicKey;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.RuntimeSshException;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.signature.Signature;
import org.apache.sshd.common.signature.SignatureFactoriesHolder;
import org.apache.sshd.common.util.buffer.Buffer;
/**
* Custom {@link UserAuthPublicKey} implementation fixing SSHD-1105: if there
* are several signature algorithms applicable for a public key type, we must
* try them all, in the correct order.
*
* @see <a href="https://issues.apache.org/jira/browse/SSHD-1105">SSHD-1105</a>
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=572056">Bug
* 572056</a>
*/
public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
private final List<String> algorithms = new LinkedList<>();
JGitPublicKeyAuthentication(List<NamedFactory<Signature>> factories) {
super(factories);
}
@Override
protected boolean sendAuthDataRequest(ClientSession session, String service)
throws Exception {
if (current == null) {
algorithms.clear();
}
String currentAlgorithm = null;
if (current != null && !algorithms.isEmpty()) {
currentAlgorithm = algorithms.remove(0);
}
if (currentAlgorithm == null) {
try {
if (keys == null || !keys.hasNext()) {
if (log.isDebugEnabled()) {
log.debug(
"sendAuthDataRequest({})[{}] no more keys to send", //$NON-NLS-1$
session, service);
}
return false;
}
current = keys.next();
algorithms.clear();
} catch (Error e) { // Copied from superclass
warn("sendAuthDataRequest({})[{}] failed ({}) to get next key: {}", //$NON-NLS-1$
session, service, e.getClass().getSimpleName(),
e.getMessage(), e);
throw new RuntimeSshException(e);
}
}
PublicKey key;
try {
key = current.getPublicKey();
} catch (Error e) { // Copied from superclass
warn("sendAuthDataRequest({})[{}] failed ({}) to retrieve public key: {}", //$NON-NLS-1$
session, service, e.getClass().getSimpleName(),
e.getMessage(), e);
throw new RuntimeSshException(e);
}
if (currentAlgorithm == null) {
String keyType = KeyUtils.getKeyType(key);
Set<String> aliases = new HashSet<>(
KeyUtils.getAllEquivalentKeyTypes(keyType));
aliases.add(keyType);
List<NamedFactory<Signature>> existingFactories;
if (current instanceof SignatureFactoriesHolder) {
existingFactories = ((SignatureFactoriesHolder) current)
.getSignatureFactories();
} else {
existingFactories = getSignatureFactories();
}
if (existingFactories != null) {
// Select the factories by name and in order
existingFactories.forEach(f -> {
if (aliases.contains(f.getName())) {
algorithms.add(f.getName());
}
});
}
currentAlgorithm = algorithms.isEmpty() ? keyType
: algorithms.remove(0);
}
String name = getName();
if (log.isDebugEnabled()) {
log.debug(
"sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}", //$NON-NLS-1$
session, service, name, currentAlgorithm,
KeyUtils.getFingerPrint(key));
}
Buffer buffer = session
.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
buffer.putString(session.getUsername());
buffer.putString(service);
buffer.putString(name);
buffer.putBoolean(false);
buffer.putString(currentAlgorithm);
buffer.putPublicKey(key);
session.writePacket(buffer);
return true;
}
@Override
protected void releaseKeys() throws IOException {
algorithms.clear();
current = null;
super.releaseKeys();
}
}