#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <pwd.h>
#else
#include <winsock2.h>
#endif
#include <sys/types.h>
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/options.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/bind.h"
#include "libssh/bind_config.h"
#endif
int ssh_options_copy(ssh_session src, ssh_session *dest)
{
ssh_session new;
struct ssh_iterator *it = 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;
}
}
for (id = ssh_list_pop_head(char *, new->opts.identity);
id != NULL;
id = ssh_list_pop_head(char *, new->opts.identity)) {
SAFE_FREE(id);
}
if (src->opts.identity != NULL) {
it = ssh_list_get_iterator(src->opts.identity);
while (it) {
int rc;
id = strdup((char *) it->data);
if (id == NULL) {
ssh_free(new);
return -1;
}
rc = ssh_list_append(new->opts.identity, id);
if (rc < 0) {
free(id);
ssh_free(new);
return -1;
}
it = it->next;
}
}
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;
}
}
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->common.log_verbosity = src->common.log_verbosity;
new->common.callbacks = src->common.callbacks;
*dest = new;
return 0;
}
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
const char *list)
{
char *p = NULL;
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(session->opts.wanted_methods[algo]);
session->opts.wanted_methods[algo] = p;
return 0;
}
int ssh_options_set(ssh_session session, enum ssh_options_e type,
const void *value) {
const char *v;
char *p, *q;
long int i;
unsigned int u;
int rc;
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 {
q = strdup(value);
if (q == NULL) {
ssh_set_error_oom(session);
return -1;
}
p = strchr(q, '@');
SAFE_FREE(session->opts.host);
if (p) {
*p = '\0';
session->opts.host = strdup(p + 1);
if (session->opts.host == NULL) {
SAFE_FREE(q);
ssh_set_error_oom(session);
return -1;
}
SAFE_FREE(session->opts.username);
session->opts.username = strdup(q);
SAFE_FREE(q);
if (session->opts.username == NULL) {
ssh_set_error_oom(session);
return -1;
}
} else {
session->opts.host = q;
}
}
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) {
SAFE_FREE(q);
}
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 {
session->opts.username = strdup(value);
if (session->opts.username == NULL) {
ssh_set_error_oom(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;
}
rc = ssh_list_prepend(session->opts.identity, q);
if (rc < 0) {
free(q);
return -1;
}
break;
case SSH_OPTIONS_KNOWNHOSTS:
v = value;
SAFE_FREE(session->opts.knownhosts);
if (v == NULL) {
} 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;
}
}
break;
case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
v = value;
SAFE_FREE(session->opts.global_knownhosts);
if (v == NULL) {
session->opts.global_knownhosts =
strdup("/etc/ssh/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;
}
}
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) {
SAFE_FREE(q);
}
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 {
if (ssh_options_set_algo(session, SSH_CRYPT_C_S, v) < 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 {
if (ssh_options_set_algo(session, SSH_CRYPT_S_C, v) < 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 {
if (ssh_options_set_algo(session, SSH_KEX, v) < 0)
return -1;
}
break;
case SSH_OPTIONS_HOSTKEYS:
v = value;
if (v == NULL || v[0] == '\0') {
ssh_set_error_invalid(session);
return -1;
} else {
if (ssh_options_set_algo(session, SSH_HOSTKEYS, v) < 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 {
if (ssh_fips_mode()) {
p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
} else {
p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
}
if (p == NULL) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Setting method: no known public key algorithm (%s)",
v);
return -1;
}
SAFE_FREE(session->opts.pubkey_accepted_types);
session->opts.pubkey_accepted_types = p;
}
break;
case SSH_OPTIONS_HMAC_C_S:
v = value;
if (v == NULL || v[0] == '\0') {
ssh_set_error_invalid(session);
return -1;
} else {
if (ssh_options_set_algo(session, SSH_MAC_C_S, v) < 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 {
if (ssh_options_set_algo(session, SSH_MAC_S_C, v) < 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 {
if (strcasecmp(value,"yes")==0){
if(ssh_options_set_algo(session,SSH_COMP_C_S,"zlib@openssh.com,zlib") < 0)
return -1;
} else if (strcasecmp(value,"no")==0){
if(ssh_options_set_algo(session,SSH_COMP_C_S,"none") < 0)
return -1;
} else {
if (ssh_options_set_algo(session, SSH_COMP_C_S, v) < 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 {
if (strcasecmp(value,"yes")==0){
if(ssh_options_set_algo(session,SSH_COMP_S_C,"zlib@openssh.com,zlib") < 0)
return -1;
} else if (strcasecmp(value,"no")==0){
if(ssh_options_set_algo(session,SSH_COMP_S_C,"none") < 0)
return -1;
} else {
if (ssh_options_set_algo(session, SSH_COMP_S_C, v) < 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;
}
session->opts.StrictHostKeyChecking = *(int*)value;
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);
rc = strcasecmp(v, "none");
if (rc != 0) {
q = strdup(v);
if (q == NULL) {
return -1;
}
session->opts.ProxyCommand = q;
}
}
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;
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_IDENTITY_AGENT:
v = value;
SAFE_FREE(session->opts.agent_socket);
if (v == NULL) {
} 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;
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return -1;
break;
}
return 0;
}
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;
}
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 = ssh_list_get_iterator(session->opts.identity);
if (it == NULL) {
return SSH_ERROR;
}
src = ssh_iterator_value(char *, 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;
}
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;
}
int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
{
#ifdef _MSC_VER
(void)session;
(void)argcptr;
(void)argv;
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 usersa = 0;
int usedss = 0;
int compress = 0;
int cont = 1;
size_t current = 0;
int saveoptind = optind;
int saveopterr = opterr;
int opt;
if (argc <= 1) {
return SSH_OK;
}
opterr = 0;
while((opt = getopt(argc, argv, "c:i:Cl:p:vb:rd12")) != -1) {
switch(opt) {
case 'l':
user = optarg;
break;
case 'p':
port = optarg;
break;
case 'v':
debuglevel++;
break;
case 'r':
usersa++;
break;
case 'd':
usedss++;
break;
case 'c':
cipher = optarg;
break;
case 'i':
identity = optarg;
break;
case 'C':
compress++;
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++;
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++;
}
}
}
}
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++;
}
if (usersa && usedss) {
ssh_set_error(session, SSH_FATAL, "Either RSA or DSS must be chosen");
cont = 0;
}
ssh_set_log_level(debuglevel);
optind = saveoptind;
if(!cont) {
SAFE_FREE(save);
return -1;
}
for (i = 0; i < current; i++) {
argv[ i + 1] = save[i];
}
argv[current + 1] = NULL;
*argcptr = current + 1;
SAFE_FREE(save);
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
}
int ssh_options_parse_config(ssh_session session, const char *filename) {
char *expanded_filename;
int r;
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;
}
}
if (filename == NULL) {
expanded_filename = ssh_path_expand_escape(session, "%d/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) {
r = ssh_config_parse_file(session, GLOBAL_CLIENT_CONFIG);
}
session->opts.config_processed = true;
out:
free(expanded_filename);
return r;
}
int ssh_options_apply(ssh_session session) {
struct ssh_iterator *it;
char *tmp;
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.knownhosts == NULL) {
tmp = ssh_path_expand_escape(session, "%d/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;
if (session->opts.global_knownhosts == NULL) {
tmp = strdup("/etc/ssh/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;
if (session->opts.ProxyCommand != NULL) {
tmp = ssh_path_expand_escape(session, session->opts.ProxyCommand);
if (tmp == NULL) {
return -1;
}
free(session->opts.ProxyCommand);
session->opts.ProxyCommand = tmp;
}
for (it = ssh_list_get_iterator(session->opts.identity);
it != NULL;
it = it->next) {
char *id = (char *) it->data;
if (strncmp(id, "pkcs11:", 6) == 0) {
continue;
}
tmp = ssh_path_expand_escape(session, id);
if (tmp == NULL) {
return -1;
}
free(id);
it->data = tmp;
}
return 0;
}
#ifdef WITH_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 *p = NULL;
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(sshbind, SSH_REQUEST_DENIED,
"Setting method: no algorithm for method \"%s\" (%s)",
ssh_kex_get_description(algo), list);
return -1;
}
SAFE_FREE(sshbind->wanted_methods[algo]);
sshbind->wanted_methods[algo] = p;
return 0;
}
int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
const void *value)
{
char *p, *q;
const char *v;
int i, rc;
if (sshbind == NULL) {
return -1;
}
switch (type) {
case SSH_BIND_OPTIONS_HOSTKEY:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
int key_type;
ssh_key key;
ssh_key *bind_key_loc = NULL;
char **bind_key_path_loc;
rc = ssh_pki_import_privkey_file(value, NULL, NULL, NULL, &key);
if (rc != SSH_OK) {
return -1;
}
key_type = ssh_key_type(key);
switch (key_type) {
case SSH_KEYTYPE_DSS:
#ifdef HAVE_DSA
bind_key_loc = &sshbind->dsa;
bind_key_path_loc = &sshbind->dsakey;
#else
ssh_set_error(sshbind,
SSH_FATAL,
"DSS key used and libssh compiled "
"without DSA support");
#endif
break;
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 (bind_key_loc == NULL) {
ssh_key_free(key);
return -1;
}
rc = ssh_bind_set_key(sshbind, bind_key_path_loc, value);
if (rc < 0) {
ssh_key_free(key);
return -1;
}
ssh_key_free(*bind_key_loc);
*bind_key_loc = key;
}
break;
case SSH_BIND_OPTIONS_IMPORT_KEY:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
int key_type;
ssh_key *bind_key_loc = NULL;
ssh_key key = (ssh_key)value;
key_type = ssh_key_type(key);
switch (key_type) {
case SSH_KEYTYPE_DSS:
#ifdef HAVE_DSA
bind_key_loc = &sshbind->dsa;
#else
ssh_set_error(sshbind,
SSH_FATAL,
"DSA key used and libssh compiled "
"without DSA support");
#endif
break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
#ifdef HAVE_ECC
bind_key_loc = &sshbind->ecdsa;
#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;
break;
case SSH_KEYTYPE_ED25519:
bind_key_loc = &sshbind->ed25519;
break;
default:
ssh_set_error(sshbind,
SSH_FATAL,
"Unsupported key type %d", key_type);
}
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) {
SAFE_FREE(q);
}
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) {
SAFE_FREE(q);
}
SAFE_FREE(q);
ssh_set_log_level(i & 0xffffU);
}
break;
case SSH_BIND_OPTIONS_DSAKEY:
rc = ssh_bind_set_key(sshbind, &sshbind->dsakey, value);
if (rc < 0) {
return -1;
}
break;
case SSH_BIND_OPTIONS_RSAKEY:
rc = ssh_bind_set_key(sshbind, &sshbind->rsakey, value);
if (rc < 0) {
return -1;
}
break;
case SSH_BIND_OPTIONS_ECDSAKEY:
rc = ssh_bind_set_key(sshbind, &sshbind->ecdsakey, value);
if (rc < 0) {
return -1;
}
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 {
if (ssh_bind_set_algo(sshbind, SSH_CRYPT_C_S, v) < 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 {
if (ssh_bind_set_algo(sshbind, SSH_CRYPT_S_C, v) < 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);
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 {
if (ssh_bind_set_algo(sshbind, SSH_MAC_C_S, v) < 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 {
if (ssh_bind_set_algo(sshbind, SSH_MAC_S_C, v) < 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 {
if (ssh_fips_mode()) {
p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
} else {
p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
}
if (p == NULL) {
ssh_set_error(sshbind, SSH_REQUEST_DENIED,
"Setting method: no known public key algorithm (%s)",
v);
return -1;
}
SAFE_FREE(sshbind->pubkey_accepted_key_types);
sshbind->pubkey_accepted_key_types = p;
}
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);
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;
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;
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);
x = realloc(buf, strlen(buf) + 1);
if (x == NULL) {
ssh_set_error_oom(sshbind);
free(buf);
}
return x;
}
int ssh_bind_options_parse_config(ssh_bind sshbind, const char *filename)
{
int rc = 0;
char *expanded_filename;
if (sshbind == NULL) {
return -1;
}
if (!(sshbind->config_processed)) {
rc = ssh_bind_config_parse_file(sshbind, GLOBAL_BIND_CONFIG);
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;
}
rc = ssh_bind_config_parse_file(sshbind, expanded_filename);
free(expanded_filename);
}
return rc;
}
#endif