SshdSessionFactory.java

  1. /*
  2.  * Copyright (C) 2018, 2020 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.transport.sshd;

  11. import java.io.Closeable;
  12. import java.io.File;
  13. import java.io.IOException;
  14. import java.nio.file.Files;
  15. import java.nio.file.Path;
  16. import java.security.KeyPair;
  17. import java.time.Duration;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import java.util.concurrent.ConcurrentHashMap;
  26. import java.util.concurrent.atomic.AtomicBoolean;
  27. import java.util.function.Supplier;
  28. import java.util.stream.Collectors;

  29. import org.apache.sshd.client.ClientBuilder;
  30. import org.apache.sshd.client.SshClient;
  31. import org.apache.sshd.client.auth.UserAuthFactory;
  32. import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
  33. import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
  34. import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
  35. import org.apache.sshd.common.SshException;
  36. import org.apache.sshd.common.NamedFactory;
  37. import org.apache.sshd.common.compression.BuiltinCompressions;
  38. import org.apache.sshd.common.config.keys.FilePasswordProvider;
  39. import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions;
  40. import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
  41. import org.apache.sshd.common.signature.BuiltinSignatures;
  42. import org.apache.sshd.common.signature.Signature;
  43. import org.eclipse.jgit.annotations.NonNull;
  44. import org.eclipse.jgit.errors.TransportException;
  45. import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
  46. import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException;
  47. import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider;
  48. import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory;
  49. import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory;
  50. import org.eclipse.jgit.internal.transport.sshd.JGitServerKeyVerifier;
  51. import org.eclipse.jgit.internal.transport.sshd.JGitSshClient;
  52. import org.eclipse.jgit.internal.transport.sshd.JGitSshConfig;
  53. import org.eclipse.jgit.internal.transport.sshd.JGitUserInteraction;
  54. import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase;
  55. import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
  56. import org.eclipse.jgit.internal.transport.sshd.SshdText;
  57. import org.eclipse.jgit.transport.CredentialsProvider;
  58. import org.eclipse.jgit.transport.SshConfigStore;
  59. import org.eclipse.jgit.transport.SshConstants;
  60. import org.eclipse.jgit.transport.SshSessionFactory;
  61. import org.eclipse.jgit.transport.URIish;
  62. import org.eclipse.jgit.util.FS;

  63. /**
  64.  * A {@link SshSessionFactory} that uses Apache MINA sshd. Classes from Apache
  65.  * MINA sshd are kept private to avoid API evolution problems when Apache MINA
  66.  * sshd interfaces change.
  67.  *
  68.  * @since 5.2
  69.  */
  70. public class SshdSessionFactory extends SshSessionFactory implements Closeable {

  71.     private static final String MINA_SSHD = "mina-sshd"; //$NON-NLS-1$

  72.     private final AtomicBoolean closing = new AtomicBoolean();

  73.     private final Set<SshdSession> sessions = new HashSet<>();

  74.     private final Map<Tuple, HostConfigEntryResolver> defaultHostConfigEntryResolver = new ConcurrentHashMap<>();

  75.     private final Map<Tuple, ServerKeyDatabase> defaultServerKeyDatabase = new ConcurrentHashMap<>();

  76.     private final Map<Tuple, Iterable<KeyPair>> defaultKeys = new ConcurrentHashMap<>();

  77.     private final KeyCache keyCache;

  78.     private final ProxyDataFactory proxies;

  79.     private File sshDirectory;

  80.     private File homeDirectory;

  81.     /**
  82.      * Creates a new {@link SshdSessionFactory} without key cache and a
  83.      * {@link DefaultProxyDataFactory}.
  84.      */
  85.     public SshdSessionFactory() {
  86.         this(null, new DefaultProxyDataFactory());
  87.     }

  88.     /**
  89.      * Creates a new {@link SshdSessionFactory} using the given {@link KeyCache}
  90.      * and {@link ProxyDataFactory}. The {@code keyCache} is used for all sessions
  91.      * created through this session factory; cached keys are destroyed when the
  92.      * session factory is {@link #close() closed}.
  93.      * <p>
  94.      * Caching ssh keys in memory for an extended period of time is generally
  95.      * considered bad practice, but there may be circumstances where using a
  96.      * {@link KeyCache} is still the right choice, for instance to avoid that a
  97.      * user gets prompted several times for the same password for the same key.
  98.      * In general, however, it is preferable <em>not</em> to use a key cache but
  99.      * to use a {@link #createKeyPasswordProvider(CredentialsProvider)
  100.      * KeyPasswordProvider} that has access to some secure storage and can save
  101.      * and retrieve passwords from there without user interaction. Another
  102.      * approach is to use an ssh agent.
  103.      * </p>
  104.      * <p>
  105.      * Note that the underlying ssh library (Apache MINA sshd) may or may not
  106.      * keep ssh keys in memory for unspecified periods of time irrespective of
  107.      * the use of a {@link KeyCache}.
  108.      * </p>
  109.      *
  110.      * @param keyCache
  111.      *            {@link KeyCache} to use for caching ssh keys, or {@code null}
  112.      *            to not use a key cache
  113.      * @param proxies
  114.      *            {@link ProxyDataFactory} to use, or {@code null} to not use a
  115.      *            proxy database (in which case connections through proxies will
  116.      *            not be possible)
  117.      */
  118.     public SshdSessionFactory(KeyCache keyCache, ProxyDataFactory proxies) {
  119.         super();
  120.         this.keyCache = keyCache;
  121.         this.proxies = proxies;
  122.         // sshd limits the number of BCrypt KDF rounds to 255 by default.
  123.         // Decrypting such a key takes about two seconds on my machine.
  124.         // I consider this limit too low. The time increases linearly with the
  125.         // number of rounds.
  126.         BCryptKdfOptions.setMaxAllowedRounds(16384);
  127.     }

  128.     @Override
  129.     public String getType() {
  130.         return MINA_SSHD;
  131.     }

  132.     /** A simple general map key. */
  133.     private static final class Tuple {
  134.         private Object[] objects;

  135.         public Tuple(Object[] objects) {
  136.             this.objects = objects;
  137.         }

  138.         @Override
  139.         public boolean equals(Object obj) {
  140.             if (obj == this) {
  141.                 return true;
  142.             }
  143.             if (obj != null && obj.getClass() == Tuple.class) {
  144.                 Tuple other = (Tuple) obj;
  145.                 return Arrays.equals(objects, other.objects);
  146.             }
  147.             return false;
  148.         }

  149.         @Override
  150.         public int hashCode() {
  151.             return Arrays.hashCode(objects);
  152.         }
  153.     }

  154.     // We can't really use a single client. Clients need to be stopped
  155.     // properly, and we don't really know when to do that. Instead we use
  156.     // a dedicated SshClient instance per session. We need a bit of caching to
  157.     // avoid re-loading the ssh config and keys repeatedly.

  158.     @Override
  159.     public SshdSession getSession(URIish uri,
  160.             CredentialsProvider credentialsProvider, FS fs, int tms)
  161.             throws TransportException {
  162.         SshdSession session = null;
  163.         try {
  164.             session = new SshdSession(uri, () -> {
  165.                 File home = getHomeDirectory();
  166.                 if (home == null) {
  167.                     // Always use the detected filesystem for the user home!
  168.                     // It makes no sense to have different "user home"
  169.                     // directories depending on what file system a repository
  170.                     // is.
  171.                     home = FS.DETECTED.userHome();
  172.                 }
  173.                 File sshDir = getSshDirectory();
  174.                 if (sshDir == null) {
  175.                     sshDir = new File(home, SshConstants.SSH_DIR);
  176.                 }
  177.                 HostConfigEntryResolver configFile = getHostConfigEntryResolver(
  178.                         home, sshDir);
  179.                 KeyIdentityProvider defaultKeysProvider = toKeyIdentityProvider(
  180.                         getDefaultKeys(sshDir));
  181.                 SshClient client = ClientBuilder.builder()
  182.                         .factory(JGitSshClient::new)
  183.                         .filePasswordProvider(createFilePasswordProvider(
  184.                                 () -> createKeyPasswordProvider(
  185.                                         credentialsProvider)))
  186.                         .hostConfigEntryResolver(configFile)
  187.                         .serverKeyVerifier(new JGitServerKeyVerifier(
  188.                                 getServerKeyDatabase(home, sshDir)))
  189.                         .signatureFactories(getSignatureFactories())
  190.                         .compressionFactories(
  191.                                 new ArrayList<>(BuiltinCompressions.VALUES))
  192.                         .build();
  193.                 client.setUserInteraction(
  194.                         new JGitUserInteraction(credentialsProvider));
  195.                 client.setUserAuthFactories(getUserAuthFactories());
  196.                 client.setKeyIdentityProvider(defaultKeysProvider);
  197.                 // JGit-specific things:
  198.                 JGitSshClient jgitClient = (JGitSshClient) client;
  199.                 jgitClient.setKeyCache(getKeyCache());
  200.                 jgitClient.setCredentialsProvider(credentialsProvider);
  201.                 jgitClient.setProxyDatabase(proxies);
  202.                 String defaultAuths = getDefaultPreferredAuthentications();
  203.                 if (defaultAuths != null) {
  204.                     jgitClient.setAttribute(
  205.                             JGitSshClient.PREFERRED_AUTHENTICATIONS,
  206.                             defaultAuths);
  207.                 }
  208.                 // Other things?
  209.                 return client;
  210.             });
  211.             session.addCloseListener(s -> unregister(s));
  212.             register(session);
  213.             session.connect(Duration.ofMillis(tms));
  214.             return session;
  215.         } catch (Exception e) {
  216.             unregister(session);
  217.             if (e instanceof TransportException) {
  218.                 throw (TransportException) e;
  219.             }
  220.             Throwable cause = e;
  221.             if (e instanceof SshException && e
  222.                     .getCause() instanceof AuthenticationCanceledException) {
  223.                 // Results in a nicer error message
  224.                 cause = e.getCause();
  225.             }
  226.             throw new TransportException(uri, cause.getMessage(), cause);
  227.         }
  228.     }

  229.     @Override
  230.     public void close() {
  231.         closing.set(true);
  232.         boolean cleanKeys = false;
  233.         synchronized (this) {
  234.             cleanKeys = sessions.isEmpty();
  235.         }
  236.         if (cleanKeys) {
  237.             KeyCache cache = getKeyCache();
  238.             if (cache != null) {
  239.                 cache.close();
  240.             }
  241.         }
  242.     }

  243.     private void register(SshdSession newSession) throws IOException {
  244.         if (newSession == null) {
  245.             return;
  246.         }
  247.         if (closing.get()) {
  248.             throw new IOException(SshdText.get().sshClosingDown);
  249.         }
  250.         synchronized (this) {
  251.             sessions.add(newSession);
  252.         }
  253.     }

  254.     private void unregister(SshdSession oldSession) {
  255.         boolean cleanKeys = false;
  256.         synchronized (this) {
  257.             sessions.remove(oldSession);
  258.             cleanKeys = closing.get() && sessions.isEmpty();
  259.         }
  260.         if (cleanKeys) {
  261.             KeyCache cache = getKeyCache();
  262.             if (cache != null) {
  263.                 cache.close();
  264.             }
  265.         }
  266.     }

  267.     /**
  268.      * Set a global directory to use as the user's home directory
  269.      *
  270.      * @param homeDir
  271.      *            to use
  272.      */
  273.     public void setHomeDirectory(@NonNull File homeDir) {
  274.         if (homeDir.isAbsolute()) {
  275.             homeDirectory = homeDir;
  276.         } else {
  277.             homeDirectory = homeDir.getAbsoluteFile();
  278.         }
  279.     }

  280.     /**
  281.      * Retrieves the global user home directory
  282.      *
  283.      * @return the directory, or {@code null} if not set
  284.      */
  285.     public File getHomeDirectory() {
  286.         return homeDirectory;
  287.     }

  288.     /**
  289.      * Set a global directory to use as the .ssh directory
  290.      *
  291.      * @param sshDir
  292.      *            to use
  293.      */
  294.     public void setSshDirectory(@NonNull File sshDir) {
  295.         if (sshDir.isAbsolute()) {
  296.             sshDirectory = sshDir;
  297.         } else {
  298.             sshDirectory = sshDir.getAbsoluteFile();
  299.         }
  300.     }

  301.     /**
  302.      * Retrieves the global .ssh directory
  303.      *
  304.      * @return the directory, or {@code null} if not set
  305.      */
  306.     public File getSshDirectory() {
  307.         return sshDirectory;
  308.     }

  309.     /**
  310.      * Obtain a {@link HostConfigEntryResolver} to read the ssh config file and
  311.      * to determine host entries for connections.
  312.      *
  313.      * @param homeDir
  314.      *            home directory to use for ~ replacement
  315.      * @param sshDir
  316.      *            to use for looking for the config file
  317.      * @return the resolver
  318.      */
  319.     @NonNull
  320.     private HostConfigEntryResolver getHostConfigEntryResolver(
  321.             @NonNull File homeDir, @NonNull File sshDir) {
  322.         return defaultHostConfigEntryResolver.computeIfAbsent(
  323.                 new Tuple(new Object[] { homeDir, sshDir }),
  324.                 t -> new JGitSshConfig(createSshConfigStore(homeDir,
  325.                         getSshConfig(sshDir), getLocalUserName())));
  326.     }

  327.     /**
  328.      * Determines the ssh config file. The default implementation returns
  329.      * ~/.ssh/config. If the file does not exist and is created later it will be
  330.      * picked up. To not use a config file at all, return {@code null}.
  331.      *
  332.      * @param sshDir
  333.      *            representing ~/.ssh/
  334.      * @return the file (need not exist), or {@code null} if no config file
  335.      *         shall be used
  336.      * @since 5.5
  337.      */
  338.     protected File getSshConfig(@NonNull File sshDir) {
  339.         return new File(sshDir, SshConstants.CONFIG);
  340.     }

  341.     /**
  342.      * Obtains a {@link SshConfigStore}, or {@code null} if not SSH config is to
  343.      * be used. The default implementation returns {@code null} if
  344.      * {@code configFile == null} and otherwise an OpenSSH-compatible store
  345.      * reading host entries from the given file.
  346.      *
  347.      * @param homeDir
  348.      *            may be used for ~-replacements by the returned config store
  349.      * @param configFile
  350.      *            to use, or {@code null} if none
  351.      * @param localUserName
  352.      *            user name of the current user on the local OS
  353.      * @return A {@link SshConfigStore}, or {@code null} if none is to be used
  354.      *
  355.      * @since 5.8
  356.      */
  357.     protected SshConfigStore createSshConfigStore(@NonNull File homeDir,
  358.             File configFile, String localUserName) {
  359.         return configFile == null ? null
  360.                 : new OpenSshConfigFile(homeDir, configFile, localUserName);
  361.     }

  362.     /**
  363.      * Obtains a {@link ServerKeyDatabase} to verify server host keys. The
  364.      * default implementation returns a {@link ServerKeyDatabase} that
  365.      * recognizes the two openssh standard files {@code ~/.ssh/known_hosts} and
  366.      * {@code ~/.ssh/known_hosts2} as well as any files configured via the
  367.      * {@code UserKnownHostsFile} option in the ssh config file.
  368.      *
  369.      * @param homeDir
  370.      *            home directory to use for ~ replacement
  371.      * @param sshDir
  372.      *            representing ~/.ssh/
  373.      * @return the {@link ServerKeyDatabase}
  374.      * @since 5.5
  375.      */
  376.     @NonNull
  377.     protected ServerKeyDatabase getServerKeyDatabase(@NonNull File homeDir,
  378.             @NonNull File sshDir) {
  379.         return defaultServerKeyDatabase.computeIfAbsent(
  380.                 new Tuple(new Object[] { homeDir, sshDir }),
  381.                 t -> createServerKeyDatabase(homeDir, sshDir));

  382.     }

  383.     /**
  384.      * Creates a {@link ServerKeyDatabase} to verify server host keys. The
  385.      * default implementation returns a {@link ServerKeyDatabase} that
  386.      * recognizes the two openssh standard files {@code ~/.ssh/known_hosts} and
  387.      * {@code ~/.ssh/known_hosts2} as well as any files configured via the
  388.      * {@code UserKnownHostsFile} option in the ssh config file.
  389.      *
  390.      * @param homeDir
  391.      *            home directory to use for ~ replacement
  392.      * @param sshDir
  393.      *            representing ~/.ssh/
  394.      * @return the {@link ServerKeyDatabase}
  395.      * @since 5.8
  396.      */
  397.     @NonNull
  398.     protected ServerKeyDatabase createServerKeyDatabase(@NonNull File homeDir,
  399.             @NonNull File sshDir) {
  400.         return new OpenSshServerKeyDatabase(true,
  401.                 getDefaultKnownHostsFiles(sshDir));
  402.     }

  403.     /**
  404.      * Gets the list of default user known hosts files. The default returns
  405.      * ~/.ssh/known_hosts and ~/.ssh/known_hosts2. The ssh config
  406.      * {@code UserKnownHostsFile} overrides this default.
  407.      *
  408.      * @param sshDir
  409.      * @return the possibly empty list of default known host file paths.
  410.      */
  411.     @NonNull
  412.     protected List<Path> getDefaultKnownHostsFiles(@NonNull File sshDir) {
  413.         return Arrays.asList(sshDir.toPath().resolve(SshConstants.KNOWN_HOSTS),
  414.                 sshDir.toPath().resolve(SshConstants.KNOWN_HOSTS + '2'));
  415.     }

  416.     /**
  417.      * Determines the default keys. The default implementation will lazy load
  418.      * the {@link #getDefaultIdentities(File) default identity files}.
  419.      * <p>
  420.      * Subclasses may override and return an {@link Iterable} of whatever keys
  421.      * are appropriate. If the returned iterable lazily loads keys, it should be
  422.      * an instance of
  423.      * {@link org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider
  424.      * AbstractResourceKeyPairProvider} so that the session can later pass it
  425.      * the {@link #createKeyPasswordProvider(CredentialsProvider) password
  426.      * provider} wrapped as a {@link FilePasswordProvider} via
  427.      * {@link org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider#setPasswordFinder(FilePasswordProvider)
  428.      * AbstractResourceKeyPairProvider#setPasswordFinder(FilePasswordProvider)}
  429.      * so that encrypted, password-protected keys can be loaded.
  430.      * </p>
  431.      * <p>
  432.      * The default implementation uses exactly this mechanism; class
  433.      * {@link CachingKeyPairProvider} may serve as a model for a customized
  434.      * lazy-loading {@link Iterable} implementation
  435.      * </p>
  436.      * <p>
  437.      * If the {@link Iterable} returned has the keys already pre-loaded or
  438.      * otherwise doesn't need to decrypt encrypted keys, it can be any
  439.      * {@link Iterable}, for instance a simple {@link java.util.List List}.
  440.      * </p>
  441.      *
  442.      * @param sshDir
  443.      *            to look in for keys
  444.      * @return an {@link Iterable} over the default keys
  445.      * @since 5.3
  446.      */
  447.     @NonNull
  448.     protected Iterable<KeyPair> getDefaultKeys(@NonNull File sshDir) {
  449.         List<Path> defaultIdentities = getDefaultIdentities(sshDir);
  450.         return defaultKeys.computeIfAbsent(
  451.                 new Tuple(defaultIdentities.toArray(new Path[0])),
  452.                 t -> new CachingKeyPairProvider(defaultIdentities,
  453.                         getKeyCache()));
  454.     }

  455.     /**
  456.      * Converts an {@link Iterable} of {link KeyPair}s into a
  457.      * {@link KeyIdentityProvider}.
  458.      *
  459.      * @param keys
  460.      *            to provide via the returned {@link KeyIdentityProvider}
  461.      * @return a {@link KeyIdentityProvider} that provides the given
  462.      *         {@code keys}
  463.      */
  464.     private KeyIdentityProvider toKeyIdentityProvider(Iterable<KeyPair> keys) {
  465.         if (keys instanceof KeyIdentityProvider) {
  466.             return (KeyIdentityProvider) keys;
  467.         }
  468.         return (session) -> keys;
  469.     }

  470.     /**
  471.      * Gets a list of default identities, i.e., private key files that shall
  472.      * always be tried for public key authentication. Typically those are
  473.      * ~/.ssh/id_dsa, ~/.ssh/id_rsa, and so on. The default implementation
  474.      * returns the files defined in {@link SshConstants#DEFAULT_IDENTITIES}.
  475.      *
  476.      * @param sshDir
  477.      *            the directory that represents ~/.ssh/
  478.      * @return a possibly empty list of paths containing default identities
  479.      *         (private keys)
  480.      */
  481.     @NonNull
  482.     protected List<Path> getDefaultIdentities(@NonNull File sshDir) {
  483.         return Arrays
  484.                 .asList(SshConstants.DEFAULT_IDENTITIES).stream()
  485.                 .map(s -> new File(sshDir, s).toPath()).filter(Files::exists)
  486.                 .collect(Collectors.toList());
  487.     }

  488.     /**
  489.      * Obtains the {@link KeyCache} to use to cache loaded keys.
  490.      *
  491.      * @return the {@link KeyCache}, or {@code null} if none.
  492.      */
  493.     protected final KeyCache getKeyCache() {
  494.         return keyCache;
  495.     }

  496.     /**
  497.      * Creates a {@link KeyPasswordProvider} for a new session.
  498.      *
  499.      * @param provider
  500.      *            the {@link CredentialsProvider} to delegate to for user
  501.      *            interactions
  502.      * @return a new {@link KeyPasswordProvider}
  503.      */
  504.     @NonNull
  505.     protected KeyPasswordProvider createKeyPasswordProvider(
  506.             CredentialsProvider provider) {
  507.         return new IdentityPasswordProvider(provider);
  508.     }

  509.     /**
  510.      * Creates a {@link FilePasswordProvider} for a new session.
  511.      *
  512.      * @param providerFactory
  513.      *            providing the {@link KeyPasswordProvider} to delegate to
  514.      * @return a new {@link FilePasswordProvider}
  515.      */
  516.     @NonNull
  517.     private FilePasswordProvider createFilePasswordProvider(
  518.             Supplier<KeyPasswordProvider> providerFactory) {
  519.         return new PasswordProviderWrapper(providerFactory);
  520.     }

  521.     /**
  522.      * Gets the user authentication mechanisms (or rather, factories for them).
  523.      * By default this returns gssapi-with-mic, public-key, password, and
  524.      * keyboard-interactive, in that order. The order is only significant if the
  525.      * ssh config does <em>not</em> set {@code PreferredAuthentications}; if it
  526.      * is set, the order defined there will be taken.
  527.      *
  528.      * @return the non-empty list of factories.
  529.      */
  530.     @NonNull
  531.     private List<UserAuthFactory> getUserAuthFactories() {
  532.         // About the order of password and keyboard-interactive, see upstream
  533.         // bug https://issues.apache.org/jira/projects/SSHD/issues/SSHD-866 .
  534.         // Password auth doesn't have this problem.
  535.         return Collections.unmodifiableList(
  536.                 Arrays.asList(GssApiWithMicAuthFactory.INSTANCE,
  537.                         UserAuthPublicKeyFactory.INSTANCE,
  538.                         JGitPasswordAuthFactory.INSTANCE,
  539.                         UserAuthKeyboardInteractiveFactory.INSTANCE));
  540.     }

  541.     /**
  542.      * Gets the list of default preferred authentication mechanisms. If
  543.      * {@code null} is returned the openssh default list will be in effect. If
  544.      * the ssh config defines {@code PreferredAuthentications} the value from
  545.      * the ssh config takes precedence.
  546.      *
  547.      * @return a comma-separated list of mechanism names, or {@code null} if
  548.      *         none
  549.      */
  550.     protected String getDefaultPreferredAuthentications() {
  551.         return null;
  552.     }

  553.     /**
  554.      * Apache MINA sshd 2.6.0 has removed DSA, DSA_CERT and RSA_CERT. We have to
  555.      * set it up explicitly to still allow users to connect with DSA keys.
  556.      *
  557.      * @return a list of supported signature factories
  558.      */
  559.     @SuppressWarnings("deprecation")
  560.     private static List<NamedFactory<Signature>> getSignatureFactories() {
  561.         // @formatter:off
  562.         return Arrays.asList(
  563.                 BuiltinSignatures.nistp256_cert,
  564.                 BuiltinSignatures.nistp384_cert,
  565.                 BuiltinSignatures.nistp521_cert,
  566.                 BuiltinSignatures.ed25519_cert,
  567.                 BuiltinSignatures.rsaSHA512_cert,
  568.                 BuiltinSignatures.rsaSHA256_cert,
  569.                 BuiltinSignatures.rsa_cert,
  570.                 BuiltinSignatures.nistp256,
  571.                 BuiltinSignatures.nistp384,
  572.                 BuiltinSignatures.nistp521,
  573.                 BuiltinSignatures.ed25519,
  574.                 BuiltinSignatures.sk_ecdsa_sha2_nistp256,
  575.                 BuiltinSignatures.sk_ssh_ed25519,
  576.                 BuiltinSignatures.rsaSHA512,
  577.                 BuiltinSignatures.rsaSHA256,
  578.                 BuiltinSignatures.rsa,
  579.                 BuiltinSignatures.dsa_cert,
  580.                 BuiltinSignatures.dsa);
  581.         // @formatter:on
  582.     }
  583. }