Allow suexec to run any command as any user

The suexec command can only be run by a peer daemon, and permits
the daemon to execute another command as a specific user identity.

This is the foundation of allowing writes to be proxied from a
slave server into the master, the slave just needs to SSH into the
master and use suexec in front of the user supplied command line
to perform an action on their behalf on the master.

Unfortunately this means we have to trust the slave process, as it
can become anyone, including an administrator.  A better approach
would be to use agent authentication and authenticate back through
the slave to the user's agent process, but not every user connection
may be using an agent.  In particular batch jobs might be using an
unencrypted key and no agent to authenticate.

Change-Id: Icb8ddb16959f01189a6c0bdfc8fec45cdd99659b
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2010-01-16 14:27:28 -08:00
parent 1fc80a6eba
commit 2f8b9bc3b7
15 changed files with 411 additions and 148 deletions

View File

@@ -28,6 +28,8 @@ import org.apache.sshd.server.session.ServerSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Creates a CommandFactory using commands registered by {@link CommandModule}.
@@ -55,6 +57,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
private class Trampoline implements Command, SessionAware {
private final String commandLine;
private final String[] argv;
private InputStream in;
private OutputStream out;
private OutputStream err;
@@ -65,6 +68,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
Trampoline(final String cmdLine) {
commandLine = cmdLine;
argv = split(cmdLine);
}
public void setInputStream(final InputStream in) {
@@ -84,7 +88,8 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
}
public void setSession(final ServerSession session) {
this.ctx = new Context(session.getAttribute(SshSession.KEY));
final SshSession s = session.getAttribute(SshSession.KEY);
this.ctx = new Context(s, commandLine);
}
public void start(final Environment env) throws IOException {
@@ -92,7 +97,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
final Context old = SshScope.set(ctx);
try {
cmd = dispatcher.get();
cmd.setCommandLine(commandLine);
cmd.setArguments(argv);
cmd.setInputStream(in);
cmd.setOutputStream(out);
cmd.setErrorStream(err);
@@ -135,8 +140,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
private void log(final int rc) {
synchronized (this) {
if (!logged) {
ctx.finished = System.currentTimeMillis();
log.onExecute(ctx, commandLine, rc);
log.onExecute(rc);
logged = true;
}
}
@@ -159,4 +163,41 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
}
}
}
/** Split a command line into a string array. */
static String[] split(String commandLine) {
final List<String> list = new ArrayList<String>();
boolean inquote = false;
StringBuilder r = new StringBuilder();
for (int ip = 0; ip < commandLine.length();) {
final char b = commandLine.charAt(ip++);
switch (b) {
case '\t':
case ' ':
if (inquote)
r.append(b);
else if (r.length() > 0) {
list.add(r.toString());
r = new StringBuilder();
}
continue;
case '\'':
inquote = !inquote;
continue;
case '\\':
if (inquote || ip == commandLine.length())
r.append(b); // literal within a quote
else
r.append(commandLine.charAt(ip++));
continue;
default:
r.append(b);
continue;
}
}
if (r.length() > 0) {
list.add(r.toString());
}
return list.toArray(new String[list.size()]);
}
}