version 1.32, 2020/09/09 03:08:02 |
version 1.33, 2020/10/18 11:32:02 |
|
|
return NULL; |
return NULL; |
} |
} |
if ((ret = calloc(1, sizeof(*ret))) == NULL) { |
if ((ret = calloc(1, sizeof(*ret))) == NULL) { |
error("%s: calloc failed", __func__); |
error_f("calloc failed"); |
return NULL; |
return NULL; |
} |
} |
if ((ret->path = strdup(path)) == NULL) { |
if ((ret->path = strdup(path)) == NULL) { |
error("%s: strdup failed", __func__); |
error_f("strdup failed"); |
goto fail; |
goto fail; |
} |
} |
/* Skip the rest if we're using the linked in middleware */ |
/* Skip the rest if we're using the linked in middleware */ |
|
|
goto fail; |
goto fail; |
} |
} |
version = ret->sk_api_version(); |
version = ret->sk_api_version(); |
debug("%s: provider %s implements version 0x%08lx", __func__, |
debug_f("provider %s implements version 0x%08lx", ret->path, |
ret->path, (u_long)version); |
(u_long)version); |
if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { |
if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { |
error("Provider \"%s\" implements unsupported " |
error("Provider \"%s\" implements unsupported " |
"version 0x%08lx (supported: 0x%08lx)", |
"version 0x%08lx (supported: 0x%08lx)", |
|
|
|
|
*keyp = NULL; |
*keyp = NULL; |
if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { |
if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { |
error("%s: sshkey_new failed", __func__); |
error_f("sshkey_new failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
|
|
if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || |
if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || |
(q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || |
(q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || |
(b = sshbuf_new()) == NULL) { |
(b = sshbuf_new()) == NULL) { |
error("%s: allocation failed", __func__); |
error_f("allocation failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_put_string(b, |
if ((r = sshbuf_put_string(b, |
resp->public_key, resp->public_key_len)) != 0) { |
resp->public_key, resp->public_key_len)) != 0) { |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "sshbuf_put_string"); |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { |
if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { |
error("%s: parse key: %s", __func__, ssh_err(r)); |
error_fr(r, "parse"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
|
|
} |
} |
if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { |
if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { |
/* XXX assume it is a allocation error */ |
/* XXX assume it is a allocation error */ |
error("%s: allocation failed", __func__); |
error_f("allocation failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
|
|
|
|
*keyp = NULL; |
*keyp = NULL; |
if (resp->public_key_len != ED25519_PK_SZ) { |
if (resp->public_key_len != ED25519_PK_SZ) { |
error("%s: invalid size: %zu", __func__, resp->public_key_len); |
error_f("invalid size: %zu", resp->public_key_len); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { |
if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { |
error("%s: sshkey_new failed", __func__); |
error_f("sshkey_new failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { |
if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { |
error("%s: malloc failed", __func__); |
error_f("malloc failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
|
|
|
|
/* Check response validity */ |
/* Check response validity */ |
if (resp->public_key == NULL || resp->key_handle == NULL) { |
if (resp->public_key == NULL || resp->key_handle == NULL) { |
error("%s: sk_enroll response invalid", __func__); |
error_f("sk_enroll response invalid"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
|
|
goto out; |
goto out; |
break; |
break; |
default: |
default: |
error("%s: unsupported algorithm %d", __func__, alg); |
error_f("unsupported algorithm %d", alg); |
r = SSH_ERR_INVALID_ARGUMENT; |
r = SSH_ERR_INVALID_ARGUMENT; |
goto out; |
goto out; |
} |
} |
key->sk_flags = flags; |
key->sk_flags = flags; |
if ((key->sk_key_handle = sshbuf_new()) == NULL || |
if ((key->sk_key_handle = sshbuf_new()) == NULL || |
(key->sk_reserved = sshbuf_new()) == NULL) { |
(key->sk_reserved = sshbuf_new()) == NULL) { |
error("%s: allocation failed", __func__); |
error_f("allocation failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
if ((key->sk_application = strdup(application)) == NULL) { |
if ((key->sk_application = strdup(application)) == NULL) { |
error("%s: strdup application failed", __func__); |
error_f("strdup application failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, |
if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, |
resp->key_handle_len)) != 0) { |
resp->key_handle_len)) != 0) { |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "put key handle"); |
goto out; |
goto out; |
} |
} |
/* success */ |
/* success */ |
|
|
|
|
if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ |
if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ |
sizeof(*opts))) == NULL) { |
sizeof(*opts))) == NULL) { |
error("%s: array alloc failed", __func__); |
error_f("array alloc failed"); |
return SSH_ERR_ALLOC_FAIL; |
return SSH_ERR_ALLOC_FAIL; |
} |
} |
*optsp = opts; |
*optsp = opts; |
*noptsp = nopts + 1; |
*noptsp = nopts + 1; |
if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { |
if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { |
error("%s: alloc failed", __func__); |
error_f("alloc failed"); |
return SSH_ERR_ALLOC_FAIL; |
return SSH_ERR_ALLOC_FAIL; |
} |
} |
if ((opts[nopts]->name = strdup(name)) == NULL || |
if ((opts[nopts]->name = strdup(name)) == NULL || |
(opts[nopts]->value = strdup(value)) == NULL) { |
(opts[nopts]->value = strdup(value)) == NULL) { |
error("%s: alloc failed", __func__); |
error_f("alloc failed"); |
return SSH_ERR_ALLOC_FAIL; |
return SSH_ERR_ALLOC_FAIL; |
} |
} |
opts[nopts]->required = required; |
opts[nopts]->required = required; |
|
|
resp->authdata, resp->authdata_len)) != 0 || |
resp->authdata, resp->authdata_len)) != 0 || |
(r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ |
(r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ |
(r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { |
(r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
return r; |
return r; |
} |
} |
/* success */ |
/* success */ |
|
|
int r = SSH_ERR_INTERNAL_ERROR; |
int r = SSH_ERR_INTERNAL_ERROR; |
int alg; |
int alg; |
|
|
debug("%s: provider \"%s\", device \"%s\", application \"%s\", " |
debug_f("provider \"%s\", device \"%s\", application \"%s\", " |
"userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__, |
"userid \"%s\", flags 0x%02x, challenge len %zu%s", |
provider_path, device, application, userid, flags, |
provider_path, device, application, userid, flags, |
challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), |
challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), |
(pin != NULL && *pin != '\0') ? " with-pin" : ""); |
(pin != NULL && *pin != '\0') ? " with-pin" : ""); |
|
|
alg = SSH_SK_ED25519; |
alg = SSH_SK_ED25519; |
break; |
break; |
default: |
default: |
error("%s: unsupported key type", __func__); |
error_f("unsupported key type"); |
r = SSH_ERR_INVALID_ARGUMENT; |
r = SSH_ERR_INVALID_ARGUMENT; |
goto out; |
goto out; |
} |
} |
if (provider_path == NULL) { |
if (provider_path == NULL) { |
error("%s: missing provider", __func__); |
error_f("missing provider"); |
r = SSH_ERR_INVALID_ARGUMENT; |
r = SSH_ERR_INVALID_ARGUMENT; |
goto out; |
goto out; |
} |
} |
if (application == NULL || *application == '\0') { |
if (application == NULL || *application == '\0') { |
error("%s: missing application", __func__); |
error_f("missing application"); |
r = SSH_ERR_INVALID_ARGUMENT; |
r = SSH_ERR_INVALID_ARGUMENT; |
goto out; |
goto out; |
} |
} |
if (challenge_buf == NULL) { |
if (challenge_buf == NULL) { |
debug("%s: using random challenge", __func__); |
debug_f("using random challenge"); |
arc4random_buf(randchall, sizeof(randchall)); |
arc4random_buf(randchall, sizeof(randchall)); |
challenge = randchall; |
challenge = randchall; |
challenge_len = sizeof(randchall); |
challenge_len = sizeof(randchall); |
|
|
} else { |
} else { |
challenge = sshbuf_ptr(challenge_buf); |
challenge = sshbuf_ptr(challenge_buf); |
challenge_len = sshbuf_len(challenge_buf); |
challenge_len = sshbuf_len(challenge_buf); |
debug3("%s: using explicit challenge len=%zd", |
debug3_f("using explicit challenge len=%zd", challenge_len); |
__func__, challenge_len); |
|
} |
} |
if ((skp = sshsk_open(provider_path)) == NULL) { |
if ((skp = sshsk_open(provider_path)) == NULL) { |
r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ |
r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ |
|
|
/* enroll key */ |
/* enroll key */ |
if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, |
if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, |
flags, pin, opts, &resp)) != 0) { |
flags, pin, opts, &resp)) != 0) { |
debug("%s: provider \"%s\" returned failure %d", __func__, |
debug_f("provider \"%s\" failure %d", provider_path, r); |
provider_path, r); |
|
r = skerr_to_ssherr(r); |
r = skerr_to_ssherr(r); |
goto out; |
goto out; |
} |
} |
|
|
|
|
/* Check response validity */ |
/* Check response validity */ |
if (resp->sig_r == NULL || resp->sig_s == NULL) { |
if (resp->sig_r == NULL || resp->sig_s == NULL) { |
error("%s: sk_sign response invalid", __func__); |
error_f("sk_sign response invalid"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
|
|
resp->sig_r, resp->sig_r_len)) != 0 || |
resp->sig_r, resp->sig_r_len)) != 0 || |
(r = sshbuf_put_bignum2_bytes(inner_sig, |
(r = sshbuf_put_bignum2_bytes(inner_sig, |
resp->sig_s, resp->sig_s_len)) != 0) { |
resp->sig_s, resp->sig_s_len)) != 0) { |
debug("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "compose inner"); |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || |
if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || |
(r = sshbuf_put_u8(sig, resp->flags)) != 0 || |
(r = sshbuf_put_u8(sig, resp->flags)) != 0 || |
(r = sshbuf_put_u32(sig, resp->counter)) != 0) { |
(r = sshbuf_put_u32(sig, resp->counter)) != 0) { |
debug("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
goto out; |
goto out; |
} |
} |
#ifdef DEBUG_SK |
#ifdef DEBUG_SK |
|
|
|
|
/* Check response validity */ |
/* Check response validity */ |
if (resp->sig_r == NULL) { |
if (resp->sig_r == NULL) { |
error("%s: sk_sign response invalid", __func__); |
error_f("sk_sign response invalid"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
|
|
resp->sig_r, resp->sig_r_len)) != 0 || |
resp->sig_r, resp->sig_r_len)) != 0 || |
(r = sshbuf_put_u8(sig, resp->flags)) != 0 || |
(r = sshbuf_put_u8(sig, resp->flags)) != 0 || |
(r = sshbuf_put_u32(sig, resp->counter)) != 0) { |
(r = sshbuf_put_u32(sig, resp->counter)) != 0) { |
debug("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
goto out; |
goto out; |
} |
} |
#ifdef DEBUG_SK |
#ifdef DEBUG_SK |
|
|
struct sshbuf *inner_sig = NULL, *sig = NULL; |
struct sshbuf *inner_sig = NULL, *sig = NULL; |
struct sk_option **opts = NULL; |
struct sk_option **opts = NULL; |
|
|
debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, |
debug_f("provider \"%s\", key %s, flags 0x%02x%s", |
provider_path, sshkey_type(key), key->sk_flags, |
provider_path, sshkey_type(key), key->sk_flags, |
(pin != NULL && *pin != '\0') ? " with-pin" : ""); |
(pin != NULL && *pin != '\0') ? " with-pin" : ""); |
|
|
|
|
if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, |
if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, |
sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), |
sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), |
key->sk_flags, pin, opts, &resp)) != 0) { |
key->sk_flags, pin, opts, &resp)) != 0) { |
debug("%s: sk_sign failed with code %d", __func__, r); |
debug_f("sk_sign failed with code %d", r); |
r = skerr_to_ssherr(r); |
r = skerr_to_ssherr(r); |
goto out; |
goto out; |
} |
} |
|
|
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { |
if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { |
debug("%s: buffer error (outer): %s", __func__, ssh_err(r)); |
error_fr(r, "compose outer"); |
goto out; |
goto out; |
} |
} |
switch (type) { |
switch (type) { |
|
|
uint8_t flags; |
uint8_t flags; |
struct sk_option **opts = NULL; |
struct sk_option **opts = NULL; |
|
|
debug("%s: provider \"%s\"%s", __func__, provider_path, |
debug_f("provider \"%s\"%s", provider_path, |
(pin != NULL && *pin != '\0') ? ", have-pin": ""); |
(pin != NULL && *pin != '\0') ? ", have-pin": ""); |
|
|
if (keysp == NULL || nkeysp == NULL) |
if (keysp == NULL || nkeysp == NULL) |
|
|
goto out; |
goto out; |
} |
} |
for (i = 0; i < nrks; i++) { |
for (i = 0; i < nrks; i++) { |
debug3("%s: rk %zu: slot = %zu, alg = %d, application = \"%s\"", |
debug3_f("rk %zu: slot = %zu, alg = %d, application = \"%s\"", |
__func__, i, rks[i]->slot, rks[i]->alg, |
i, rks[i]->slot, rks[i]->alg, rks[i]->application); |
rks[i]->application); |
|
/* XXX need better filter here */ |
/* XXX need better filter here */ |
if (strncmp(rks[i]->application, "ssh:", 4) != 0) |
if (strncmp(rks[i]->application, "ssh:", 4) != 0) |
continue; |
continue; |
|
|
goto out; |
goto out; |
if ((tmp = recallocarray(keys, nkeys, nkeys + 1, |
if ((tmp = recallocarray(keys, nkeys, nkeys + 1, |
sizeof(*tmp))) == NULL) { |
sizeof(*tmp))) == NULL) { |
error("%s: recallocarray failed", __func__); |
error_f("recallocarray failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |