Implement originalhost/host separation and Match support

Signed-off-by: Rui Li <ruili3422@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Rui Li
2026-03-10 17:28:31 -07:00
committed by Jakub Jelen
parent 1ab8a35c5d
commit a2ebc7ea9b
6 changed files with 82 additions and 20 deletions

View File

@@ -286,6 +286,9 @@ struct ssh_session_struct {
int control_master;
char *control_path;
int address_family;
char *originalhost; /* user-supplied host for config matching */
bool config_hostname_only; /* config hostname path: update host only,
not originalhost */
} opts;
/* server options */

View File

@@ -582,9 +582,8 @@ int ssh_connect(ssh_session session)
session->client = 1;
if (session->opts.fd == SSH_INVALID_SOCKET &&
session->opts.host == NULL &&
session->opts.ProxyCommand == NULL)
{
session->opts.originalhost == NULL &&
session->opts.ProxyCommand == NULL) {
ssh_set_error(session, SSH_FATAL, "Hostname required");
return SSH_ERROR;
}

View File

@@ -567,7 +567,12 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} else if (parse_entry) {
/* We actually care only about the first item */
rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false, false);
rv = ssh_config_parse_uri(cp,
&username,
&hostname,
&port,
false,
false);
if (rv != SSH_OK) {
ssh_set_error_invalid(session);
goto out;
@@ -1041,20 +1046,20 @@ static int ssh_config_parse_line_internal(ssh_session session,
break;
case MATCH_ORIGINALHOST:
/* Skip one argument */
/* Here we match only one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument", count, p2);
ssh_set_error(session,
SSH_FATAL,
"line %d: ERROR - Match originalhost keyword "
"requires argument",
count);
SAFE_FREE(x);
return -1;
}
result &=
ssh_config_match(session->opts.originalhost, p, negate);
args++;
SSH_LOG(SSH_LOG_TRACE,
"line %d: Unsupported Match keyword '%s', ignoring",
count,
p2);
result = 0;
break;
case MATCH_HOST:
@@ -1067,7 +1072,11 @@ static int ssh_config_parse_line_internal(ssh_session session,
SAFE_FREE(x);
return -1;
}
result &= ssh_config_match(session->opts.host, p, negate);
result &= ssh_config_match(session->opts.host
? session->opts.host
: session->opts.originalhost,
p,
negate);
args++;
break;
@@ -1155,7 +1164,9 @@ static int ssh_config_parse_line_internal(ssh_session session,
int ok = 0, result = -1;
*parsing = 0;
lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL;
lowerhost = (session->opts.originalhost)
? ssh_lowercase(session->opts.originalhost)
: NULL;
for (p = ssh_config_get_str_tok(&s, NULL);
p != NULL && p[0] != '\0';
p = ssh_config_get_str_tok(&s, NULL)) {
@@ -1182,7 +1193,9 @@ static int ssh_config_parse_line_internal(ssh_session session,
if (z == NULL) {
z = strdup(p);
}
session->opts.config_hostname_only = true;
ssh_options_set(session, SSH_OPTIONS_HOST, z);
session->opts.config_hostname_only = false;
free(z);
}
break;

View File

@@ -1432,6 +1432,8 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
case 'h':
if (session->opts.host) {
x = strdup(session->opts.host);
} else if (session->opts.originalhost) {
x = strdup(session->opts.originalhost);
} else {
ssh_set_error(session, SSH_FATAL, "Cannot expand host");
free(buf);

View File

@@ -23,6 +23,7 @@
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -106,6 +107,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
}
if (src->opts.originalhost != NULL) {
new->opts.originalhost = strdup(src->opts.originalhost);
if (new->opts.originalhost == NULL) {
ssh_free(new);
return -1;
}
}
if (src->opts.bindaddr != NULL) {
new->opts.bindaddr = strdup(src->opts.bindaddr);
if (new->opts.bindaddr == NULL) {
@@ -718,18 +727,52 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
return -1;
} else {
char *username = NULL, *hostname = NULL;
rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true, true);
if (rc != SSH_OK) {
char *strict_hostname = NULL;
/* Non-strict parse: reject shell metacharacters */
rc = ssh_config_parse_uri(value,
&username,
&hostname,
NULL,
true,
false);
if (rc != SSH_OK || hostname == NULL) {
SAFE_FREE(username);
SAFE_FREE(hostname);
ssh_set_error_invalid(session);
return -1;
}
/* Non-strict passed: set username and originalhost */
if (username != NULL) {
SAFE_FREE(session->opts.username);
session->opts.username = username;
}
if (hostname != NULL) {
if (!session->opts.config_hostname_only) {
SAFE_FREE(session->opts.originalhost);
session->opts.originalhost = hostname;
} else {
SAFE_FREE(hostname);
}
/* Strict parse: set host only if valid hostname or IP */
rc = ssh_config_parse_uri(value,
NULL,
&strict_hostname,
NULL,
true,
true);
if (rc != SSH_OK || strict_hostname == NULL) {
SAFE_FREE(session->opts.host);
session->opts.host = hostname;
SAFE_FREE(strict_hostname);
if (session->opts.config_hostname_only) {
/* Config path: Hostname must be valid */
ssh_set_error_invalid(session);
return -1;
}
} else {
SAFE_FREE(session->opts.host);
session->opts.host = strict_hostname;
}
}
break;
@@ -1646,7 +1689,8 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
switch(type)
{
case SSH_OPTIONS_HOST:
src = session->opts.host;
src = session->opts.host ? session->opts.host
: session->opts.originalhost;
break;
case SSH_OPTIONS_USER:
@@ -1980,7 +2024,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
if (session == NULL) {
return -1;
}
if (session->opts.host == NULL) {
if (session->opts.originalhost == NULL) {
ssh_set_error_invalid(session);
return -1;
}

View File

@@ -406,6 +406,7 @@ void ssh_free(ssh_session session)
SAFE_FREE(session->opts.bindaddr);
SAFE_FREE(session->opts.username);
SAFE_FREE(session->opts.host);
SAFE_FREE(session->opts.originalhost);
SAFE_FREE(session->opts.homedir);
SAFE_FREE(session->opts.sshdir);
SAFE_FREE(session->opts.knownhosts);