aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2014-08-03 22:29:49 +0200
committerAndreas Schneider <asn@cryptomilk.org>2017-12-15 12:00:49 +0100
commitf818e63f8f3efaea3daf737d280450209c806541 (patch)
tree4b0067b810f9c67d4d4f88f80f18a35a72acb55d
parent094aa5eb024582d23b8e0cd5ebfea3cb29ed188d (diff)
downloadlibssh-f818e63f8f3efaea3daf737d280450209c806541.tar.gz
libssh-f818e63f8f3efaea3daf737d280450209c806541.tar.xz
libssh-f818e63f8f3efaea3daf737d280450209c806541.zip
Add new options
Pair-Programmed-With: Jakub Jelen <jjelen@redhat.com> Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be> Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r--include/libssh/libssh.h5
-rw-r--r--include/libssh/session.h9
-rw-r--r--src/auth.c5
-rw-r--r--src/config.c194
-rw-r--r--src/known_hosts.c43
-rw-r--r--src/misc.c13
-rw-r--r--src/options.c61
-rw-r--r--src/session.c3
-rw-r--r--tests/unittests/torture_options.c29
9 files changed, 343 insertions, 19 deletions
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 504a645a..be4b35c2 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -351,6 +351,11 @@ enum ssh_options_e {
SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
SSH_OPTIONS_HMAC_C_S,
SSH_OPTIONS_HMAC_S_C,
+ SSH_OPTIONS_PASSWORD_AUTH,
+ SSH_OPTIONS_PUBKEY_AUTH,
+ SSH_OPTIONS_KBDINT_AUTH,
+ SSH_OPTIONS_GSSAPI_AUTH,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS
};
enum {
diff --git a/include/libssh/session.h b/include/libssh/session.h
index 60d78578..69a78dc4 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -79,6 +79,13 @@ enum ssh_pending_call_e {
/* Don't block at all */
#define SSH_TIMEOUT_NONBLOCKING 0
+/* options flags */
+/* Authentication with *** allowed */
+#define SSH_OPT_FLAG_PASSWORD_AUTH 0x1
+#define SSH_OPT_FLAG_PUBKEY_AUTH 0x2
+#define SSH_OPT_FLAG_KBDINT_AUTH 0x4
+#define SSH_OPT_FLAG_GSSAPI_AUTH 0x8
+
/* members that are common to ssh_session and ssh_bind */
struct ssh_common_struct {
struct error_struct error;
@@ -182,6 +189,7 @@ struct ssh_session_struct {
char *bindaddr; /* bind the client to an ip addr */
char *sshdir;
char *knownhosts;
+ char *global_knownhosts;
char *wanted_methods[10];
char *ProxyCommand;
char *custombanner;
@@ -196,6 +204,7 @@ struct ssh_session_struct {
char *gss_server_identity;
char *gss_client_identity;
int gss_delegate_creds;
+ int flags;
} opts;
/* counters */
ssh_counter socket_counter;
diff --git a/src/auth.c b/src/auth.c
index 59b6f134..bb038614 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -921,7 +921,10 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (session == NULL) {
return SSH_AUTH_ERROR;
}
-
+ if (! (session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH)) {
+ session->auth_methods &= ~SSH_AUTH_METHOD_PUBLICKEY;
+ return SSH_AUTH_DENIED;
+ }
if (session->common.callbacks) {
auth_fn = session->common.callbacks->auth_function;
auth_data = session->common.callbacks->userdata;
diff --git a/src/config.c b/src/config.c
index 25d64998..221f8906 100644
--- a/src/config.c
+++ b/src/config.c
@@ -36,8 +36,14 @@
#define MAX_LINE_SIZE 1024
enum ssh_config_opcode_e {
+ /* Unknown opcode */
+ SOC_UNKNOWN = -3,
+ /* Known and not applicable to libssh */
+ SOC_NA = -2,
+ /* Known but not supported by current libssh version */
SOC_UNSUPPORTED = -1,
SOC_HOST,
+ SOC_MATCH,
SOC_HOSTNAME,
SOC_PORT,
SOC_USERNAME,
@@ -53,6 +59,17 @@ enum ssh_config_opcode_e {
SOC_GSSAPICLIENTIDENTITY,
SOC_GSSAPIDELEGATECREDENTIALS,
SOC_INCLUDE,
+ SOC_BINDADDRESS,
+ SOC_CONNECTTIMEOUT,
+ SOC_GLOBALKNOWNHOSTSFILE,
+ SOC_LOGLEVEL,
+ SOC_HOSTKEYALGORITHMS,
+ SOC_KEXALGORITHMS,
+ SOC_MAC,
+ SOC_GSSAPIAUTHENTICATION,
+ SOC_KBDINTERACTIVEAUTHENTICATION,
+ SOC_PASSWORDAUTHENTICATION,
+ SOC_PUBKEYAUTHENTICATION,
SOC_END /* Keep this one last in the list */
};
@@ -64,6 +81,7 @@ struct ssh_config_keyword_table_s {
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "host", SOC_HOST },
+ { "match", SOC_MATCH },
{ "hostname", SOC_HOSTNAME },
{ "port", SOC_PORT },
{ "user", SOC_USERNAME },
@@ -79,7 +97,76 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "gssapiserveridentity", SOC_GSSAPICLIENTIDENTITY },
{ "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS },
{ "include", SOC_INCLUDE },
- { NULL, SOC_UNSUPPORTED }
+ { "bindaddress", SOC_BINDADDRESS},
+ { "connecttimeout", SOC_CONNECTTIMEOUT},
+ { "globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE},
+ { "loglevel", SOC_LOGLEVEL},
+ { "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS},
+ { "kexalgorithms", SOC_KEXALGORITHMS},
+ { "mac", SOC_MAC},
+ { "gssapiauthentication", SOC_GSSAPIAUTHENTICATION},
+ { "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION},
+ { "passwordauthentication", SOC_PASSWORDAUTHENTICATION},
+ { "pubkeyauthentication", SOC_PUBKEYAUTHENTICATION},
+ { "addressfamily", SOC_UNSUPPORTED},
+ { "batchmode", SOC_UNSUPPORTED},
+ { "canonicaldomains", SOC_UNSUPPORTED},
+ { "canonicalizefallbacklocal", SOC_UNSUPPORTED},
+ { "canonicalizehostname", SOC_UNSUPPORTED},
+ { "canonicalizemaxdots", SOC_UNSUPPORTED},
+ { "canonicalizepermittedcnames", SOC_UNSUPPORTED},
+ { "challengeresponseauthentication", SOC_UNSUPPORTED},
+ { "checkhostip", SOC_UNSUPPORTED},
+ { "cipher", SOC_UNSUPPORTED},
+ { "compressionlevel", SOC_UNSUPPORTED},
+ { "connectionattempts", SOC_UNSUPPORTED},
+ { "enablesshkeysign", SOC_UNSUPPORTED},
+ { "forwardagent", SOC_UNSUPPORTED},
+ { "gssapikeyexchange", SOC_UNSUPPORTED},
+ { "gssapirenewalforcesrekey", SOC_UNSUPPORTED},
+ { "gssapitrustdns", SOC_UNSUPPORTED},
+ { "hashknownhosts", SOC_UNSUPPORTED},
+ { "hostbasedauthentication", SOC_UNSUPPORTED},
+ { "hostkeyalias", SOC_UNSUPPORTED},
+ { "identitiesonly", SOC_UNSUPPORTED},
+ { "ipqos", SOC_UNSUPPORTED},
+ { "kbdinteractivedevices", SOC_UNSUPPORTED},
+ { "nohostauthenticationforlocalhost", SOC_UNSUPPORTED},
+ { "numberofpasswordprompts", SOC_UNSUPPORTED},
+ { "pkcs11provider", SOC_UNSUPPORTED},
+ { "preferredauthentications", SOC_UNSUPPORTED},
+ { "proxyusefdpass", SOC_UNSUPPORTED},
+ { "rekeylimit", SOC_UNSUPPORTED},
+ { "rhostsrsaauthentication", SOC_UNSUPPORTED},
+ { "rsaauthentication", SOC_UNSUPPORTED},
+ { "serveralivecountmax", SOC_UNSUPPORTED},
+ { "serveraliveinterval", SOC_UNSUPPORTED},
+ { "tcpkeepalive", SOC_UNSUPPORTED},
+ { "useprivilegedport", SOC_UNSUPPORTED},
+ { "verifyhostkeydns", SOC_UNSUPPORTED},
+ { "visualhostkey", SOC_UNSUPPORTED},
+ { "clearallforwardings", SOC_NA},
+ { "controlmaster", SOC_NA},
+ { "controlpersist", SOC_NA},
+ { "controlpath", SOC_NA},
+ { "dynamicforward", SOC_NA},
+ { "escapechar", SOC_NA},
+ { "exitonforwardfailure", SOC_NA},
+ { "forwardx11", SOC_NA},
+ { "forwardx11timeout", SOC_NA},
+ { "forwardx11trusted", SOC_NA},
+ { "gatewayports", SOC_NA},
+ { "ignoreunknown", SOC_NA},
+ { "localcommand", SOC_NA},
+ { "localforward", SOC_NA},
+ { "permitlocalcommand", SOC_NA},
+ { "remoteforward", SOC_NA},
+ { "requesttty", SOC_NA},
+ { "sendenv", SOC_NA},
+ { "tunnel", SOC_NA},
+ { "tunneldevice", SOC_NA},
+ { "xauthlocation", SOC_NA},
+ { NULL, SOC_UNKNOWN }
};
static int ssh_config_parse_line(ssh_session session, const char *line,
@@ -94,7 +181,7 @@ static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
}
}
- return SOC_UNSUPPORTED;
+ return SOC_UNKNOWN;
}
static char *ssh_config_get_cmd(char **str) {
@@ -297,13 +384,11 @@ static int ssh_config_parse_line(ssh_session session, const char *line,
}
break;
case SOC_PORT:
- if (session->opts.port == 0) {
- p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
- }
- }
- break;
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
+ }
+ break;
case SOC_USERNAME:
if (session->opts.username == NULL) {
p = ssh_config_get_str_tok(&s, NULL);
@@ -408,10 +493,101 @@ static int ssh_config_parse_line(ssh_session session, const char *line,
ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
}
break;
+ case SOC_BINDADDRESS:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_BINDADDR, p);
+ }
+ break;
+ case SOC_CONNECTTIMEOUT:
+ i = ssh_config_get_int(&s, 0);
+ if (i >= 0 && *parsing) {
+ long t = i;
+ ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &t);
+ }
+ break;
+ case SOC_GLOBALKNOWNHOSTSFILE:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, p);
+ }
+ break;
+ case SOC_LOGLEVEL:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ if (strcasecmp(p, "quiet") == 0) {
+ ssh_set_log_level(SSH_LOG_NONE);
+ } else if (strcasecmp(p, "fatal") == 0 ||
+ strcasecmp(p, "error")== 0 ||
+ strcasecmp(p, "info") == 0) {
+ ssh_set_log_level(SSH_LOG_WARN);
+ } else if (strcasecmp(p, "verbose") == 0) {
+ ssh_set_log_level(SSH_LOG_INFO);
+ } else if (strcasecmp(p, "DEBUG") == 0 ||
+ strcasecmp(p, "DEBUG1") == 0) {
+ ssh_set_log_level(SSH_LOG_DEBUG);
+ } else if (strcasecmp(p, "DEBUG2") == 0 ||
+ strcasecmp(p, "DEBUG3") == 0) {
+ ssh_set_log_level(SSH_LOG_TRACE);
+ }
+ }
+ break;
+ case SOC_HOSTKEYALGORITHMS:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
+ }
+ break;
+ case SOC_KEXALGORITHMS:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, p);
+ }
+ break;
+ case SOC_MAC:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
+ ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
+ }
+ break;
+ case SOC_GSSAPIAUTHENTICATION:
+ case SOC_KBDINTERACTIVEAUTHENTICATION:
+ case SOC_PASSWORDAUTHENTICATION:
+ case SOC_PUBKEYAUTHENTICATION:
+ i = ssh_config_get_yesno(&s, 0);
+ if (i>=0 && *parsing) {
+ switch(opcode){
+ case SOC_GSSAPIAUTHENTICATION:
+ ssh_options_set(session, SSH_OPTIONS_GSSAPI_AUTH, &i);
+ break;
+ case SOC_KBDINTERACTIVEAUTHENTICATION:
+ ssh_options_set(session, SSH_OPTIONS_KBDINT_AUTH, &i);
+ break;
+ case SOC_PASSWORDAUTHENTICATION:
+ ssh_options_set(session, SSH_OPTIONS_PASSWORD_AUTH, &i);
+ break;
+ case SOC_PUBKEYAUTHENTICATION:
+ ssh_options_set(session, SSH_OPTIONS_PUBKEY_AUTH, &i);
+ break;
+ /* make gcc happy */
+ default:
+ break;
+ }
+ }
+ break;
+ case SOC_NA:
+ SSH_LOG(SSH_LOG_INFO, "Unapplicable option: %s, line: %d\n",
+ keyword, count);
+ break;
case SOC_UNSUPPORTED:
SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d",
keyword, count);
break;
+ case SOC_UNKNOWN:
+ SSH_LOG(SSH_LOG_WARN, "Unknown option: %s, line: %d\n",
+ keyword, count);
+ break;
default:
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
opcode);
diff --git a/src/known_hosts.c b/src/known_hosts.c
index 2f66cc27..a6bb32f1 100644
--- a/src/known_hosts.c
+++ b/src/known_hosts.c
@@ -410,6 +410,8 @@ int ssh_is_server_known(ssh_session session) {
char *hostport;
const char *type;
int match;
+ int i=0;
+ char * files[3];
int ret = SSH_SERVER_NOT_KNOWN;
if (session->opts.knownhosts == NULL) {
@@ -444,14 +446,27 @@ int ssh_is_server_known(ssh_session session) {
return SSH_SERVER_ERROR;
}
+ /* set the list of known hosts */
+ i = 0;
+ if (session->opts.global_knownhosts != NULL){
+ files[i++]=session->opts.global_knownhosts;
+ }
+ files[i++] = session->opts.knownhosts;
+ files[i] = NULL;
+ i = 0;
+
do {
tokens = ssh_get_knownhost_line(&file,
- session->opts.knownhosts,
+ files[i],
&type);
- /* End of file, return the current state */
+ /* End of file, return the current state or use next file */
if (tokens == NULL) {
- break;
+ ++i;
+ if(files[i] == NULL)
+ break;
+ else
+ continue;
}
match = match_hashed_host(host, tokens[0]);
if (match == 0){
@@ -694,7 +709,8 @@ char **ssh_knownhosts_algorithms(ssh_session session) {
const char *type;
int match;
char **array;
- int i=0, j;
+ char *files[3] = { NULL };
+ int i=0, j, k;
if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) {
@@ -720,13 +736,26 @@ char **ssh_knownhosts_algorithms(ssh_session session) {
return NULL;
}
+ /* set the list of known hosts */
+ if (session->opts.global_knownhosts != NULL){
+ files[i++]=session->opts.global_knownhosts;
+ }
+ files[i++] = session->opts.knownhosts;
+ files[i] = NULL;
+ k = 0;
+ i = 0;
+
do {
- tokens = ssh_get_knownhost_line(&file,
- session->opts.knownhosts, &type);
+ tokens = ssh_get_knownhost_line(&file, files[k], &type);
/* End of file, return the current state */
if (tokens == NULL) {
- break;
+ ++k;
+ if (files[k] == NULL) {
+ break;
+ } else {
+ continue;
+ }
}
match = match_hashed_host(host, tokens[0]);
if (match == 0){
diff --git a/src/misc.c b/src/misc.c
index 5b260b15..fad33294 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -681,6 +681,17 @@ char *ssh_path_expand_tilde(const char *d) {
return r;
}
+/** @internal
+ * @brief expands a string in function of session options
+ * @param[in] s Format string to expand. Known parameters:
+ * %d SSH configuration directory (~/.ssh)
+ * %h target host name
+ * %u local username
+ * %l local hostname
+ * %r remote username
+ * %p remote port
+ * @returns Expanded string.
+ */
char *ssh_path_expand_escape(ssh_session session, const char *s) {
char host[NI_MAXHOST];
char buf[MAX_BUF_SIZE];
@@ -971,7 +982,7 @@ int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
* -2 means user-defined timeout as available in
* session->timeout, session->timeout_usec.
*/
- fprintf(stderr, "ssh_timeout_elapsed called with -2. this needs to "
+ SSH_LOG(SSH_LOG_WARN, "ssh_timeout_elapsed called with -2. this needs to "
"be fixed. please set a breakpoint on %s:%d and "
"fix the caller\n", __FILE__, __LINE__);
return 0;
diff --git a/src/options.c b/src/options.c
index 9346b6d1..0bc15497 100644
--- a/src/options.c
+++ b/src/options.c
@@ -374,6 +374,28 @@ int ssh_options_set_algo(ssh_session session,
* Set it to specify that GSSAPI should delegate credentials
* to the server (int, 0 = false).
*
+ * - SSH_OPTIONS_PASSWORD_AUTH
+ * Set it if password authentication should be used
+ * in ssh_userauth_auto_pubkey(). (int, 0=false).
+ * Currently without effect (ssh_userauth_auto_pubkey doesn't use
+ * password authentication).
+ *
+ * - SSH_OPTIONS_PUBKEY_AUTH
+ * Set it if pubkey authentication should be used
+ * in ssh_userauth_auto_pubkey(). (int, 0=false).
+ *
+ * - SSH_OPTIONS_KBDINT_AUTH
+ * Set it if keyboard-interactive authentication should be used
+ * in ssh_userauth_auto_pubkey(). (int, 0=false).
+ * Currently without effect (ssh_userauth_auto_pubkey doesn't use
+ * keyboard-interactive authentication).
+ *
+ * - SSH_OPTIONS_GSSAPI_AUTH
+ * Set it if gssapi authentication should be used
+ * in ssh_userauth_auto_pubkey(). (int, 0=false).
+ * Currently without effect (ssh_userauth_auto_pubkey doesn't use
+ * gssapi authentication).
+ *
* @param value The value to set. This is a generic pointer and the
* datatype which is used should be set according to the
* type set.
@@ -385,6 +407,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
const char *v;
char *p, *q;
long int i;
+ unsigned int u;
int rc;
if (session == NULL) {
@@ -574,6 +597,20 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
}
}
break;
+ case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
+ v = value;
+ SAFE_FREE(session->opts.global_knownhosts);
+ if (v == NULL || v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ session->opts.global_knownhosts = strdup(v);
+ if (session->opts.global_knownhosts == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ }
+ break;
case SSH_OPTIONS_TIMEOUT:
if (value == NULL) {
ssh_set_error_invalid(session);
@@ -858,6 +895,30 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.gss_delegate_creds = (x & 0xff);
}
break;
+ case SSH_OPTIONS_PASSWORD_AUTH:
+ case SSH_OPTIONS_PUBKEY_AUTH:
+ case SSH_OPTIONS_KBDINT_AUTH:
+ case SSH_OPTIONS_GSSAPI_AUTH:
+ u = 0;
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ int x = *(int *)value;
+ u = type == SSH_OPTIONS_PASSWORD_AUTH ?
+ SSH_OPT_FLAG_PASSWORD_AUTH:
+ type == SSH_OPTIONS_PUBKEY_AUTH ?
+ SSH_OPT_FLAG_PUBKEY_AUTH:
+ type == SSH_OPTIONS_KBDINT_AUTH ?
+ SSH_OPT_FLAG_KBDINT_AUTH:
+ SSH_OPT_FLAG_GSSAPI_AUTH;
+ if (x != 0){
+ session->opts.flags |= u;
+ } else {
+ session->opts.flags &= ~u;
+ }
+ }
+ break;
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
diff --git a/src/session.c b/src/session.c
index 60c72b92..bb01f169 100644
--- a/src/session.c
+++ b/src/session.c
@@ -112,7 +112,8 @@ ssh_session ssh_new(void) {
#else
session->opts.ssh1 = 0;
#endif
-
+ session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH | SSH_OPT_FLAG_PUBKEY_AUTH |
+ SSH_OPT_FLAG_KBDINT_AUTH | SSH_OPT_FLAG_GSSAPI_AUTH;
session->opts.identity = ssh_list_new();
if (session->opts.identity == NULL) {
goto err;
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index f3197b8f..c2d6d4f9 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -239,6 +239,34 @@ static void torture_options_proxycommand(void **state) {
assert_null(session->opts.ProxyCommand);
}
+static void torture_options_config_host(void **state) {
+ ssh_session session = *state;
+ FILE *config = NULL;
+
+ /* create a new config file */
+ config = fopen("test_config", "w");
+ assert_non_null(config);
+ fputs("Host testhost1\nPort 42\nHost testhost2,testhost3\nPort 43\n", config);
+ fclose(config);
+
+ ssh_options_set(session, SSH_OPTIONS_HOST, "testhost1");
+ ssh_options_parse_config(session, "test_config");
+
+ assert_int_equal(session->opts.port, 42);
+
+ ssh_options_set(session, SSH_OPTIONS_HOST, "testhost2");
+ ssh_options_parse_config(session, "test_config");
+ assert_int_equal(session->opts.port, 43);
+
+ session->opts.port = 0;
+
+ ssh_options_set(session, SSH_OPTIONS_HOST, "testhost3");
+ ssh_options_parse_config(session, "test_config");
+ assert_int_equal(session->opts.port, 43);
+
+ unlink("test_config");
+}
+
#ifdef WITH_SERVER
/* sshbind options */
@@ -304,6 +332,7 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_options_proxycommand, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_set_ciphers, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_set_macs, setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_config_host, setup, teardown)
};
#ifdef WITH_SERVER