Changeset View
Changeset View
Standalone View
Standalone View
krl.c
/* | /* | ||||
* Copyright (c) 2012 Damien Miller <djm@mindrot.org> | * Copyright (c) 2012 Damien Miller <djm@mindrot.org> | ||||
* | * | ||||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||||
* copyright notice and this permission notice appear in all copies. | * copyright notice and this permission notice appear in all copies. | ||||
* | * | ||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
/* $OpenBSD: krl.c,v 1.42 2018/09/12 01:21:34 djm Exp $ */ | /* $OpenBSD: krl.c,v 1.51 2020/08/27 01:06:18 djm Exp $ */ | ||||
#include "includes.h" | #include "includes.h" | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <openbsd-compat/sys-tree.h> | #include <openbsd-compat/sys-tree.h> | ||||
#include <openbsd-compat/sys-queue.h> | #include <openbsd-compat/sys-queue.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <stdlib.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "sshbuf.h" | #include "sshbuf.h" | ||||
#include "ssherr.h" | #include "ssherr.h" | ||||
#include "sshkey.h" | #include "sshkey.h" | ||||
#include "authfile.h" | #include "authfile.h" | ||||
#include "misc.h" | #include "misc.h" | ||||
#include "log.h" | #include "log.h" | ||||
#include "digest.h" | #include "digest.h" | ||||
#include "bitmap.h" | #include "bitmap.h" | ||||
#include "utf8.h" | |||||
#include "krl.h" | #include "krl.h" | ||||
/* #define DEBUG_KRL */ | /* #define DEBUG_KRL */ | ||||
#ifdef DEBUG_KRL | #ifdef DEBUG_KRL | ||||
# define KRL_DBG(x) debug3 x | # define KRL_DBG(x) debug3 x | ||||
#else | #else | ||||
# define KRL_DBG(x) | # define KRL_DBG(x) | ||||
▲ Show 20 Lines • Show All 679 Lines • ▼ Show 20 Lines | |||||
out: | out: | ||||
bitmap_free(bitmap); | bitmap_free(bitmap); | ||||
sshbuf_free(sect); | sshbuf_free(sect); | ||||
return r; | return r; | ||||
} | } | ||||
int | int | ||||
ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, | ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, | ||||
const struct sshkey **sign_keys, u_int nsign_keys) | 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; | size_t slen, i; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, | ||||
} | } | ||||
for (i = 0; i < nsign_keys; i++) { | for (i = 0; i < nsign_keys; i++) { | ||||
KRL_DBG(("%s: signature key %s", __func__, | KRL_DBG(("%s: signature key %s", __func__, | ||||
sshkey_ssh_name(sign_keys[i]))); | sshkey_ssh_name(sign_keys[i]))); | ||||
if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || | if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || | ||||
(r = sshkey_puts(sign_keys[i], buf)) != 0) | (r = sshkey_puts(sign_keys[i], buf)) != 0) | ||||
goto out; | goto out; | ||||
/* XXX support sk-* keys */ | |||||
if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, | if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, | ||||
sshbuf_ptr(buf), sshbuf_len(buf), NULL, 0)) != 0) | sshbuf_ptr(buf), sshbuf_len(buf), NULL, NULL, | ||||
NULL, 0)) != 0) | |||||
goto out; | goto out; | ||||
KRL_DBG(("%s: signature sig len %zu", __func__, slen)); | KRL_DBG(("%s: signature sig len %zu", __func__, slen)); | ||||
if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) | if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) | ||||
goto out; | goto out; | ||||
} | } | ||||
r = 0; | r = 0; | ||||
out: | out: | ||||
▲ Show 20 Lines • Show All 249 Lines • ▼ Show 20 Lines | while (sshbuf_len(copy) > 0) { | ||||
sig_off = sshbuf_len(buf) - sshbuf_len(copy); | sig_off = sshbuf_len(buf) - sshbuf_len(copy); | ||||
/* Second string component is the signature itself */ | /* Second string component is the signature itself */ | ||||
if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) { | if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) { | ||||
r = SSH_ERR_INVALID_FORMAT; | r = SSH_ERR_INVALID_FORMAT; | ||||
goto out; | goto out; | ||||
} | } | ||||
/* Check signature over entire KRL up to this point */ | /* Check signature over entire KRL up to this point */ | ||||
if ((r = sshkey_verify(key, blob, blen, | if ((r = sshkey_verify(key, blob, blen, | ||||
sshbuf_ptr(buf), sig_off, NULL, 0)) != 0) | sshbuf_ptr(buf), sig_off, NULL, 0, NULL)) != 0) | ||||
goto out; | goto out; | ||||
/* Check if this key has already signed this KRL */ | /* Check if this key has already signed this KRL */ | ||||
for (i = 0; i < nca_used; i++) { | for (i = 0; i < nca_used; i++) { | ||||
if (sshkey_equal(ca_used[i], key)) { | if (sshkey_equal(ca_used[i], key)) { | ||||
error("KRL signed more than once with " | error("KRL signed more than once with " | ||||
"the same key"); | "the same key"); | ||||
r = SSH_ERR_INVALID_FORMAT; | r = SSH_ERR_INVALID_FORMAT; | ||||
goto out; | goto out; | ||||
▲ Show 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key) | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
ssh_krl_file_contains_key(const char *path, const struct sshkey *key) | ssh_krl_file_contains_key(const char *path, const struct sshkey *key) | ||||
{ | { | ||||
struct sshbuf *krlbuf = NULL; | struct sshbuf *krlbuf = NULL; | ||||
struct ssh_krl *krl = NULL; | struct ssh_krl *krl = NULL; | ||||
int oerrno = 0, r, fd; | int oerrno = 0, r; | ||||
if (path == NULL) | if (path == NULL) | ||||
return 0; | return 0; | ||||
if ((r = sshbuf_load_file(path, &krlbuf)) != 0) { | |||||
if ((krlbuf = sshbuf_new()) == NULL) | |||||
return SSH_ERR_ALLOC_FAIL; | |||||
if ((fd = open(path, O_RDONLY)) == -1) { | |||||
r = SSH_ERR_SYSTEM_ERROR; | |||||
oerrno = errno; | oerrno = errno; | ||||
goto out; | goto out; | ||||
} | } | ||||
if ((r = sshkey_load_file(fd, krlbuf)) != 0) { | |||||
oerrno = errno; | |||||
goto out; | |||||
} | |||||
if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0) | if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0) | ||||
goto out; | goto out; | ||||
debug2("%s: checking KRL %s", __func__, path); | debug2("%s: checking KRL %s", __func__, path); | ||||
r = ssh_krl_check_key(krl, key); | r = ssh_krl_check_key(krl, key); | ||||
out: | out: | ||||
if (fd != -1) | |||||
close(fd); | |||||
sshbuf_free(krlbuf); | sshbuf_free(krlbuf); | ||||
ssh_krl_free(krl); | ssh_krl_free(krl); | ||||
if (r != 0) | if (r != 0) | ||||
errno = oerrno; | errno = oerrno; | ||||
return r; | return r; | ||||
} | |||||
int | |||||
krl_dump(struct ssh_krl *krl, FILE *f) | |||||
{ | |||||
struct sshkey *key = NULL; | |||||
struct revoked_blob *rb; | |||||
struct revoked_certs *rc; | |||||
struct revoked_serial *rs; | |||||
struct revoked_key_id *rki; | |||||
int r, ret = 0; | |||||
char *fp, timestamp[64]; | |||||
/* Try to print in a KRL spec-compatible format */ | |||||
format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); | |||||
fprintf(f, "# KRL version %llu\n", | |||||
(unsigned long long)krl->krl_version); | |||||
fprintf(f, "# Generated at %s\n", timestamp); | |||||
if (krl->comment != NULL && *krl->comment != '\0') { | |||||
r = INT_MAX; | |||||
asmprintf(&fp, INT_MAX, &r, "%s", krl->comment); | |||||
fprintf(f, "# Comment: %s\n", fp); | |||||
free(fp); | |||||
} | |||||
fputc('\n', f); | |||||
RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { | |||||
if ((r = sshkey_from_blob(rb->blob, rb->len, &key)) != 0) { | |||||
ret = SSH_ERR_INVALID_FORMAT; | |||||
error("Parse key in KRL: %s", ssh_err(r)); | |||||
continue; | |||||
} | |||||
if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, | |||||
SSH_FP_DEFAULT)) == NULL) { | |||||
ret = SSH_ERR_INVALID_FORMAT; | |||||
error("sshkey_fingerprint failed"); | |||||
continue; | |||||
} | |||||
fprintf(f, "hash: SHA256:%s # %s\n", fp, sshkey_ssh_name(key)); | |||||
free(fp); | |||||
free(key); | |||||
} | |||||
RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { | |||||
fp = tohex(rb->blob, rb->len); | |||||
fprintf(f, "hash: SHA256:%s\n", fp); | |||||
free(fp); | |||||
} | |||||
RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { | |||||
/* | |||||
* There is not KRL spec keyword for raw SHA1 hashes, so | |||||
* print them as comments. | |||||
*/ | |||||
fp = tohex(rb->blob, rb->len); | |||||
fprintf(f, "# hash SHA1:%s\n", fp); | |||||
free(fp); | |||||
} | |||||
TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { | |||||
fputc('\n', f); | |||||
if (rc->ca_key == NULL) | |||||
fprintf(f, "# Wildcard CA\n"); | |||||
else { | |||||
if ((fp = sshkey_fingerprint(rc->ca_key, | |||||
SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) { | |||||
ret = SSH_ERR_INVALID_FORMAT; | |||||
error("sshkey_fingerprint failed"); | |||||
continue; | |||||
} | |||||
fprintf(f, "# CA key %s %s\n", | |||||
sshkey_ssh_name(rc->ca_key), fp); | |||||
free(fp); | |||||
} | |||||
RB_FOREACH(rs, revoked_serial_tree, &rc->revoked_serials) { | |||||
if (rs->lo == rs->hi) { | |||||
fprintf(f, "serial: %llu\n", | |||||
(unsigned long long)rs->lo); | |||||
} else { | |||||
fprintf(f, "serial: %llu-%llu\n", | |||||
(unsigned long long)rs->lo, | |||||
(unsigned long long)rs->hi); | |||||
} | |||||
} | |||||
RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { | |||||
/* | |||||
* We don't want key IDs with embedded newlines to | |||||
* mess up the display. | |||||
*/ | |||||
r = INT_MAX; | |||||
asmprintf(&fp, INT_MAX, &r, "%s", rki->key_id); | |||||
fprintf(f, "id: %s\n", fp); | |||||
free(fp); | |||||
} | |||||
} | |||||
return ret; | |||||
} | } |