version 1.56, 2023/07/17 03:57:21 |
version 1.57, 2023/07/17 04:01:10 |
|
|
} |
} |
|
|
int |
int |
ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, |
ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf) |
struct sshkey **sign_keys, u_int nsign_keys) |
|
{ |
{ |
int r = SSH_ERR_INTERNAL_ERROR; |
int r = SSH_ERR_INTERNAL_ERROR; |
struct revoked_certs *rc; |
struct revoked_certs *rc; |
struct revoked_blob *rb; |
struct revoked_blob *rb; |
struct sshbuf *sect; |
struct sshbuf *sect; |
u_char *sblob = NULL; |
u_char *sblob = NULL; |
size_t slen, i; |
|
|
|
if (krl->generated_date == 0) |
if (krl->generated_date == 0) |
krl->generated_date = time(NULL); |
krl->generated_date = time(NULL); |
|
|
(r = sshbuf_put_stringb(buf, sect)) != 0) |
(r = sshbuf_put_stringb(buf, sect)) != 0) |
goto out; |
goto out; |
} |
} |
|
/* success */ |
for (i = 0; i < nsign_keys; i++) { |
|
KRL_DBG(("sig key %s", sshkey_ssh_name(sign_keys[i]))); |
|
if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || |
|
(r = sshkey_puts(sign_keys[i], buf)) != 0) |
|
goto out; |
|
/* XXX support sk-* keys */ |
|
if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, |
|
sshbuf_ptr(buf), sshbuf_len(buf), NULL, NULL, |
|
NULL, 0)) != 0) |
|
goto out; |
|
KRL_DBG(("signature sig len %zu", slen)); |
|
if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) |
|
goto out; |
|
} |
|
|
|
r = 0; |
r = 0; |
out: |
out: |
free(sblob); |
free(sblob); |
|
|
return r; |
return r; |
} |
} |
|
|
/* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */ |
/* Attempt to parse a KRL */ |
int |
int |
ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, |
ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp) |
const struct sshkey **sign_ca_keys, size_t nsign_ca_keys) |
|
{ |
{ |
struct sshbuf *copy = NULL, *sect = NULL; |
struct sshbuf *copy = NULL, *sect = NULL; |
struct ssh_krl *krl = NULL; |
struct ssh_krl *krl = NULL; |
char timestamp[64]; |
char timestamp[64]; |
int r = SSH_ERR_INTERNAL_ERROR, sig_seen; |
int r = SSH_ERR_INTERNAL_ERROR; |
struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used; |
|
u_char type; |
u_char type; |
const u_char *blob; |
|
size_t i, j, sig_off, sects_off, blen, nca_used; |
|
u_int format_version; |
u_int format_version; |
|
|
nca_used = 0; |
|
*krlp = NULL; |
*krlp = NULL; |
if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 || |
|
memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) { |
|
debug3_f("not a KRL"); |
|
return SSH_ERR_KRL_BAD_MAGIC; |
|
} |
|
|
|
/* Take a copy of the KRL buffer so we can verify its signature later */ |
/* KRL must begin with magic string */ |
if ((copy = sshbuf_fromb(buf)) == NULL) { |
if ((r = sshbuf_cmp(buf, 0, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0) { |
r = SSH_ERR_ALLOC_FAIL; |
debug2_f("bad KRL magic header"); |
goto out; |
return r; |
} |
} |
if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0) |
|
goto out; |
|
|
|
if ((krl = ssh_krl_init()) == NULL) { |
if ((krl = ssh_krl_init()) == NULL) { |
error_f("alloc failed"); |
error_f("alloc failed"); |
goto out; |
goto out; |
} |
} |
|
/* Don't modify buffer */ |
if ((r = sshbuf_get_u32(copy, &format_version)) != 0) |
if ((copy = sshbuf_fromb(buf)) == NULL) { |
|
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
|
} |
|
if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0 || |
|
(r = sshbuf_get_u32(copy, &format_version)) != 0) |
|
goto out; |
if (format_version != KRL_FORMAT_VERSION) { |
if (format_version != KRL_FORMAT_VERSION) { |
|
error_f("unsupported KRL format version %u", format_version); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
|
|
(r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || |
(r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || |
(r = sshbuf_get_u64(copy, &krl->flags)) != 0 || |
(r = sshbuf_get_u64(copy, &krl->flags)) != 0 || |
(r = sshbuf_skip_string(copy)) != 0 || |
(r = sshbuf_skip_string(copy)) != 0 || |
(r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) |
(r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) { |
|
error_fr(r, "parse KRL header"); |
goto out; |
goto out; |
|
} |
format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); |
format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); |
debug("KRL version %llu generated at %s%s%s", |
debug("KRL version %llu generated at %s%s%s", |
(long long unsigned)krl->krl_version, timestamp, |
(long long unsigned)krl->krl_version, timestamp, |
*krl->comment ? ": " : "", krl->comment); |
*krl->comment ? ": " : "", krl->comment); |
|
|
/* |
/* Parse and load the KRL sections. */ |
* 1st pass: verify signatures, if any. This is done to avoid |
|
* detailed parsing of data whose provenance is unverified. |
|
*/ |
|
sig_seen = 0; |
|
if (sshbuf_len(buf) < sshbuf_len(copy)) { |
|
/* Shouldn't happen */ |
|
r = SSH_ERR_INTERNAL_ERROR; |
|
goto out; |
|
} |
|
sects_off = sshbuf_len(buf) - sshbuf_len(copy); |
|
while (sshbuf_len(copy) > 0) { |
while (sshbuf_len(copy) > 0) { |
if ((r = sshbuf_get_u8(copy, &type)) != 0 || |
|
(r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) |
|
goto out; |
|
KRL_DBG(("first pass, section 0x%02x", type)); |
|
if (type != KRL_SECTION_SIGNATURE) { |
|
if (sig_seen) { |
|
error("KRL contains non-signature section " |
|
"after signature"); |
|
r = SSH_ERR_INVALID_FORMAT; |
|
goto out; |
|
} |
|
/* Not interested for now. */ |
|
continue; |
|
} |
|
sig_seen = 1; |
|
/* First string component is the signing key */ |
|
if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { |
|
r = SSH_ERR_INVALID_FORMAT; |
|
goto out; |
|
} |
|
if (sshbuf_len(buf) < sshbuf_len(copy)) { |
|
/* Shouldn't happen */ |
|
r = SSH_ERR_INTERNAL_ERROR; |
|
goto out; |
|
} |
|
sig_off = sshbuf_len(buf) - sshbuf_len(copy); |
|
/* Second string component is the signature itself */ |
|
if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) { |
|
r = SSH_ERR_INVALID_FORMAT; |
|
goto out; |
|
} |
|
/* Check signature over entire KRL up to this point */ |
|
if ((r = sshkey_verify(key, blob, blen, |
|
sshbuf_ptr(buf), sig_off, NULL, 0, NULL)) != 0) |
|
goto out; |
|
/* Check if this key has already signed this KRL */ |
|
for (i = 0; i < nca_used; i++) { |
|
if (sshkey_equal(ca_used[i], key)) { |
|
error("KRL signed more than once with " |
|
"the same key"); |
|
r = SSH_ERR_INVALID_FORMAT; |
|
goto out; |
|
} |
|
} |
|
/* Record keys used to sign the KRL */ |
|
tmp_ca_used = recallocarray(ca_used, nca_used, nca_used + 1, |
|
sizeof(*ca_used)); |
|
if (tmp_ca_used == NULL) { |
|
r = SSH_ERR_ALLOC_FAIL; |
|
goto out; |
|
} |
|
ca_used = tmp_ca_used; |
|
ca_used[nca_used++] = key; |
|
key = NULL; |
|
} |
|
|
|
if (sshbuf_len(copy) != 0) { |
|
/* Shouldn't happen */ |
|
r = SSH_ERR_INTERNAL_ERROR; |
|
goto out; |
|
} |
|
|
|
/* |
|
* 2nd pass: parse and load the KRL, skipping the header to the point |
|
* where the section start. |
|
*/ |
|
sshbuf_free(copy); |
|
if ((copy = sshbuf_fromb(buf)) == NULL) { |
|
r = SSH_ERR_ALLOC_FAIL; |
|
goto out; |
|
} |
|
if ((r = sshbuf_consume(copy, sects_off)) != 0) |
|
goto out; |
|
while (sshbuf_len(copy) > 0) { |
|
sshbuf_free(sect); |
sshbuf_free(sect); |
sect = NULL; |
sect = NULL; |
if ((r = sshbuf_get_u8(copy, &type)) != 0 || |
if ((r = sshbuf_get_u8(copy, &type)) != 0 || |
(r = sshbuf_froms(copy, §)) != 0) |
(r = sshbuf_froms(copy, §)) != 0) |
goto out; |
goto out; |
KRL_DBG(("second pass, section 0x%02x", type)); |
KRL_DBG(("section 0x%02x", type)); |
|
|
switch (type) { |
switch (type) { |
case KRL_SECTION_CERTIFICATES: |
case KRL_SECTION_CERTIFICATES: |
|
|
} |
} |
} |
} |
|
|
/* Check that the key(s) used to sign the KRL weren't revoked */ |
/* Success */ |
sig_seen = 0; |
|
for (i = 0; i < nca_used; i++) { |
|
if (ssh_krl_check_key(krl, ca_used[i]) == 0) |
|
sig_seen = 1; |
|
else { |
|
sshkey_free(ca_used[i]); |
|
ca_used[i] = NULL; |
|
} |
|
} |
|
if (nca_used && !sig_seen) { |
|
error("All keys used to sign KRL were revoked"); |
|
r = SSH_ERR_KEY_REVOKED; |
|
goto out; |
|
} |
|
|
|
/* If we have CA keys, then verify that one was used to sign the KRL */ |
|
if (sig_seen && nsign_ca_keys != 0) { |
|
sig_seen = 0; |
|
for (i = 0; !sig_seen && i < nsign_ca_keys; i++) { |
|
for (j = 0; j < nca_used; j++) { |
|
if (ca_used[j] == NULL) |
|
continue; |
|
if (sshkey_equal(ca_used[j], sign_ca_keys[i])) { |
|
sig_seen = 1; |
|
break; |
|
} |
|
} |
|
} |
|
if (!sig_seen) { |
|
r = SSH_ERR_SIGNATURE_INVALID; |
|
error("KRL not signed with any trusted key"); |
|
goto out; |
|
} |
|
} |
|
|
|
*krlp = krl; |
*krlp = krl; |
r = 0; |
r = 0; |
out: |
out: |
if (r != 0) |
if (r != 0) |
ssh_krl_free(krl); |
ssh_krl_free(krl); |
for (i = 0; i < nca_used; i++) |
|
sshkey_free(ca_used[i]); |
|
free(ca_used); |
|
sshkey_free(key); |
|
sshbuf_free(copy); |
sshbuf_free(copy); |
sshbuf_free(sect); |
sshbuf_free(sect); |
return r; |
return r; |
|
|
oerrno = errno; |
oerrno = errno; |
goto out; |
goto out; |
} |
} |
if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0) |
if ((r = ssh_krl_from_blob(krlbuf, &krl)) != 0) |
goto out; |
goto out; |
debug2_f("checking KRL %s", path); |
debug2_f("checking KRL %s", path); |
r = ssh_krl_check_key(krl, key); |
r = ssh_krl_check_key(krl, key); |