mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-11-26 01:03:15 +03:00
Handle automatic certificate authentication
This involves reading the certificates from configuration files through options and handling them similarly as the OpenSSH does when doing the auto pubkey authentication, also in combination with agent or identities only. Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Sahana Prasad <sahana@redhat.com>
This commit is contained in:
committed by
Sahana Prasad
parent
c1630fa097
commit
aae1bc1058
@@ -65,6 +65,7 @@ enum ssh_config_opcode_e {
|
||||
SOC_IDENTITIESONLY,
|
||||
SOC_CONTROLMASTER,
|
||||
SOC_CONTROLPATH,
|
||||
SOC_CERTIFICATE,
|
||||
|
||||
SOC_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
@@ -416,6 +416,7 @@ enum ssh_options_e {
|
||||
SSH_OPTIONS_IDENTITIES_ONLY,
|
||||
SSH_OPTIONS_CONTROL_MASTER,
|
||||
SSH_OPTIONS_CONTROL_PATH,
|
||||
SSH_OPTIONS_CERTIFICATE,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -231,6 +231,8 @@ struct ssh_session_struct {
|
||||
struct {
|
||||
struct ssh_list *identity;
|
||||
struct ssh_list *identity_non_exp;
|
||||
struct ssh_list *certificate;
|
||||
struct ssh_list *certificate_non_exp;
|
||||
char *username;
|
||||
char *host;
|
||||
char *bindaddr; /* bind the client to an ip addr */
|
||||
|
||||
232
src/auth.c
232
src/auth.c
@@ -557,6 +557,7 @@ int ssh_userauth_try_publickey(ssh_session session,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Trying signature type %s", sig_type_c);
|
||||
/* request */
|
||||
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
|
||||
SSH2_MSG_USERAUTH_REQUEST,
|
||||
@@ -690,6 +691,7 @@ int ssh_userauth_publickey(ssh_session session,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Sending signature type %s", sig_type_c);
|
||||
/* request */
|
||||
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
|
||||
SSH2_MSG_USERAUTH_REQUEST,
|
||||
@@ -859,6 +861,7 @@ fail:
|
||||
enum ssh_agent_state_e {
|
||||
SSH_AGENT_STATE_NONE = 0,
|
||||
SSH_AGENT_STATE_PUBKEY,
|
||||
SSH_AGENT_STATE_CERT,
|
||||
SSH_AGENT_STATE_AUTH
|
||||
};
|
||||
|
||||
@@ -908,7 +911,9 @@ int ssh_userauth_agent(ssh_session session,
|
||||
int rc = SSH_AUTH_ERROR;
|
||||
struct ssh_agent_state_struct *state = NULL;
|
||||
ssh_key *configKeys = NULL;
|
||||
ssh_key *configCerts = NULL;
|
||||
size_t configKeysCount = 0;
|
||||
size_t configCertsCount = 0;
|
||||
size_t i;
|
||||
|
||||
if (session == NULL) {
|
||||
@@ -944,17 +949,24 @@ int ssh_userauth_agent(ssh_session session,
|
||||
* is in there.
|
||||
*/
|
||||
size_t identityLen = ssh_list_count(session->opts.identity);
|
||||
size_t certsLen = ssh_list_count(session->opts.certificate);
|
||||
struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
|
||||
|
||||
configKeys = malloc(identityLen * sizeof(configKeys[0]));
|
||||
if (!configKeys) {
|
||||
configKeys = malloc(identityLen * sizeof(ssh_key));
|
||||
configCerts = malloc((certsLen + identityLen) * sizeof(ssh_key));
|
||||
if (configKeys == NULL || configCerts == NULL) {
|
||||
free(configKeys);
|
||||
free(configCerts);
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
while (it != NULL && configKeysCount < identityLen) {
|
||||
const char *privkeyFile = it->data;
|
||||
size_t certPathLen;
|
||||
char *certFile = NULL;
|
||||
ssh_key pubkey = NULL;
|
||||
ssh_key cert = NULL;
|
||||
|
||||
/*
|
||||
* Read the private key file listed in the config, but we're only
|
||||
@@ -967,9 +979,7 @@ int ssh_userauth_agent(ssh_session session,
|
||||
char *pubkeyFile = NULL;
|
||||
size_t pubkeyPathLen = strlen(privkeyFile) + sizeof(".pub");
|
||||
|
||||
if (pubkey) {
|
||||
SSH_KEY_FREE(pubkey);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we couldn't get the public key from the private key file,
|
||||
@@ -983,13 +993,47 @@ int ssh_userauth_agent(ssh_session session,
|
||||
}
|
||||
snprintf(pubkeyFile, pubkeyPathLen, "%s.pub", privkeyFile);
|
||||
rc = ssh_pki_import_pubkey_file(pubkeyFile, &pubkey);
|
||||
free(pubkeyFile);
|
||||
if (rc == SSH_OK) {
|
||||
configKeys[configKeysCount++] = pubkey;
|
||||
} else if (pubkey) {
|
||||
SSH_KEY_FREE(pubkey);
|
||||
}
|
||||
free(pubkeyFile);
|
||||
}
|
||||
/* Now try to see if there is a certificate with default name
|
||||
* do not merge it yet with the key as we need to try first the
|
||||
* non-certified key */
|
||||
certPathLen = strlen(privkeyFile) + sizeof("-cert.pub");
|
||||
certFile = malloc(certPathLen);
|
||||
if (!certFile) {
|
||||
ssh_set_error_oom(session);
|
||||
rc = SSH_AUTH_ERROR;
|
||||
goto done;
|
||||
}
|
||||
snprintf(certFile, certPathLen, "%s-cert.pub", privkeyFile);
|
||||
rc = ssh_pki_import_cert_file(certFile, &cert);
|
||||
free(certFile);
|
||||
if (rc == SSH_OK) {
|
||||
configCerts[configCertsCount++] = cert;
|
||||
} else if (cert) {
|
||||
SSH_KEY_FREE(cert);
|
||||
}
|
||||
|
||||
it = it->next;
|
||||
}
|
||||
/* And now load separately-listed certificates. */
|
||||
it = ssh_list_get_iterator(session->opts.certificate);
|
||||
while (it != NULL && configCertsCount < certsLen + identityLen) {
|
||||
const char *certFile = it->data;
|
||||
ssh_key cert = NULL;
|
||||
|
||||
rc = ssh_pki_import_cert_file(certFile, &cert);
|
||||
if (rc == SSH_OK) {
|
||||
configCerts[configCertsCount++] = cert;
|
||||
} else if (cert) {
|
||||
SSH_KEY_FREE(cert);
|
||||
}
|
||||
|
||||
it = it->next;
|
||||
}
|
||||
}
|
||||
@@ -1011,6 +1055,16 @@ int ssh_userauth_agent(ssh_session session,
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* or in separate certificates */
|
||||
for (i = 0; i < configCertsCount; i++) {
|
||||
int cmp = ssh_key_cmp(state->pubkey,
|
||||
configCerts[i],
|
||||
SSH_KEY_CMP_PUBLIC);
|
||||
if (cmp == 0) {
|
||||
found_key = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_key) {
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
@@ -1031,19 +1085,36 @@ int ssh_userauth_agent(ssh_session session,
|
||||
}
|
||||
}
|
||||
if (state->state == SSH_AGENT_STATE_NONE ||
|
||||
state->state == SSH_AGENT_STATE_PUBKEY) {
|
||||
state->state == SSH_AGENT_STATE_PUBKEY ||
|
||||
state->state == SSH_AGENT_STATE_CERT) {
|
||||
rc = ssh_userauth_try_publickey(session, username, state->pubkey);
|
||||
if (rc == SSH_AUTH_ERROR) {
|
||||
ssh_agent_state_free(state);
|
||||
session->agent_state = NULL;
|
||||
goto done;
|
||||
} else if (rc == SSH_AUTH_AGAIN) {
|
||||
state->state = SSH_AGENT_STATE_PUBKEY;
|
||||
state->state = (state->state == SSH_AGENT_STATE_NONE ?
|
||||
SSH_AGENT_STATE_PUBKEY : state->state);
|
||||
goto done;
|
||||
} else if (rc != SSH_AUTH_SUCCESS) {
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Public key of %s refused by server",
|
||||
state->comment);
|
||||
if (state->state == SSH_AGENT_STATE_PUBKEY) {
|
||||
for (i = 0; i < configCertsCount; i++) {
|
||||
int cmp = ssh_key_cmp(state->pubkey,
|
||||
configCerts[i],
|
||||
SSH_KEY_CMP_PUBLIC);
|
||||
if (cmp == 0) {
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Retry with matching certificate");
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
state->pubkey = ssh_key_dup(configCerts[i]);
|
||||
state->state = SSH_AGENT_STATE_CERT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
SSH_STRING_FREE_CHAR(state->comment);
|
||||
state->comment = NULL;
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
@@ -1092,6 +1163,10 @@ done:
|
||||
ssh_key_free(configKeys[i]);
|
||||
}
|
||||
free(configKeys);
|
||||
for (i = 0; i < configCertsCount; i++) {
|
||||
ssh_key_free(configCerts[i]);
|
||||
}
|
||||
free(configCerts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1099,6 +1174,8 @@ enum ssh_auth_auto_state_e {
|
||||
SSH_AUTH_AUTO_STATE_NONE = 0,
|
||||
SSH_AUTH_AUTO_STATE_PUBKEY,
|
||||
SSH_AUTH_AUTO_STATE_KEY_IMPORTED,
|
||||
SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE,
|
||||
SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION,
|
||||
SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED
|
||||
};
|
||||
|
||||
@@ -1107,6 +1184,8 @@ struct ssh_auth_auto_state_struct {
|
||||
struct ssh_iterator *it;
|
||||
ssh_key privkey;
|
||||
ssh_key pubkey;
|
||||
ssh_key cert;
|
||||
struct ssh_iterator *cert_it;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1186,6 +1265,9 @@ int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
|
||||
* @note Most server implementations do not permit changing the username during
|
||||
* authentication. The username should only be set with ssh_options_set() only
|
||||
* before you connect to the server.
|
||||
*
|
||||
* The OpenSSH iterates over the identities and first try the plain public key
|
||||
* and then the certificate if it is in place.
|
||||
*/
|
||||
int ssh_userauth_publickey_auto(ssh_session session,
|
||||
const char *username,
|
||||
@@ -1241,6 +1323,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Trying to authenticate with %s",
|
||||
privkey_file);
|
||||
state->cert = NULL;
|
||||
state->privkey = NULL;
|
||||
state->pubkey = NULL;
|
||||
|
||||
@@ -1302,7 +1385,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
rc = ssh_pki_export_privkey_to_pubkey(state->privkey,
|
||||
&state->pubkey);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_key_free(state->privkey);
|
||||
SSH_KEY_FREE(state->privkey);
|
||||
SAFE_FREE(session->auth.auto_state);
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
@@ -1316,28 +1399,101 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
}
|
||||
state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED;
|
||||
}
|
||||
if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
|
||||
rc = ssh_userauth_try_publickey(session, username, state->pubkey);
|
||||
if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED ||
|
||||
state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE ||
|
||||
state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
|
||||
ssh_key k = state->pubkey;
|
||||
if (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
|
||||
k = state->cert;
|
||||
}
|
||||
rc = ssh_userauth_try_publickey(session, username, k);
|
||||
if (rc == SSH_AUTH_ERROR) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Public key authentication error for %s",
|
||||
privkey_file);
|
||||
ssh_key_free(state->privkey);
|
||||
state->privkey = NULL;
|
||||
ssh_key_free(state->pubkey);
|
||||
state->pubkey = NULL;
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->privkey);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
SAFE_FREE(session->auth.auto_state);
|
||||
return rc;
|
||||
} else if (rc == SSH_AUTH_AGAIN) {
|
||||
return rc;
|
||||
} else if (rc != SSH_AUTH_SUCCESS) {
|
||||
int r; /* do not reuse `rc` as it is used to return from here */
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Public key for %s refused by server",
|
||||
"Public key for %s%s refused by server",
|
||||
privkey_file,
|
||||
(state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED
|
||||
? " (with certificate)" : ""));
|
||||
/* Try certificate file by appending -cert.pub (if present) */
|
||||
if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
|
||||
char cert_file[PATH_MAX] = {0};
|
||||
ssh_key cert = NULL;
|
||||
|
||||
snprintf(cert_file,
|
||||
sizeof(cert_file),
|
||||
"%s-cert.pub",
|
||||
privkey_file);
|
||||
ssh_key_free(state->privkey);
|
||||
state->privkey = NULL;
|
||||
ssh_key_free(state->pubkey);
|
||||
state->pubkey = NULL;
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Trying to load the certificate %s (default path)",
|
||||
cert_file);
|
||||
r = ssh_pki_import_cert_file(cert_file, &cert);
|
||||
if (r == SSH_OK) {
|
||||
/* TODO check the pubkey and certs match */
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Certificate loaded %s. Retry the authentication.",
|
||||
cert_file);
|
||||
state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE;
|
||||
SSH_KEY_FREE(state->cert);
|
||||
state->cert = cert;
|
||||
/* try to authenticate with this certificate */
|
||||
continue;
|
||||
}
|
||||
/* if the file does not exists, try configuration options */
|
||||
state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
|
||||
}
|
||||
/* Try certificate files loaded through options */
|
||||
if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
|
||||
SSH_KEY_FREE(state->cert);
|
||||
if (state->cert_it == NULL) {
|
||||
state->cert_it = ssh_list_get_iterator(session->opts.certificate);
|
||||
}
|
||||
while (state->cert_it != NULL) {
|
||||
const char *cert_file = state->cert_it->data;
|
||||
ssh_key cert = NULL;
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Trying to load the certificate %s (options)",
|
||||
cert_file);
|
||||
r = ssh_pki_import_cert_file(cert_file, &cert);
|
||||
if (r == SSH_OK) {
|
||||
int cmp = ssh_key_cmp(cert,
|
||||
state->pubkey,
|
||||
SSH_KEY_CMP_PUBLIC);
|
||||
if (cmp != 0) {
|
||||
state->cert_it = state->cert_it->next;
|
||||
SSH_KEY_FREE(cert);
|
||||
continue; /* with next cert */
|
||||
}
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Found matching certificate %s in options. Retry the authentication.",
|
||||
cert_file);
|
||||
state->cert = cert;
|
||||
cert = NULL;
|
||||
state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
|
||||
/* try to authenticate with this identity */
|
||||
break; /* try this cert */
|
||||
}
|
||||
/* continue with next identity */
|
||||
}
|
||||
if (state->cert != NULL) {
|
||||
continue; /* retry with the certificate */
|
||||
}
|
||||
}
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->privkey);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
state->it = state->it->next;
|
||||
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
|
||||
continue;
|
||||
@@ -1353,8 +1509,8 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
auth_data,
|
||||
&state->privkey);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_key_free(state->pubkey);
|
||||
state->pubkey = NULL;
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to read private key: %s",
|
||||
@@ -1364,8 +1520,8 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
continue;
|
||||
} else if (rc == SSH_EOF) {
|
||||
/* If the file doesn't exist, continue */
|
||||
ssh_key_free(state->pubkey);
|
||||
state->pubkey = NULL;
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Private key %s doesn't exist.",
|
||||
privkey_file);
|
||||
@@ -1374,16 +1530,33 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (state->cert != NULL && !is_cert_type(state->privkey->cert_type)) {
|
||||
rc = ssh_pki_copy_cert_to_privkey(state->cert, state->privkey);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->privkey);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to copy cert to private key");
|
||||
state->it = state->it->next;
|
||||
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_userauth_publickey(session, username, state->privkey);
|
||||
if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) {
|
||||
ssh_key_free(state->privkey);
|
||||
ssh_key_free(state->pubkey);
|
||||
bool cert_used = (state->cert != NULL);
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->privkey);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
SAFE_FREE(session->auth.auto_state);
|
||||
if (rc == SSH_AUTH_SUCCESS) {
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Successfully authenticated using %s",
|
||||
privkey_file);
|
||||
"Successfully authenticated using %s%s",
|
||||
privkey_file,
|
||||
(cert_used ? " and certificate" : ""));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1391,8 +1564,9 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssh_key_free(state->privkey);
|
||||
ssh_key_free(state->pubkey);
|
||||
SSH_KEY_FREE(state->cert);
|
||||
SSH_KEY_FREE(state->privkey);
|
||||
SSH_KEY_FREE(state->pubkey);
|
||||
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"The server accepted the public key but refused the signature");
|
||||
|
||||
@@ -92,7 +92,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{ "canonicalizehostname", SOC_UNSUPPORTED},
|
||||
{ "canonicalizemaxdots", SOC_UNSUPPORTED},
|
||||
{ "canonicalizepermittedcnames", SOC_UNSUPPORTED},
|
||||
{ "certificatefile", SOC_UNSUPPORTED},
|
||||
{ "certificatefile", SOC_CERTIFICATE},
|
||||
{ "kbdinteractiveauthentication", SOC_UNSUPPORTED},
|
||||
{ "checkhostip", SOC_UNSUPPORTED},
|
||||
{ "connectionattempts", SOC_UNSUPPORTED},
|
||||
@@ -623,6 +623,7 @@ ssh_config_parse_line(ssh_session session,
|
||||
opcode != SOC_MATCH &&
|
||||
opcode != SOC_INCLUDE &&
|
||||
opcode != SOC_IDENTITY &&
|
||||
opcode != SOC_CERTIFICATE &&
|
||||
opcode > SOC_UNSUPPORTED &&
|
||||
opcode < SOC_MAX) { /* Ignore all unknown types here */
|
||||
/* Skip all the options that were already applied */
|
||||
@@ -1218,6 +1219,12 @@ ssh_config_parse_line(ssh_session session,
|
||||
ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, p);
|
||||
}
|
||||
break;
|
||||
case SOC_CERTIFICATE:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
|
||||
opcode);
|
||||
|
||||
@@ -138,6 +138,32 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
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) {
|
||||
@@ -353,6 +379,21 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* 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).
|
||||
*
|
||||
@@ -753,6 +794,22 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
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);
|
||||
@@ -1753,6 +1810,23 @@ int ssh_options_apply(ssh_session session)
|
||||
}
|
||||
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);
|
||||
if (tmp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
free(id);
|
||||
|
||||
rc = ssh_list_append(session->opts.certificate, tmp);
|
||||
if (rc != SSH_OK) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,17 @@ ssh_session ssh_new(void)
|
||||
goto err;
|
||||
}
|
||||
|
||||
session->opts.certificate = ssh_list_new();
|
||||
if (session->opts.certificate == NULL) {
|
||||
goto err;
|
||||
}
|
||||
session->opts.certificate_non_exp = ssh_list_new();
|
||||
if (session->opts.certificate_non_exp == NULL) {
|
||||
goto err;
|
||||
}
|
||||
/* the default certificates are loaded automatically from the default
|
||||
* identities later */
|
||||
|
||||
id = strdup("%d/id_ed25519");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
@@ -288,6 +299,28 @@ void ssh_free(ssh_session session)
|
||||
ssh_list_free(session->opts.identity_non_exp);
|
||||
}
|
||||
|
||||
if (session->opts.certificate) {
|
||||
char *cert = NULL;
|
||||
|
||||
for (cert = ssh_list_pop_head(char *, session->opts.certificate);
|
||||
cert != NULL;
|
||||
cert = ssh_list_pop_head(char *, session->opts.certificate)) {
|
||||
SAFE_FREE(cert);
|
||||
}
|
||||
ssh_list_free(session->opts.certificate);
|
||||
}
|
||||
|
||||
if (session->opts.certificate_non_exp) {
|
||||
char *cert = NULL;
|
||||
|
||||
for (cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp);
|
||||
cert != NULL;
|
||||
cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) {
|
||||
SAFE_FREE(cert);
|
||||
}
|
||||
ssh_list_free(session->opts.certificate_non_exp);
|
||||
}
|
||||
|
||||
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
|
||||
session->out_queue)) != NULL) {
|
||||
SSH_BUFFER_FREE(b);
|
||||
|
||||
Reference in New Issue
Block a user