From f7f11b39108edf4cddf975f5ed9349ee33d443ac Mon Sep 17 00:00:00 2001 From: Aris Adamantiadis Date: Sun, 3 Aug 2014 22:29:49 +0200 Subject: Add new options --- include/libssh/libssh.h | 5 + include/libssh/session.h | 9 ++ src/auth.c | 5 +- src/config.c | 192 ++++++++++++++++++++++++++++++++++++-- src/known_hosts.c | 41 ++++++-- src/match.c | 1 + src/misc.c | 14 ++- src/options.c | 61 ++++++++++++ src/session.c | 3 +- tests/unittests/torture_options.c | 29 ++++++ 10 files changed, 343 insertions(+), 17 deletions(-) diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index f4e4fe93..afe32977 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -345,6 +345,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 29bdd60b..72c588e1 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 1 +#define SSH_OPT_FLAG_PUBKEY_AUTH 2 +#define SSH_OPT_FLAG_KBDINT_AUTH 4 +#define SSH_OPT_FLAG_GSSAPI_AUTH 8 + /* 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 d56111d2..5133bd70 100644 --- a/src/auth.c +++ b/src/auth.c @@ -897,7 +897,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 0d44c215..a26ee47f 100644 --- a/src/config.c +++ b/src/config.c @@ -34,8 +34,14 @@ #include "libssh/options.h" 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, @@ -50,6 +56,17 @@ enum ssh_config_opcode_e { SOC_GSSAPISERVERIDENTITY, SOC_GSSAPICLIENTIDENTITY, SOC_GSSAPIDELEGATECREDENTIALS, + SOC_BINDADDRESS, + SOC_CONNECTTIMEOUT, + SOC_GLOBALKNOWNHOSTSFILE, + SOC_LOGLEVEL, + SOC_HOSTKEYALGORITHMS, + SOC_KEXALGORITHMS, + SOC_MAC, + SOC_GSSAPIAUTHENTICATION, + SOC_KBDINTERACTIVEAUTHENTICATION, + SOC_PASSWORDAUTHENTICATION, + SOC_PUBKEYAUTHENTICATION, }; struct ssh_config_keyword_table_s { @@ -59,6 +76,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 }, @@ -73,7 +91,76 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = { { "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY }, { "gssapiserveridentity", SOC_GSSAPICLIENTIDENTITY }, { "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS }, - { 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 enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) { @@ -85,7 +172,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) { @@ -245,12 +332,10 @@ 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); - } - } + 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) { @@ -356,10 +441,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\n", 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\n", opcode); diff --git a/src/known_hosts.c b/src/known_hosts.c index f7828d5f..1e8de54a 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){ @@ -667,7 +682,8 @@ char **ssh_knownhosts_algorithms(ssh_session session) { const char *type; int match; char **array; - int i=0, j; + char *files[3]; + int i=0, j, k; if (session->opts.knownhosts == NULL) { if (ssh_options_apply(session) < 0) { @@ -693,13 +709,26 @@ char **ssh_knownhosts_algorithms(ssh_session session) { return NULL; } + /* 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; + k = 0; + do { tokens = ssh_get_knownhost_line(&file, - session->opts.knownhosts, &type); + 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/match.c b/src/match.c index 53620bdd..36cc922a 100644 --- a/src/match.c +++ b/src/match.c @@ -109,6 +109,7 @@ static int match_pattern(const char *s, const char *pattern) { } /* NOTREACHED */ + return 0; } /* diff --git a/src/misc.c b/src/misc.c index 6daf60ab..6afc81d2 100644 --- a/src/misc.c +++ b/src/misc.c @@ -696,6 +696,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]; @@ -967,9 +978,10 @@ 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; case -1: /* -1 means infinite timeout */ return 0; case 0: /* 0 means no timeout */ diff --git a/src/options.c b/src/options.c index 2b8abb48..16fcc7aa 100644 --- a/src/options.c +++ b/src/options.c @@ -379,6 +379,28 @@ int ssh_options_set_algo(ssh_session session, int algo, * 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. @@ -390,6 +412,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) { @@ -579,6 +602,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); @@ -863,6 +900,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 63364c51..d2a9ef29 100644 --- a/src/session.c +++ b/src/session.c @@ -109,7 +109,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 6f5df1bb..471c387a 100644 --- a/tests/unittests/torture_options.c +++ b/tests/unittests/torture_options.c @@ -193,6 +193,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; + int rc; + + /* 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); + +} + int torture_run_tests(void) { int rc; const UnitTest tests[] = { @@ -206,6 +234,7 @@ int torture_run_tests(void) { unit_test_setup_teardown(torture_options_set_identity, setup, teardown), unit_test_setup_teardown(torture_options_get_identity, setup, teardown), unit_test_setup_teardown(torture_options_proxycommand, setup, teardown), + unit_test_setup_teardown(torture_options_config_host, setup, teardown) }; ssh_init(); -- cgit v1.2.3