mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-28 07:13:54 +09:00
- Add a warning to ssh_bind_listen() clarifying that it implicitly calls ssh_bind_options_parse_config(), which may override options previously set via ssh_bind_options_set(). - Add a warning to ssh_bind_options_set() and ssh_bind_config_parse_string() explaining that options may be overridden if configuration files are parsed afterwards, either implicitly via ssh_bind_listen() or by an explicit call to ssh_bind_options_parse_config(). Signed-off-by: Francesco <eferollo@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2978 lines
102 KiB
C
2978 lines
102 KiB
C
/*
|
||
* options.c - handle pre-connection options
|
||
*
|
||
* This file is part of the SSH Library
|
||
*
|
||
* Copyright (c) 2003-2008 by Aris Adamantiadis
|
||
* Copyright (c) 2009-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||
*
|
||
* The SSH Library is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU Lesser General Public License as published by
|
||
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
||
* option) any later version.
|
||
*
|
||
* The SSH Library is distributed in the hope that it will be useful, but
|
||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||
* License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public License
|
||
* along with the SSH Library; see the file COPYING. If not, write to
|
||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||
* MA 02111-1307, USA.
|
||
*/
|
||
|
||
#include "config.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#ifndef _WIN32
|
||
#include <pwd.h>
|
||
#else
|
||
#include <winsock2.h>
|
||
#endif
|
||
#include "libssh/config.h"
|
||
#include "libssh/config_parser.h"
|
||
#include "libssh/misc.h"
|
||
#include "libssh/options.h"
|
||
#include "libssh/pki.h"
|
||
#include "libssh/pki_context.h"
|
||
#include "libssh/pki_priv.h"
|
||
#include "libssh/priv.h"
|
||
#include "libssh/session.h"
|
||
#include <sys/types.h>
|
||
#include "libssh/misc.h"
|
||
#include "libssh/options.h"
|
||
#include "libssh/config_parser.h"
|
||
#include "libssh/gssapi.h"
|
||
#include "libssh/token.h"
|
||
#ifdef WITH_SERVER
|
||
#include "libssh/server.h"
|
||
#include "libssh/bind.h"
|
||
#include "libssh/bind_config.h"
|
||
#endif
|
||
|
||
/**
|
||
* @addtogroup libssh_session
|
||
* @{
|
||
*/
|
||
|
||
/**
|
||
* @brief Duplicate the options of a session structure.
|
||
*
|
||
* If you make several sessions with the same options this is useful. You
|
||
* cannot use twice the same option structure in ssh_connect.
|
||
*
|
||
* @param src The session to use to copy the options.
|
||
*
|
||
* @param dest A pointer to store the allocated session with duplicated
|
||
* options. You have to free the memory using ssh_free()
|
||
*
|
||
* @returns 0 on success, -1 on error with errno set.
|
||
*
|
||
* @see ssh_connect()
|
||
* @see ssh_free()
|
||
*/
|
||
int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||
{
|
||
ssh_session new = NULL;
|
||
struct ssh_iterator *it = NULL;
|
||
struct ssh_list *list = NULL;
|
||
char *id = NULL;
|
||
int i;
|
||
|
||
if (src == NULL || dest == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
new = ssh_new();
|
||
if (new == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
if (src->opts.username != NULL) {
|
||
new->opts.username = strdup(src->opts.username);
|
||
if (new->opts.username == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.host != NULL) {
|
||
new->opts.host = strdup(src->opts.host);
|
||
if (new->opts.host == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.bindaddr != NULL) {
|
||
new->opts.bindaddr = strdup(src->opts.bindaddr);
|
||
if (new->opts.bindaddr == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* Remove the default identities */
|
||
for (id = ssh_list_pop_head(char *, new->opts.identity_non_exp);
|
||
id != NULL;
|
||
id = ssh_list_pop_head(char *, new->opts.identity_non_exp)) {
|
||
SAFE_FREE(id);
|
||
}
|
||
/* Copy the new identities from the source list */
|
||
list = new->opts.identity_non_exp;
|
||
it = ssh_list_get_iterator(src->opts.identity_non_exp);
|
||
for (i = 0; i < 2; i++) {
|
||
while (it) {
|
||
int rc;
|
||
|
||
id = strdup((char *)it->data);
|
||
if (id == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
|
||
rc = ssh_list_append(list, id);
|
||
if (rc < 0) {
|
||
free(id);
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
it = it->next;
|
||
}
|
||
|
||
/* copy the identity list if there is any already */
|
||
list = new->opts.identity;
|
||
it = ssh_list_get_iterator(src->opts.identity);
|
||
}
|
||
|
||
list = new->opts.certificate_non_exp;
|
||
it = ssh_list_get_iterator(src->opts.certificate_non_exp);
|
||
for (i = 0; i < 2; i++) {
|
||
while (it) {
|
||
int rc;
|
||
|
||
id = strdup((char *)it->data);
|
||
if (id == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
|
||
rc = ssh_list_append(list, id);
|
||
if (rc < 0) {
|
||
free(id);
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
it = it->next;
|
||
}
|
||
|
||
/* copy the certificate list if there is any already */
|
||
list = new->opts.certificate;
|
||
it = ssh_list_get_iterator(src->opts.certificate);
|
||
}
|
||
|
||
if (src->opts.sshdir != NULL) {
|
||
new->opts.sshdir = strdup(src->opts.sshdir);
|
||
if (new->opts.sshdir == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.knownhosts != NULL) {
|
||
new->opts.knownhosts = strdup(src->opts.knownhosts);
|
||
if (new->opts.knownhosts == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.global_knownhosts != NULL) {
|
||
new->opts.global_knownhosts = strdup(src->opts.global_knownhosts);
|
||
if (new->opts.global_knownhosts == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < SSH_KEX_METHODS; i++) {
|
||
if (src->opts.wanted_methods[i] != NULL) {
|
||
new->opts.wanted_methods[i] = strdup(src->opts.wanted_methods[i]);
|
||
if (new->opts.wanted_methods[i] == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (src->opts.ProxyCommand != NULL) {
|
||
new->opts.ProxyCommand = strdup(src->opts.ProxyCommand);
|
||
if (new->opts.ProxyCommand == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.pubkey_accepted_types != NULL) {
|
||
new->opts.pubkey_accepted_types = strdup(src->opts.pubkey_accepted_types);
|
||
if (new->opts.pubkey_accepted_types == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.gss_server_identity != NULL) {
|
||
new->opts.gss_server_identity = strdup(src->opts.gss_server_identity);
|
||
if (new->opts.gss_server_identity == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.gss_client_identity != NULL) {
|
||
new->opts.gss_client_identity = strdup(src->opts.gss_client_identity);
|
||
if (new->opts.gss_client_identity == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (src->opts.control_path != NULL) {
|
||
new->opts.control_path = strdup(src->opts.control_path);
|
||
if (new->opts.control_path == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
memcpy(new->opts.options_seen, src->opts.options_seen,
|
||
sizeof(new->opts.options_seen));
|
||
|
||
new->opts.fd = src->opts.fd;
|
||
new->opts.port = src->opts.port;
|
||
new->opts.timeout = src->opts.timeout;
|
||
new->opts.timeout_usec = src->opts.timeout_usec;
|
||
new->opts.compressionlevel = src->opts.compressionlevel;
|
||
new->opts.StrictHostKeyChecking = src->opts.StrictHostKeyChecking;
|
||
new->opts.gss_delegate_creds = src->opts.gss_delegate_creds;
|
||
new->opts.flags = src->opts.flags;
|
||
new->opts.nodelay = src->opts.nodelay;
|
||
new->opts.config_processed = src->opts.config_processed;
|
||
new->opts.control_master = src->opts.control_master;
|
||
new->opts.address_family = src->opts.address_family;
|
||
new->common.log_verbosity = src->common.log_verbosity;
|
||
new->common.callbacks = src->common.callbacks;
|
||
|
||
SSH_PKI_CTX_FREE(new->pki_context);
|
||
if (src->pki_context != NULL) {
|
||
new->pki_context = ssh_pki_ctx_dup(src->pki_context);
|
||
if (new->pki_context == NULL) {
|
||
ssh_free(new);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
*dest = new;
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ssh_options_set_algo(ssh_session session,
|
||
enum ssh_kex_types_e algo,
|
||
const char *list,
|
||
char **place)
|
||
{
|
||
/* When the list start with +,-,^ the filtration of unknown algorithms
|
||
* gets handled inside the helper functions, otherwise the list is taken
|
||
* as it is. */
|
||
char *p = (char *)list;
|
||
|
||
if (algo < SSH_COMP_C_S) {
|
||
if (list[0] == '+') {
|
||
p = ssh_add_to_default_algos(algo, list+1);
|
||
} else if (list[0] == '-') {
|
||
p = ssh_remove_from_default_algos(algo, list+1);
|
||
} else if (list[0] == '^') {
|
||
p = ssh_prefix_default_algos(algo, list+1);
|
||
}
|
||
}
|
||
|
||
if (p == list) {
|
||
if (ssh_fips_mode()) {
|
||
p = ssh_keep_fips_algos(algo, list);
|
||
} else {
|
||
p = ssh_keep_known_algos(algo, list);
|
||
}
|
||
}
|
||
|
||
if (p == NULL) {
|
||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||
"Setting method: no allowed algorithm for method \"%s\" (%s)",
|
||
ssh_kex_get_description(algo), list);
|
||
return -1;
|
||
}
|
||
|
||
SAFE_FREE(*place);
|
||
*place = p;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief This function can set all possible ssh options.
|
||
*
|
||
* @param session An allocated SSH session structure.
|
||
*
|
||
* @param type The option type to set. This could be one of the
|
||
* following:
|
||
*
|
||
* - SSH_OPTIONS_HOST:
|
||
* The hostname or ip address to connect to. It can be also in
|
||
* the format of URI, containing also username, such as
|
||
* [username@]hostname. The IPv6 addresses can be enclosed
|
||
* within square braces, for example [::1]. The IPv4 address
|
||
* supports any format supported by OS. The hostname needs to be
|
||
* encoded to match RFC1035, so for IDN it needs to be encoded
|
||
* in punycode.
|
||
* (const char *).
|
||
*
|
||
* - SSH_OPTIONS_PORT:
|
||
* The port to connect to (unsigned int).
|
||
*
|
||
* - SSH_OPTIONS_PORT_STR:
|
||
* The port to connect to (const char *).
|
||
*
|
||
* - SSH_OPTIONS_FD:
|
||
* The file descriptor to use (socket_t).\n
|
||
* \n
|
||
* If you wish to open the socket yourself for a reason
|
||
* or another, set the file descriptor and take care of closing
|
||
* it (this is new behavior in libssh 0.10).
|
||
* Don't forget to set the hostname as the hostname is used
|
||
* as a key in the known_host mechanism.
|
||
*
|
||
* - SSH_OPTIONS_BINDADDR:
|
||
* The address to bind the client to (const char *).
|
||
*
|
||
* - SSH_OPTIONS_USER:
|
||
* The username for authentication (const char *).\n
|
||
* \n
|
||
* If the value is NULL, the username is set to the
|
||
* default username.
|
||
*
|
||
* - SSH_OPTIONS_SSH_DIR:
|
||
* Set the ssh directory (const char *,format string).\n
|
||
* \n
|
||
* If the value is NULL, the directory is set to the
|
||
* default ssh directory.\n
|
||
* \n
|
||
* The ssh directory is used for files like known_hosts
|
||
* and identity (private and public key). It may include
|
||
* "%s" which will be replaced by the user home
|
||
* directory.
|
||
*
|
||
* - SSH_OPTIONS_KNOWNHOSTS:
|
||
* Set the known hosts file name (const char *,format string).\n
|
||
* \n
|
||
* If the value is NULL, the directory is set to the
|
||
* default known hosts file, normally
|
||
* ~/.ssh/known_hosts.\n
|
||
* \n
|
||
* The known hosts file is used to certify remote hosts
|
||
* are genuine. It may include "%d" which will be
|
||
* replaced by the user home directory.
|
||
*
|
||
* - SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
|
||
* Set the global known hosts file name (const char *,format string).\n
|
||
* \n
|
||
* If the value is NULL, the directory is set to the
|
||
* default global known hosts file, normally
|
||
* /etc/ssh/ssh_known_hosts.\n
|
||
* \n
|
||
* The known hosts file is used to certify remote hosts
|
||
* are genuine.
|
||
*
|
||
* - SSH_OPTIONS_ADD_IDENTITY (or SSH_OPTIONS_IDENTITY):
|
||
* Add a new identity file (const char *, format string) to
|
||
* the identity list.\n
|
||
* \n
|
||
* By default id_rsa, id_ecdsa and id_ed25519 files are used.\n
|
||
* If libssh is built with FIDO2/U2F support, id_ecdsa_sk and\n
|
||
* id_ed25519_sk files are also used by default.\n
|
||
* \n
|
||
* The identity used to authenticate with public key will be
|
||
* prepended to the list.
|
||
* It may include "%s" which will be replaced by the
|
||
* user home directory.
|
||
*
|
||
* - SSH_OPTIONS_CERTIFICATE:
|
||
* Add a new certificate file (const char *, format string) to
|
||
* the certificate list.\n
|
||
* \n
|
||
* By default id_rsa-cert.pub, id_ecdsa-cert.pub and
|
||
* id_ed25519-cert.pub files are used, when the underlying
|
||
* private key is present.\n
|
||
* \n
|
||
* The certificate itself can not be used to authenticate to
|
||
* remote server so it needs to be paired with private key
|
||
* (aka identity file) provided with separate option, from agent
|
||
* or from PKCS#11 token.
|
||
* It may include "%s" which will be replaced by the
|
||
* user home directory.
|
||
*
|
||
* - SSH_OPTIONS_TIMEOUT:
|
||
* Set a timeout for the connection in seconds (long).
|
||
*
|
||
* - SSH_OPTIONS_TIMEOUT_USEC:
|
||
* Set a timeout for the connection in micro seconds
|
||
* (long).
|
||
*
|
||
* - SSH_OPTIONS_SSH1:
|
||
* Deprecated
|
||
*
|
||
* - SSH_OPTIONS_SSH2:
|
||
* Unused
|
||
*
|
||
* - SSH_OPTIONS_LOG_VERBOSITY:
|
||
* Set the session logging verbosity (int).\n
|
||
* \n
|
||
* The verbosity of the messages. Every log smaller or
|
||
* equal to verbosity will be shown.
|
||
* - SSH_LOG_NOLOG: No logging
|
||
* - SSH_LOG_WARNING: Only warnings
|
||
* - SSH_LOG_PROTOCOL: High level protocol information
|
||
* - SSH_LOG_PACKET: Lower level protocol information, packet level
|
||
* - SSH_LOG_FUNCTIONS: Every function path
|
||
* The default is SSH_LOG_NOLOG.
|
||
*
|
||
* - SSH_OPTIONS_LOG_VERBOSITY_STR:
|
||
* Set the session logging verbosity via a
|
||
* string that will be converted to a numerical
|
||
* value (e.g. "3") and interpreted according
|
||
* to the values of
|
||
* SSH_OPTIONS_LOG_VERBOSITY above (const
|
||
* char *).
|
||
*
|
||
* - SSH_OPTIONS_CIPHERS_C_S:
|
||
* Set the symmetric cipher client to server (const char *,
|
||
* comma-separated list). The list can be prepended by +,-,^
|
||
* which can append, remove or move to the beginning
|
||
* (prioritizing) of the default list respectively. Giving an
|
||
* empty list after + and ^ will cause error.
|
||
*
|
||
* - SSH_OPTIONS_CIPHERS_S_C:
|
||
* Set the symmetric cipher server to client (const char *,
|
||
* comma-separated list). The list can be prepended by +,-,^
|
||
* which can append, remove or move to the beginning
|
||
* (prioritizing) of the default list respectively. Giving an
|
||
* empty list after + and ^ will cause error.
|
||
*
|
||
* - SSH_OPTIONS_KEY_EXCHANGE:
|
||
* Set the key exchange method to be used (const char *,
|
||
* comma-separated list). ex:
|
||
* "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
|
||
* The list can be prepended by +,-,^ which will append,
|
||
* remove or move to the beginning (prioritizing) of the
|
||
* default list respectively. Giving an empty list
|
||
* after + and ^ will cause error.
|
||
*
|
||
* - SSH_OPTIONS_HMAC_C_S:
|
||
* Set the Message Authentication Code algorithm client to server
|
||
* (const char *, comma-separated list). The list can be
|
||
* prepended by +,-,^ which will append, remove or move to
|
||
* the beginning (prioritizing) of the default list
|
||
* respectively. Giving an empty list after + and ^ will
|
||
* cause error.
|
||
*
|
||
* - SSH_OPTIONS_HMAC_S_C:
|
||
* Set the Message Authentication Code algorithm server to client
|
||
* (const char *, comma-separated list). The list can be
|
||
* prepended by +,-,^ which will append, remove or move to
|
||
* the beginning (prioritizing) of the default list
|
||
* respectively. Giving an empty list after + and ^ will
|
||
* cause error.
|
||
*
|
||
* - SSH_OPTIONS_HOSTKEYS:
|
||
* Set the preferred server host key types (const char *,
|
||
* comma-separated list). ex:
|
||
* "ssh-rsa,ecdh-sha2-nistp256". The list can be
|
||
* prepended by +,-,^ which will append, remove or move to
|
||
* the beginning (prioritizing) of the default list
|
||
* respectively. Giving an empty list after + and ^ will
|
||
* cause error.
|
||
*
|
||
* - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
|
||
* Set the preferred public key algorithms to be used for
|
||
* authentication (const char *, comma-separated list). ex:
|
||
* "ssh-rsa,rsa-sha2-256,ecdh-sha2-nistp256"
|
||
* The list can be prepended by +,-,^ which will append,
|
||
* remove or move to the beginning (prioritizing) of the
|
||
* default list respectively. Giving an empty list
|
||
* after + and ^ will cause error.
|
||
*
|
||
* - SSH_OPTIONS_COMPRESSION_C_S:
|
||
* Set the compression to use for client to server
|
||
* communication (const char *, "yes", "no" or a specific
|
||
* algorithm name if needed ("zlib","zlib@openssh.com","none").
|
||
*
|
||
* - SSH_OPTIONS_COMPRESSION_S_C:
|
||
* Set the compression to use for server to client
|
||
* communication (const char *, "yes", "no" or a specific
|
||
* algorithm name if needed ("zlib","zlib@openssh.com","none").
|
||
*
|
||
* - SSH_OPTIONS_COMPRESSION:
|
||
* Set the compression to use for both directions
|
||
* communication (const char *, "yes", "no" or a specific
|
||
* algorithm name if needed ("zlib","zlib@openssh.com","none").
|
||
*
|
||
* - SSH_OPTIONS_COMPRESSION_LEVEL:
|
||
* Set the compression level to use for zlib functions. (int,
|
||
* value from 1 to 9, 9 being the most efficient but slower).
|
||
*
|
||
* - SSH_OPTIONS_STRICTHOSTKEYCHECK:
|
||
* Set the parameter StrictHostKeyChecking to avoid
|
||
* asking about a fingerprint (int, 0 = false).
|
||
*
|
||
* - SSH_OPTIONS_PROXYCOMMAND:
|
||
* Set the command to be executed in order to connect to
|
||
* server (const char *).
|
||
*
|
||
* - SSH_OPTIONS_PROXYJUMP:
|
||
* Set the comma separated jump hosts in order to connect to
|
||
* server (const char *). Set to "none" to disable.
|
||
* Example:
|
||
* "alice@127.0.0.1:5555,bob@127.0.0.2"
|
||
*
|
||
* If environment variable OPENSSH_PROXYJUMP is set to 1 then proxyjump will be
|
||
* handled by the OpenSSH binary.
|
||
*
|
||
* - SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND:
|
||
* Append the callbacks struct for a jump in order of
|
||
* SSH_OPTIONS_PROXYJUMP. Append as many times
|
||
* as the number of jumps (struct ssh_jump_callbacks_struct *).
|
||
*
|
||
* - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY
|
||
* Set it to specify the GSSAPI server identity that libssh
|
||
* should expect when connecting to the server (const char *).
|
||
*
|
||
* - SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY
|
||
* Set it to specify the GSSAPI client identity that libssh
|
||
* should expect when connecting to the server (const char *).
|
||
*
|
||
* - SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS
|
||
* Set it to specify that GSSAPI should delegate credentials
|
||
* to the server (int, 0 = false).
|
||
*
|
||
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE
|
||
* Set to true to allow GSSAPI key exchange (bool).
|
||
*
|
||
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
|
||
* Set the GSSAPI key exchange method to be used (const char *,
|
||
* comma-separated list). ex:
|
||
* "gss-curve25519-sha256-,gss-nistp256-sha256-"
|
||
* These will prefix the default algorithms if
|
||
* SSH_OPTIONS_GSSAPI_KEY_EXCHANGE is true.
|
||
*
|
||
* - 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).
|
||
*
|
||
* - SSH_OPTIONS_NODELAY
|
||
* Set it to disable Nagle's Algorithm (TCP_NODELAY) on the
|
||
* session socket. (int, 0=false)
|
||
*
|
||
* - SSH_OPTIONS_PROCESS_CONFIG
|
||
* Set it to false to disable automatic processing of per-user
|
||
* and system-wide OpenSSH configuration files. LibSSH
|
||
* automatically uses these configuration files unless
|
||
* you provide it with this option or with different file (bool).
|
||
*
|
||
* - SSH_OPTIONS_REKEY_DATA
|
||
* Set the data limit that can be transferred with a single
|
||
* key in bytes. RFC 4253 Section 9 recommends 1GB of data, while
|
||
* RFC 4344 provides more specific restrictions, that are applied
|
||
* automatically. When specified, the lower value will be used.
|
||
* (uint64_t, 0=default)
|
||
*
|
||
* - SSH_OPTIONS_REKEY_TIME
|
||
* Set the time limit for a session before initializing a rekey
|
||
* in seconds. RFC 4253 Section 9 recommends one hour.
|
||
* (uint32_t, 0=off)
|
||
*
|
||
* - SSH_OPTIONS_RSA_MIN_SIZE
|
||
* Set the minimum RSA key size in bits to be accepted by the
|
||
* client for both authentication and hostkey verification.
|
||
* The values under 1024 bits are not accepted even with this
|
||
* configuration option as they are considered completely broken.
|
||
* Setting 0 will revert the value to defaults.
|
||
* Default is 3072 bits or 2048 bits in FIPS mode.
|
||
* (int)
|
||
*
|
||
* - SSH_OPTIONS_IDENTITY_AGENT
|
||
* Set the path to the SSH agent socket. If unset, the
|
||
* SSH_AUTH_SOCK environment is consulted.
|
||
* (const char *)
|
||
*
|
||
* - SSH_OPTIONS_IDENTITIES_ONLY
|
||
* Use only keys specified in the SSH config, even if agent
|
||
* offers more.
|
||
* (bool)
|
||
*
|
||
* - SSH_OPTIONS_CONTROL_MASTER
|
||
* Set the option to enable the sharing of multiple sessions over a
|
||
* single network connection using connection multiplexing (int).
|
||
*
|
||
* The possible options are among the following:
|
||
* - SSH_CONTROL_MASTER_AUTO: enable connection sharing if possible
|
||
* - SSH_CONTROL_MASTER_YES: enable connection sharing unconditionally
|
||
* - SSH_CONTROL_MASTER_ASK: ask for confirmation if connection sharing is to be enabled
|
||
* - SSH_CONTROL_MASTER_AUTOASK: enable connection sharing if possible,
|
||
* but ask for confirmation
|
||
* - SSH_CONTROL_MASTER_NO: disable connection sharing unconditionally
|
||
*
|
||
* The default is SSH_CONTROL_MASTER_NO.
|
||
*
|
||
* - SSH_OPTIONS_CONTROL_PATH
|
||
* Set the path to the control socket used for connection sharing.
|
||
* Set to "none" to disable connection sharing.
|
||
* (const char *)
|
||
*
|
||
* - SSH_OPTIONS_PKI_CONTEXT
|
||
* Attach a previously created generic PKI context to the
|
||
* session. This allows supplying per-session PKI
|
||
* configuration options for PKI operations.
|
||
* All fields from the user's context are copied to the session's
|
||
* own context. The user retains ownership of the original
|
||
* context and can free it after this call.
|
||
* (ssh_pki_ctx)
|
||
*
|
||
* - SSH_OPTIONS_ADDRESS_FAMILY
|
||
* Specify which address family to use when connecting.
|
||
*
|
||
* Possible options:
|
||
* - SSH_ADDRESS_FAMILY_ANY: use any address family
|
||
* - SSH_ADDRESS_FAMILY_INET: IPv4 only
|
||
* - SSH_ADDRESS_FAMILY_INET6: IPv6 only
|
||
*
|
||
* @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.
|
||
*
|
||
* @return 0 on success, < 0 on error.
|
||
*
|
||
* @warning When the option value to set is represented via a pointer
|
||
* (e.g const char * in case of strings, ssh_key in case of a
|
||
* libssh key), the value parameter should be that pointer.
|
||
* Do NOT pass a pointer to a pointer (const char **, ssh_key *)
|
||
*
|
||
* @warning When the option value to set is not a pointer (e.g int,
|
||
* unsigned int, bool, long), the value parameter should be
|
||
* a pointer to the location storing the value to set (int *,
|
||
* unsigned int *, bool *, long *)
|
||
*
|
||
* @warning If the value parameter has an invalid type (e.g if its not a
|
||
* pointer when it should have been a pointer, or if its a pointer
|
||
* to a pointer when it should have just been a pointer), then the
|
||
* behaviour is undefined.
|
||
*/
|
||
int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||
const void *value)
|
||
{
|
||
const char *v = NULL;
|
||
char *p = NULL, *q = NULL;
|
||
long int i;
|
||
unsigned int u;
|
||
int rc;
|
||
char **wanted_methods = session->opts.wanted_methods;
|
||
struct ssh_jump_callbacks_struct *j = NULL;
|
||
|
||
if (session == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
switch (type) {
|
||
case SSH_OPTIONS_HOST:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
char *username = NULL, *hostname = NULL;
|
||
rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
|
||
if (rc != SSH_OK) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
if (username != NULL) {
|
||
SAFE_FREE(session->opts.username);
|
||
session->opts.username = username;
|
||
}
|
||
if (hostname != NULL) {
|
||
SAFE_FREE(session->opts.host);
|
||
session->opts.host = hostname;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PORT:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *) value;
|
||
if (*x <= 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->opts.port = *x & 0xffffU;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PORT_STR:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
q = strdup(v);
|
||
if (q == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
i = strtol(q, &p, 10);
|
||
if (q == p) {
|
||
SSH_LOG(SSH_LOG_DEBUG, "No port number was parsed");
|
||
SAFE_FREE(q);
|
||
return -1;
|
||
}
|
||
SAFE_FREE(q);
|
||
if (i <= 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->opts.port = i & 0xffffU;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_FD:
|
||
if (value == NULL) {
|
||
session->opts.fd = SSH_INVALID_SOCKET;
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
socket_t *x = (socket_t *) value;
|
||
if (*x < 0) {
|
||
session->opts.fd = SSH_INVALID_SOCKET;
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->opts.fd = *x & 0xffff;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_BINDADDR:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
q = strdup(v);
|
||
if (q == NULL) {
|
||
return -1;
|
||
}
|
||
SAFE_FREE(session->opts.bindaddr);
|
||
session->opts.bindaddr = q;
|
||
break;
|
||
case SSH_OPTIONS_USER:
|
||
v = value;
|
||
SAFE_FREE(session->opts.username);
|
||
if (v == NULL) {
|
||
q = ssh_get_local_username();
|
||
if (q == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
session->opts.username = q;
|
||
} else if (v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else { /* username provided */
|
||
session->opts.username = strdup(value);
|
||
if (session->opts.username == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
rc = ssh_check_username_syntax(session->opts.username);
|
||
if (rc != SSH_OK) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_SSH_DIR:
|
||
v = value;
|
||
SAFE_FREE(session->opts.sshdir);
|
||
if (v == NULL) {
|
||
session->opts.sshdir = ssh_path_expand_tilde("~/.ssh");
|
||
if (session->opts.sshdir == NULL) {
|
||
return -1;
|
||
}
|
||
} else if (v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
session->opts.sshdir = ssh_path_expand_tilde(v);
|
||
if (session->opts.sshdir == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_IDENTITY:
|
||
case SSH_OPTIONS_ADD_IDENTITY:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
q = strdup(v);
|
||
if (q == NULL) {
|
||
return -1;
|
||
}
|
||
if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
|
||
rc = ssh_list_append(session->opts.identity_non_exp, q);
|
||
} else {
|
||
rc = ssh_list_prepend(session->opts.identity_non_exp, q);
|
||
}
|
||
if (rc < 0) {
|
||
free(q);
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_CERTIFICATE:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
q = strdup(v);
|
||
if (q == NULL) {
|
||
return -1;
|
||
}
|
||
rc = ssh_list_append(session->opts.certificate_non_exp, q);
|
||
if (rc < 0) {
|
||
free(q);
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_KNOWNHOSTS:
|
||
v = value;
|
||
SAFE_FREE(session->opts.knownhosts);
|
||
if (v == NULL) {
|
||
/* The default value will be set by the ssh_options_apply() */
|
||
} else if (v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
session->opts.knownhosts = strdup(v);
|
||
if (session->opts.knownhosts == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
|
||
v = value;
|
||
SAFE_FREE(session->opts.global_knownhosts);
|
||
if (v == NULL) {
|
||
session->opts.global_knownhosts =
|
||
strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
|
||
if (session->opts.global_knownhosts == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
} else if (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;
|
||
}
|
||
session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_TIMEOUT:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
long *x = (long *) value;
|
||
if (*x < 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->opts.timeout = *x & 0xffffffffU;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_TIMEOUT_USEC:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
long *x = (long *) value;
|
||
if (*x < 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->opts.timeout_usec = *x & 0xffffffffU;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_SSH1:
|
||
break;
|
||
case SSH_OPTIONS_SSH2:
|
||
break;
|
||
case SSH_OPTIONS_LOG_VERBOSITY:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *) value;
|
||
if (*x < 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->common.log_verbosity = *x & 0xffffU;
|
||
ssh_set_log_level(*x & 0xffffU);
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_LOG_VERBOSITY_STR:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
session->common.log_verbosity = 0;
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
q = strdup(v);
|
||
if (q == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
i = strtol(q, &p, 10);
|
||
if (q == p) {
|
||
SSH_LOG(SSH_LOG_DEBUG, "No log verbositiy was parsed");
|
||
SAFE_FREE(q);
|
||
return -1;
|
||
}
|
||
SAFE_FREE(q);
|
||
if (i < 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
session->common.log_verbosity = i & 0xffffU;
|
||
ssh_set_log_level(i & 0xffffU);
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_CIPHERS_C_S:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_CRYPT_C_S,
|
||
v,
|
||
&wanted_methods[SSH_CRYPT_C_S]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_CIPHERS_S_C:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_CRYPT_S_C,
|
||
v,
|
||
&wanted_methods[SSH_CRYPT_S_C]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_KEY_EXCHANGE:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_KEX,
|
||
v,
|
||
&wanted_methods[SSH_KEX]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_HOSTKEYS:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_HOSTKEYS,
|
||
v,
|
||
&wanted_methods[SSH_HOSTKEYS]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_HOSTKEYS,
|
||
v,
|
||
&session->opts.pubkey_accepted_types);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_HMAC_C_S:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_MAC_C_S,
|
||
v,
|
||
&wanted_methods[SSH_MAC_C_S]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_HMAC_S_C:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_MAC_S_C,
|
||
v,
|
||
&wanted_methods[SSH_MAC_S_C]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_COMPRESSION_C_S:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
const char *tmp = v;
|
||
if (strcasecmp(value, "yes") == 0){
|
||
tmp = "zlib@openssh.com,none";
|
||
} else if (strcasecmp(value, "no") == 0){
|
||
tmp = "none,zlib@openssh.com";
|
||
}
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_COMP_C_S,
|
||
tmp,
|
||
&wanted_methods[SSH_COMP_C_S]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_COMPRESSION_S_C:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
const char *tmp = v;
|
||
if (strcasecmp(value, "yes") == 0){
|
||
tmp = "zlib@openssh.com,none";
|
||
} else if (strcasecmp(value, "no") == 0){
|
||
tmp = "none,zlib@openssh.com";
|
||
}
|
||
|
||
rc = ssh_options_set_algo(session,
|
||
SSH_COMP_S_C,
|
||
tmp,
|
||
&wanted_methods[SSH_COMP_S_C]);
|
||
if (rc < 0)
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_COMPRESSION:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
if(ssh_options_set(session,SSH_OPTIONS_COMPRESSION_C_S, v) < 0)
|
||
return -1;
|
||
if(ssh_options_set(session,SSH_OPTIONS_COMPRESSION_S_C, v) < 0)
|
||
return -1;
|
||
break;
|
||
case SSH_OPTIONS_COMPRESSION_LEVEL:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *)value;
|
||
if (*x < 1 || *x > 9) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
session->opts.compressionlevel = *x & 0xff;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_STRICTHOSTKEYCHECK:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *) value;
|
||
|
||
session->opts.StrictHostKeyChecking = (*x & 0xff) > 0 ? 1 : 0;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PROXYCOMMAND:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(session->opts.ProxyCommand);
|
||
/* Setting the command to 'none' disables this option. */
|
||
rc = strcasecmp(v, "none");
|
||
if (rc != 0) {
|
||
q = strdup(v);
|
||
if (q == NULL) {
|
||
return -1;
|
||
}
|
||
session->opts.ProxyCommand = q;
|
||
session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_PROXYCOMMAND;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PROXYJUMP:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_config_parse_proxy_jump(session, v, true);
|
||
if (rc != SSH_OK) {
|
||
return SSH_ERROR;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND:
|
||
j = (struct ssh_jump_callbacks_struct *)value;
|
||
if (j == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_list_prepend(session->opts.proxy_jumps_user_cb, j);
|
||
if (rc != SSH_OK) {
|
||
ssh_set_error_oom(session);
|
||
return SSH_ERROR;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(session->opts.gss_server_identity);
|
||
session->opts.gss_server_identity = strdup(v);
|
||
if (session->opts.gss_server_identity == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(session->opts.gss_client_identity);
|
||
session->opts.gss_client_identity = strdup(v);
|
||
if (session->opts.gss_client_identity == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int x = *(int *)value;
|
||
|
||
session->opts.gss_delegate_creds = (x & 0xff);
|
||
}
|
||
break;
|
||
#ifdef WITH_GSSAPI
|
||
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
bool *x = (bool *)value;
|
||
session->opts.gssapi_key_exchange = *x;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
/* Check if algorithms are supported */
|
||
char *ret =
|
||
ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, v);
|
||
if (ret == NULL) {
|
||
ssh_set_error(session,
|
||
SSH_FATAL,
|
||
"GSSAPI key exchange algorithms not "
|
||
"supported or invalid");
|
||
return -1;
|
||
}
|
||
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
|
||
session->opts.gssapi_key_exchange_algs = ret;
|
||
}
|
||
break;
|
||
#endif
|
||
case SSH_OPTIONS_PASSWORD_AUTH:
|
||
case SSH_OPTIONS_PUBKEY_AUTH:
|
||
case SSH_OPTIONS_KBDINT_AUTH:
|
||
case SSH_OPTIONS_GSSAPI_AUTH:
|
||
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;
|
||
case SSH_OPTIONS_NODELAY:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *) value;
|
||
session->opts.nodelay = (*x & 0xff) > 0 ? 1 : 0;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PROCESS_CONFIG:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
bool *x = (bool *)value;
|
||
session->opts.config_processed = !(*x);
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_REKEY_DATA:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
uint64_t *x = (uint64_t *)value;
|
||
session->opts.rekey_data = *x;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_REKEY_TIME:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
uint32_t *x = (uint32_t *)value;
|
||
if ((*x * 1000) < *x) {
|
||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||
"The provided value (%" PRIu32 ") for rekey"
|
||
" time is too large", *x);
|
||
return -1;
|
||
}
|
||
session->opts.rekey_time = (*x) * 1000;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_RSA_MIN_SIZE:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *)value;
|
||
|
||
if (*x < 0) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
/* (*x == 0) is allowed as it is used to revert to default */
|
||
|
||
if (*x > 0 && *x < RSA_MIN_KEY_SIZE) {
|
||
ssh_set_error(session,
|
||
SSH_REQUEST_DENIED,
|
||
"The provided value (%d) for minimal RSA key "
|
||
"size is too small. Use at least %d bits.",
|
||
*x,
|
||
RSA_MIN_KEY_SIZE);
|
||
return -1;
|
||
}
|
||
session->opts.rsa_min_size = *x;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_IDENTITY_AGENT:
|
||
v = value;
|
||
SAFE_FREE(session->opts.agent_socket);
|
||
if (v == NULL) {
|
||
/* The default value will be set by the ssh_options_apply() */
|
||
} else if (v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
session->opts.agent_socket = ssh_path_expand_tilde(v);
|
||
if (session->opts.agent_socket == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_IDENTITIES_ONLY:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
bool *x = (bool *)value;
|
||
session->opts.identities_only = *x;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_CONTROL_MASTER:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *) value;
|
||
if (*x < SSH_CONTROL_MASTER_NO || *x > SSH_CONTROL_MASTER_AUTOASK) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
session->opts.control_master = *x;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_CONTROL_PATH:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(session->opts.control_path);
|
||
rc = strcasecmp(v, "none");
|
||
if (rc != 0) {
|
||
session->opts.control_path = ssh_path_expand_tilde(v);
|
||
if (session->opts.control_path == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_CONTROL_PATH;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_PKI_CONTEXT:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
SSH_PKI_CTX_FREE(session->pki_context);
|
||
|
||
session->pki_context = ssh_pki_ctx_dup((const ssh_pki_ctx)value);
|
||
if (session->pki_context == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
break;
|
||
case SSH_OPTIONS_ADDRESS_FAMILY:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *)value;
|
||
if (*x < SSH_ADDRESS_FAMILY_ANY ||
|
||
*x > SSH_ADDRESS_FAMILY_INET6) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
session->opts.address_family = *x;
|
||
}
|
||
break;
|
||
default:
|
||
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
|
||
return -1;
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief This function returns the current algorithms used for algorithm
|
||
* negotiation. It is either libssh default, option manually set or option
|
||
* read from configuration file.
|
||
*
|
||
* This function will return NULL on error
|
||
*
|
||
* @param session An allocated SSH session structure.
|
||
* @param algo One of the ssh_kex_types_e values.
|
||
*/
|
||
char *ssh_options_get_algo(ssh_session session,
|
||
enum ssh_kex_types_e algo)
|
||
{
|
||
char *value = NULL;
|
||
|
||
/* Check session and algo values are valid */
|
||
|
||
if (session == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
if (algo >= SSH_LANG_C_S) {
|
||
ssh_set_error_invalid(session);
|
||
return NULL;
|
||
}
|
||
|
||
/* Get the option the user has set, if there is one */
|
||
value = session->opts.wanted_methods[algo];
|
||
if (value == NULL) {
|
||
/* The user has not set a value, return the appropriate default */
|
||
if (ssh_fips_mode())
|
||
value = (char *)ssh_kex_get_fips_methods(algo);
|
||
else
|
||
value = (char *)ssh_kex_get_default_methods(algo);
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief This function can get ssh the ssh port. It must only be used on
|
||
* a valid ssh session. This function is useful when the session
|
||
* options have been automatically inferred from the environment
|
||
* or configuration files and one
|
||
*
|
||
* @param session An allocated SSH session structure.
|
||
*
|
||
* @param port_target An unsigned integer into which the
|
||
* port will be set from the ssh session.
|
||
*
|
||
* @return 0 on success, < 0 on error.
|
||
*
|
||
*/
|
||
int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
|
||
if (session == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
if (session->opts.port == 0) {
|
||
*port_target = 22;
|
||
return 0;
|
||
}
|
||
|
||
*port_target = session->opts.port;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief This function can get ssh options, it does not support all options provided for
|
||
* ssh options set, but mostly those which a user-space program may care about having
|
||
* trusted the ssh driver to infer these values from underlying configuration files.
|
||
* It operates only on those SSH_OPTIONS_* which return char*. If you wish to receive
|
||
* the port then please use ssh_options_get_port() which returns an unsigned int.
|
||
*
|
||
* @param session An allocated SSH session structure.
|
||
*
|
||
* @param type The option type to get. This could be one of the
|
||
* following:
|
||
*
|
||
* - SSH_OPTIONS_HOST:
|
||
* The hostname or ip address to connect to (const char *).
|
||
*
|
||
* - SSH_OPTIONS_USER:
|
||
* The username for authentication (const char *).\n
|
||
* \n when not explicitly set this will be inferred from the
|
||
* ~/.ssh/config file.
|
||
*
|
||
* - SSH_OPTIONS_IDENTITY:
|
||
* Get the first identity file name (const char *).\n
|
||
* \n
|
||
* By default `id_rsa`, `id_ecdsa`, `id_ed25519`, `id_ecdsa_sk`
|
||
* and `id_ed25519_sk` (when SK support is built in) files are
|
||
* used.
|
||
*
|
||
* - SSH_OPTIONS_NEXT_IDENTITY:
|
||
* Get the next identity file name (const char *).\n
|
||
* \n
|
||
* Repeat calls to get all key paths. SSH_EOF is returned when
|
||
* the end of list is reached. Another call will start another
|
||
* iteration over the same list.
|
||
*
|
||
* - SSH_OPTIONS_PROXYCOMMAND:
|
||
* Get the proxycommand necessary to log into the
|
||
* remote host. When not explicitly set, it will be read
|
||
* from the ~/.ssh/config file.
|
||
*
|
||
* - SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
|
||
* Get the path to the global known_hosts file being used.
|
||
*
|
||
* - SSH_OPTIONS_KNOWNHOSTS:
|
||
* Get the path to the known_hosts file being used.
|
||
*
|
||
* - SSH_OPTIONS_CONTROL_PATH:
|
||
* Get the path to the control socket being used for connection
|
||
* multiplexing.
|
||
*
|
||
* - SSH_OPTIONS_KEY_EXCHANGE:
|
||
* Get the key exchange methods to be used. If the option has
|
||
* not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_HOSTKEYS:
|
||
* Get the preferred server host key types. If the option has
|
||
* not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
|
||
* Get the preferred public key algorithms to be used for
|
||
* authentication.
|
||
*
|
||
* - SSH_OPTIONS_CIPHERS_C_S:
|
||
* Get the symmetric cipher client to server. If the option has
|
||
* not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_CIPHERS_S_C:
|
||
* Get the symmetric cipher server to client. If the option has
|
||
* not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_HMAC_C_S:
|
||
* Get the Message Authentication Code algorithm client to server
|
||
* If the option has not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_HMAC_S_C:
|
||
* Get the Message Authentication Code algorithm server to client
|
||
* If the option has not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_COMPRESSION_C_S:
|
||
* Get the compression to use for client to server communication
|
||
* If the option has not been set, returns the defaults.
|
||
*
|
||
* - SSH_OPTIONS_COMPRESSION_S_C:
|
||
* Get the compression to use for server to client communication
|
||
* If the option has not been set, returns the defaults.
|
||
*
|
||
* @param value The value to get into. As a char**, space will be
|
||
* allocated by the function for the value, it is
|
||
* your responsibility to free the memory using
|
||
* ssh_string_free_char().
|
||
*
|
||
* @return SSH_OK on success, SSH_ERROR on error.
|
||
*/
|
||
int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
|
||
{
|
||
char *src = NULL;
|
||
|
||
if (session == NULL) {
|
||
return SSH_ERROR;
|
||
}
|
||
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return SSH_ERROR;
|
||
}
|
||
|
||
switch(type)
|
||
{
|
||
case SSH_OPTIONS_HOST:
|
||
src = session->opts.host;
|
||
break;
|
||
|
||
case SSH_OPTIONS_USER:
|
||
src = session->opts.username;
|
||
break;
|
||
|
||
case SSH_OPTIONS_IDENTITY: {
|
||
struct ssh_iterator *it = NULL;
|
||
it = ssh_list_get_iterator(session->opts.identity);
|
||
if (it == NULL) {
|
||
it = ssh_list_get_iterator(session->opts.identity_non_exp);
|
||
}
|
||
if (it == NULL) {
|
||
return SSH_ERROR;
|
||
}
|
||
src = ssh_iterator_value(char *, it);
|
||
break;
|
||
}
|
||
|
||
case SSH_OPTIONS_NEXT_IDENTITY: {
|
||
if (session->opts.identity_it != NULL) {
|
||
/* Move to the next item */
|
||
session->opts.identity_it = session->opts.identity_it->next;
|
||
if (session->opts.identity_it == NULL) {
|
||
*value = NULL;
|
||
return SSH_EOF;
|
||
}
|
||
} else {
|
||
/* Get iterator from opts */
|
||
struct ssh_iterator *it = NULL;
|
||
it = ssh_list_get_iterator(session->opts.identity);
|
||
if (it == NULL) {
|
||
it = ssh_list_get_iterator(session->opts.identity_non_exp);
|
||
}
|
||
if (it == NULL) {
|
||
return SSH_ERROR;
|
||
}
|
||
session->opts.identity_it = it;
|
||
}
|
||
src = ssh_iterator_value(char *, session->opts.identity_it);
|
||
break;
|
||
}
|
||
|
||
case SSH_OPTIONS_PROXYCOMMAND:
|
||
src = session->opts.ProxyCommand;
|
||
break;
|
||
|
||
case SSH_OPTIONS_KNOWNHOSTS:
|
||
src = session->opts.knownhosts;
|
||
break;
|
||
|
||
case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
|
||
src = session->opts.global_knownhosts;
|
||
break;
|
||
case SSH_OPTIONS_CONTROL_PATH:
|
||
src = session->opts.control_path;
|
||
break;
|
||
|
||
case SSH_OPTIONS_CIPHERS_C_S:
|
||
src = ssh_options_get_algo(session, SSH_CRYPT_C_S);
|
||
break;
|
||
|
||
case SSH_OPTIONS_CIPHERS_S_C:
|
||
src = ssh_options_get_algo(session, SSH_CRYPT_S_C);
|
||
break;
|
||
|
||
case SSH_OPTIONS_KEY_EXCHANGE:
|
||
src = ssh_options_get_algo(session, SSH_KEX);
|
||
break;
|
||
|
||
case SSH_OPTIONS_HOSTKEYS:
|
||
src = ssh_options_get_algo(session, SSH_HOSTKEYS);
|
||
break;
|
||
|
||
case SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
|
||
src = session->opts.pubkey_accepted_types;
|
||
break;
|
||
|
||
case SSH_OPTIONS_HMAC_C_S:
|
||
src = ssh_options_get_algo(session, SSH_MAC_C_S);
|
||
break;
|
||
|
||
case SSH_OPTIONS_HMAC_S_C:
|
||
src = ssh_options_get_algo(session, SSH_MAC_S_C);
|
||
break;
|
||
|
||
case SSH_OPTIONS_COMPRESSION_C_S:
|
||
src = ssh_options_get_algo(session, SSH_COMP_C_S);
|
||
break;
|
||
|
||
case SSH_OPTIONS_COMPRESSION_S_C:
|
||
src = ssh_options_get_algo(session, SSH_COMP_S_C);
|
||
break;
|
||
|
||
default:
|
||
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
|
||
return SSH_ERROR;
|
||
break;
|
||
}
|
||
if (src == NULL) {
|
||
return SSH_ERROR;
|
||
}
|
||
*value = strdup(src);
|
||
if (*value == NULL) {
|
||
ssh_set_error_oom(session);
|
||
return SSH_ERROR;
|
||
}
|
||
return SSH_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief Parse command line arguments.
|
||
*
|
||
* This is a helper for your application to generate the appropriate
|
||
* options from the command line arguments.\n
|
||
* The argv array and argc value are changed so that the parsed
|
||
* arguments won't appear anymore in them.\n
|
||
* The single arguments (without switches) are not parsed. thus,
|
||
* myssh -l user localhost\n
|
||
* The command won't set the hostname value of options to localhost.
|
||
*
|
||
* @param session The session to configure.
|
||
*
|
||
* @param argcptr The pointer to the argument count.
|
||
*
|
||
* @param argv The arguments list pointer.
|
||
*
|
||
* @returns 0 on success, < 0 on error.
|
||
*
|
||
* @see ssh_session_new()
|
||
*/
|
||
int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
|
||
{
|
||
#ifdef _MSC_VER
|
||
(void)session;
|
||
(void)argcptr;
|
||
(void)argv;
|
||
/* Not supported with a Microsoft compiler */
|
||
return -1;
|
||
#else
|
||
char *user = NULL;
|
||
char *cipher = NULL;
|
||
char *identity = NULL;
|
||
char *port = NULL;
|
||
char **save = NULL;
|
||
char **tmp = NULL;
|
||
size_t i = 0;
|
||
int argc = *argcptr;
|
||
int debuglevel = 0;
|
||
int compress = 0;
|
||
int cont = 1;
|
||
size_t current = 0;
|
||
int opt_rc = 0;
|
||
int saveoptind = optind; /* need to save 'em */
|
||
int saveopterr = opterr;
|
||
int opt;
|
||
|
||
/* Nothing to do here */
|
||
if (argc <= 1) {
|
||
return SSH_OK;
|
||
}
|
||
|
||
opterr = 0; /* shut up getopt */
|
||
while ((opt = getopt(argc, argv, "c:i:o:Cl:p:vb:r12")) != -1) {
|
||
switch(opt) {
|
||
case 'l':
|
||
user = optarg;
|
||
break;
|
||
case 'p':
|
||
port = optarg;
|
||
break;
|
||
case 'v':
|
||
debuglevel++;
|
||
ssh_set_log_level(debuglevel);
|
||
break;
|
||
case 'r':
|
||
break;
|
||
case 'c':
|
||
cipher = optarg;
|
||
break;
|
||
case 'i':
|
||
identity = optarg;
|
||
break;
|
||
case 'C':
|
||
compress++;
|
||
break;
|
||
case 'o':
|
||
opt_rc = ssh_config_parse_line_cli(session, optarg);
|
||
break;
|
||
case '2':
|
||
break;
|
||
case '1':
|
||
break;
|
||
default:
|
||
{
|
||
tmp = realloc(save, (current + 1) * sizeof(char*));
|
||
if (tmp == NULL) {
|
||
SAFE_FREE(save);
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
save = tmp;
|
||
save[current] = argv[optind-1];
|
||
current++;
|
||
/* We can not use optarg here as getopt does not set it for
|
||
* unknown options. We need to manually extract following
|
||
* option and skip it manually from further processing */
|
||
if (optind < argc && argv[optind][0] != '-') {
|
||
tmp = realloc(save, (current + 1) * sizeof(char*));
|
||
if (tmp == NULL) {
|
||
SAFE_FREE(save);
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
save = tmp;
|
||
save[current++] = argv[optind];
|
||
optind++;
|
||
}
|
||
}
|
||
} /* switch */
|
||
if (opt_rc == SSH_ERROR) {
|
||
break;
|
||
}
|
||
} /* while */
|
||
opterr = saveopterr;
|
||
tmp = realloc(save, (current + (argc - optind)) * sizeof(char*));
|
||
if (tmp == NULL) {
|
||
SAFE_FREE(save);
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
save = tmp;
|
||
while (optind < argc) {
|
||
tmp = realloc(save, (current + 1) * sizeof(char*));
|
||
if (tmp == NULL) {
|
||
SAFE_FREE(save);
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
save = tmp;
|
||
save[current] = argv[optind];
|
||
current++;
|
||
optind++;
|
||
}
|
||
|
||
optind = saveoptind;
|
||
|
||
if (opt_rc == SSH_ERROR) {
|
||
SAFE_FREE(save);
|
||
return SSH_ERROR;
|
||
}
|
||
|
||
if(!cont) {
|
||
SAFE_FREE(save);
|
||
return -1;
|
||
}
|
||
|
||
/* first recopy the save vector into the original's */
|
||
for (i = 0; i < current; i++) {
|
||
/* don't erase argv[0] */
|
||
argv[ i + 1] = save[i];
|
||
}
|
||
argv[current + 1] = NULL;
|
||
*argcptr = current + 1;
|
||
SAFE_FREE(save);
|
||
|
||
/* set a new option struct */
|
||
if (compress) {
|
||
if (ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes") < 0) {
|
||
cont = 0;
|
||
}
|
||
}
|
||
|
||
if (cont && cipher) {
|
||
if (ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher) < 0) {
|
||
cont = 0;
|
||
}
|
||
if (cont && ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher) < 0) {
|
||
cont = 0;
|
||
}
|
||
}
|
||
|
||
if (cont && user) {
|
||
if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
|
||
cont = 0;
|
||
}
|
||
}
|
||
|
||
if (cont && identity) {
|
||
if (ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity) < 0) {
|
||
cont = 0;
|
||
}
|
||
}
|
||
|
||
if (port != NULL) {
|
||
ssh_options_set(session, SSH_OPTIONS_PORT_STR, port);
|
||
}
|
||
|
||
if (!cont) {
|
||
return SSH_ERROR;
|
||
}
|
||
|
||
return SSH_OK;
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief Parse the ssh config file.
|
||
*
|
||
* This should be the last call of all options, it may overwrite options which
|
||
* are already set. It requires that the host name is already set with
|
||
* ssh_options_set(SSH_OPTIONS_HOST).
|
||
*
|
||
* @param session SSH session handle
|
||
*
|
||
* @param filename The options file to use, if NULL the default
|
||
* ~/.ssh/config and /etc/ssh/ssh_config will be used.
|
||
* If complied with support for hermetic-usr,
|
||
* /usr/etc/ssh/ssh_config will be used last.
|
||
*
|
||
* @return 0 on success, < 0 on error.
|
||
*
|
||
* @see ssh_options_set()
|
||
*/
|
||
int ssh_options_parse_config(ssh_session session, const char *filename)
|
||
{
|
||
char *expanded_filename = NULL;
|
||
int r;
|
||
FILE *fp = NULL;
|
||
|
||
if (session == NULL) {
|
||
return -1;
|
||
}
|
||
if (session->opts.host == NULL) {
|
||
ssh_set_error_invalid(session);
|
||
return -1;
|
||
}
|
||
|
||
if (session->opts.sshdir == NULL) {
|
||
r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL);
|
||
if (r < 0) {
|
||
ssh_set_error_oom(session);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* set default filename */
|
||
if (filename == NULL) {
|
||
expanded_filename = ssh_path_expand_escape(session, "%d/.ssh/config");
|
||
} else {
|
||
expanded_filename = ssh_path_expand_escape(session, filename);
|
||
}
|
||
if (expanded_filename == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
r = ssh_config_parse_file(session, expanded_filename);
|
||
if (r < 0) {
|
||
goto out;
|
||
}
|
||
if (filename == NULL) {
|
||
fp = ssh_strict_fopen(GLOBAL_CLIENT_CONFIG, SSH_MAX_CONFIG_FILE_SIZE);
|
||
if (fp != NULL) {
|
||
filename = GLOBAL_CLIENT_CONFIG;
|
||
#ifdef USR_GLOBAL_CLIENT_CONFIG
|
||
} else {
|
||
fp = ssh_strict_fopen(USR_GLOBAL_CLIENT_CONFIG,
|
||
SSH_MAX_CONFIG_FILE_SIZE);
|
||
if (fp != NULL) {
|
||
filename = USR_GLOBAL_CLIENT_CONFIG;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
if (fp) {
|
||
SSH_LOG(SSH_LOG_PACKET,
|
||
"Reading configuration data from %s",
|
||
filename);
|
||
r = ssh_config_parse(session, fp, true);
|
||
fclose(fp);
|
||
}
|
||
}
|
||
|
||
/* Do not process the default configuration as part of connection again */
|
||
session->opts.config_processed = true;
|
||
out:
|
||
free(expanded_filename);
|
||
return r;
|
||
}
|
||
|
||
int ssh_options_apply(ssh_session session)
|
||
{
|
||
char *tmp = NULL;
|
||
int rc;
|
||
|
||
if (session->opts.sshdir == NULL) {
|
||
rc = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (session->opts.username == NULL) {
|
||
rc = ssh_options_set(session, SSH_OPTIONS_USER, NULL);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
|
||
if (session->opts.knownhosts == NULL) {
|
||
tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
|
||
} else {
|
||
tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
|
||
}
|
||
if (tmp == NULL) {
|
||
return -1;
|
||
}
|
||
free(session->opts.knownhosts);
|
||
session->opts.knownhosts = tmp;
|
||
session->opts.exp_flags |= SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||
}
|
||
|
||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
|
||
if (session->opts.global_knownhosts == NULL) {
|
||
tmp = strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
|
||
} else {
|
||
tmp = ssh_path_expand_escape(session,
|
||
session->opts.global_knownhosts);
|
||
}
|
||
if (tmp == NULL) {
|
||
return -1;
|
||
}
|
||
free(session->opts.global_knownhosts);
|
||
session->opts.global_knownhosts = tmp;
|
||
session->opts.exp_flags |= SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
|
||
}
|
||
|
||
|
||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND) == 0) {
|
||
if (session->opts.ProxyCommand != NULL) {
|
||
char *p = NULL;
|
||
size_t plen = strlen(session->opts.ProxyCommand) +
|
||
5 /* strlen("exec ") */;
|
||
|
||
if (strncmp(session->opts.ProxyCommand, "exec ", 5) != 0) {
|
||
p = malloc(plen + 1 /* \0 */);
|
||
if (p == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
|
||
if ((size_t)rc != plen) {
|
||
free(p);
|
||
return -1;
|
||
}
|
||
tmp = ssh_path_expand_escape(session, p);
|
||
free(p);
|
||
} else {
|
||
tmp = ssh_path_expand_escape(session,
|
||
session->opts.ProxyCommand);
|
||
}
|
||
|
||
if (tmp == NULL) {
|
||
return -1;
|
||
}
|
||
free(session->opts.ProxyCommand);
|
||
session->opts.ProxyCommand = tmp;
|
||
session->opts.exp_flags |= SSH_OPT_EXP_FLAG_PROXYCOMMAND;
|
||
}
|
||
}
|
||
|
||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_CONTROL_PATH) == 0) {
|
||
if (session->opts.control_path != NULL) {
|
||
tmp = ssh_path_expand_escape(session, session->opts.control_path);
|
||
if (tmp == NULL) {
|
||
return -1;
|
||
}
|
||
free(session->opts.control_path);
|
||
session->opts.control_path = tmp;
|
||
session->opts.exp_flags |= SSH_OPT_EXP_FLAG_CONTROL_PATH;
|
||
}
|
||
}
|
||
|
||
for (tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp);
|
||
tmp != NULL;
|
||
tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
|
||
char *id = tmp;
|
||
if (strncmp(id, "pkcs11:", 6) != 0) {
|
||
/* PKCS#11 URIs are using percent-encoding so we can not mix
|
||
* it with ssh expansion of ssh escape characters.
|
||
*/
|
||
tmp = ssh_path_expand_escape(session, id);
|
||
free(id);
|
||
if (tmp == NULL) {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* use append to keep the order at first call and use prepend
|
||
* to put anything that comes on the nth calls to the beginning */
|
||
if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
|
||
rc = ssh_list_prepend(session->opts.identity, tmp);
|
||
} else {
|
||
rc = ssh_list_append(session->opts.identity, tmp);
|
||
}
|
||
if (rc != SSH_OK) {
|
||
free(tmp);
|
||
return -1;
|
||
}
|
||
}
|
||
session->opts.exp_flags |= SSH_OPT_EXP_FLAG_IDENTITY;
|
||
|
||
for (tmp = ssh_list_pop_head(char *, session->opts.certificate_non_exp);
|
||
tmp != NULL;
|
||
tmp = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) {
|
||
char *id = tmp;
|
||
|
||
tmp = ssh_path_expand_escape(session, id);
|
||
free(id);
|
||
if (tmp == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
rc = ssh_list_append(session->opts.certificate, tmp);
|
||
if (rc != SSH_OK) {
|
||
free(tmp);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
#ifdef WITH_GSSAPI
|
||
if (session->opts.gssapi_key_exchange) {
|
||
rc = ssh_gssapi_check_client_config(session);
|
||
if (rc != SSH_OK) {
|
||
SSH_LOG(SSH_LOG_WARN, "Disabled GSSAPI key exchange");
|
||
session->opts.gssapi_key_exchange = false;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
|
||
/** @} */
|
||
|
||
#ifdef WITH_SERVER
|
||
static bool ssh_bind_key_size_allowed(ssh_bind sshbind, ssh_key key)
|
||
{
|
||
int min_size = 0;
|
||
|
||
switch (ssh_key_type(key)) {
|
||
case SSH_KEYTYPE_RSA:
|
||
case SSH_KEYTYPE_RSA_CERT01:
|
||
min_size = sshbind->rsa_min_size;
|
||
return ssh_key_size_allowed_rsa(min_size, key);
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @addtogroup libssh_server
|
||
* @{
|
||
*/
|
||
static int
|
||
ssh_bind_set_key(ssh_bind sshbind, char **key_loc, const void *value)
|
||
{
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(*key_loc);
|
||
*key_loc = strdup(value);
|
||
if (*key_loc == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int ssh_bind_set_algo(ssh_bind sshbind,
|
||
enum ssh_kex_types_e algo,
|
||
const char *list,
|
||
char **place)
|
||
{
|
||
/* sshbind is needed only for ssh_set_error which takes void*
|
||
* the typecast is only to satisfy function parameter type */
|
||
return ssh_options_set_algo((ssh_session)sshbind, algo, list, place);
|
||
}
|
||
|
||
/**
|
||
* @brief Set options for an SSH server bind.
|
||
*
|
||
* @param sshbind The ssh server bind to configure.
|
||
*
|
||
* @param type The option type to set. This should be one of the
|
||
* following:
|
||
*
|
||
* - SSH_BIND_OPTIONS_HOSTKEY:
|
||
* Set the path to an ssh host key, regardless
|
||
* of type. Only one key from per key type
|
||
* (RSA, ED25519 and ECDSA) is allowed in an ssh_bind
|
||
* at a time, and later calls to this function
|
||
* with this option for the same key type will
|
||
* override prior calls (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_BINDADDR:
|
||
* Set the IP address to bind (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_BINDPORT:
|
||
* Set the port to bind (unsigned int).
|
||
*
|
||
* - SSH_BIND_OPTIONS_BINDPORT_STR:
|
||
* Set the port to bind (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_LOG_VERBOSITY:
|
||
* Set the session logging verbosity (int).
|
||
* The logging verbosity should have one of the
|
||
* following values, which are listed in order
|
||
* of increasing verbosity. Every log message
|
||
* with verbosity less than or equal to the
|
||
* logging verbosity will be shown.
|
||
* - SSH_LOG_NOLOG: No logging
|
||
* - SSH_LOG_WARNING: Only warnings
|
||
* - SSH_LOG_PROTOCOL: High level protocol information
|
||
* - SSH_LOG_PACKET: Lower level protocol information,
|
||
* packet level
|
||
* - SSH_LOG_FUNCTIONS: Every function path
|
||
* The default is SSH_LOG_NOLOG.
|
||
*
|
||
* - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR:
|
||
* Set the session logging verbosity via a
|
||
* string that will be converted to a numerical
|
||
* value (e.g. "3") and interpreted according
|
||
* to the values of
|
||
* SSH_BIND_OPTIONS_LOG_VERBOSITY above
|
||
* (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_RSAKEY:
|
||
* Deprecated alias to SSH_BIND_OPTIONS_HOSTKEY
|
||
* (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_ECDSAKEY:
|
||
* Deprecated alias to SSH_BIND_OPTIONS_HOSTKEY
|
||
* (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_BANNER:
|
||
* Set the server banner sent to clients (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_DSAKEY:
|
||
* This is DEPRECATED, please do not use.
|
||
*
|
||
* - SSH_BIND_OPTIONS_IMPORT_KEY:
|
||
* Set the Private Key for the server directly
|
||
* (ssh_key). It will be free'd by ssh_bind_free().
|
||
*
|
||
* - SSH_BIND_OPTIONS_IMPORT_KEY_STR:
|
||
* Set the Private key for the server from a
|
||
* base64 encoded buffer (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_CIPHERS_C_S:
|
||
* Set the symmetric cipher client to server
|
||
* (const char *, comma-separated list).
|
||
*
|
||
* - SSH_BIND_OPTIONS_CIPHERS_S_C:
|
||
* Set the symmetric cipher server to client
|
||
* (const char *, comma-separated list).
|
||
*
|
||
* - SSH_BIND_OPTIONS_KEY_EXCHANGE:
|
||
* Set the key exchange method to be used
|
||
* (const char *, comma-separated list). ex:
|
||
* "ecdh-sha2-nistp256,diffie-hellman-group14-sha1"
|
||
*
|
||
* - SSH_BIND_OPTIONS_HMAC_C_S:
|
||
* Set the Message Authentication Code algorithm client
|
||
* to server (const char *, comma-separated list).
|
||
*
|
||
* - SSH_BIND_OPTIONS_HMAC_S_C:
|
||
* Set the Message Authentication Code algorithm server
|
||
* to client (const char *, comma-separated list).
|
||
*
|
||
* - SSH_BIND_OPTIONS_CONFIG_DIR:
|
||
* Set the directory (const char *, format string)
|
||
* to be used when the "%d" scape is used when providing
|
||
* paths of configuration files to
|
||
* ssh_bind_options_parse_config().
|
||
*
|
||
* - SSH_BIND_OPTIONS_PROCESS_CONFIG
|
||
* Set it to false to disable automatic processing of
|
||
* system-wide configuration files. LibSSH automatically
|
||
* uses these configuration files otherwise. This
|
||
* option will only have effect if set before any call
|
||
* to ssh_bind_options_parse_config() (bool).
|
||
*
|
||
* - SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES:
|
||
* Set the public key algorithm accepted by the server
|
||
* (const char *, comma-separated list).
|
||
*
|
||
* - SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS:
|
||
* Set the list of allowed hostkey signatures algorithms
|
||
* to offer to the client, ordered by preference. This
|
||
* list is used as a filter when creating the list of
|
||
* algorithms to offer to the client: first the list of
|
||
* possible algorithms is created from the list of keys
|
||
* set and then filtered against this list.
|
||
* (const char *, comma-separated list).
|
||
*
|
||
* - SSH_BIND_OPTIONS_MODULI
|
||
* Set the path to the moduli file. Defaults to
|
||
* /etc/ssh/moduli if not specified (const char *).
|
||
*
|
||
* - SSH_BIND_OPTIONS_RSA_MIN_SIZE
|
||
* Set the minimum RSA key size in bits to be accepted by
|
||
* the server for both authentication and hostkey
|
||
* operations. The values under 1024 bits are not accepted
|
||
* even with this configuration option as they are
|
||
* considered completely broken. Setting 0 will revert
|
||
* the value to defaults.
|
||
* Default is 3072 bits or 2048 bits in FIPS mode.
|
||
* (int)
|
||
*
|
||
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE
|
||
* Set true to enable GSSAPI key exchange,
|
||
* false to disable GSSAPI key exchange. (bool)
|
||
*
|
||
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
|
||
* Set the GSSAPI key exchange method to be used
|
||
* (const char *, comma-separated list).
|
||
* ex: "gss-group14-sha256-,gss-group16-sha512-"
|
||
*
|
||
* @param value The value to set. This is a generic pointer and the
|
||
* datatype which should be used is described at the
|
||
* corresponding value of type above.
|
||
*
|
||
* @return 0 on success, < 0 on error, invalid option, or
|
||
* parameter.
|
||
*
|
||
* @warning When the option value to set is represented via a
|
||
* pointer (e.g const char * in case of strings, ssh_key
|
||
* in case of a libssh key), the value parameter should be
|
||
* that pointer. Do NOT pass a pointer to a pointer (const
|
||
* char **, ssh_key *)
|
||
*
|
||
* @warning When the option value to set is not a pointer (e.g int,
|
||
* unsigned int, bool, long), the value parameter should be
|
||
* a pointer to the location storing the value to set (int
|
||
* *, unsigned int *, bool *, long *)
|
||
*
|
||
* @warning If the value parameter has an invalid type (e.g if its
|
||
* not a pointer when it should have been a pointer, or if
|
||
* its a pointer to a pointer when it should have just been
|
||
* a pointer), then the behaviour is undefined.
|
||
*
|
||
* @warning Options set via this function may be overridden if a
|
||
* configuration file is parsed afterwards (e.g., by an
|
||
* implicit call to ssh_bind_options_parse_config() inside
|
||
* ssh_bind_listen(), or by a manual call to the same
|
||
* function) and contains the same options.\n
|
||
* It is the caller’s responsibility to ensure the correct
|
||
* order of API calls if explicit options must take
|
||
* precedence.
|
||
*/
|
||
int
|
||
ssh_bind_options_set(ssh_bind sshbind,
|
||
enum ssh_bind_options_e type,
|
||
const void *value)
|
||
{
|
||
bool allowed;
|
||
char *p = NULL, *q = NULL;
|
||
const char *v = NULL;
|
||
int i, rc;
|
||
char **wanted_methods = sshbind->wanted_methods;
|
||
|
||
if (sshbind == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
switch (type) {
|
||
case SSH_BIND_OPTIONS_RSAKEY:
|
||
case SSH_BIND_OPTIONS_ECDSAKEY:
|
||
/* deprecated */
|
||
case SSH_BIND_OPTIONS_HOSTKEY:
|
||
case SSH_BIND_OPTIONS_IMPORT_KEY:
|
||
case SSH_BIND_OPTIONS_IMPORT_KEY_STR:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
int key_type;
|
||
ssh_key *bind_key_loc = NULL;
|
||
ssh_key key = NULL;
|
||
char **bind_key_path_loc = NULL;
|
||
|
||
if (type == SSH_BIND_OPTIONS_IMPORT_KEY_STR) {
|
||
const char *key_str = (const char *)value;
|
||
rc = ssh_pki_import_privkey_base64(key_str,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&key);
|
||
if (rc == SSH_ERROR) {
|
||
ssh_set_error(sshbind,
|
||
SSH_FATAL,
|
||
"Failed to import key from buffer");
|
||
return -1;
|
||
}
|
||
} else if (type == SSH_BIND_OPTIONS_IMPORT_KEY) {
|
||
key = (ssh_key)value;
|
||
} else {
|
||
rc = ssh_pki_import_privkey_file(value, NULL, NULL, NULL, &key);
|
||
if (rc != SSH_OK) {
|
||
return -1;
|
||
}
|
||
}
|
||
allowed = ssh_bind_key_size_allowed(sshbind, key);
|
||
if (!allowed) {
|
||
ssh_set_error(sshbind,
|
||
SSH_FATAL,
|
||
"The host key size %d is too small.",
|
||
ssh_key_size(key));
|
||
if (type != SSH_BIND_OPTIONS_IMPORT_KEY) {
|
||
SSH_KEY_FREE(key);
|
||
}
|
||
return -1;
|
||
}
|
||
key_type = ssh_key_type(key);
|
||
switch (key_type) {
|
||
case SSH_KEYTYPE_ECDSA_P256:
|
||
case SSH_KEYTYPE_ECDSA_P384:
|
||
case SSH_KEYTYPE_ECDSA_P521:
|
||
#ifdef HAVE_ECC
|
||
bind_key_loc = &sshbind->ecdsa;
|
||
bind_key_path_loc = &sshbind->ecdsakey;
|
||
#else
|
||
ssh_set_error(sshbind,
|
||
SSH_FATAL,
|
||
"ECDSA key used and libssh compiled "
|
||
"without ECDSA support");
|
||
#endif
|
||
break;
|
||
case SSH_KEYTYPE_RSA:
|
||
bind_key_loc = &sshbind->rsa;
|
||
bind_key_path_loc = &sshbind->rsakey;
|
||
break;
|
||
case SSH_KEYTYPE_ED25519:
|
||
bind_key_loc = &sshbind->ed25519;
|
||
bind_key_path_loc = &sshbind->ed25519key;
|
||
break;
|
||
default:
|
||
ssh_set_error(sshbind,
|
||
SSH_FATAL,
|
||
"Unsupported key type %d",
|
||
key_type);
|
||
}
|
||
if (type == SSH_BIND_OPTIONS_RSAKEY ||
|
||
type == SSH_BIND_OPTIONS_ECDSAKEY ||
|
||
type == SSH_BIND_OPTIONS_HOSTKEY) {
|
||
if (bind_key_loc == NULL) {
|
||
ssh_key_free(key);
|
||
return -1;
|
||
}
|
||
/* Set the location of the key on disk even though we don't
|
||
need it in case some other function wants it */
|
||
rc = ssh_bind_set_key(sshbind, bind_key_path_loc, value);
|
||
if (rc < 0) {
|
||
ssh_key_free(key);
|
||
return -1;
|
||
}
|
||
} else if (type == SSH_BIND_OPTIONS_IMPORT_KEY_STR) {
|
||
if (bind_key_loc == NULL) {
|
||
ssh_key_free(key);
|
||
return -1;
|
||
}
|
||
} else {
|
||
if (bind_key_loc == NULL) {
|
||
return -1;
|
||
}
|
||
}
|
||
ssh_key_free(*bind_key_loc);
|
||
*bind_key_loc = key;
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_BINDADDR:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(sshbind->bindaddr);
|
||
sshbind->bindaddr = strdup(value);
|
||
if (sshbind->bindaddr == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_BINDPORT:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *)value;
|
||
sshbind->bindport = *x & 0xffffU;
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_BINDPORT_STR:
|
||
if (value == NULL) {
|
||
sshbind->bindport = 22 & 0xffffU;
|
||
} else {
|
||
q = strdup(value);
|
||
if (q == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
i = strtol(q, &p, 10);
|
||
if (q == p) {
|
||
SSH_LOG(SSH_LOG_DEBUG, "No bind port was parsed");
|
||
SAFE_FREE(q);
|
||
return -1;
|
||
}
|
||
SAFE_FREE(q);
|
||
|
||
sshbind->bindport = i & 0xffffU;
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_LOG_VERBOSITY:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *)value;
|
||
ssh_set_log_level(*x & 0xffffU);
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR:
|
||
if (value == NULL) {
|
||
ssh_set_log_level(0);
|
||
} else {
|
||
q = strdup(value);
|
||
if (q == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
i = strtol(q, &p, 10);
|
||
if (q == p) {
|
||
SSH_LOG(SSH_LOG_DEBUG, "No log verbositiy was parsed");
|
||
SAFE_FREE(q);
|
||
return -1;
|
||
}
|
||
SAFE_FREE(q);
|
||
|
||
ssh_set_log_level(i & 0xffffU);
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_BANNER:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(sshbind->banner);
|
||
sshbind->banner = strdup(value);
|
||
if (sshbind->banner == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_CIPHERS_C_S:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_CRYPT_C_S,
|
||
v,
|
||
&wanted_methods[SSH_CRYPT_C_S]);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_CIPHERS_S_C:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_CRYPT_S_C,
|
||
v,
|
||
&wanted_methods[SSH_CRYPT_S_C]);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_KEY_EXCHANGE:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_KEX,
|
||
v,
|
||
&wanted_methods[SSH_KEX]);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_HMAC_C_S:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_MAC_C_S,
|
||
v,
|
||
&wanted_methods[SSH_MAC_C_S]);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_HMAC_S_C:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_MAC_S_C,
|
||
v,
|
||
&wanted_methods[SSH_MAC_S_C]);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_CONFIG_DIR:
|
||
v = value;
|
||
SAFE_FREE(sshbind->config_dir);
|
||
if (v == NULL) {
|
||
break;
|
||
} else if (v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
sshbind->config_dir = ssh_path_expand_tilde(v);
|
||
if (sshbind->config_dir == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_HOSTKEYS,
|
||
v,
|
||
&sshbind->pubkey_accepted_key_types);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS:
|
||
v = value;
|
||
if (v == NULL || v[0] == '\0') {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
rc = ssh_bind_set_algo(sshbind,
|
||
SSH_HOSTKEYS,
|
||
v,
|
||
&wanted_methods[SSH_HOSTKEYS]);
|
||
if (rc < 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_PROCESS_CONFIG:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
bool *x = (bool *)value;
|
||
sshbind->config_processed = !(*x);
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_MODULI:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
SAFE_FREE(sshbind->moduli_file);
|
||
sshbind->moduli_file = strdup(value);
|
||
if (sshbind->moduli_file == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return -1;
|
||
}
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_RSA_MIN_SIZE:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
int *x = (int *)value;
|
||
|
||
if (*x < 0) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
}
|
||
|
||
/* (*x == 0) is allowed as it is used to revert to default */
|
||
|
||
if (*x > 0 && *x < RSA_MIN_KEY_SIZE) {
|
||
ssh_set_error(sshbind,
|
||
SSH_REQUEST_DENIED,
|
||
"The provided value (%d) for minimal RSA key "
|
||
"size is too small. Use at least %d bits.",
|
||
*x,
|
||
RSA_MIN_KEY_SIZE);
|
||
return -1;
|
||
}
|
||
sshbind->rsa_min_size = *x;
|
||
}
|
||
break;
|
||
#ifdef WITH_GSSAPI
|
||
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
bool *x = (bool *)value;
|
||
sshbind->gssapi_key_exchange = *x;
|
||
}
|
||
break;
|
||
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
|
||
if (value == NULL) {
|
||
ssh_set_error_invalid(sshbind);
|
||
return -1;
|
||
} else {
|
||
char *ret = NULL;
|
||
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
|
||
ret = ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, value);
|
||
if (ret == NULL) {
|
||
ssh_set_error(
|
||
sshbind,
|
||
SSH_REQUEST_DENIED,
|
||
"GSSAPI key exchange algorithms not supported or invalid");
|
||
return -1;
|
||
}
|
||
sshbind->gssapi_key_exchange_algs = ret;
|
||
}
|
||
break;
|
||
#endif /* WITH_GSSAPI */
|
||
default:
|
||
ssh_set_error(sshbind,
|
||
SSH_REQUEST_DENIED,
|
||
"Unknown ssh option %d",
|
||
type);
|
||
return -1;
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
|
||
{
|
||
char *buf = NULL;
|
||
char *r = NULL;
|
||
char *x = NULL;
|
||
const char *p = NULL;
|
||
size_t i, l;
|
||
|
||
r = ssh_path_expand_tilde(s);
|
||
if (r == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
return NULL;
|
||
}
|
||
|
||
if (strlen(r) > MAX_BUF_SIZE) {
|
||
ssh_set_error(sshbind, SSH_FATAL, "string to expand too long");
|
||
free(r);
|
||
return NULL;
|
||
}
|
||
|
||
buf = malloc(MAX_BUF_SIZE);
|
||
if (buf == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
free(r);
|
||
return NULL;
|
||
}
|
||
|
||
p = r;
|
||
buf[0] = '\0';
|
||
|
||
for (i = 0; *p != '\0'; p++) {
|
||
if (*p != '%') {
|
||
buf[i] = *p;
|
||
i++;
|
||
if (i >= MAX_BUF_SIZE) {
|
||
free(buf);
|
||
free(r);
|
||
return NULL;
|
||
}
|
||
buf[i] = '\0';
|
||
continue;
|
||
}
|
||
|
||
p++;
|
||
if (*p == '\0') {
|
||
break;
|
||
}
|
||
|
||
switch (*p) {
|
||
case 'd':
|
||
x = strdup(sshbind->config_dir);
|
||
break;
|
||
default:
|
||
ssh_set_error(sshbind, SSH_FATAL,
|
||
"Wrong escape sequence detected");
|
||
free(buf);
|
||
free(r);
|
||
return NULL;
|
||
}
|
||
|
||
if (x == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
free(buf);
|
||
free(r);
|
||
return NULL;
|
||
}
|
||
|
||
i += strlen(x);
|
||
if (i >= MAX_BUF_SIZE) {
|
||
ssh_set_error(sshbind, SSH_FATAL,
|
||
"String too long");
|
||
free(buf);
|
||
free(x);
|
||
free(r);
|
||
return NULL;
|
||
}
|
||
l = strlen(buf);
|
||
strncpy(buf + l, x, MAX_BUF_SIZE - l - 1);
|
||
buf[i] = '\0';
|
||
SAFE_FREE(x);
|
||
}
|
||
|
||
free(r);
|
||
|
||
/* strip the unused space by realloc */
|
||
x = realloc(buf, strlen(buf) + 1);
|
||
if (x == NULL) {
|
||
ssh_set_error_oom(sshbind);
|
||
free(buf);
|
||
}
|
||
return x;
|
||
}
|
||
|
||
/**
|
||
* @brief Parse a ssh bind options configuration file.
|
||
*
|
||
* This parses the options file and set them to the ssh_bind handle provided. If
|
||
* an option was previously set, it is overridden. If the global configuration
|
||
* hasn't been processed yet, it is processed prior to the provided file.
|
||
*
|
||
* @param sshbind SSH bind handle
|
||
*
|
||
* @param filename The options file to use; if NULL only the global
|
||
* configuration is parsed and applied (if it hasn't been
|
||
* processed before).
|
||
*
|
||
* @return 0 on success, < 0 on error.
|
||
*/
|
||
int ssh_bind_options_parse_config(ssh_bind sshbind, const char *filename)
|
||
{
|
||
int rc = 0;
|
||
char *expanded_filename = NULL;
|
||
|
||
if (sshbind == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
/* If the global default configuration hasn't been processed yet, process it
|
||
* before the provided configuration. */
|
||
if (!(sshbind->config_processed)) {
|
||
if (ssh_file_readaccess_ok(GLOBAL_BIND_CONFIG)) {
|
||
rc = ssh_bind_config_parse_file(sshbind, GLOBAL_BIND_CONFIG);
|
||
#ifdef USR_GLOBAL_BIND_CONFIG
|
||
} else {
|
||
rc = ssh_bind_config_parse_file(sshbind, USR_GLOBAL_BIND_CONFIG);
|
||
#endif
|
||
}
|
||
if (rc != 0) {
|
||
return rc;
|
||
}
|
||
sshbind->config_processed = true;
|
||
}
|
||
|
||
if (filename != NULL) {
|
||
expanded_filename = ssh_bind_options_expand_escape(sshbind, filename);
|
||
if (expanded_filename == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
/* Apply the user provided configuration */
|
||
rc = ssh_bind_config_parse_file(sshbind, expanded_filename);
|
||
free(expanded_filename);
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
#endif
|
||
|
||
/** @} */
|