View Javadoc

1   /**
2    * Copyright 2010-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util.secure;
17  
18  import java.io.File;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.Properties;
24  
25  import org.apache.commons.io.FileUtils;
26  import org.apache.commons.lang3.StringUtils;
27  import org.kuali.common.util.LocationUtils;
28  import org.kuali.common.util.PropertyUtils;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  import org.springframework.util.Assert;
32  
33  public class SSHUtils {
34  
35  	private static final Logger logger = LoggerFactory.getLogger(SSHUtils.class);
36  
37  	private static final String FS = File.separator;
38  	private static final String IDENTITY_FILE = "IdentityFile";
39  	private static final String TILDE = "~";
40  	private static final String USER_HOME = FileUtils.getUserDirectoryPath();
41  	private static final String SSHDIR = USER_HOME + FS + ".ssh";
42  	private static final String IDENTITY = SSHDIR + FS + "identity";
43  	private static final String ID_DSA = SSHDIR + FS + "id_dsa";
44  	private static final String ID_RSA = SSHDIR + FS + "id_rsa";
45  	private static final String ID_ECDSA = SSHDIR + FS + "id_ecdsa";
46  	private static final int PORT_NUMBER_LOWEST = 1;
47  	private static final int PORT_NUMBER_HIGHEST = 65535;
48  
49  	public static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
50  	public static final String NO = "no";
51  	public static final List<String> PRIVATE_KEY_DEFAULTS = Arrays.asList(IDENTITY, ID_DSA, ID_RSA, ID_ECDSA);
52  	public static final File DEFAULT_CONFIG_FILE = new File(SSHDIR + FS + "config");
53  	public static final int DEFAULT_PORT = 22;
54  	public static final File DEFAULT_KNOWN_HOSTS = new File(SSHDIR + FS + "known_hosts");
55  
56  	/**
57  	 * Return true if <code>port &gt;= 1</code> and <code>port &lt;= 65535</code>, false otherwise.
58  	 */
59  	public static final boolean isValidPort(int port) {
60  		return port >= PORT_NUMBER_LOWEST && port <= PORT_NUMBER_HIGHEST;
61  	}
62  
63  	public static final void addPort(List<String> args, String portOption, int port, int defaultPort) {
64  		if (port != defaultPort) {
65  			Assert.isTrue(SSHUtils.isValidPort(port));
66  			logger.debug("port={}", port);
67  			args.add(portOption);
68  			args.add(Integer.toString(port));
69  		}
70  	}
71  
72  	public static final void addOptions(List<String> args, Properties options) {
73  		if (options == null) {
74  			return;
75  		}
76  		List<String> keys = PropertyUtils.getSortedKeys(options);
77  		for (String key : keys) {
78  			String value = options.getProperty(key);
79  			logger.debug("Adding option [-o {}={}]", key, value);
80  			args.add("-o");
81  			args.add(key + "=" + value);
82  		}
83  	}
84  
85  	public static final void addConfigFile(List<String> args, File configFile, File defaultConfigFile) {
86  		if (configFile == null) {
87  			return;
88  		}
89  		String defaultPath = LocationUtils.getCanonicalPath(defaultConfigFile);
90  		String configFilePath = LocationUtils.getCanonicalPath(configFile);
91  		if (!StringUtils.equals(defaultPath, configFilePath)) {
92  			logger.debug("SSH config=[{}]", configFilePath);
93  			args.add("-F");
94  			args.add(configFilePath);
95  		}
96  	}
97  
98  	public static final void addIdentityFile(List<String> args, File identityFile) {
99  		if (identityFile != null) {
100 			String path = LocationUtils.getCanonicalPath(identityFile);
101 			logger.debug("Private key=[{}]", path);
102 			args.add("-i");
103 			args.add(path);
104 		}
105 	}
106 
107 	/**
108 	 * Return a non-null list containing any private keys found by examining default private key locations in <code>~/.ssh</code> and
109 	 * parsing <code>config</code>. Any files returned by this method are guaranteed to exist and be readable.
110 	 */
111 	public static final List<File> getPrivateKeys(File config) {
112 		List<String> paths = getFilenames(config);
113 		return getExistingAndReadable(paths);
114 	}
115 
116 	/**
117 	 * Return a non-null list containing any private keys found by examining default private key locations in <code>~/.ssh</code> and
118 	 * parsing <code>~/.ssh/config</code>. Any files returned by this method are guaranteed to exist and be readable.
119 	 */
120 	public static final List<File> getDefaultPrivateKeys() {
121 		return getPrivateKeys(DEFAULT_CONFIG_FILE);
122 	}
123 
124 	public static final Properties getDefaultOptions() {
125 		Properties options = new Properties();
126 		options.setProperty(STRICT_HOST_KEY_CHECKING, NO);
127 		return options;
128 	}
129 
130 	public static final List<File> getExistingAndReadable(List<String> filenames) {
131 		List<File> files = new ArrayList<File>();
132 		for (String filename : filenames) {
133 			File file = new File(filename);
134 			if (file.exists() && file.canRead()) {
135 				files.add(file);
136 			}
137 		}
138 		return files;
139 	}
140 
141 	public static final List<String> getFilenames(File config) {
142 		if (config.exists() && config.canRead()) {
143 			List<String> lines = LocationUtils.readLines(config);
144 			List<String> identityFileLines = getIdentityFileLines(lines);
145 			return getFilenames(identityFileLines);
146 		} else {
147 			return Collections.<String> emptyList();
148 		}
149 	}
150 
151 	public static final List<String> getIdentityFileLines(List<String> lines) {
152 		List<String> identityFileLines = new ArrayList<String>();
153 		for (String line : lines) {
154 			String trimmed = StringUtils.trim(line);
155 			if (StringUtils.startsWith(trimmed, IDENTITY_FILE)) {
156 				identityFileLines.add(trimmed);
157 			}
158 		}
159 		return identityFileLines;
160 	}
161 
162 	public static final List<String> getFilenames(List<String> identityFileLines) {
163 		List<String> filenames = new ArrayList<String>();
164 		for (String identityFileLine : identityFileLines) {
165 			String originalFilename = StringUtils.substring(identityFileLine, IDENTITY_FILE.length());
166 			String resolvedFilename = StringUtils.replace(originalFilename, TILDE, USER_HOME);
167 			String trimmed = StringUtils.trim(resolvedFilename);
168 			filenames.add(trimmed);
169 		}
170 		return filenames;
171 	}
172 }