1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 package com.sshtools.daemon.configuration;
27
28 import com.sshtools.daemon.session;
29
30 import com.sshtools.j2ssh.configuration;
31 import com.sshtools.j2ssh.transport.publickey;
32
33 import org.apache.commons.logging;
34
35 import org.xml.sax;
36 import org.xml.sax.helpers;
37
38 import java.io;
39
40 import java.util;
41
42 import javax.xml.parsers;
43
44
45 /**
46 *
47 *
48 * @author $author$
49 * @version $Revision: 1.12 $
50 */
51 public class ServerConfiguration extends DefaultHandler {
52 private static Log log = LogFactory.getLog(ServerConfiguration.class);
53 private Map allowedSubsystems = new HashMap();
54 private Map serverHostKeys = new HashMap();
55 private List allowedAuthentications = new ArrayList();
56 private List requiredAuthentications = new ArrayList();
57 private int commandPort = 12222;
58 private int port = 22;
59 private String listenAddress = "0.0.0.0";
60 private int maxConnections = 10;
61 private int maxAuthentications = 5;
62 private String terminalProvider = "";
63 private String authorizationFile = "authorization.xml";
64 private String userConfigDirectory = "%D/.ssh2";
65 private String authenticationBanner = "";
66 private boolean allowTcpForwarding = true;
67 private String currentElement = null;
68 private Class sessionChannelImpl = SessionChannelServer.class;
69
70 /**
71 * Creates a new ServerConfiguration object.
72 *
73 * @param in
74 *
75 * @throws SAXException
76 * @throws ParserConfigurationException
77 * @throws IOException
78 */
79 public ServerConfiguration(InputStream in)
80 throws SAXException, ParserConfigurationException, IOException {
81 reload(in);
82 }
83
84 /**
85 *
86 *
87 * @param in
88 *
89 * @throws SAXException
90 * @throws ParserConfigurationException
91 * @throws IOException
92 */
93 public void reload(InputStream in)
94 throws SAXException, ParserConfigurationException, IOException {
95 allowedSubsystems.clear();
96 serverHostKeys.clear();
97 allowedAuthentications.clear();
98 requiredAuthentications.clear();
99 commandPort = 12222;
100 port = 22;
101 listenAddress = "0.0.0.0";
102 maxConnections = 10;
103 maxAuthentications = 5;
104 terminalProvider = "";
105 authorizationFile = "authorization.xml";
106 userConfigDirectory = "%D/.ssh2";
107 authenticationBanner = "";
108 allowTcpForwarding = true;
109 currentElement = null;
110
111 SAXParserFactory saxFactory = SAXParserFactory.newInstance();
112 SAXParser saxParser = saxFactory.newSAXParser();
113 saxParser.parse(in, this);
114 }
115
116 /**
117 *
118 *
119 * @param uri
120 * @param localName
121 * @param qname
122 * @param attrs
123 *
124 * @throws SAXException
125 */
126 public void startElement(String uri, String localName, String qname,
127 Attributes attrs) throws SAXException {
128 if (currentElement == null) {
129 if (!qname.equals("ServerConfiguration")) {
130 throw new SAXException("Unexpected root element " + qname);
131 }
132 } else {
133 if (currentElement.equals("ServerConfiguration")) {
134 if (qname.equals("ServerHostKey")) {
135 //String algorithm = attrs.getValue("AlgorithmName");
136 String privateKey = attrs.getValue("PrivateKeyFile");
137
138 if (privateKey == null) {
139 throw new SAXException(
140 "Required attributes missing from <ServerHostKey> element");
141 }
142
143 log.debug("ServerHostKey PrivateKeyFile=" + privateKey);
144
145 File f = new File(privateKey);
146
147 if (!f.exists()) {
148 privateKey = ConfigurationLoader.getConfigurationDirectory() +
149 privateKey;
150 f = new File(privateKey);
151 }
152
153 try {
154 if (f.exists()) {
155 SshPrivateKeyFile pkf = SshPrivateKeyFile.parse(f);
156 SshPrivateKey key = pkf.toPrivateKey(null);
157 serverHostKeys.put(key.getAlgorithmName(), key);
158 } else {
159 log.warn("Private key file '" + privateKey +
160 "' could not be found");
161 }
162 } catch (InvalidSshKeyException ex) {
163 log.warn("Failed to load private key '" + privateKey, ex);
164 } catch (IOException ioe) {
165 log.warn("Failed to load private key '" + privateKey,
166 ioe);
167 }
168 } else if (qname.equals("Subsystem")) {
169 String type = attrs.getValue("Type");
170 String name = attrs.getValue("Name");
171 String provider = attrs.getValue("Provider");
172
173 if ((type == null) || (name == null) || (provider == null)) {
174 throw new SAXException(
175 "Required attributes missing from <Subsystem> element");
176 }
177
178 log.debug("Subsystem Type=" + type + " Name=" + name +
179 " Provider=" + provider);
180 allowedSubsystems.put(name,
181 new AllowedSubsystem(type, name, provider));
182 } else if (!qname.equals("AuthenticationBanner") &&
183 !qname.equals("MaxConnections") &&
184 !qname.equals("MaxAuthentications") &&
185 !qname.equals("ListenAddress") &&
186 !qname.equals("Port") && !qname.equals("CommandPort") &&
187 !qname.equals("TerminalProvider") &&
188 !qname.equals("AllowedAuthentication") &&
189 !qname.equals("RequiredAuthentication") &&
190 !qname.equals("AuthorizationFile") &&
191 !qname.equals("UserConfigDirectory") &&
192 !qname.equals("AllowTcpForwarding")) {
193 throw new SAXException("Unexpected <" + qname +
194 "> element after SshAPIConfiguration");
195 }
196 }
197 }
198
199 currentElement = qname;
200 }
201
202 /**
203 *
204 *
205 * @param ch
206 * @param start
207 * @param length
208 *
209 * @throws SAXException
210 */
211 public void characters(char[] ch, int start, int length)
212 throws SAXException {
213 String value = new String(ch, start, length);
214
215 if (currentElement != null) {
216 if (currentElement.equals("AuthenticationBanner")) {
217 authenticationBanner = value;
218 log.debug("AuthenticationBanner=" + authenticationBanner);
219 } else if (currentElement.equals("MaxConnections")) {
220 maxConnections = Integer.parseInt(value);
221 log.debug("MaxConnections=" + value);
222 } else if (currentElement.equals("ListenAddress")) {
223 listenAddress = value;
224 log.debug("ListenAddress=" + listenAddress);
225 } else if (currentElement.equals("Port")) {
226 port = Integer.parseInt(value);
227 log.debug("Port=" + value);
228 } else if (currentElement.equals("CommandPort")) {
229 commandPort = Integer.parseInt(value);
230 log.debug("CommandPort=" + value);
231 } else if (currentElement.equals("TerminalProvider")) {
232 terminalProvider = value;
233 log.debug("TerminalProvider=" + terminalProvider);
234 } else if (currentElement.equals("AllowedAuthentication")) {
235 if (!allowedAuthentications.contains(value)) {
236 allowedAuthentications.add(value);
237 log.debug("AllowedAuthentication=" + value);
238 }
239 } else if (currentElement.equals("RequiredAuthentication")) {
240 if (!requiredAuthentications.contains(value)) {
241 requiredAuthentications.add(value);
242 log.debug("RequiredAuthentication=" + value);
243 }
244 } else if (currentElement.equals("AuthorizationFile")) {
245 authorizationFile = value;
246 log.debug("AuthorizationFile=" + authorizationFile);
247 } else if (currentElement.equals("UserConfigDirectory")) {
248 userConfigDirectory = value;
249 log.debug("UserConfigDirectory=" + userConfigDirectory);
250 } else if (currentElement.equals("SessionChannelImpl")) {
251 try {
252 sessionChannelImpl = ConfigurationLoader.getExtensionClass(value);
253 } catch (Exception e) {
254 log.error("Failed to load SessionChannelImpl " + value, e);
255 }
256 } else if (currentElement.equals("MaxAuthentications")) {
257 maxAuthentications = Integer.parseInt(value);
258 log.debug("MaxAuthentications=" + value);
259 } else if (currentElement.equals("AllowTcpForwarding")) {
260 allowTcpForwarding = Boolean.valueOf(value).booleanValue();
261 }
262 }
263 }
264
265 /**
266 *
267 *
268 * @param uri
269 * @param localName
270 * @param qname
271 *
272 * @throws SAXException
273 */
274 public void endElement(String uri, String localName, String qname)
275 throws SAXException {
276 if (currentElement != null) {
277 if (!currentElement.equals(qname)) {
278 throw new SAXException("Unexpected end element found <" +
279 qname + ">");
280 } else if (currentElement.equals("ServerConfiguration")) {
281 currentElement = null;
282 } else if (currentElement.equals("AuthenticationBanner") ||
283 currentElement.equals("ServerHostKey") ||
284 currentElement.equals("Subsystem") ||
285 currentElement.equals("MaxConnections") ||
286 currentElement.equals("MaxAuthentications") ||
287 currentElement.equals("ListenAddress") ||
288 currentElement.equals("Port") ||
289 currentElement.equals("CommandPort") ||
290 currentElement.equals("TerminalProvider") ||
291 currentElement.equals("AllowedAuthentication") ||
292 currentElement.equals("RequiredAuthentication") ||
293 currentElement.equals("AuthorizationFile") ||
294 currentElement.equals("UserConfigDirectory") ||
295 currentElement.equals("AllowTcpForwarding")) {
296 currentElement = "ServerConfiguration";
297 }
298 } else {
299 throw new SAXException("Unexpected end element <" + qname +
300 "> found");
301 }
302 }
303
304 /**
305 *
306 *
307 * @return
308 */
309 public List getRequiredAuthentications() {
310 return requiredAuthentications;
311 }
312
313 /**
314 *
315 *
316 * @return
317 */
318 public List getAllowedAuthentications() {
319 return allowedAuthentications;
320 }
321
322 /**
323 *
324 *
325 * @return
326 */
327 public boolean getAllowTcpForwarding() {
328 return allowTcpForwarding;
329 }
330
331 /**
332 *
333 *
334 * @return
335 */
336 public String getAuthenticationBanner() {
337 return authenticationBanner;
338 }
339
340 /**
341 *
342 *
343 * @return
344 */
345 public int getCommandPort() {
346 return commandPort;
347 }
348
349 /**
350 *
351 *
352 * @return
353 */
354 public String getUserConfigDirectory() {
355 return userConfigDirectory;
356 }
357
358 /**
359 *
360 *
361 * @return
362 */
363 public String getAuthorizationFile() {
364 return authorizationFile;
365 }
366
367 /**
368 *
369 *
370 * @return
371 */
372 public String getListenAddress() {
373 return listenAddress;
374 }
375
376 /**
377 *
378 *
379 * @return
380 */
381 public int getMaxConnections() {
382 return maxConnections;
383 }
384
385 /**
386 *
387 *
388 * @return
389 */
390 public int getMaxAuthentications() {
391 return maxAuthentications;
392 }
393
394 /**
395 *
396 *
397 * @return
398 */
399 public int getPort() {
400 return port;
401 }
402
403 /*public Class getSessionChannelImpl() {
404 return sessionChannelImpl;
405 }*/
406 public Map getServerHostKeys() {
407 return serverHostKeys;
408 }
409
410 /**
411 *
412 *
413 * @return
414 */
415 public Map getSubsystems() {
416 return allowedSubsystems;
417 }
418
419 /**
420 *
421 *
422 * @return
423 */
424 public String getTerminalProvider() {
425 return terminalProvider;
426 }
427
428 /**
429 *
430 *
431 * @return
432 */
433 public String toString() {
434 String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
435 xml += "<!-- Server configuration file - If filenames are not absolute they are assummed to be in the same directory as this configuration file. -->\n";
436 xml += "<ServerConfiguration>\n";
437 xml += " <!-- The available host keys for server authentication -->\n";
438
439 Map.Entry entry;
440 Iterator it = serverHostKeys.entrySet().iterator();
441
442 while (it.hasNext()) {
443 entry = (Map.Entry) it.next();
444 xml += (" <ServerHostKey PrivateKeyFile=\"" + entry.getValue() +
445 "\"/>\n");
446 }
447
448 xml += " <!-- Add any number of subsystem elements here -->\n";
449
450 AllowedSubsystem subsystem;
451 it = allowedSubsystems.entrySet().iterator();
452
453 while (it.hasNext()) {
454 subsystem = (AllowedSubsystem) ((Map.Entry) it.next()).getValue();
455 xml += (" <Subsystem Name=\"" + subsystem.getName() +
456 "\" Type=\"" + subsystem.getType() + "\" Provider=\"" +
457 subsystem.getProvider() + "\"/>\n");
458 }
459
460 xml += " <!-- Display the following authentication banner before authentication -->\n";
461 xml += (" <AuthenticationBanner>" + authenticationBanner +
462 "</AuthenticationBanner>\n");
463 xml += " <!-- The maximum number of connected sessions available -->\n";
464 xml += (" <MaxConnections>" + String.valueOf(maxConnections) +
465 "</MaxConnections>\n");
466 xml += " <!-- The maximum number of authentication attemtps for each connection -->\n";
467 xml += (" <MaxAuthentications>" + String.valueOf(maxAuthentications) +
468 "</MaxAuthentications>\n");
469 xml += " <!-- Bind to the following address to listen for connections -->\n";
470 xml += (" <ListenAddress>" + listenAddress + "</ListenAddress>\n");
471 xml += " <!-- The port to listen to -->\n";
472 xml += (" <Port>" + String.valueOf(port) + "</Port>\n");
473 xml += " <!-- Listen on the following port (on localhost) for server commands such as stop -->\n";
474 xml += (" <CommandPort>" + String.valueOf(commandPort) +
475 "</CommandPort>\n");
476 xml += " <!-- Specify the executable that provides the default shell -->\n";
477 xml += (" <TerminalProvider>" + terminalProvider +
478 "</TerminalProvider>\n");
479 xml += " <!-- Specify any number of allowed authentications -->\n";
480 it = allowedAuthentications.iterator();
481
482 while (it.hasNext()) {
483 xml += (" <AllowedAuthentication>" + it.next().toString() +
484 "</AllowedAuthentication>\n");
485 }
486
487 xml += " <!-- Specify any number of required authentications -->\n";
488 it = requiredAuthentications.iterator();
489
490 while (it.hasNext()) {
491 xml += (" <RequiredAuthentication>" + it.next().toString() +
492 "</RequiredAuthentication>\n");
493 }
494
495 xml += " <!-- The users authorizations file -->\n";
496 xml += (" <AuthorizationFile>" + authorizationFile +
497 "</AuthorizationFile>\n");
498 xml += " <!-- The users configuration directory where files such as AuthorizationFile are found. For users home directory specify %D For users name specify %U -->\n";
499 xml += (" <UserConfigDirectory>" + userConfigDirectory +
500 "</UserConfigDirectory>\n");
501 xml += ("<AllowTcpForwarding>" + String.valueOf(allowTcpForwarding) +
502 "</AllowTcpForwarding>\n");
503 xml += "</ServerConfiguration>\n";
504
505 return xml;
506 }
507 }