mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-11-26 01:03:15 +03:00
add control master and path option
Signed-off-by: Ahsen Kamal <itsahsenkamal@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Norbert Pocs <npocs@redhat.com>
This commit is contained in:
@@ -63,6 +63,8 @@ enum ssh_config_opcode_e {
|
||||
SOC_REKEYLIMIT,
|
||||
SOC_IDENTITYAGENT,
|
||||
SOC_IDENTITIESONLY,
|
||||
SOC_CONTROLMASTER,
|
||||
SOC_CONTROLPATH,
|
||||
|
||||
SOC_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
@@ -360,6 +360,14 @@ enum {
|
||||
|
||||
/** @} */
|
||||
|
||||
enum ssh_control_master_options_e {
|
||||
SSH_CONTROL_MASTER_NO,
|
||||
SSH_CONTROL_MASTER_AUTO,
|
||||
SSH_CONTROL_MASTER_YES,
|
||||
SSH_CONTROL_MASTER_ASK,
|
||||
SSH_CONTROL_MASTER_AUTOASK
|
||||
};
|
||||
|
||||
enum ssh_options_e {
|
||||
SSH_OPTIONS_HOST,
|
||||
SSH_OPTIONS_PORT,
|
||||
@@ -405,6 +413,8 @@ enum ssh_options_e {
|
||||
SSH_OPTIONS_RSA_MIN_SIZE,
|
||||
SSH_OPTIONS_IDENTITY_AGENT,
|
||||
SSH_OPTIONS_IDENTITIES_ONLY,
|
||||
SSH_OPTIONS_CONTROL_MASTER,
|
||||
SSH_OPTIONS_CONTROL_PATH,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -106,6 +106,7 @@ enum ssh_pending_call_e {
|
||||
#define SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS 0x2
|
||||
#define SSH_OPT_EXP_FLAG_PROXYCOMMAND 0x4
|
||||
#define SSH_OPT_EXP_FLAG_IDENTITY 0x8
|
||||
#define SSH_OPT_EXP_FLAG_CONTROL_PATH 0x10
|
||||
|
||||
/* extensions flags */
|
||||
/* negotiation enabled */
|
||||
@@ -260,6 +261,8 @@ struct ssh_session_struct {
|
||||
uint32_t rekey_time;
|
||||
int rsa_min_size;
|
||||
bool identities_only;
|
||||
int control_master;
|
||||
char *control_path;
|
||||
} opts;
|
||||
/* counters */
|
||||
ssh_counter socket_counter;
|
||||
|
||||
36
src/config.c
36
src/config.c
@@ -127,9 +127,9 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{ "verifyhostkeydns", SOC_UNSUPPORTED},
|
||||
{ "visualhostkey", SOC_UNSUPPORTED},
|
||||
{ "clearallforwardings", SOC_NA},
|
||||
{ "controlmaster", SOC_NA},
|
||||
{ "controlmaster", SOC_CONTROLMASTER},
|
||||
{ "controlpersist", SOC_NA},
|
||||
{ "controlpath", SOC_NA},
|
||||
{ "controlpath", SOC_CONTROLPATH},
|
||||
{ "dynamicforward", SOC_NA},
|
||||
{ "escapechar", SOC_NA},
|
||||
{ "exitonforwardfailure", SOC_NA},
|
||||
@@ -1186,6 +1186,38 @@ ssh_config_parse_line(ssh_session session,
|
||||
ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
|
||||
}
|
||||
break;
|
||||
case SOC_CONTROLMASTER:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
int value = -1;
|
||||
|
||||
if (strcasecmp(p, "auto") == 0) {
|
||||
value = SSH_CONTROL_MASTER_AUTO;
|
||||
} else if (strcasecmp(p, "yes") == 0) {
|
||||
value = SSH_CONTROL_MASTER_YES;
|
||||
} else if (strcasecmp(p, "no") == 0) {
|
||||
value = SSH_CONTROL_MASTER_NO;
|
||||
} else if (strcasecmp(p, "autoask") == 0) {
|
||||
value = SSH_CONTROL_MASTER_AUTOASK;
|
||||
} else if (strcasecmp(p, "ask") == 0) {
|
||||
value = SSH_CONTROL_MASTER_ASK;
|
||||
}
|
||||
|
||||
if (value != -1) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOC_CONTROLPATH:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p == NULL) {
|
||||
SAFE_FREE(x);
|
||||
return -1;
|
||||
}
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, p);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
|
||||
opcode);
|
||||
|
||||
@@ -204,6 +204,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
@@ -217,6 +225,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
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->common.log_verbosity = src->common.log_verbosity;
|
||||
new->common.callbacks = src->common.callbacks;
|
||||
|
||||
@@ -536,6 +545,26 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* 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.
|
||||
*
|
||||
* 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 *)
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
@@ -1173,6 +1202,37 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
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;
|
||||
default:
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
|
||||
return -1;
|
||||
@@ -1247,6 +1307,9 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
|
||||
* - 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.
|
||||
*
|
||||
* @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
|
||||
@@ -1301,6 +1364,10 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
|
||||
src = session->opts.global_knownhosts;
|
||||
break;
|
||||
}
|
||||
case SSH_OPTIONS_CONTROL_PATH: {
|
||||
src = session->opts.control_path;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
|
||||
return SSH_ERROR;
|
||||
@@ -1646,6 +1713,18 @@ int ssh_options_apply(ssh_session session)
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -109,6 +109,7 @@ ssh_session ssh_new(void)
|
||||
session->opts.compressionlevel = 7;
|
||||
session->opts.nodelay = 0;
|
||||
session->opts.identities_only = false;
|
||||
session->opts.control_master = SSH_CONTROL_MASTER_NO;
|
||||
|
||||
session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH |
|
||||
SSH_OPT_FLAG_PUBKEY_AUTH |
|
||||
@@ -320,6 +321,7 @@ void ssh_free(ssh_session session)
|
||||
SAFE_FREE(session->opts.gss_server_identity);
|
||||
SAFE_FREE(session->opts.gss_client_identity);
|
||||
SAFE_FREE(session->opts.pubkey_accepted_types);
|
||||
SAFE_FREE(session->opts.control_path);
|
||||
|
||||
for (i = 0; i < SSH_KEX_METHODS; i++) {
|
||||
if (session->opts.wanted_methods[i]) {
|
||||
|
||||
Reference in New Issue
Block a user