summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Jelen <jjelen@redhat.com>2019-09-16 15:01:43 +0200
committerJakub Jelen <jjelen@redhat.com>2019-10-01 10:24:01 +0200
commitc983c994a2dee1af5d6abb5f9843be57ddc3d505 (patch)
tree6983872f247373dd7759224bdf44f2144b1c46dd
parentc7da113f1d27fb74f3b15ef6d62e5e421aa7d197 (diff)
downloadlibssh-c983c994a2dee1af5d6abb5f9843be57ddc3d505.tar.gz
libssh-c983c994a2dee1af5d6abb5f9843be57ddc3d505.tar.xz
libssh-c983c994a2dee1af5d6abb5f9843be57ddc3d505.zip
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>
-rw-r--r--src/config.c143
1 files changed, 136 insertions, 7 deletions
diff --git a/src/config.c b/src/config.c
index 2b813dcb..2ba21a20 100644
--- a/src/config.c
+++ b/src/config.c
@@ -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: