View Javadoc
1   /*
2    * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.internal.transport.sshd;
11  
12  import static java.text.MessageFormat.format;
13  import static org.eclipse.jgit.transport.SshConstants.PUBKEY_ACCEPTED_ALGORITHMS;
14  
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.NoSuchElementException;
18  
19  import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
20  import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
21  import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey;
22  import org.apache.sshd.client.config.hosts.HostConfigEntry;
23  import org.apache.sshd.client.session.ClientSession;
24  import org.apache.sshd.common.NamedFactory;
25  import org.apache.sshd.common.signature.Signature;
26  import org.eclipse.jgit.util.StringUtils;
27  
28  /**
29   * Custom {@link UserAuthPublicKey} implementation for handling SSH config
30   * PubkeyAcceptedAlgorithms.
31   */
32  public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
33  
34  	JGitPublicKeyAuthentication(List<NamedFactory<Signature>> factories) {
35  		super(factories);
36  	}
37  
38  	@Override
39  	public void init(ClientSession rawSession, String service)
40  			throws Exception {
41  		if (!(rawSession instanceof JGitClientSession)) {
42  			throw new IllegalStateException("Wrong session type: " //$NON-NLS-1$
43  					+ rawSession.getClass().getCanonicalName());
44  		}
45  		JGitClientSession session = (JGitClientSession) rawSession;
46  		HostConfigEntry hostConfig = session.getHostConfigEntry();
47  		// Set signature algorithms for public key authentication
48  		String pubkeyAlgos = hostConfig.getProperty(PUBKEY_ACCEPTED_ALGORITHMS);
49  		if (!StringUtils.isEmptyOrNull(pubkeyAlgos)) {
50  			List<String> signatures = session.getSignatureFactoriesNames();
51  			signatures = session.modifyAlgorithmList(signatures,
52  					session.getAllAvailableSignatureAlgorithms(), pubkeyAlgos,
53  					PUBKEY_ACCEPTED_ALGORITHMS);
54  			if (!signatures.isEmpty()) {
55  				if (log.isDebugEnabled()) {
56  					log.debug(PUBKEY_ACCEPTED_ALGORITHMS + ' ' + signatures);
57  				}
58  				setSignatureFactoriesNames(signatures);
59  			} else {
60  				log.warn(format(SshdText.get().configNoKnownAlgorithms,
61  						PUBKEY_ACCEPTED_ALGORITHMS, pubkeyAlgos));
62  			}
63  		}
64  		// If we don't set signature factories here, the default ones from the
65  		// session will be used.
66  		super.init(session, service);
67  		// In sshd 2.7.0, we end up now with a key iterator that uses keys
68  		// provided by an ssh-agent even if IdentitiesOnly is true. So if
69  		// needed, filter out any KeyAgentIdentity.
70  		if (hostConfig.isIdentitiesOnly()) {
71  			Iterator<PublicKeyIdentity> original = keys;
72  			// The original iterator will already have gotten the identities
73  			// from the agent. Unfortunately there's nothing we can do about
74  			// that; it'll have to be fixed upstream. (As will, ultimately,
75  			// respecting isIdentitiesOnly().) At least we can simply not
76  			// use the keys the agent provided.
77  			//
78  			// See https://issues.apache.org/jira/browse/SSHD-1218
79  			keys = new Iterator<>() {
80  
81  				private PublicKeyIdentity value;
82  
83  				@Override
84  				public boolean hasNext() {
85  					if (value != null) {
86  						return true;
87  					}
88  					PublicKeyIdentity next = null;
89  					while (original.hasNext()) {
90  						next = original.next();
91  						if (!(next instanceof KeyAgentIdentity)) {
92  							value = next;
93  							return true;
94  						}
95  					}
96  					return false;
97  				}
98  
99  				@Override
100 				public PublicKeyIdentity next() {
101 					if (hasNext()) {
102 						PublicKeyIdentity result = value;
103 						value = null;
104 						return result;
105 					}
106 					throw new NoSuchElementException();
107 				}
108 			};
109 		}
110 	}
111 }