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.

OpenSshConfigTest.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. * Copyright (C) 2008, 2017 Google Inc. 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;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.junit.Assert.assertArrayEquals;
  13. import static org.junit.Assert.assertEquals;
  14. import static org.junit.Assert.assertFalse;
  15. import static org.junit.Assert.assertNotNull;
  16. import static org.junit.Assert.assertNotSame;
  17. import static org.junit.Assert.assertNull;
  18. import static org.junit.Assert.assertTrue;
  19. import java.io.File;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.OutputStreamWriter;
  23. import java.time.Instant;
  24. import java.util.concurrent.TimeUnit;
  25. import org.eclipse.jgit.junit.RepositoryTestCase;
  26. import org.eclipse.jgit.lib.Constants;
  27. import org.eclipse.jgit.transport.OpenSshConfig.Host;
  28. import org.eclipse.jgit.util.FS;
  29. import org.eclipse.jgit.util.FileUtils;
  30. import org.eclipse.jgit.util.SystemReader;
  31. import org.junit.Before;
  32. import org.junit.Test;
  33. import com.jcraft.jsch.ConfigRepository;
  34. import com.jcraft.jsch.ConfigRepository.Config;
  35. public class OpenSshConfigTest extends RepositoryTestCase {
  36. private File home;
  37. private File configFile;
  38. private OpenSshConfig osc;
  39. @Override
  40. @Before
  41. public void setUp() throws Exception {
  42. super.setUp();
  43. home = new File(trash, "home");
  44. FileUtils.mkdir(home);
  45. configFile = new File(new File(home, ".ssh"), Constants.CONFIG);
  46. FileUtils.mkdir(configFile.getParentFile());
  47. mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "jex_junit");
  48. osc = new OpenSshConfig(home, configFile);
  49. }
  50. private void config(String data) throws IOException {
  51. FS fs = FS.DETECTED;
  52. long resolution = FS.getFileStoreAttributes(configFile.toPath())
  53. .getFsTimestampResolution().toNanos();
  54. Instant lastMtime = fs.lastModifiedInstant(configFile);
  55. do {
  56. try (final OutputStreamWriter fw = new OutputStreamWriter(
  57. new FileOutputStream(configFile), UTF_8)) {
  58. fw.write(data);
  59. TimeUnit.NANOSECONDS.sleep(resolution);
  60. } catch (InterruptedException e) {
  61. Thread.interrupted();
  62. }
  63. } while (lastMtime.equals(fs.lastModifiedInstant(configFile)));
  64. }
  65. @Test
  66. public void testNoConfig() {
  67. final Host h = osc.lookup("repo.or.cz");
  68. assertNotNull(h);
  69. assertEquals("repo.or.cz", h.getHostName());
  70. assertEquals("jex_junit", h.getUser());
  71. assertEquals(22, h.getPort());
  72. assertEquals(1, h.getConnectionAttempts());
  73. assertNull(h.getIdentityFile());
  74. }
  75. @Test
  76. public void testSeparatorParsing() throws Exception {
  77. config("Host\tfirst\n" +
  78. "\tHostName\tfirst.tld\n" +
  79. "\n" +
  80. "Host second\n" +
  81. " HostName\tsecond.tld\n" +
  82. "Host=third\n" +
  83. "HostName=third.tld\n\n\n" +
  84. "\t Host = fourth\n\n\n" +
  85. " \t HostName\t=fourth.tld\n" +
  86. "Host\t = last\n" +
  87. "HostName \t last.tld");
  88. assertNotNull(osc.lookup("first"));
  89. assertEquals("first.tld", osc.lookup("first").getHostName());
  90. assertNotNull(osc.lookup("second"));
  91. assertEquals("second.tld", osc.lookup("second").getHostName());
  92. assertNotNull(osc.lookup("third"));
  93. assertEquals("third.tld", osc.lookup("third").getHostName());
  94. assertNotNull(osc.lookup("fourth"));
  95. assertEquals("fourth.tld", osc.lookup("fourth").getHostName());
  96. assertNotNull(osc.lookup("last"));
  97. assertEquals("last.tld", osc.lookup("last").getHostName());
  98. }
  99. @Test
  100. public void testQuoteParsing() throws Exception {
  101. config("Host \"good\"\n" +
  102. " HostName=\"good.tld\"\n" +
  103. " Port=\"6007\"\n" +
  104. " User=\"gooduser\"\n" +
  105. "Host multiple unquoted and \"quoted\" \"hosts\"\n" +
  106. " Port=\"2222\"\n" +
  107. "Host \"spaced\"\n" +
  108. "# Bad host name, but testing preservation of spaces\n" +
  109. " HostName=\" spaced\ttld \"\n" +
  110. "# Misbalanced quotes\n" +
  111. "Host \"bad\"\n" +
  112. "# OpenSSH doesn't allow this but ...\n" +
  113. " HostName=bad.tld\"\n");
  114. assertEquals("good.tld", osc.lookup("good").getHostName());
  115. assertEquals("gooduser", osc.lookup("good").getUser());
  116. assertEquals(6007, osc.lookup("good").getPort());
  117. assertEquals(2222, osc.lookup("multiple").getPort());
  118. assertEquals(2222, osc.lookup("quoted").getPort());
  119. assertEquals(2222, osc.lookup("and").getPort());
  120. assertEquals(2222, osc.lookup("unquoted").getPort());
  121. assertEquals(2222, osc.lookup("hosts").getPort());
  122. assertEquals(" spaced\ttld ", osc.lookup("spaced").getHostName());
  123. assertEquals("bad.tld\"", osc.lookup("bad").getHostName());
  124. }
  125. @Test
  126. public void testCaseInsensitiveKeyLookup() throws Exception {
  127. config("Host orcz\n" + "Port 29418\n"
  128. + "\tHostName repo.or.cz\nStrictHostKeyChecking yes\n");
  129. final Host h = osc.lookup("orcz");
  130. Config c = h.getConfig();
  131. String exactCase = c.getValue("StrictHostKeyChecking");
  132. assertEquals("yes", exactCase);
  133. assertEquals(exactCase, c.getValue("stricthostkeychecking"));
  134. assertEquals(exactCase, c.getValue("STRICTHOSTKEYCHECKING"));
  135. assertEquals(exactCase, c.getValue("sTrIcThostKEYcheckING"));
  136. assertNull(c.getValue("sTrIcThostKEYcheckIN"));
  137. }
  138. @Test
  139. public void testAlias_DoesNotMatch() throws Exception {
  140. config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n");
  141. final Host h = osc.lookup("repo.or.cz");
  142. assertNotNull(h);
  143. assertEquals("repo.or.cz", h.getHostName());
  144. assertEquals("jex_junit", h.getUser());
  145. assertEquals(22, h.getPort());
  146. assertNull(h.getIdentityFile());
  147. final Host h2 = osc.lookup("orcz");
  148. assertEquals("repo.or.cz", h.getHostName());
  149. assertEquals("jex_junit", h.getUser());
  150. assertEquals(29418, h2.getPort());
  151. assertNull(h.getIdentityFile());
  152. }
  153. @Test
  154. public void testAlias_OptionsSet() throws Exception {
  155. config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\tPort 2222\n"
  156. + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
  157. + "\tForwardX11 no\n");
  158. final Host h = osc.lookup("orcz");
  159. assertNotNull(h);
  160. assertEquals("repo.or.cz", h.getHostName());
  161. assertEquals("jex", h.getUser());
  162. assertEquals(2222, h.getPort());
  163. assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
  164. }
  165. @Test
  166. public void testAlias_OptionsKeywordCaseInsensitive() throws Exception {
  167. config("hOsT orcz\n" + "\thOsTnAmE repo.or.cz\n" + "\tPORT 2222\n"
  168. + "\tuser jex\n" + "\tidentityfile .ssh/id_jex\n"
  169. + "\tForwardX11 no\n");
  170. final Host h = osc.lookup("orcz");
  171. assertNotNull(h);
  172. assertEquals("repo.or.cz", h.getHostName());
  173. assertEquals("jex", h.getUser());
  174. assertEquals(2222, h.getPort());
  175. assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
  176. }
  177. @Test
  178. public void testAlias_OptionsInherit() throws Exception {
  179. config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
  180. + "\tHostName not.a.host.example.com\n" + "\tPort 2222\n"
  181. + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
  182. + "\tForwardX11 no\n");
  183. final Host h = osc.lookup("orcz");
  184. assertNotNull(h);
  185. assertEquals("repo.or.cz", h.getHostName());
  186. assertEquals("jex", h.getUser());
  187. assertEquals(2222, h.getPort());
  188. assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
  189. }
  190. @Test
  191. public void testAlias_PreferredAuthenticationsDefault() throws Exception {
  192. final Host h = osc.lookup("orcz");
  193. assertNotNull(h);
  194. assertNull(h.getPreferredAuthentications());
  195. }
  196. @Test
  197. public void testAlias_PreferredAuthentications() throws Exception {
  198. config("Host orcz\n" + "\tPreferredAuthentications publickey\n");
  199. final Host h = osc.lookup("orcz");
  200. assertNotNull(h);
  201. assertEquals("publickey", h.getPreferredAuthentications());
  202. }
  203. @Test
  204. public void testAlias_InheritPreferredAuthentications() throws Exception {
  205. config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
  206. + "\tPreferredAuthentications publickey, hostbased\n");
  207. final Host h = osc.lookup("orcz");
  208. assertNotNull(h);
  209. assertEquals("publickey,hostbased", h.getPreferredAuthentications());
  210. }
  211. @Test
  212. public void testAlias_BatchModeDefault() throws Exception {
  213. final Host h = osc.lookup("orcz");
  214. assertNotNull(h);
  215. assertFalse(h.isBatchMode());
  216. }
  217. @Test
  218. public void testAlias_BatchModeYes() throws Exception {
  219. config("Host orcz\n" + "\tBatchMode yes\n");
  220. final Host h = osc.lookup("orcz");
  221. assertNotNull(h);
  222. assertTrue(h.isBatchMode());
  223. }
  224. @Test
  225. public void testAlias_InheritBatchMode() throws Exception {
  226. config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
  227. + "\tBatchMode yes\n");
  228. final Host h = osc.lookup("orcz");
  229. assertNotNull(h);
  230. assertTrue(h.isBatchMode());
  231. }
  232. @Test
  233. public void testAlias_ConnectionAttemptsDefault() throws Exception {
  234. final Host h = osc.lookup("orcz");
  235. assertNotNull(h);
  236. assertEquals(1, h.getConnectionAttempts());
  237. }
  238. @Test
  239. public void testAlias_ConnectionAttempts() throws Exception {
  240. config("Host orcz\n" + "\tConnectionAttempts 5\n");
  241. final Host h = osc.lookup("orcz");
  242. assertNotNull(h);
  243. assertEquals(5, h.getConnectionAttempts());
  244. }
  245. @Test
  246. public void testAlias_invalidConnectionAttempts() throws Exception {
  247. config("Host orcz\n" + "\tConnectionAttempts -1\n");
  248. final Host h = osc.lookup("orcz");
  249. assertNotNull(h);
  250. assertEquals(1, h.getConnectionAttempts());
  251. }
  252. @Test
  253. public void testAlias_badConnectionAttempts() throws Exception {
  254. config("Host orcz\n" + "\tConnectionAttempts xxx\n");
  255. final Host h = osc.lookup("orcz");
  256. assertNotNull(h);
  257. assertEquals(1, h.getConnectionAttempts());
  258. }
  259. @Test
  260. public void testDefaultBlock() throws Exception {
  261. config("ConnectionAttempts 5\n\nHost orcz\nConnectionAttempts 3\n");
  262. final Host h = osc.lookup("orcz");
  263. assertNotNull(h);
  264. assertEquals(5, h.getConnectionAttempts());
  265. }
  266. @Test
  267. public void testHostCaseInsensitive() throws Exception {
  268. config("hOsT orcz\nConnectionAttempts 3\n");
  269. final Host h = osc.lookup("orcz");
  270. assertNotNull(h);
  271. assertEquals(3, h.getConnectionAttempts());
  272. }
  273. @Test
  274. public void testListValueSingle() throws Exception {
  275. config("Host orcz\nUserKnownHostsFile /foo/bar\n");
  276. final ConfigRepository.Config c = osc.getConfig("orcz");
  277. assertNotNull(c);
  278. assertEquals("/foo/bar", c.getValue("UserKnownHostsFile"));
  279. }
  280. @Test
  281. public void testListValueMultiple() throws Exception {
  282. // Tilde expansion occurs within the parser
  283. config("Host orcz\nUserKnownHostsFile \"~/foo/ba z\" /foo/bar \n");
  284. final ConfigRepository.Config c = osc.getConfig("orcz");
  285. assertNotNull(c);
  286. assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
  287. "/foo/bar" },
  288. c.getValues("UserKnownHostsFile"));
  289. }
  290. @Test
  291. public void testRepeatedLookupsWithModification() throws Exception {
  292. config("Host orcz\n" + "\tConnectionAttempts -1\n");
  293. final Host h1 = osc.lookup("orcz");
  294. assertNotNull(h1);
  295. assertEquals(1, h1.getConnectionAttempts());
  296. config("Host orcz\n" + "\tConnectionAttempts 5\n");
  297. final Host h2 = osc.lookup("orcz");
  298. assertNotNull(h2);
  299. assertNotSame(h1, h2);
  300. assertEquals(5, h2.getConnectionAttempts());
  301. assertEquals(1, h1.getConnectionAttempts());
  302. assertNotSame(h1.getConfig(), h2.getConfig());
  303. }
  304. @Test
  305. public void testIdentityFile() throws Exception {
  306. config("Host orcz\nIdentityFile \"~/foo/ba z\"\nIdentityFile /foo/bar");
  307. final Host h = osc.lookup("orcz");
  308. assertNotNull(h);
  309. File f = h.getIdentityFile();
  310. assertNotNull(f);
  311. // Host does tilde replacement
  312. assertEquals(new File(home, "foo/ba z"), f);
  313. final ConfigRepository.Config c = h.getConfig();
  314. // Config does tilde replacement, too
  315. assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
  316. "/foo/bar" },
  317. c.getValues("IdentityFile"));
  318. }
  319. @Test
  320. public void testMultiIdentityFile() throws Exception {
  321. config("IdentityFile \"~/foo/ba z\"\nHost orcz\nIdentityFile /foo/bar\nHOST *\nIdentityFile /foo/baz");
  322. final Host h = osc.lookup("orcz");
  323. assertNotNull(h);
  324. File f = h.getIdentityFile();
  325. assertNotNull(f);
  326. // Host does tilde replacement
  327. assertEquals(new File(home, "foo/ba z"), f);
  328. final ConfigRepository.Config c = h.getConfig();
  329. // Config does tilde replacement, too
  330. assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
  331. "/foo/bar", "/foo/baz" },
  332. c.getValues("IdentityFile"));
  333. }
  334. @Test
  335. public void testNegatedPattern() throws Exception {
  336. config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST !*.or.cz\nIdentityFile /foo/baz");
  337. final Host h = osc.lookup("repo.or.cz");
  338. assertNotNull(h);
  339. assertEquals(new File(home, "foo/bar"), h.getIdentityFile());
  340. assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath() },
  341. h.getConfig().getValues("IdentityFile"));
  342. }
  343. @Test
  344. public void testPattern() throws Exception {
  345. config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz");
  346. final Host h = osc.lookup("repo.or.cz");
  347. assertNotNull(h);
  348. assertEquals(new File(home, "foo/bar"), h.getIdentityFile());
  349. assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(),
  350. "/foo/baz" },
  351. h.getConfig().getValues("IdentityFile"));
  352. }
  353. @Test
  354. public void testMultiHost() throws Exception {
  355. config("Host orcz *.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz");
  356. final Host h1 = osc.lookup("repo.or.cz");
  357. assertNotNull(h1);
  358. assertEquals(new File(home, "foo/bar"), h1.getIdentityFile());
  359. assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(),
  360. "/foo/baz" },
  361. h1.getConfig().getValues("IdentityFile"));
  362. final Host h2 = osc.lookup("orcz");
  363. assertNotNull(h2);
  364. assertEquals(new File(home, "foo/bar"), h2.getIdentityFile());
  365. assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath() },
  366. h2.getConfig().getValues("IdentityFile"));
  367. }
  368. @Test
  369. public void testEqualsSign() throws Exception {
  370. config("Host=orcz\n\tConnectionAttempts = 5\n\tUser=\t foobar\t\n");
  371. final Host h = osc.lookup("orcz");
  372. assertNotNull(h);
  373. assertEquals(5, h.getConnectionAttempts());
  374. assertEquals("foobar", h.getUser());
  375. }
  376. @Test
  377. public void testMissingArgument() throws Exception {
  378. config("Host=orcz\n\tSendEnv\nIdentityFile\t\nForwardX11\n\tUser=\t foobar\t\n");
  379. final Host h = osc.lookup("orcz");
  380. assertNotNull(h);
  381. assertEquals("foobar", h.getUser());
  382. assertArrayEquals(new String[0], h.getConfig().getValues("SendEnv"));
  383. assertNull(h.getIdentityFile());
  384. assertNull(h.getConfig().getValue("ForwardX11"));
  385. }
  386. @Test
  387. public void testHomeDirUserReplacement() throws Exception {
  388. config("Host=orcz\n\tIdentityFile %d/.ssh/%u_id_dsa");
  389. final Host h = osc.lookup("orcz");
  390. assertNotNull(h);
  391. assertEquals(new File(new File(home, ".ssh"), "jex_junit_id_dsa"),
  392. h.getIdentityFile());
  393. }
  394. @Test
  395. public void testHostnameReplacement() throws Exception {
  396. config("Host=orcz\nHost *.*\n\tHostname %h\nHost *\n\tHostname %h.example.org");
  397. final Host h = osc.lookup("orcz");
  398. assertNotNull(h);
  399. assertEquals("orcz.example.org", h.getHostName());
  400. }
  401. @Test
  402. public void testRemoteUserReplacement() throws Exception {
  403. config("Host=orcz\n\tUser foo\n" + "Host *.*\n\tHostname %h\n"
  404. + "Host *\n\tHostname %h.ex%%20ample.org\n\tIdentityFile ~/.ssh/%h_%r_id_dsa");
  405. final Host h = osc.lookup("orcz");
  406. assertNotNull(h);
  407. assertEquals(
  408. new File(new File(home, ".ssh"),
  409. "orcz.ex%20ample.org_foo_id_dsa"),
  410. h.getIdentityFile());
  411. }
  412. @Test
  413. public void testLocalhostFQDNReplacement() throws Exception {
  414. String localhost = SystemReader.getInstance().getHostname();
  415. config("Host=orcz\n\tIdentityFile ~/.ssh/%l_id_dsa");
  416. final Host h = osc.lookup("orcz");
  417. assertNotNull(h);
  418. assertEquals(
  419. new File(new File(home, ".ssh"), localhost + "_id_dsa"),
  420. h.getIdentityFile());
  421. }
  422. }