Skip to content

Commit

Permalink
session client ssh ch UPDATE add knownhosts API
Browse files Browse the repository at this point in the history
  • Loading branch information
roman authored and michalvasko committed Sep 2, 2024
1 parent 848bdaa commit 81242fe
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 26 deletions.
6 changes: 4 additions & 2 deletions src/session_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,15 @@ static struct nc_client_context context_main = {
.auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
.auth_password = sshauth_password,
.auth_interactive = sshauth_interactive,
.auth_privkey_passphrase = sshauth_privkey_passphrase
.auth_privkey_passphrase = sshauth_privkey_passphrase,
.knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK
},
.ssh_ch_opts = {
.auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
.auth_password = sshauth_password,
.auth_interactive = sshauth_interactive,
.auth_privkey_passphrase = sshauth_privkey_passphrase
.auth_privkey_passphrase = sshauth_privkey_passphrase,
.knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK
},
#endif /* NC_ENABLED_SSH_TLS */
/* .tls_ structures zeroed */
Expand Down
2 changes: 2 additions & 0 deletions src/session_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx);
/**
* @brief Set the behaviour of checking the host key and adding/reading entries to/from the known_hosts file.
*
* The default mode is ::NC_SSH_KNOWNHOSTS_ASK.
*
* @param[in] mode Server host key checking mode.
*/
void nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode);
Expand Down
21 changes: 21 additions & 0 deletions src/session_client_ch.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,27 @@ int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **sess
* @{
*/

/**
* @brief Set SSH Call Home behaviour of checking the host key and adding/reading entries to/from the known_hosts file.
*
* The default mode is ::NC_SSH_KNOWNHOSTS_ASK.
*
* @param[in] mode Server host key checking mode.
*/
void nc_client_ssh_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode);

/**
* @brief Set SSH Call Home path to the known_hosts file for connections.
*
* Repetetive calling replaces the value. If the given file doesn't exist and the process has sufficient
* rights, it gets created whenever the file is needed, otherwise an error occurs. If NULL is passed or the
* path isn't set, the default known_hosts file will be used.
*
* @param[in] path Path to the known_hosts file.
* @return 0 on success, 1 on error.
*/
int nc_client_ssh_ch_set_knownhosts_path(const char *path);

/**
* @brief Set SSH Call Home password authentication callback.
*
Expand Down
102 changes: 79 additions & 23 deletions src/session_client_ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,56 @@ nc_client_ssh_do_dnssec_sshfp_check(ssh_session session, enum ssh_keytypes_e srv

#endif

/**
* @brief Convert knownhosts mode to string.
*
* @param[in] knownhosts_mode Knownhosts mode.
* @return Knownhosts mode string.
*/
static const char *
nc_client_ssh_knownhosts_mode2str(NC_SSH_KNOWNHOSTS_MODE knownhosts_mode)
{
const char *mode_str;

switch (knownhosts_mode) {
case NC_SSH_KNOWNHOSTS_ASK:
mode_str = "ask";
break;
case NC_SSH_KNOWNHOSTS_STRICT:
mode_str = "strict";
break;
case NC_SSH_KNOWNHOSTS_ACCEPT_NEW:
mode_str = "accept-new";
break;
case NC_SSH_KNOWNHOSTS_ACCEPT:
mode_str = "accept";
break;
case NC_SSH_KNOWNHOSTS_SKIP:
mode_str = "skip";
break;
default:
mode_str = "unknown";
break;
}

return mode_str;
}

/**
* @brief Perform the hostkey check.
*
* @param[in] hostname Expected hostname.
* @param[in] port Expected port.
* @param[in] knownhosts_mode Knownhosts mode.
* @param[in] session libssh session.
* @return 0 on success, -1 on error.
*/
static int
nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, ssh_session session)
nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port,
NC_SSH_KNOWNHOSTS_MODE knownhosts_mode, ssh_session session)
{
char *hexa = NULL;
unsigned char *hash_sha1 = NULL;
NC_SSH_KNOWNHOSTS_MODE knownhosts_mode = ssh_opts.knownhosts_mode;
enum ssh_keytypes_e srv_pubkey_type;
int state;

Expand All @@ -420,6 +464,8 @@ nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, ssh_sessio
int dnssec_ret;
#endif

VRB(NULL, "Server hostkey check mode: %s.", nc_client_ssh_knownhosts_mode2str(knownhosts_mode));

if (knownhosts_mode == NC_SSH_KNOWNHOSTS_SKIP) {
/* skip all hostkey checks */
return 0;
Expand Down Expand Up @@ -744,28 +790,46 @@ sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv))
#endif
}

API int
nc_client_ssh_set_knownhosts_path(const char *path)
static int
_nc_client_ssh_set_knownhosts_path(const char *path, struct nc_client_ssh_opts *opts)
{
free(ssh_opts.knownhosts_path);
free(opts->knownhosts_path);

if (!path) {
ssh_opts.knownhosts_path = NULL;
opts->knownhosts_path = NULL;
return 0;
}

ssh_opts.knownhosts_path = strdup(path);
NC_CHECK_ERRMEM_RET(!ssh_opts.knownhosts_path, 1);
opts->knownhosts_path = strdup(path);
NC_CHECK_ERRMEM_RET(!opts->knownhosts_path, 1);

return 0;
}

API int
nc_client_ssh_set_knownhosts_path(const char *path)
{
return _nc_client_ssh_set_knownhosts_path(path, &ssh_opts);
}

API int
nc_client_ssh_ch_set_knownhosts_path(const char *path)
{
return _nc_client_ssh_set_knownhosts_path(path, &ssh_ch_opts);
}

API void
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode)
{
ssh_opts.knownhosts_mode = mode;
}

API void
nc_client_ssh_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode)
{
ssh_ch_opts.knownhosts_mode = mode;
}

static void
_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
void *priv, struct nc_client_ssh_opts *opts)
Expand Down Expand Up @@ -1254,7 +1318,7 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts,
return -1;
}

if (nc_client_ssh_auth_hostkey_check(session->host, session->port, ssh_sess)) {
if (nc_client_ssh_auth_hostkey_check(session->host, session->port, opts->knownhosts_mode, ssh_sess)) {
ERR(session, "Checking the host key failed.");
return -1;
}
Expand Down Expand Up @@ -1701,7 +1765,6 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
struct nc_session *session = NULL;
char *buf = NULL;
size_t buf_len = 0;
char *known_hosts_path = NULL;

/* process parameters */
if (!host || (host[0] == '\0')) {
Expand All @@ -1727,15 +1790,6 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
pw = nc_getpw(0, username, &pw_buf, &buf, &buf_len);
}

if (ssh_opts.knownhosts_path) {
/* known_hosts file path was set so use it */
known_hosts_path = strdup(ssh_opts.knownhosts_path);
NC_CHECK_ERRMEM_GOTO(!known_hosts_path, , fail);
} else if (pw) {
/* path not set explicitly, but current user's username found in /etc/passwd, so create the path */
NC_CHECK_ERRMEM_GOTO(asprintf(&known_hosts_path, "%s/.ssh/known_hosts", pw->pw_dir) == -1, , fail);
}

/* prepare session structure */
session = nc_new_session(NC_CLIENT, 0);
NC_CHECK_ERRMEM_GOTO(!session, , fail);
Expand All @@ -1757,8 +1811,8 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
if (known_hosts_path) {
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_path);
if (ssh_opts.knownhosts_path) {
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, ssh_opts.knownhosts_path);
}

/* create and assign communication socket */
Expand Down Expand Up @@ -1800,12 +1854,10 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
session->port = port;

free(buf);
free(known_hosts_path);
return session;

fail:
free(buf);
free(known_hosts_path);
free(ip_host);
nc_session_free(session, NULL);
return NULL;
Expand Down Expand Up @@ -1922,6 +1974,10 @@ nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly
ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
}

if (ssh_ch_opts.knownhosts_path) {
ssh_options_set(sess, SSH_OPTIONS_KNOWNHOSTS, ssh_ch_opts.knownhosts_path);
}

ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
"ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
Expand Down
2 changes: 1 addition & 1 deletion tests/test_ch.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ client_thread_ssh(void *arg)
struct ln2_test_ctx *test_ctx = arg;

/* skip all hostkey and known_hosts checks */
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
nc_client_ssh_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);

/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
Expand Down

0 comments on commit 81242fe

Please sign in to comment.