aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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: