History

fix: Prevent usernames with leading `-` from being passed to SSH

This detects ambiguous usernames in dangerous cases where they would be passed to external commands to form SSH connections, if they would be misinterpreted as option arguments. This change is analogous to b06a0dd, hardening `gix-transport` and applications that use it against options smuggled in URLs, but for the non-mandatory username portion of a URL, rather than the host and path portions that were covered there. For example, commands like these no longer pass `-F...` options to `ssh`: gix clone 'ssh://-Fconfigfile@example.com/abc' gix clone -- '-Fconfigfile@example.com:abc/def' Instead, they refuse to run `ssh`, producing the error: Error: Username '-Fconfigfile' could be mistaken for a command-line argument

Eliah Kagan committed 2 years ago

Commit f56ad391 parent 54286092 files · +17 −3Browse files

Unified · Split

2 files
  1. gix-transport/src/client/blocking_io/ssh/program_kind.rs +10 −3
  2. gix-url/src/lib.rs +7 −0
gix-transport/src/client/blocking_io/ssh/program_kind.rs+10−3
@@ -60,8 +60,12 @@ impl ProgramKind { · 59 unmodified lines
6060 }
6161 }
6262 };
63− let host_as_ssh_arg = match url.user() {
63+ let host_maybe_with_user_as_ssh_arg = match url.user() {
6464 Some(user) => {
65+ // FIXME: See the fixme comment on Url::user_argument_safe() about its return type.
66+ if url.user_argument_safe() != Some(user) {
67+ return Err(ssh::invocation::Error::AmbiguousUserName { user: user.into() });
68+ }
6569 let host = url.host().expect("present in ssh urls");
6670 format!("{user}@{host}")
6771 }
@@ -75,8 +79,11 @@ impl ProgramKind { · 7 unmodified lines
7579 }
7680 };
7781
78− // Try to force ssh to yield english messages (for parsing later)
79− Ok(prepare.arg(host_as_ssh_arg).env("LANG", "C").env("LC_ALL", "C"))
82+ // Try to force ssh to yield English messages (for parsing later).
83+ Ok(prepare
84+ .arg(host_maybe_with_user_as_ssh_arg)
85+ .env("LANG", "C")
86+ .env("LC_ALL", "C"))
8087 }
8188
8289 /// Note that the caller has to assure that the ssh program is launched in English by setting the locale.
gix-url/src/lib.rs+7−0
@@ -180,6 +180,13 @@ impl Url { · 179 unmodified lines
180180 ///
181181 /// Use this method if the user or a portion of the URL that begins with it will be passed to a command-line application.
182182 pub fn user_argument_safe(&self) -> Option<&str> {
183+ // FIXME: A return value of None from this method, or host_argument_safe(), is ambiguous: the user (or host) is
184+ // either present but unsafe, or absent. Furthermore, in practice the value is usually needed even if unsafe,
185+ // in order to report it in an error message. In gix-transport, the ambiguity makes it easy to write a new bug
186+ // while using this interface for user_argument_safe(). In contrast, in host_argument_safe(), the ambiguity is
187+ // much less of a problem, because the host is expected to be present. Yet the host() method must still be
188+ // called when handling the None case, to include it in the error. If possible, both methods should be replaced
189+ // by methods with a richer return type (a new enum). If not, the ambiguity should be prominently documented.
183190 self.user().filter(|user| !looks_like_argument(user.as_bytes()))
184191 }
185192