Bug 2094 - Executing commands via ssh on a remote host has different parameter passing properties
Summary: Executing commands via ssh on a remote host has different parameter passing p...
Status: CLOSED WONTFIX
Alias: None
Product: Portable OpenSSH
Classification: Unclassified
Component: ssh (show other bugs)
Version: 6.2p1
Hardware: Other OpenBSD
: P5 normal
Assignee: Assigned to nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-04-21 00:02 AEST by Laurence Tratt
Modified: 2015-08-11 23:03 AEST (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Laurence Tratt 2013-04-21 00:02:58 AEST
Consider the file /tmp/ap.sh which simply prints out all the arguments to it:

  #! /bin/sh

  while [ $# -ge 1 ]; do
	  echo "arg: $1"
      shift
  done

If I create a C file ex_local.c:

  #include <unistd.h>

  int main(int argc, char** argv) {
      execlp(argv[1], argv[1], "a", "a'b", NULL);
      return 0;
  }

and then compile and run it, the arguments passed to it are printed out:

  $ ex_local /tmp/ap.sh
  a
  a'b
  $

which is correct. If I have this C file ex_ssh.c:

  #include <unistd.h>

  int main(int argc, char** argv) {
      execlp("/usr/bin/ssh", "/usr/bin/ssh", argv[1], argv[2], "a", "a'b", NULL);
      return 0;
  }

and run it:

  $ ex_ssh somehost.com /tmp/ap.sh
  zsh:1: unmatched '

then /tmp/ap.sh isn't even executed on the remote machine. This surprised me, because (perhaps naively), I expected parameters passed to a command to work identically whether that command is local or run via ssh. Just to show that it isn't the fault of zsh on the remote machine, other shells show the same problem even with a simpler version of the escaping problem:

  $ echo ls \'
  '
  $ ssh someotherhost.com echo ls \'
  bash: -c: line 0: unexpected EOF while looking for matching `''
  bash: -c: line 1: syntax error: unexpected end of file

The basic problem, as far as I can tell, is that ssh is passing arguments as unprotected strings to the remote shell to execute the command. In essence, "funny" characters (e.g. ') are being reevaluated remotely, even though they would not locally.

Assuming I have got the right end of the stick so far (which is no by no means guaranteed), it's not obvious to me what the best behaviour should be. When a user executes a command via ssh, there is a soft notion that a login shell will be executed, with the users normal customisation, before the command is executed. I presume this is what ssh is doing at the moment, and where the parameter problem occurs. The only way I can see for arguments to be passed reliably is for ssh to invoke a middle-man command which then execv's with precisely the parameters passed to ssh itself. This might be rather heavy-weight for most uses, and will break backwards compatibility (some people are bound to have inadvertently come to rely on the current behaviour): perhaps it could be enabled via an optional switch?
Comment 1 Damien Miller 2013-10-24 10:43:24 AEDT
This is a known limitation of the SSH protocol, and the rcp protocol before that - dating back 30 years. It's simply too late to change it.

A better approach might be to define a protocol extension that allows explicit argument passing, rather than just a single string command. We don't have any plans to do this; the workaround of adding quoting to the remote command is well-understood and easy to implement.
Comment 2 Darren Tucker 2013-10-24 10:59:22 AEDT
I think you meant "rsh" not "rcp".

Anyway, here's how RFC4254 defines the protocol message to run a remote command (section 6.5):

      byte      SSH_MSG_CHANNEL_REQUEST
      uint32    recipient channel
      string    "exec"
      boolean   want reply
      string    command

   This message will request that the server start the execution of the
   given command.  The 'command' string may contain a path.  Normal
   precautions MUST be taken to prevent the execution of unauthorized
   commands.

Note that there's only a single string for the command, there's no facility to pass a list of args other than quoting it.  Exactly what quoting is required is dependent on the user's shell at the other end.

You can work around some quoting issues by doing something like this:

$ ssh <<EOF localhost /bin/sh
echo "'quoted"
EOF
'quoted

instead of

$ ssh localhost echo "'quoted"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
Comment 3 Damien Miller 2015-08-11 23:03:57 AEST
Set all RESOLVED bugs to CLOSED with release of OpenSSH 7.1