From a1e15a7490278090d01e0c01ecc3cfc59f180046 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 8 Feb 2025 20:04:33 +0100 Subject: Fix LF support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- sshexec.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/sshexec.c b/sshexec.c index 4074795..e8089a6 100644 --- a/sshexec.c +++ b/sshexec.c @@ -199,7 +199,11 @@ build_command_reserve(size_t n) static void build_command_escape(const char *arg) { +#define IS_ALWAYS_SAFE(C) (isalnum((C)) || (C) == '_' || (C) == '/') +#define IS_INITIAL_SAFE(C) (isalpha((C)) || (C) == '_' || (C) == '/') + size_t n = 0; + size_t lfs = 0; /* Quote empty string */ if (!*arg) { @@ -208,7 +212,7 @@ build_command_escape(const char *arg) } /* If the string only contains safe characters, add it would escaping */ - while (isalnum(arg[n]) || arg[n] == '_' || arg[n] == '/') + while (IS_ALWAYS_SAFE(arg[n])) n += 1; if (!arg[n]) { build_command(arg, n); @@ -217,22 +221,43 @@ build_command_escape(const char *arg) /* Escape string, using quoted printf(1) statement */ build_command_asis("\"$(printf '"); - goto start; + goto start; /* already have a count of safe initial characters, let's add them immidately */ while (*arg) { - build_command_reserve(4); - command[command_len++] = '\\'; - command[command_len] = (((unsigned char)*arg >> 6) & 7) + '0'; - command_len += (command[command_len] != '0'); - command[command_len] = (((unsigned char)*arg >> 3) & 7) + '0'; - command_len += (command[command_len] != '0'); - command[command_len++] = ((unsigned char)*arg & 7) + '0'; - arg = &arg[1]; + /* Since process substation removes at least one terminal + * LF, we must hold of on adding them */ + if (*arg == '\n') { + lfs += 1; + arg = &arg[1]; + continue; + } + + /* Adding any held back LF */ + if (lfs) { + build_command_reserve(lfs * 2U); + for (; lfs; lfs--) { + command[command_len++] = '\\'; + command[command_len++] = 'n'; + } + } + /* Character is unsafe, escape it */ + if (!IS_ALWAYS_SAFE(*arg)) { + build_command_reserve(4); + command[command_len++] = '\\'; + command[command_len] = (((unsigned char)*arg >> 6) & 7) + '0'; + command_len += (command[command_len] != '0'); + command[command_len] = (((unsigned char)*arg >> 3) & 7) + '0'; + command_len += (command[command_len] != '0'); + command[command_len++] = ((unsigned char)*arg & 7) + '0'; + arg = &arg[1]; + } + + /* Add any safe characters as is */ n = 0; - while (isalpha(arg[n]) || arg[n] == '_' || arg[n] == '/') + while (IS_INITIAL_SAFE(arg[n]) || arg[n] == '_' || arg[n] == '/') n += 1; if (n) { - while (isalnum(arg[n]) || arg[n] == '_' || arg[n] == '/') + while (IS_ALWAYS_SAFE(arg[n])) n += 1; start: build_command(arg, n); @@ -240,7 +265,17 @@ build_command_escape(const char *arg) } } build_command_asis("\\n')\""); - /* TODO sh(1) may remove all, not just the last LF */ + /* Add any held back terminal LF's */ + if (lfs) { + build_command_reserve(lfs + 2U); + command[command_len++] = '\''; + memset(&command[command_len], '\n', lfs); + command_len += lfs; + command[command_len++] = '\''; + } + +#undef IS_ALWAYS_SAFE +#undef IS_INITIAL_SAFE } -- cgit v1.2.3-70-g09d2