You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SshTestBase.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*
  2. * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport.ssh;
  44. import static org.junit.Assert.assertArrayEquals;
  45. import static org.junit.Assert.assertEquals;
  46. import static org.junit.Assert.assertFalse;
  47. import static org.junit.Assert.assertNotNull;
  48. import static org.junit.Assert.assertTrue;
  49. import static org.junit.Assume.assumeTrue;
  50. import java.io.File;
  51. import java.io.IOException;
  52. import java.nio.charset.StandardCharsets;
  53. import java.nio.file.Files;
  54. import java.util.List;
  55. import java.util.Map;
  56. import org.eclipse.jgit.api.errors.TransportException;
  57. import org.eclipse.jgit.transport.CredentialItem;
  58. import org.eclipse.jgit.transport.JschConfigSessionFactory;
  59. import org.eclipse.jgit.transport.URIish;
  60. import org.junit.Test;
  61. import org.junit.experimental.theories.DataPoints;
  62. import org.junit.experimental.theories.Theory;
  63. /**
  64. * The ssh tests. Concrete subclasses can re-use these tests by implementing the
  65. * abstract operations from {@link SshTestHarness}. This gives a way to test
  66. * different ssh clients against a unified test suite.
  67. */
  68. public abstract class SshTestBase extends SshTestHarness {
  69. @DataPoints
  70. public static String[] KEY_RESOURCES = { //
  71. "id_dsa", //
  72. "id_rsa_1024", //
  73. "id_rsa_2048", //
  74. "id_rsa_3072", //
  75. "id_rsa_4096", //
  76. "id_ecdsa_256", //
  77. "id_ecdsa_384", //
  78. "id_ecdsa_521", //
  79. // And now encrypted. Passphrase is "testpass".
  80. "id_dsa_testpass", //
  81. "id_rsa_1024_testpass", //
  82. "id_rsa_2048_testpass", //
  83. "id_rsa_3072_testpass", //
  84. "id_rsa_4096_testpass", //
  85. "id_ecdsa_256_testpass", //
  86. "id_ecdsa_384_testpass", //
  87. "id_ecdsa_521_testpass" };
  88. protected File defaultCloneDir;
  89. @Override
  90. public void setUp() throws Exception {
  91. super.setUp();
  92. defaultCloneDir = new File(getTemporaryDirectory(), "cloned");
  93. }
  94. @Test(expected = TransportException.class)
  95. public void testSshWithoutConfig() throws Exception {
  96. cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
  97. + "/doesntmatter", defaultCloneDir, null);
  98. }
  99. @Test
  100. public void testSshWithGlobalIdentity() throws Exception {
  101. cloneWith(
  102. "ssh://" + TEST_USER + "@localhost:" + testPort
  103. + "/doesntmatter",
  104. defaultCloneDir, null,
  105. "IdentityFile " + privateKey1.getAbsolutePath());
  106. }
  107. @Test
  108. public void testSshWithDefaultIdentity() throws Exception {
  109. File idRsa = new File(privateKey1.getParentFile(), "id_rsa");
  110. Files.copy(privateKey1.toPath(), idRsa.toPath());
  111. // We expect the session factory to pick up these keys...
  112. cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
  113. + "/doesntmatter", defaultCloneDir, null);
  114. }
  115. @Test
  116. public void testSshWithConfig() throws Exception {
  117. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  118. "Host localhost", //
  119. "HostName localhost", //
  120. "Port " + testPort, //
  121. "User " + TEST_USER, //
  122. "IdentityFile " + privateKey1.getAbsolutePath());
  123. }
  124. @Test
  125. public void testSshWithConfigEncryptedUnusedKey() throws Exception {
  126. // Copy the encrypted test key from the bundle.
  127. File encryptedKey = new File(sshDir, "id_dsa");
  128. copyTestResource("id_dsa_testpass", encryptedKey);
  129. TestCredentialsProvider provider = new TestCredentialsProvider(
  130. "testpass");
  131. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  132. "Host localhost", //
  133. "HostName localhost", //
  134. "Port " + testPort, //
  135. "User " + TEST_USER, //
  136. "IdentityFile " + privateKey1.getAbsolutePath());
  137. assertEquals("CredentialsProvider should not have been called", 0,
  138. provider.getLog().size());
  139. }
  140. @Test
  141. public void testSshWithConfigEncryptedUnusedKeyInConfigLast()
  142. throws Exception {
  143. // Copy the encrypted test key from the bundle.
  144. File encryptedKey = new File(sshDir, "id_dsa_test_key");
  145. copyTestResource("id_dsa_testpass", encryptedKey);
  146. TestCredentialsProvider provider = new TestCredentialsProvider(
  147. "testpass");
  148. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  149. "Host localhost", //
  150. "HostName localhost", //
  151. "Port " + testPort, //
  152. "User " + TEST_USER, //
  153. "IdentityFile " + privateKey1.getAbsolutePath(),
  154. "IdentityFile " + encryptedKey.getAbsolutePath());
  155. // This test passes with JSch per chance because JSch completely ignores
  156. // the second IdentityFile
  157. assertEquals("CredentialsProvider should not have been called", 0,
  158. provider.getLog().size());
  159. }
  160. @Test
  161. public void testSshWithConfigEncryptedUnusedKeyInConfigFirst()
  162. throws Exception {
  163. // Test cannot pass with JSch; it handles only one IdentityFile.
  164. // assumeTrue(!(getSessionFactory() instanceof
  165. // JschConfigSessionFactory)); gives in bazel a failure with "Never
  166. // found parameters that satisfied method assumptions."
  167. // In maven it's fine!?
  168. if (getSessionFactory() instanceof JschConfigSessionFactory) {
  169. return;
  170. }
  171. // Copy the encrypted test key from the bundle.
  172. File encryptedKey = new File(sshDir, "id_dsa_test_key");
  173. copyTestResource("id_dsa_testpass", encryptedKey);
  174. TestCredentialsProvider provider = new TestCredentialsProvider(
  175. "testpass");
  176. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  177. "Host localhost", //
  178. "HostName localhost", //
  179. "Port " + testPort, //
  180. "User " + TEST_USER, //
  181. "IdentityFile " + encryptedKey.getAbsolutePath(),
  182. "IdentityFile " + privateKey1.getAbsolutePath());
  183. assertEquals("CredentialsProvider should have been called once", 1,
  184. provider.getLog().size());
  185. }
  186. @Test
  187. public void testSshEncryptedUsedKeyCached() throws Exception {
  188. // Make sure we are asked for the password only once if we do several
  189. // operations with an encrypted key.
  190. File encryptedKey = new File(sshDir, "id_dsa_test_key");
  191. copyTestResource("id_dsa_testpass", encryptedKey);
  192. File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
  193. copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
  194. server.setTestUserPublicKey(encryptedPublicKey.toPath());
  195. TestCredentialsProvider provider = new TestCredentialsProvider(
  196. "testpass");
  197. pushTo(provider,
  198. cloneWith("ssh://localhost/doesntmatter", //
  199. defaultCloneDir, provider, //
  200. "Host localhost", //
  201. "HostName localhost", //
  202. "Port " + testPort, //
  203. "User " + TEST_USER, //
  204. "IdentityFile " + encryptedKey.getAbsolutePath()));
  205. assertEquals("CredentialsProvider should have been called once", 1,
  206. provider.getLog().size());
  207. }
  208. @Test(expected = TransportException.class)
  209. public void testSshWithoutKnownHosts() throws Exception {
  210. assertTrue("Could not delete known_hosts", knownHosts.delete());
  211. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  212. "Host localhost", //
  213. "HostName localhost", //
  214. "Port " + testPort, //
  215. "User " + TEST_USER, //
  216. "IdentityFile " + privateKey1.getAbsolutePath());
  217. }
  218. @Test
  219. public void testSshWithoutKnownHostsWithProviderAsk()
  220. throws Exception {
  221. File copiedHosts = new File(knownHosts.getParentFile(),
  222. "copiedKnownHosts");
  223. assertTrue("Failed to rename known_hosts",
  224. knownHosts.renameTo(copiedHosts));
  225. // The provider will answer "yes" to all questions, so we should be able
  226. // to connect and end up with a new known_hosts file with the host key.
  227. TestCredentialsProvider provider = new TestCredentialsProvider();
  228. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  229. "Host localhost", //
  230. "HostName localhost", //
  231. "Port " + testPort, //
  232. "User " + TEST_USER, //
  233. "IdentityFile " + privateKey1.getAbsolutePath());
  234. Map<URIish, List<CredentialItem>> messages = provider.getLog();
  235. assertFalse("Expected user interaction", messages.isEmpty());
  236. if (getSessionFactory() instanceof JschConfigSessionFactory) {
  237. // JSch doesn't create a non-existing file.
  238. assertEquals("Expected to be asked about the key", 1,
  239. messages.size());
  240. return;
  241. }
  242. assertEquals(
  243. "Expected to be asked about the key, and the file creation",
  244. 2, messages.size());
  245. assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
  246. // Instead of checking the file contents, let's just clone again
  247. // without provider. If it works, the server host key was written
  248. // correctly.
  249. File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
  250. cloneWith("ssh://localhost/doesntmatter", clonedAgain, provider, //
  251. "Host localhost", //
  252. "HostName localhost", //
  253. "Port " + testPort, //
  254. "User " + TEST_USER, //
  255. "IdentityFile " + privateKey1.getAbsolutePath());
  256. }
  257. @Test
  258. public void testSshWithoutKnownHostsWithProviderAcceptNew()
  259. throws Exception {
  260. File copiedHosts = new File(knownHosts.getParentFile(),
  261. "copiedKnownHosts");
  262. assertTrue("Failed to rename known_hosts",
  263. knownHosts.renameTo(copiedHosts));
  264. TestCredentialsProvider provider = new TestCredentialsProvider();
  265. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  266. "Host localhost", //
  267. "HostName localhost", //
  268. "Port " + testPort, //
  269. "User " + TEST_USER, //
  270. "StrictHostKeyChecking accept-new", //
  271. "IdentityFile " + privateKey1.getAbsolutePath());
  272. if (getSessionFactory() instanceof JschConfigSessionFactory) {
  273. // JSch doesn't create new files.
  274. assertTrue("CredentialsProvider not called",
  275. provider.getLog().isEmpty());
  276. return;
  277. }
  278. assertEquals("Expected to be asked about the file creation", 1,
  279. provider.getLog().size());
  280. assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
  281. // Instead of checking the file contents, let's just clone again
  282. // without provider. If it works, the server host key was written
  283. // correctly.
  284. File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
  285. cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, //
  286. "Host localhost", //
  287. "HostName localhost", //
  288. "Port " + testPort, //
  289. "User " + TEST_USER, //
  290. "IdentityFile " + privateKey1.getAbsolutePath());
  291. }
  292. @Test(expected = TransportException.class)
  293. public void testSshWithoutKnownHostsDeny() throws Exception {
  294. File copiedHosts = new File(knownHosts.getParentFile(),
  295. "copiedKnownHosts");
  296. assertTrue("Failed to rename known_hosts",
  297. knownHosts.renameTo(copiedHosts));
  298. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  299. "Host localhost", //
  300. "HostName localhost", //
  301. "Port " + testPort, //
  302. "User " + TEST_USER, //
  303. "StrictHostKeyChecking yes", //
  304. "IdentityFile " + privateKey1.getAbsolutePath());
  305. }
  306. @Test(expected = TransportException.class)
  307. public void testSshModifiedHostKeyDeny()
  308. throws Exception {
  309. File copiedHosts = new File(knownHosts.getParentFile(),
  310. "copiedKnownHosts");
  311. assertTrue("Failed to rename known_hosts",
  312. knownHosts.renameTo(copiedHosts));
  313. // Now produce a new known_hosts file containing some other key.
  314. createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
  315. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  316. "Host localhost", //
  317. "HostName localhost", //
  318. "Port " + testPort, //
  319. "User " + TEST_USER, //
  320. "StrictHostKeyChecking yes", //
  321. "IdentityFile " + privateKey1.getAbsolutePath());
  322. }
  323. @Test(expected = TransportException.class)
  324. public void testSshModifiedHostKeyWithProviderDeny() throws Exception {
  325. File copiedHosts = new File(knownHosts.getParentFile(),
  326. "copiedKnownHosts");
  327. assertTrue("Failed to rename known_hosts",
  328. knownHosts.renameTo(copiedHosts));
  329. // Now produce a new known_hosts file containing some other key.
  330. createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
  331. TestCredentialsProvider provider = new TestCredentialsProvider();
  332. try {
  333. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  334. "Host localhost", //
  335. "HostName localhost", //
  336. "Port " + testPort, //
  337. "User " + TEST_USER, //
  338. "StrictHostKeyChecking yes", //
  339. "IdentityFile " + privateKey1.getAbsolutePath());
  340. } catch (Exception e) {
  341. assertEquals("Expected to be told about the modified key", 1,
  342. provider.getLog().size());
  343. assertTrue("Only messages expected", provider.getLog().values()
  344. .stream().flatMap(List::stream).allMatch(
  345. c -> c instanceof CredentialItem.InformationalMessage));
  346. throw e;
  347. }
  348. }
  349. private void checkKnownHostsModifiedHostKey(File backup, File newFile,
  350. String wrongKey) throws IOException {
  351. List<String> oldLines = Files.readAllLines(backup.toPath(),
  352. StandardCharsets.UTF_8);
  353. // Find the original entry. We should have that again in known_hosts.
  354. String oldKeyPart = null;
  355. for (String oldLine : oldLines) {
  356. if (oldLine.contains("[localhost]:")) {
  357. String[] parts = oldLine.split("\\s+");
  358. if (parts.length > 2) {
  359. oldKeyPart = parts[parts.length - 2] + ' '
  360. + parts[parts.length - 1];
  361. break;
  362. }
  363. }
  364. }
  365. assertNotNull("Old key not found", oldKeyPart);
  366. List<String> newLines = Files.readAllLines(newFile.toPath(),
  367. StandardCharsets.UTF_8);
  368. assertFalse("Old host key still found in known_hosts file" + newFile,
  369. hasHostKey("localhost", testPort, wrongKey, newLines));
  370. assertTrue("New host key not found in known_hosts file" + newFile,
  371. hasHostKey("localhost", testPort, oldKeyPart, newLines));
  372. }
  373. @Test
  374. public void testSshModifiedHostKeyAllow() throws Exception {
  375. assertTrue("Failed to delete known_hosts", knownHosts.delete());
  376. createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
  377. File backup = new File(getTemporaryDirectory(), "backupKnownHosts");
  378. Files.copy(knownHosts.toPath(), backup.toPath());
  379. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  380. "Host localhost", //
  381. "HostName localhost", //
  382. "Port " + testPort, //
  383. "User " + TEST_USER, //
  384. "StrictHostKeyChecking no", //
  385. "IdentityFile " + privateKey1.getAbsolutePath());
  386. // File should not have been updated!
  387. String[] oldLines = Files
  388. .readAllLines(backup.toPath(), StandardCharsets.UTF_8)
  389. .toArray(new String[0]);
  390. String[] newLines = Files
  391. .readAllLines(knownHosts.toPath(), StandardCharsets.UTF_8)
  392. .toArray(new String[0]);
  393. assertArrayEquals("Known hosts file should not be modified", oldLines,
  394. newLines);
  395. }
  396. @Test
  397. public void testSshModifiedHostKeyAsk() throws Exception {
  398. File copiedHosts = new File(knownHosts.getParentFile(),
  399. "copiedKnownHosts");
  400. assertTrue("Failed to rename known_hosts",
  401. knownHosts.renameTo(copiedHosts));
  402. String wrongKeyPart = createKnownHostsFile(knownHosts, "localhost",
  403. testPort, publicKey1);
  404. TestCredentialsProvider provider = new TestCredentialsProvider();
  405. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
  406. "Host localhost", //
  407. "HostName localhost", //
  408. "Port " + testPort, //
  409. "User " + TEST_USER, //
  410. "IdentityFile " + privateKey1.getAbsolutePath());
  411. checkKnownHostsModifiedHostKey(copiedHosts, knownHosts, wrongKeyPart);
  412. assertEquals("Expected to be asked about the modified key", 1,
  413. provider.getLog().size());
  414. }
  415. @Test
  416. public void testSshCloneWithConfigAndPush() throws Exception {
  417. pushTo(cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  418. "Host localhost", //
  419. "HostName localhost", //
  420. "Port " + testPort, //
  421. "User " + TEST_USER, //
  422. "IdentityFile " + privateKey1.getAbsolutePath()));
  423. }
  424. @Test
  425. public void testSftpWithConfig() throws Exception {
  426. cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
  427. "Host localhost", //
  428. "HostName localhost", //
  429. "Port " + testPort, //
  430. "User " + TEST_USER, //
  431. "IdentityFile " + privateKey1.getAbsolutePath());
  432. }
  433. @Test
  434. public void testSftpCloneWithConfigAndPush() throws Exception {
  435. pushTo(cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
  436. "Host localhost", //
  437. "HostName localhost", //
  438. "Port " + testPort, //
  439. "User " + TEST_USER, //
  440. "IdentityFile " + privateKey1.getAbsolutePath()));
  441. }
  442. @Test(expected = TransportException.class)
  443. public void testSshWithConfigWrongKey() throws Exception {
  444. cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
  445. "Host localhost", //
  446. "HostName localhost", //
  447. "Port " + testPort, //
  448. "User " + TEST_USER, //
  449. "IdentityFile " + privateKey2.getAbsolutePath());
  450. }
  451. @Test
  452. public void testSshWithWrongUserNameInConfig() throws Exception {
  453. // Bug 526778
  454. cloneWith(
  455. "ssh://" + TEST_USER + "@localhost:" + testPort
  456. + "/doesntmatter",
  457. defaultCloneDir, null, //
  458. "Host localhost", //
  459. "HostName localhost", //
  460. "User sombody_else", //
  461. "IdentityFile " + privateKey1.getAbsolutePath());
  462. }
  463. @Test
  464. public void testSshWithWrongPortInConfig() throws Exception {
  465. // Bug 526778
  466. cloneWith(
  467. "ssh://" + TEST_USER + "@localhost:" + testPort
  468. + "/doesntmatter",
  469. defaultCloneDir, null, //
  470. "Host localhost", //
  471. "HostName localhost", //
  472. "Port 22", //
  473. "User " + TEST_USER, //
  474. "IdentityFile " + privateKey1.getAbsolutePath());
  475. }
  476. @Test
  477. public void testSshWithAliasInConfig() throws Exception {
  478. // Bug 531118
  479. cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
  480. "Host git", //
  481. "HostName localhost", //
  482. "Port " + testPort, //
  483. "User " + TEST_USER, //
  484. "IdentityFile " + privateKey1.getAbsolutePath(), "", //
  485. "Host localhost", //
  486. "HostName localhost", //
  487. "Port 22", //
  488. "User someone_else", //
  489. "IdentityFile " + privateKey2.getAbsolutePath());
  490. }
  491. @Test
  492. public void testSshWithUnknownCiphersInConfig() throws Exception {
  493. // Bug 535672
  494. cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
  495. "Host git", //
  496. "HostName localhost", //
  497. "Port " + testPort, //
  498. "User " + TEST_USER, //
  499. "IdentityFile " + privateKey1.getAbsolutePath(), //
  500. "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
  501. }
  502. @Test
  503. public void testSshWithUnknownHostKeyAlgorithmsInConfig()
  504. throws Exception {
  505. // Bug 535672
  506. cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
  507. "Host git", //
  508. "HostName localhost", //
  509. "Port " + testPort, //
  510. "User " + TEST_USER, //
  511. "IdentityFile " + privateKey1.getAbsolutePath(), //
  512. "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss");
  513. }
  514. @Test
  515. public void testSshWithUnknownKexAlgorithmsInConfig()
  516. throws Exception {
  517. // Bug 535672
  518. cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
  519. "Host git", //
  520. "HostName localhost", //
  521. "Port " + testPort, //
  522. "User " + TEST_USER, //
  523. "IdentityFile " + privateKey1.getAbsolutePath(), //
  524. "KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
  525. }
  526. @Test
  527. public void testSshWithMinimalHostKeyAlgorithmsInConfig()
  528. throws Exception {
  529. // Bug 537790
  530. cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
  531. "Host git", //
  532. "HostName localhost", //
  533. "Port " + testPort, //
  534. "User " + TEST_USER, //
  535. "IdentityFile " + privateKey1.getAbsolutePath(), //
  536. "HostKeyAlgorithms ssh-rsa,ssh-dss");
  537. }
  538. @Theory
  539. public void testSshKeys(String keyName) throws Exception {
  540. // JSch fails on ECDSA 384/521 keys. Compare
  541. // https://sourceforge.net/p/jsch/patches/10/
  542. assumeTrue(!(getSessionFactory() instanceof JschConfigSessionFactory
  543. && (keyName.startsWith("id_ecdsa_384")
  544. || keyName.startsWith("id_ecdsa_521"))));
  545. File cloned = new File(getTemporaryDirectory(), "cloned");
  546. String keyFileName = keyName + "_key";
  547. File privateKey = new File(sshDir, keyFileName);
  548. copyTestResource(keyName, privateKey);
  549. File publicKey = new File(sshDir, keyFileName + ".pub");
  550. copyTestResource(keyName + ".pub", publicKey);
  551. server.setTestUserPublicKey(publicKey.toPath());
  552. TestCredentialsProvider provider = new TestCredentialsProvider(
  553. "testpass");
  554. pushTo(provider,
  555. cloneWith("ssh://localhost/doesntmatter", //
  556. cloned, provider, //
  557. "Host localhost", //
  558. "HostName localhost", //
  559. "Port " + testPort, //
  560. "User " + TEST_USER, //
  561. "IdentityFile " + privateKey.getAbsolutePath()));
  562. int expectedCalls = keyName.endsWith("testpass") ? 1 : 0;
  563. assertEquals("Unexpected calls to CredentialsProvider", expectedCalls,
  564. provider.getLog().size());
  565. // Should now also work without credentials provider, even if the key
  566. // was encrypted.
  567. cloned = new File(getTemporaryDirectory(), "cloned2");
  568. pushTo(null,
  569. cloneWith("ssh://localhost/doesntmatter", //
  570. cloned, null, //
  571. "Host localhost", //
  572. "HostName localhost", //
  573. "Port " + testPort, //
  574. "User " + TEST_USER, //
  575. "IdentityFile " + privateKey.getAbsolutePath()));
  576. }
  577. }