1
1

config: Implement match exec keyword

The implementation does not work on Windows, where it still reports unsupported
configuration option. On windows, separate code invoking subprocess needs to be
implemented.

Fixes T169

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Этот коммит содержится в:
Jakub Jelen 2019-09-16 15:01:43 +02:00
родитель c7da113f1d
Коммит c983c994a2

Просмотреть файл

@ -32,6 +32,14 @@
#endif
#include <stdbool.h>
#include <limits.h>
#ifndef _WIN32
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <errno.h>
# include <signal.h>
# include <sys/wait.h>
#endif
#include "libssh/config_parser.h"
#include "libssh/config.h"
@ -287,6 +295,125 @@ ssh_config_match(char *value, const char *pattern, bool negate)
return result;
}
#ifdef _WIN32
static int
ssh_match_exec(ssh_session session, const char *command, bool negate)
{
(void) session;
(void) command;
(void) negate;
SSH_LOG(SSH_LOG_TRACE, "Unsupported 'exec' command on Windows '%s'",
command);
return 0;
}
#else /* _WIN32 */
static int
ssh_exec_shell(char *cmd)
{
char *shell = NULL;
pid_t pid;
int status, devnull, rc;
shell = getenv("SHELL");
if (shell == NULL || shell[0] == '\0') {
shell = (char *)"/bin/sh";
}
rc = access(shell, X_OK);
if (rc != 0) {
SSH_LOG(SSH_LOG_WARN, "The shell '%s' is not executable", shell);
return -1;
}
/* Need this to redirect subprocess stdin/out */
devnull = open("/dev/null", O_RDWR);
if (devnull == -1) {
SSH_LOG(SSH_LOG_WARN, "Failed to open(/dev/null): %s", strerror(errno));
return -1;
}
SSH_LOG(SSH_LOG_DEBUG, "Running command '%s'", cmd);
pid = fork();
if (pid == 0) { /* Child */
char *argv[4];
/* Redirect child stdin and stdout. Leave stderr */
rc = dup2(devnull, STDIN_FILENO);
if (rc == -1) {
SSH_LOG(SSH_LOG_WARN, "dup2: %s", strerror(errno));
exit(1);
}
rc = dup2(devnull, STDOUT_FILENO);
if (rc == -1) {
SSH_LOG(SSH_LOG_WARN, "dup2: %s", strerror(errno));
exit(1);
}
if (devnull > STDERR_FILENO) {
close(devnull);
}
argv[0] = shell;
argv[1] = (char *) "-c";
argv[2] = strdup(cmd);
argv[3] = NULL;
rc = execv(argv[0], argv);
if (rc == -1) {
SSH_LOG(SSH_LOG_WARN, "Failed to execute command '%s': %s", cmd,
strerror(errno));
/* Die with signal to make this error apparent to parent. */
signal(SIGTERM, SIG_DFL);
kill(getpid(), SIGTERM);
_exit(1);
}
}
/* Parent */
close(devnull);
if (pid == -1) { /* Error */
SSH_LOG(SSH_LOG_WARN, "Failed to fork child: %s", strerror(errno));
return -1;
}
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s", strerror(errno));
return -1;
}
}
if (!WIFEXITED(status)) {
SSH_LOG(SSH_LOG_WARN, "Command %s exitted abnormally", cmd);
return -1;
}
SSH_LOG(SSH_LOG_TRACE, "Command '%s' returned %d", cmd, WEXITSTATUS(status));
return WEXITSTATUS(status);
}
static int
ssh_match_exec(ssh_session session, const char *command, bool negate)
{
int rv, result = 0;
char *cmd = NULL;
/* TODO There should be more supported expansions */
cmd = ssh_path_expand_escape(session, command);
rv = ssh_exec_shell(cmd);
if (rv > 0 && negate == true) {
result = 1;
} else if (rv == 0 && negate == false) {
result = 1;
}
SSH_LOG(SSH_LOG_TRACE, "%s 'exec' command '%s'%s (rv=%d)",
result == 1 ? "Matched" : "Not matched", cmd,
negate == true ? " (negated)" : "", rv);
free(cmd);
return result;
}
#endif /* _WIN32 */
/* @brief: Parse the ProxyJump configuration line and if parsing,
* stores the result in the configuration option
*/
@ -497,20 +624,22 @@ ssh_config_parse_line(ssh_session session,
break;
case MATCH_EXEC:
/* Skip to the end of line as unsupported */
p = ssh_config_get_cmd(&s);
/* Skip one argument (including in quotes) */
p = ssh_config_get_token(&s);
if (p == NULL || p[0] == '\0') {
SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
"'%s' requires argument", count, p2);
SAFE_FREE(x);
return -1;
}
if (result != 1) {
SSH_LOG(SSH_LOG_INFO, "line %d: Skipped match exec "
"'%s' as previous conditions already failed.",
count, p2);
continue;
}
result &= ssh_match_exec(session, p, negate);
args++;
SSH_LOG(SSH_LOG_WARN,
"line %d: Unsupported Match keyword '%s', ignoring",
count,
p2);
result = 0;
break;
case MATCH_LOCALUSER: