001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.bsd; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.net.ServerSocket; 024import java.net.Socket; 025import java.nio.charset.StandardCharsets; 026 027import org.apache.commons.net.SocketClient; 028import org.apache.commons.net.io.SocketInputStream; 029import org.apache.commons.net.util.NetConstants; 030 031/** 032 * RExecClient implements the rexec() facility that first appeared in 4.2BSD Unix. This class will probably only be of use for connecting to UNIX systems and 033 * only when the rexecd daemon is configured to run, which is a rarity these days because of the security risks involved. However, rexec() can be very useful 034 * for performing administrative tasks on a network behind a firewall. 035 * <p> 036 * As with virtually all the client classes in org.apache.commons.net, this class derives from SocketClient, inheriting its connection methods. The way to 037 * use RExecClient is to first connect to the server, call the {@link #rexec rexec()} method, and then fetch the connection's input, output, and optionally 038 * error streams. Interaction with the remote command is controlled entirely through the I/O streams. Once you have finished processing the streams, you should 039 * invoke {@link #disconnect disconnect()} to clean up properly. 040 * <p> 041 * By default, the standard output and standard error streams of the remote process are transmitted over the same connection, readable from the input stream 042 * returned by {@link #getInputStream getInputStream()}. However, it is possible to tell the rexecd daemon to return the standard error stream over a separate 043 * connection, readable from the input stream returned by {@link #getErrorStream getErrorStream()}. You can specify that a separate connection should be created 044 * for standard error by setting the boolean <code>separateErrorStream</code> parameter of {@link #rexec rexec()} to <code>true</code>. The standard input 045 * of the remote process can be written to through the output stream returned by {@link #getOutputStream getOutputSream()}. 046 * 047 * @see SocketClient 048 * @see RCommandClient 049 * @see RLoginClient 050 */ 051public class RExecClient extends SocketClient { 052 053 /** 054 * @since 3.3 055 */ 056 protected static final char NULL_CHAR = '\0'; 057 058 /** 059 * The default rexec port. Set to 512 in BSD Unix. 060 */ 061 public static final int DEFAULT_PORT = 512; 062 063 private boolean remoteVerificationEnabled; 064 065 /** 066 * If a separate error stream is requested, <code>_errorStream_</code> will point to an InputStream from which the standard error of the remote process can 067 * be read (after a call to rexec()). Otherwise, <code>_errorStream_</code> will be null. 068 */ 069 protected InputStream _errorStream_; 070 071 /** 072 * The default RExecClient constructor. Initializes the default port to <code>DEFAULT_PORT</code>. 073 */ 074 public RExecClient() { 075 _errorStream_ = null; 076 setDefaultPort(DEFAULT_PORT); 077 } 078 079 // This can be overridden in local package to implement port range 080 // limitations of rcmd and rlogin 081 InputStream createErrorStream() throws IOException { 082 final Socket socket; 083 084 try (final ServerSocket server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress())) { 085 _output_.write(Integer.toString(server.getLocalPort()).getBytes(StandardCharsets.UTF_8)); // $NON-NLS-1$ 086 _output_.write(NULL_CHAR); 087 _output_.flush(); 088 socket = server.accept(); 089 } 090 091 if (remoteVerificationEnabled && !verifyRemote(socket)) { 092 socket.close(); 093 throw new IOException("Security violation: unexpected connection attempt by " + socket.getInetAddress().getHostAddress()); 094 } 095 096 return new SocketInputStream(socket, socket.getInputStream()); 097 } 098 099 /** 100 * Disconnects from the server, closing all associated open sockets and streams. 101 * 102 * @throws IOException If an error occurs while disconnecting. 103 */ 104 @Override 105 public void disconnect() throws IOException { 106 if (_errorStream_ != null) { 107 _errorStream_.close(); 108 } 109 _errorStream_ = null; 110 super.disconnect(); 111 } 112 113 /** 114 * Returns the InputStream from which the standard error of the remote process can be read if a separate error stream is requested from the server. 115 * Otherwise, null will be returned. The error stream will only be set after a successful rexec() invocation. 116 * 117 * @return The InputStream from which the standard error of the remote process can be read if a separate error stream is requested from the server. 118 * Otherwise, null will be returned. 119 */ 120 public InputStream getErrorStream() { 121 return _errorStream_; 122 } 123 124 /** 125 * Returns the InputStream from which the standard output of the remote process can be read. The input stream will only be set after a successful rexec() 126 * invocation. 127 * 128 * @return The InputStream from which the standard output of the remote process can be read. 129 */ 130 public InputStream getInputStream() { 131 return _input_; 132 } 133 134 /** 135 * Returns the OutputStream through which the standard input of the remote process can be written. The output stream will only be set after a successful 136 * rexec() invocation. 137 * 138 * @return The OutputStream through which the standard input of the remote process can be written. 139 */ 140 public OutputStream getOutputStream() { 141 return _output_; 142 } 143 144 /** 145 * Return whether or not verification of the remote host providing a separate error stream is enabled. The default behavior is for verification to be 146 * enabled. 147 * 148 * @return True if verification is enabled, false if not. 149 */ 150 public final boolean isRemoteVerificationEnabled() { 151 return remoteVerificationEnabled; 152 } 153 154 /** 155 * Same as <code>rexec(user, password, command, false);</code> 156 * 157 * @param user the user name 158 * @param password the password 159 * @param command the command to run 160 * @throws IOException if an error occurs 161 */ 162 public void rexec(final String user, final String password, final String command) throws IOException { 163 rexec(user, password, command, false); 164 } 165 166 /** 167 * Remotely executes a command through the rexecd daemon on the server to which the RExecClient is connected. After calling this method, you may interact 168 * with the remote process through its standard input, output, and error streams. You will typically be able to detect the termination of the remote process 169 * after reaching end of file on its standard output (accessible through {@link #getInputStream getInputStream()}). Disconnecting from the server or closing 170 * the process streams before reaching end of file will not necessarily terminate the remote process. 171 * <p> 172 * If a separate error stream is requested, the remote server will connect to a local socket opened by RExecClient, providing an independent stream through 173 * which standard error will be transmitted. RExecClient will do a simple security check when it accepts a connection for this error stream. If the 174 * connection does not originate from the remote server, an IOException will be thrown. This serves as a simple protection against possible hijacking of the 175 * error stream by an attacker monitoring the rexec() negotiation. You may disable this behavior with {@link #setRemoteVerificationEnabled 176 * setRemoteVerificationEnabled()} . 177 * 178 * @param user The account name on the server through which to execute the command. 179 * @param password The plain text password of the user account. 180 * @param command The command, including any arguments, to execute. 181 * @param separateErrorStream True if you would like the standard error to be transmitted through a different stream than standard output. False if not. 182 * @throws IOException If the rexec() attempt fails. The exception will contain a message indicating the nature of the failure. 183 */ 184 public void rexec(final String user, final String password, final String command, final boolean separateErrorStream) throws IOException { 185 int ch; 186 187 if (separateErrorStream) { 188 _errorStream_ = createErrorStream(); 189 } else { 190 _output_.write(NULL_CHAR); 191 } 192 193 _output_.write(user.getBytes(getCharset())); 194 _output_.write(NULL_CHAR); 195 _output_.write(password.getBytes(getCharset())); 196 _output_.write(NULL_CHAR); 197 _output_.write(command.getBytes(getCharset())); 198 _output_.write(NULL_CHAR); 199 _output_.flush(); 200 201 ch = _input_.read(); 202 if (ch > 0) { 203 final StringBuilder buffer = new StringBuilder(); 204 205 while ((ch = _input_.read()) != NetConstants.EOS && ch != '\n') { 206 buffer.append((char) ch); 207 } 208 209 throw new IOException(buffer.toString()); 210 } 211 if (ch < 0) { 212 throw new IOException("Server closed connection."); 213 } 214 } 215 216 /** 217 * Enable or disable verification that the remote host connecting to create a separate error stream is the same as the host to which the standard out stream 218 * is connected. The default is for verification to be enabled. You may set this value at any time, whether the client is currently connected or not. 219 * 220 * @param enable True to enable verification, false to disable verification. 221 */ 222 public final void setRemoteVerificationEnabled(final boolean enable) { 223 remoteVerificationEnabled = enable; 224 } 225 226}