version 1.271, 2018/06/26 02:02:36 |
version 1.272, 2018/07/03 11:39:54 |
|
|
|
|
void userauth(Authctxt *, char *); |
void userauth(Authctxt *, char *); |
|
|
static int sign_and_send_pubkey(Authctxt *, Identity *); |
static int sign_and_send_pubkey(struct ssh *ssh, Authctxt *, Identity *); |
static void pubkey_prepare(Authctxt *); |
static void pubkey_prepare(Authctxt *); |
static void pubkey_cleanup(Authctxt *); |
static void pubkey_cleanup(Authctxt *); |
static void pubkey_reset(Authctxt *); |
static void pubkey_reset(Authctxt *); |
|
|
*/ |
*/ |
TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { |
TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { |
if (key_equal(key, id->key)) { |
if (key_equal(key, id->key)) { |
sent = sign_and_send_pubkey(authctxt, id); |
sent = sign_and_send_pubkey(ssh, authctxt, id); |
break; |
break; |
} |
} |
} |
} |
|
|
return 0; |
return 0; |
} |
} |
|
|
static const char * |
|
key_sign_encode(const struct sshkey *key) |
|
{ |
|
struct ssh *ssh = active_state; |
|
|
|
if (key->type == KEY_RSA) { |
|
switch (ssh->kex->rsa_sha2) { |
|
case 256: |
|
return "rsa-sha2-256"; |
|
case 512: |
|
return "rsa-sha2-512"; |
|
} |
|
} |
|
return key_ssh_name(key); |
|
} |
|
|
|
/* |
/* |
* Some agents will return ssh-rsa signatures when asked to make a |
* Select an algorithm for publickey signatures. |
* rsa-sha2-* signature. Check what they actually gave back and warn the |
* Returns algorithm (caller must free) or NULL if no mutual algorithm found. |
* user if the agent has returned an unexpected type. |
* |
|
* Call with ssh==NULL to ignore server-sig-algs extension list and |
|
* only attempt with the key's base signature type. |
*/ |
*/ |
static int |
static char * |
check_sigtype(const struct sshkey *key, const u_char *sig, size_t len) |
key_sig_algorithm(struct ssh *ssh, const struct sshkey *key) |
{ |
{ |
int r; |
char *allowed, *oallowed, *cp, *alg = NULL; |
char *sigtype = NULL; |
|
const char *alg = key_sign_encode(key); |
|
|
|
if (sshkey_is_cert(key)) |
/* |
return 0; |
* The signature algorithm will only differ from the key algorithm |
if ((r = sshkey_sigtype(sig, len, &sigtype)) != 0) |
* for RSA keys/certs and when the server advertises support for |
return r; |
* newer (SHA2) algorithms. |
if (strcmp(sigtype, alg) != 0) { |
*/ |
logit("warning: agent returned different signature type %s " |
if (ssh == NULL || ssh->kex->server_sig_algs == NULL || |
"(expected %s)", sigtype, alg); |
(key->type != KEY_RSA && key->type != KEY_RSA_CERT)) { |
|
/* Filter base key signature alg against our configuration */ |
|
return match_list(key_ssh_name(key), |
|
options.pubkey_key_types, NULL); |
} |
} |
free(sigtype); |
|
/* Incorrect signature types aren't an error ... yet */ |
/* |
return 0; |
* For RSA keys/certs, since these might have a different sig type: |
|
* find the first entry in PubkeyAcceptedKeyTypes of the right type |
|
* that also appears in the supported signature algorithms list from |
|
* the server. |
|
*/ |
|
oallowed = allowed = xstrdup(options.pubkey_key_types); |
|
while ((cp = strsep(&allowed, ",")) != NULL) { |
|
if (sshkey_type_from_name(cp) != key->type) |
|
continue; |
|
alg = match_list(cp, ssh->kex->server_sig_algs, NULL); |
|
if (alg != NULL) |
|
break; |
|
} |
|
free(oallowed); |
|
return alg; |
} |
} |
|
|
static int |
static int |
identity_sign(struct identity *id, u_char **sigp, size_t *lenp, |
identity_sign(struct identity *id, u_char **sigp, size_t *lenp, |
const u_char *data, size_t datalen, u_int compat) |
const u_char *data, size_t datalen, u_int compat, const char *alg) |
{ |
{ |
struct sshkey *prv; |
struct sshkey *prv; |
int r; |
int r; |
|
|
/* the agent supports this key */ |
/* The agent supports this key. */ |
if (id->key != NULL && id->agent_fd != -1) { |
if (id->key != NULL && id->agent_fd != -1) { |
if ((r = ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, |
return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, |
data, datalen, key_sign_encode(id->key), compat)) != 0 || |
data, datalen, alg, compat); |
(r = check_sigtype(id->key, *sigp, *lenp)) != 0) |
|
return r; |
|
return 0; |
|
} |
} |
|
|
/* |
/* |
* we have already loaded the private key or |
* We have already loaded the private key or the private key is |
* the private key is stored in external hardware |
* stored in external hardware. |
*/ |
*/ |
if (id->key != NULL && |
if (id->key != NULL && |
(id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) |
(id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) { |
return (sshkey_sign(id->key, sigp, lenp, data, datalen, |
if ((r = sshkey_sign(id->key, sigp, lenp, data, datalen, |
key_sign_encode(id->key), compat)); |
alg, compat)) != 0) |
|
return r; |
|
/* |
|
* PKCS#11 tokens may not support all signature algorithms, |
|
* so check what we get back. |
|
*/ |
|
if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) |
|
return r; |
|
return 0; |
|
} |
|
|
/* load the private key from the file */ |
/* Load the private key from the file. */ |
if ((prv = load_identity_file(id)) == NULL) |
if ((prv = load_identity_file(id)) == NULL) |
return SSH_ERR_KEY_NOT_FOUND; |
return SSH_ERR_KEY_NOT_FOUND; |
if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { |
if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { |
|
|
__func__, id->filename); |
__func__, id->filename); |
return SSH_ERR_KEY_NOT_FOUND; |
return SSH_ERR_KEY_NOT_FOUND; |
} |
} |
r = sshkey_sign(prv, sigp, lenp, data, datalen, |
r = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat); |
key_sign_encode(prv), compat); |
|
sshkey_free(prv); |
sshkey_free(prv); |
return r; |
return r; |
} |
} |
|
|
} |
} |
|
|
static int |
static int |
sign_and_send_pubkey(Authctxt *authctxt, Identity *id) |
sign_and_send_pubkey(struct ssh *ssh, Authctxt *authctxt, Identity *id) |
{ |
{ |
Buffer b; |
struct sshbuf *b = NULL; |
Identity *private_id; |
Identity *private_id, *sign_id = NULL; |
u_char *blob, *signature; |
u_char *signature = NULL; |
size_t slen; |
size_t slen = 0, skip = 0; |
u_int bloblen, skip = 0; |
int r, fallback_sigtype, sent = 0; |
int matched, ret = -1, have_sig = 1; |
char *alg = NULL, *fp = NULL; |
char *fp; |
const char *loc = ""; |
|
|
if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, |
if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, |
SSH_FP_DEFAULT)) == NULL) |
SSH_FP_DEFAULT)) == NULL) |
return 0; |
return 0; |
debug3("%s: %s %s", __func__, key_type(id->key), fp); |
|
free(fp); |
|
|
|
if (key_to_blob(id->key, &blob, &bloblen) == 0) { |
debug3("%s: %s %s", __func__, sshkey_type(id->key), fp); |
/* we cannot handle this key */ |
|
debug3("sign_and_send_pubkey: cannot handle key"); |
|
return 0; |
|
} |
|
/* data to be signed */ |
|
buffer_init(&b); |
|
if (datafellows & SSH_OLD_SESSIONID) { |
|
buffer_append(&b, session_id2, session_id2_len); |
|
skip = session_id2_len; |
|
} else { |
|
buffer_put_string(&b, session_id2, session_id2_len); |
|
skip = buffer_len(&b); |
|
} |
|
buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); |
|
buffer_put_cstring(&b, authctxt->server_user); |
|
buffer_put_cstring(&b, authctxt->service); |
|
buffer_put_cstring(&b, authctxt->method->name); |
|
buffer_put_char(&b, have_sig); |
|
buffer_put_cstring(&b, key_sign_encode(id->key)); |
|
buffer_put_string(&b, blob, bloblen); |
|
|
|
/* |
/* |
* If the key is an certificate, try to find a matching private key |
* If the key is an certificate, try to find a matching private key |
* and use it to complete the signature. |
* and use it to complete the signature. |
* If no such private key exists, fall back to trying the certificate |
* If no such private key exists, fall back to trying the certificate |
* key itself in case it has a private half already loaded. |
* key itself in case it has a private half already loaded. |
|
* This will try to set sign_id to the private key that will perform |
|
* the signature. |
*/ |
*/ |
if (key_is_cert(id->key)) { |
if (sshkey_is_cert(id->key)) { |
matched = 0; |
|
TAILQ_FOREACH(private_id, &authctxt->keys, next) { |
TAILQ_FOREACH(private_id, &authctxt->keys, next) { |
if (sshkey_equal_public(id->key, private_id->key) && |
if (sshkey_equal_public(id->key, private_id->key) && |
id->key->type != private_id->key->type) { |
id->key->type != private_id->key->type) { |
id = private_id; |
sign_id = private_id; |
matched = 1; |
|
break; |
break; |
} |
} |
} |
} |
|
|
* of keeping just a private key file and public |
* of keeping just a private key file and public |
* certificate on disk. |
* certificate on disk. |
*/ |
*/ |
if (!matched && !id->isprivate && id->agent_fd == -1 && |
if (sign_id == NULL && |
|
!id->isprivate && id->agent_fd == -1 && |
(id->key->flags & SSHKEY_FLAG_EXT) == 0) { |
(id->key->flags & SSHKEY_FLAG_EXT) == 0) { |
TAILQ_FOREACH(private_id, &authctxt->keys, next) { |
TAILQ_FOREACH(private_id, &authctxt->keys, next) { |
if (private_id->key == NULL && |
if (private_id->key == NULL && |
id_filename_matches(id, private_id)) { |
id_filename_matches(id, private_id)) { |
id = private_id; |
sign_id = private_id; |
matched = 1; |
|
break; |
break; |
} |
} |
} |
} |
} |
} |
if (matched) { |
if (sign_id != NULL) { |
debug2("%s: using private key \"%s\"%s for " |
debug2("%s: using private key \"%s\"%s for " |
"certificate", __func__, id->filename, |
"certificate", __func__, id->filename, |
id->agent_fd != -1 ? " from agent" : ""); |
id->agent_fd != -1 ? " from agent" : ""); |
|
|
} |
} |
} |
} |
|
|
/* generate signature */ |
/* |
ret = identity_sign(id, &signature, &slen, |
* If the above didn't select another identity to do the signing |
buffer_ptr(&b), buffer_len(&b), datafellows); |
* then default to the one we started with. |
if (ret != 0) { |
*/ |
if (ret != SSH_ERR_KEY_NOT_FOUND) |
if (sign_id == NULL) |
error("%s: signing failed: %s", __func__, ssh_err(ret)); |
sign_id = id; |
free(blob); |
|
buffer_free(&b); |
/* assemble and sign data */ |
return 0; |
for (fallback_sigtype = 0; fallback_sigtype <= 1; fallback_sigtype++) { |
|
free(alg); |
|
slen = 0; |
|
signature = NULL; |
|
if ((alg = key_sig_algorithm(fallback_sigtype ? NULL : ssh, |
|
id->key)) == NULL) { |
|
error("%s: no mutual signature supported", __func__); |
|
goto out; |
|
} |
|
debug3("%s: signing using %s", __func__, alg); |
|
|
|
sshbuf_free(b); |
|
if ((b = sshbuf_new()) == NULL) |
|
fatal("%s: sshbuf_new failed", __func__); |
|
if (datafellows & SSH_OLD_SESSIONID) { |
|
if ((r = sshbuf_put(b, session_id2, |
|
session_id2_len)) != 0) { |
|
fatal("%s: sshbuf_put: %s", |
|
__func__, ssh_err(r)); |
|
} |
|
} else { |
|
if ((r = sshbuf_put_string(b, session_id2, |
|
session_id2_len)) != 0) { |
|
fatal("%s: sshbuf_put_string: %s", |
|
__func__, ssh_err(r)); |
|
} |
|
} |
|
skip = buffer_len(b); |
|
if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || |
|
(r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || |
|
(r = sshbuf_put_cstring(b, authctxt->service)) != 0 || |
|
(r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 || |
|
(r = sshbuf_put_u8(b, 1)) != 0 || |
|
(r = sshbuf_put_cstring(b, alg)) != 0 || |
|
(r = sshkey_puts(id->key, b)) != 0) { |
|
fatal("%s: assemble signed data: %s", |
|
__func__, ssh_err(r)); |
|
} |
|
|
|
/* generate signature */ |
|
r = identity_sign(sign_id, &signature, &slen, |
|
sshbuf_ptr(b), sshbuf_len(b), datafellows, alg); |
|
if (r == 0) |
|
break; |
|
else if (r == SSH_ERR_KEY_NOT_FOUND) |
|
goto out; /* soft failure */ |
|
else if (r == SSH_ERR_SIGN_ALG_UNSUPPORTED && |
|
!fallback_sigtype) { |
|
if (sign_id->agent_fd != -1) |
|
loc = "agent "; |
|
else if ((sign_id->key->flags & SSHKEY_FLAG_EXT) != 0) |
|
loc = "token "; |
|
logit("%skey %s %s returned incorrect signature type", |
|
loc, sshkey_type(id->key), fp); |
|
continue; |
|
} |
|
error("%s: signing failed: %s", __func__, ssh_err(r)); |
|
goto out; |
} |
} |
#ifdef DEBUG_PK |
if (slen == 0 || signature == NULL) /* shouldn't happen */ |
buffer_dump(&b); |
fatal("%s: no signature", __func__); |
#endif |
|
free(blob); |
|
|
|
/* append signature */ |
/* append signature */ |
buffer_put_string(&b, signature, slen); |
if ((r = sshbuf_put_string(b, signature, slen)) != 0) |
free(signature); |
fatal("%s: append signature: %s", __func__, ssh_err(r)); |
|
|
|
#ifdef DEBUG_PK |
|
sshbuf_dump(b, stderr); |
|
#endif |
/* skip session id and packet type */ |
/* skip session id and packet type */ |
if (buffer_len(&b) < skip + 1) |
if ((r = sshbuf_consume(b, skip + 1)) != 0) |
fatal("userauth_pubkey: internal error"); |
fatal("%s: consume: %s", __func__, ssh_err(r)); |
buffer_consume(&b, skip + 1); |
|
|
|
/* put remaining data from buffer into packet */ |
/* put remaining data from buffer into packet */ |
packet_start(SSH2_MSG_USERAUTH_REQUEST); |
if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || |
packet_put_raw(buffer_ptr(&b), buffer_len(&b)); |
(r = sshpkt_putb(ssh, b)) != 0 || |
buffer_free(&b); |
(r = sshpkt_send(ssh)) != 0) |
packet_send(); |
fatal("%s: enqueue request: %s", __func__, ssh_err(r)); |
|
|
return 1; |
/* success */ |
|
sent = 1; |
|
|
|
out: |
|
free(fp); |
|
free(alg); |
|
sshbuf_free(b); |
|
freezero(signature, slen); |
|
return sent; |
} |
} |
|
|
static int |
static int |
send_pubkey_test(Authctxt *authctxt, Identity *id) |
send_pubkey_test(struct ssh *ssh, Authctxt *authctxt, Identity *id) |
{ |
{ |
u_char *blob; |
u_char *blob = NULL; |
u_int bloblen, have_sig = 0; |
u_int bloblen, have_sig = 0; |
|
char *alg = NULL; |
|
int sent = 0; |
|
|
debug3("send_pubkey_test"); |
if ((alg = key_sig_algorithm(ssh, id->key)) == NULL) { |
|
debug("%s: no mutual signature algorithm", __func__); |
|
goto out; |
|
} |
|
|
if (key_to_blob(id->key, &blob, &bloblen) == 0) { |
if (key_to_blob(id->key, &blob, &bloblen) == 0) { |
/* we cannot handle this key */ |
/* we cannot handle this key */ |
debug3("send_pubkey_test: cannot handle key"); |
debug3("%s: cannot handle key", __func__); |
return 0; |
goto out; |
} |
} |
/* register callback for USERAUTH_PK_OK message */ |
/* register callback for USERAUTH_PK_OK message */ |
dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); |
dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); |
|
|
packet_put_cstring(authctxt->service); |
packet_put_cstring(authctxt->service); |
packet_put_cstring(authctxt->method->name); |
packet_put_cstring(authctxt->method->name); |
packet_put_char(have_sig); |
packet_put_char(have_sig); |
packet_put_cstring(key_sign_encode(id->key)); |
packet_put_cstring(alg); |
packet_put_string(blob, bloblen); |
packet_put_string(blob, bloblen); |
free(blob); |
|
packet_send(); |
packet_send(); |
return 1; |
/* success */ |
|
sent = 1; |
|
out: |
|
free(alg); |
|
free(blob); |
|
return sent; |
} |
} |
|
|
static struct sshkey * |
static struct sshkey * |
|
|
return private; |
return private; |
} |
} |
|
|
|
static int |
|
key_type_allowed_by_config(struct sshkey *key) |
|
{ |
|
if (match_pattern_list(sshkey_ssh_name(key), |
|
options.pubkey_key_types, 0) == 1) |
|
return 1; |
|
|
|
/* RSA keys/certs might be allowed by alternate signature types */ |
|
switch (key->type) { |
|
case KEY_RSA: |
|
if (match_pattern_list("rsa-sha2-512", |
|
options.pubkey_key_types, 0) == 1) |
|
return 1; |
|
if (match_pattern_list("rsa-sha2-256", |
|
options.pubkey_key_types, 0) == 1) |
|
return 1; |
|
break; |
|
case KEY_RSA_CERT: |
|
if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", |
|
options.pubkey_key_types, 0) == 1) |
|
return 1; |
|
if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", |
|
options.pubkey_key_types, 0) == 1) |
|
return 1; |
|
break; |
|
} |
|
return 0; |
|
} |
|
|
|
|
/* |
/* |
* try keys in the following order: |
* try keys in the following order: |
* 1. certificates listed in the config file |
* 1. certificates listed in the config file |
|
|
} |
} |
/* finally, filter by PubkeyAcceptedKeyTypes */ |
/* finally, filter by PubkeyAcceptedKeyTypes */ |
TAILQ_FOREACH_SAFE(id, preferred, next, id2) { |
TAILQ_FOREACH_SAFE(id, preferred, next, id2) { |
if (id->key != NULL && |
if (id->key != NULL && !key_type_allowed_by_config(key)) { |
match_pattern_list(sshkey_ssh_name(id->key), |
|
options.pubkey_key_types, 0) != 1) { |
|
debug("Skipping %s key %s - " |
debug("Skipping %s key %s - " |
"not in PubkeyAcceptedKeyTypes", |
"not in PubkeyAcceptedKeyTypes", |
sshkey_ssh_name(id->key), id->filename); |
sshkey_ssh_name(id->key), id->filename); |
|
|
int |
int |
userauth_pubkey(Authctxt *authctxt) |
userauth_pubkey(Authctxt *authctxt) |
{ |
{ |
|
struct ssh *ssh = active_state; /* XXX */ |
Identity *id; |
Identity *id; |
int sent = 0; |
int sent = 0; |
char *fp; |
char *fp; |
|
|
debug("Offering public key: %s %s %s", |
debug("Offering public key: %s %s %s", |
sshkey_type(id->key), fp, id->filename); |
sshkey_type(id->key), fp, id->filename); |
free(fp); |
free(fp); |
sent = send_pubkey_test(authctxt, id); |
sent = send_pubkey_test(ssh, authctxt, id); |
} |
} |
} else { |
} else { |
debug("Trying private key: %s", id->filename); |
debug("Trying private key: %s", id->filename); |
|
|
if (id->key != NULL) { |
if (id->key != NULL) { |
if (try_identity(id)) { |
if (try_identity(id)) { |
id->isprivate = 1; |
id->isprivate = 1; |
sent = sign_and_send_pubkey( |
sent = sign_and_send_pubkey(ssh, |
authctxt, id); |
authctxt, id); |
} |
} |
key_free(id->key); |
key_free(id->key); |
|
|
int |
int |
userauth_hostbased(Authctxt *authctxt) |
userauth_hostbased(Authctxt *authctxt) |
{ |
{ |
struct ssh *ssh = active_state; |
struct ssh *ssh = active_state; /* XXX */ |
struct sshkey *private = NULL; |
struct sshkey *private = NULL; |
struct sshbuf *b = NULL; |
struct sshbuf *b = NULL; |
u_char *sig = NULL, *keyblob = NULL; |
u_char *sig = NULL, *keyblob = NULL; |