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 17KB

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