1 /*******************************************************************************
2  * Copyright (c) 2011, 2017 SAP AG and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Lazar Kirchev, SAP AG - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.equinox.console.ssh;
15 
16 import java.io.Closeable;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
20 import java.io.PrintStream;
21 import java.util.Map;
22 
23 import org.apache.felix.service.command.CommandProcessor;
24 import org.apache.felix.service.command.CommandSession;
25 import org.eclipse.equinox.console.common.ConsoleInputHandler;
26 import org.eclipse.equinox.console.common.ConsoleInputScanner;
27 import org.eclipse.equinox.console.common.ConsoleInputStream;
28 import org.eclipse.equinox.console.common.ConsoleOutputStream;
29 import org.eclipse.equinox.console.common.KEYS;
30 import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings;
31 import org.eclipse.equinox.console.storage.SecureUserStore;
32 import org.osgi.framework.BundleContext;
33 
34 /**
35  * This class manages a ssh connection. It is responsible for wrapping the original io streams
36  * from the socket, and starting a CommandSession to execute commands from the ssh.
37  *
38  */
39 public class SshSession extends Thread implements Closeable {
40 	private CommandProcessor processor;
41 	private BundleContext context;
42 	private SshShell sshShell;
43 	private InputStream in;
44 	private OutputStream out;
45 	private TerminalTypeMappings currentMappings;
46 	private Map<String, KEYS> currentEscapesToKey;
47 
48 	private static final String PROMPT = "prompt";
49 	private static final String OSGI_PROMPT = "osgi> ";
50 	private static final String SCOPE = "SCOPE";
51 	private static final String EQUINOX_SCOPE = "equinox:*";
52 	private static final String INPUT_SCANNER = "INPUT_SCANNER";
53 	private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
54 	private static final String USER_STORAGE_PROPERTY_NAME = "osgi.console.ssh.useDefaultSecureStorage";
55 	private static final String DEFAULT_USER = "equinox";
56 	private static final String CLOSEABLE = "CLOSEABLE";
57 	private static final int ADD_USER_COUNTER_LIMIT = 2;
58 
SshSession(CommandProcessor processor, BundleContext context, SshShell sshShell, InputStream in, OutputStream out, TerminalTypeMappings currentMappings, Map<String, KEYS> currentExcapesToKey)59 	public SshSession(CommandProcessor processor, BundleContext context, SshShell sshShell, InputStream in, OutputStream out, TerminalTypeMappings currentMappings, Map<String, KEYS> currentExcapesToKey) {
60 		this.processor = processor;
61 		this.context = context;
62 		this.sshShell = sshShell;
63 		this.in = in;
64 		this.out = out;
65 		this.currentMappings = currentMappings;
66 		this.currentEscapesToKey = currentExcapesToKey;
67 	}
68 
69 	@Override
run()70 	public void run() {
71 		ConsoleInputStream input = new ConsoleInputStream();
72 		ConsoleOutputStream outp = new ConsoleOutputStream(out);
73 		SshInputHandler inputHandler = new SshInputHandler(in, input, outp);
74 		inputHandler.getScanner().setBackspace(currentMappings.getBackspace());
75 		inputHandler.getScanner().setDel(currentMappings.getDel());
76 		inputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
77 		inputHandler.getScanner().setEscapes(currentMappings.getEscapes());
78 		inputHandler.start();
79 
80 		ConsoleInputStream inp = new ConsoleInputStream();
81 		ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(input, inp, outp);
82 		consoleInputHandler.getScanner().setBackspace(currentMappings.getBackspace());
83 		consoleInputHandler.getScanner().setDel(currentMappings.getDel());
84 		consoleInputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
85 		consoleInputHandler.getScanner().setEscapes(currentMappings.getEscapes());
86 		((ConsoleInputScanner)consoleInputHandler.getScanner()).setContext(context);
87 		consoleInputHandler.start();
88 
89 		final PrintStream output = new PrintStream(outp);
90 
91 		try (CommandSession session = processor.createSession(inp, output, output)) {
92 			session.put(SCOPE, EQUINOX_SCOPE);
93 			session.put(PROMPT, OSGI_PROMPT);
94 			session.put(INPUT_SCANNER, consoleInputHandler.getScanner());
95 			session.put(SSH_INPUT_SCANNER, inputHandler.getScanner());
96 			// Store this closeable object in the session, so that the disconnect command
97 			// can close it
98 			session.put(CLOSEABLE, this);
99 			((ConsoleInputScanner) consoleInputHandler.getScanner()).setSession(session);
100 
101 			if ("true".equals(context.getProperty(USER_STORAGE_PROPERTY_NAME))) {
102 				String[] names = SecureUserStore.getUserNames();
103 				for (String name : names) {
104 					// if the default user is the only user, request creation of a new user and
105 					// delete the default
106 					if (DEFAULT_USER.equals(name)) {
107 						if (names.length == 1) {
108 							session.getConsole().println(
109 									"Currently the default user is the only one; since it will be deleted after first login, create a new user:");
110 							boolean isUserAdded = false;
111 							int count = 0;
112 							while (!isUserAdded && count < ADD_USER_COUNTER_LIMIT) {
113 								isUserAdded = ((Boolean) session.execute("addUser")).booleanValue();
114 								count++;
115 							}
116 							if (!isUserAdded) {
117 								break;
118 							}
119 						}
120 						if (SecureUserStore.existsUser(name)) {
121 							SecureUserStore.deleteUser(name);
122 						}
123 						break;
124 					}
125 				}
126 			}
127 			session.execute("gosh --login --noshutdown");
128 		} catch (Exception e) {
129 			e.printStackTrace();
130 		}
131 
132 	}
133 
134 	@Override
close()135 	public void close() throws IOException {
136 		this.interrupt();
137 		sshShell.removeSession(this);
138 	}
139 
140 }
141