summaryrefslogtreecommitdiffstats
path: root/crypto/openssh/key.c
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2010-03-09 19:16:43 +0000
committerdes <des@FreeBSD.org>2010-03-09 19:16:43 +0000
commitc3510f9e73156eaabbbfc18da7d796b9f9ff7ea9 (patch)
tree286162d33ef4c84bb329fd42641bbe0dd68cc42c /crypto/openssh/key.c
parent68f48e51f9a4d71d7fb9c332592215895a206ff6 (diff)
parent57baac6b030508c9bcd74f3b995bd6a4a4f79211 (diff)
downloadFreeBSD-src-c3510f9e73156eaabbbfc18da7d796b9f9ff7ea9.zip
FreeBSD-src-c3510f9e73156eaabbbfc18da7d796b9f9ff7ea9.tar.gz
Upgrade to OpenSSH 5.4p1.
MFC after: 1 month
Diffstat (limited to 'crypto/openssh/key.c')
-rw-r--r--crypto/openssh/key.c613
1 files changed, 578 insertions, 35 deletions
diff --git a/crypto/openssh/key.c b/crypto/openssh/key.c
index 3e17da6..0d0c912 100644
--- a/crypto/openssh/key.c
+++ b/crypto/openssh/key.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.80 2008/10/10 05:00:12 stevesk Exp $ */
+/* $OpenBSD: key.c,v 1.85 2010/03/04 01:44:57 djm Exp $ */
/*
* read_bignum():
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -52,6 +52,21 @@
#include "uuencode.h"
#include "buffer.h"
#include "log.h"
+#include "ssh2.h"
+
+static struct KeyCert *
+cert_new(void)
+{
+ struct KeyCert *cert;
+
+ cert = xcalloc(1, sizeof(*cert));
+ buffer_init(&cert->certblob);
+ buffer_init(&cert->constraints);
+ cert->key_id = NULL;
+ cert->principals = NULL;
+ cert->signature_key = NULL;
+ return cert;
+}
Key *
key_new(int type)
@@ -63,9 +78,11 @@ key_new(int type)
k->type = type;
k->dsa = NULL;
k->rsa = NULL;
+ k->cert = NULL;
switch (k->type) {
case KEY_RSA1:
case KEY_RSA:
+ case KEY_RSA_CERT:
if ((rsa = RSA_new()) == NULL)
fatal("key_new: RSA_new failed");
if ((rsa->n = BN_new()) == NULL)
@@ -75,6 +92,7 @@ key_new(int type)
k->rsa = rsa;
break;
case KEY_DSA:
+ case KEY_DSA_CERT:
if ((dsa = DSA_new()) == NULL)
fatal("key_new: DSA_new failed");
if ((dsa->p = BN_new()) == NULL)
@@ -93,16 +111,20 @@ key_new(int type)
fatal("key_new: bad key type %d", k->type);
break;
}
+
+ if (key_is_cert(k))
+ k->cert = cert_new();
+
return k;
}
-Key *
-key_new_private(int type)
+void
+key_add_private(Key *k)
{
- Key *k = key_new(type);
switch (k->type) {
case KEY_RSA1:
case KEY_RSA:
+ case KEY_RSA_CERT:
if ((k->rsa->d = BN_new()) == NULL)
fatal("key_new_private: BN_new failed");
if ((k->rsa->iqmp = BN_new()) == NULL)
@@ -117,6 +139,7 @@ key_new_private(int type)
fatal("key_new_private: BN_new failed");
break;
case KEY_DSA:
+ case KEY_DSA_CERT:
if ((k->dsa->priv_key = BN_new()) == NULL)
fatal("key_new_private: BN_new failed");
break;
@@ -125,9 +148,34 @@ key_new_private(int type)
default:
break;
}
+}
+
+Key *
+key_new_private(int type)
+{
+ Key *k = key_new(type);
+
+ key_add_private(k);
return k;
}
+static void
+cert_free(struct KeyCert *cert)
+{
+ u_int i;
+
+ buffer_free(&cert->certblob);
+ buffer_free(&cert->constraints);
+ if (cert->key_id != NULL)
+ xfree(cert->key_id);
+ for (i = 0; i < cert->nprincipals; i++)
+ xfree(cert->principals[i]);
+ if (cert->principals != NULL)
+ xfree(cert->principals);
+ if (cert->signature_key != NULL)
+ key_free(cert->signature_key);
+}
+
void
key_free(Key *k)
{
@@ -136,11 +184,13 @@ key_free(Key *k)
switch (k->type) {
case KEY_RSA1:
case KEY_RSA:
+ case KEY_RSA_CERT:
if (k->rsa != NULL)
RSA_free(k->rsa);
k->rsa = NULL;
break;
case KEY_DSA:
+ case KEY_DSA_CERT:
if (k->dsa != NULL)
DSA_free(k->dsa);
k->dsa = NULL;
@@ -151,20 +201,49 @@ key_free(Key *k)
fatal("key_free: bad key type %d", k->type);
break;
}
+ if (key_is_cert(k)) {
+ if (k->cert != NULL)
+ cert_free(k->cert);
+ k->cert = NULL;
+ }
+
xfree(k);
}
+static int
+cert_compare(struct KeyCert *a, struct KeyCert *b)
+{
+ if (a == NULL && b == NULL)
+ return 1;
+ if (a == NULL || b == NULL)
+ return 0;
+ if (buffer_len(&a->certblob) != buffer_len(&b->certblob))
+ return 0;
+ if (memcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob),
+ buffer_len(&a->certblob)) != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Compare public portions of key only, allowing comparisons between
+ * certificates and plain keys too.
+ */
int
-key_equal(const Key *a, const Key *b)
+key_equal_public(const Key *a, const Key *b)
{
- if (a == NULL || b == NULL || a->type != b->type)
+ if (a == NULL || b == NULL ||
+ key_type_plain(a->type) != key_type_plain(b->type))
return 0;
+
switch (a->type) {
case KEY_RSA1:
+ case KEY_RSA_CERT:
case KEY_RSA:
return a->rsa != NULL && b->rsa != NULL &&
BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
BN_cmp(a->rsa->n, b->rsa->n) == 0;
+ case KEY_DSA_CERT:
case KEY_DSA:
return a->dsa != NULL && b->dsa != NULL &&
BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
@@ -177,16 +256,27 @@ key_equal(const Key *a, const Key *b)
/* NOTREACHED */
}
+int
+key_equal(const Key *a, const Key *b)
+{
+ if (a == NULL || b == NULL || a->type != b->type)
+ return 0;
+ if (key_is_cert(a)) {
+ if (!cert_compare(a->cert, b->cert))
+ return 0;
+ }
+ return key_equal_public(a, b);
+}
+
u_char*
-key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
- u_int *dgst_raw_length)
+key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length)
{
const EVP_MD *md = NULL;
EVP_MD_CTX ctx;
u_char *blob = NULL;
u_char *retval = NULL;
u_int len = 0;
- int nlen, elen;
+ int nlen, elen, otype;
*dgst_raw_length = 0;
@@ -214,6 +304,14 @@ key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
case KEY_RSA:
key_to_blob(k, &blob, &len);
break;
+ case KEY_DSA_CERT:
+ case KEY_RSA_CERT:
+ /* We want a fingerprint of the _key_ not of the cert */
+ otype = k->type;
+ k->type = key_type_plain(k->type);
+ key_to_blob(k, &blob, &len);
+ k->type = otype;
+ break;
case KEY_UNSPEC:
return retval;
default:
@@ -408,7 +506,7 @@ key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k)
}
char *
-key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
+key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
{
char *retval = NULL;
u_char *dgst_raw;
@@ -522,11 +620,19 @@ key_read(Key *ret, char **cpp)
return -1;
if (!read_bignum(cpp, ret->rsa->n))
return -1;
+ /* validate the claimed number of bits */
+ if ((u_int)BN_num_bits(ret->rsa->n) != bits) {
+ verbose("key_read: claimed key size %d does not match "
+ "actual %d", bits, BN_num_bits(ret->rsa->n));
+ return -1;
+ }
success = 1;
break;
case KEY_UNSPEC:
case KEY_RSA:
case KEY_DSA:
+ case KEY_DSA_CERT:
+ case KEY_RSA_CERT:
space = strchr(cp, ' ');
if (space == NULL) {
debug3("key_read: missing whitespace");
@@ -571,25 +677,36 @@ key_read(Key *ret, char **cpp)
return -1;
}
/*XXXX*/
- if (ret->type == KEY_RSA) {
+ if (key_is_cert(ret)) {
+ if (!key_is_cert(k)) {
+ error("key_read: loaded key is not a cert");
+ key_free(k);
+ return -1;
+ }
+ if (ret->cert != NULL)
+ cert_free(ret->cert);
+ ret->cert = k->cert;
+ k->cert = NULL;
+ }
+ if (key_type_plain(ret->type) == KEY_RSA) {
if (ret->rsa != NULL)
RSA_free(ret->rsa);
ret->rsa = k->rsa;
k->rsa = NULL;
- success = 1;
#ifdef DEBUG_PK
RSA_print_fp(stderr, ret->rsa, 8);
#endif
- } else {
+ }
+ if (key_type_plain(ret->type) == KEY_DSA) {
if (ret->dsa != NULL)
DSA_free(ret->dsa);
ret->dsa = k->dsa;
k->dsa = NULL;
- success = 1;
#ifdef DEBUG_PK
DSA_print_fp(stderr, ret->dsa, 8);
#endif
}
+ success = 1;
/*XXXX*/
key_free(k);
if (success != 1)
@@ -616,28 +733,53 @@ key_write(const Key *key, FILE *f)
u_char *blob;
char *uu;
- if (key->type == KEY_RSA1 && key->rsa != NULL) {
+ if (key_is_cert(key)) {
+ if (key->cert == NULL) {
+ error("%s: no cert data", __func__);
+ return 0;
+ }
+ if (buffer_len(&key->cert->certblob) == 0) {
+ error("%s: no signed certificate blob", __func__);
+ return 0;
+ }
+ }
+
+ switch (key->type) {
+ case KEY_RSA1:
+ if (key->rsa == NULL)
+ return 0;
/* size of modulus 'n' */
bits = BN_num_bits(key->rsa->n);
fprintf(f, "%u", bits);
if (write_bignum(f, key->rsa->e) &&
- write_bignum(f, key->rsa->n)) {
- success = 1;
- } else {
- error("key_write: failed for RSA key");
- }
- } else if ((key->type == KEY_DSA && key->dsa != NULL) ||
- (key->type == KEY_RSA && key->rsa != NULL)) {
- key_to_blob(key, &blob, &len);
- uu = xmalloc(2*len);
- n = uuencode(blob, len, uu, 2*len);
- if (n > 0) {
- fprintf(f, "%s %s", key_ssh_name(key), uu);
- success = 1;
- }
- xfree(blob);
- xfree(uu);
+ write_bignum(f, key->rsa->n))
+ return 1;
+ error("key_write: failed for RSA key");
+ return 0;
+ case KEY_DSA:
+ case KEY_DSA_CERT:
+ if (key->dsa == NULL)
+ return 0;
+ break;
+ case KEY_RSA:
+ case KEY_RSA_CERT:
+ if (key->rsa == NULL)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ key_to_blob(key, &blob, &len);
+ uu = xmalloc(2*len);
+ n = uuencode(blob, len, uu, 2*len);
+ if (n > 0) {
+ fprintf(f, "%s %s", key_ssh_name(key), uu);
+ success = 1;
}
+ xfree(blob);
+ xfree(uu);
+
return success;
}
@@ -651,6 +793,10 @@ key_type(const Key *k)
return "RSA";
case KEY_DSA:
return "DSA";
+ case KEY_RSA_CERT:
+ return "RSA-CERT";
+ case KEY_DSA_CERT:
+ return "DSA-CERT";
}
return "unknown";
}
@@ -663,6 +809,10 @@ key_ssh_name(const Key *k)
return "ssh-rsa";
case KEY_DSA:
return "ssh-dss";
+ case KEY_RSA_CERT:
+ return "ssh-rsa-cert-v00@openssh.com";
+ case KEY_DSA_CERT:
+ return "ssh-dss-cert-v00@openssh.com";
}
return "ssh-unknown";
}
@@ -673,8 +823,10 @@ key_size(const Key *k)
switch (k->type) {
case KEY_RSA1:
case KEY_RSA:
+ case KEY_RSA_CERT:
return BN_num_bits(k->rsa->n);
case KEY_DSA:
+ case KEY_DSA_CERT:
return BN_num_bits(k->dsa->p);
}
return 0;
@@ -685,7 +837,7 @@ rsa_generate_private_key(u_int bits)
{
RSA *private;
- private = RSA_generate_key(bits, 35, NULL, NULL);
+ private = RSA_generate_key(bits, RSA_F4, NULL, NULL);
if (private == NULL)
fatal("rsa_generate_private_key: key generation failed.");
return private;
@@ -717,6 +869,9 @@ key_generate(int type, u_int bits)
case KEY_RSA1:
k->rsa = rsa_generate_private_key(bits);
break;
+ case KEY_RSA_CERT:
+ case KEY_DSA_CERT:
+ fatal("key_generate: cert keys cannot be generated directly");
default:
fatal("key_generate: unknown type %d", type);
}
@@ -724,12 +879,55 @@ key_generate(int type, u_int bits)
return k;
}
+void
+key_cert_copy(const Key *from_key, struct Key *to_key)
+{
+ u_int i;
+ const struct KeyCert *from;
+ struct KeyCert *to;
+
+ if (to_key->cert != NULL) {
+ cert_free(to_key->cert);
+ to_key->cert = NULL;
+ }
+
+ if ((from = from_key->cert) == NULL)
+ return;
+
+ to = to_key->cert = cert_new();
+
+ buffer_append(&to->certblob, buffer_ptr(&from->certblob),
+ buffer_len(&from->certblob));
+
+ buffer_append(&to->constraints, buffer_ptr(&from->constraints),
+ buffer_len(&from->constraints));
+
+ to->type = from->type;
+ to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id);
+ to->valid_after = from->valid_after;
+ to->valid_before = from->valid_before;
+ to->signature_key = from->signature_key == NULL ?
+ NULL : key_from_private(from->signature_key);
+
+ to->nprincipals = from->nprincipals;
+ if (to->nprincipals > CERT_MAX_PRINCIPALS)
+ fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)",
+ __func__, to->nprincipals, CERT_MAX_PRINCIPALS);
+ if (to->nprincipals > 0) {
+ to->principals = xcalloc(from->nprincipals,
+ sizeof(*to->principals));
+ for (i = 0; i < to->nprincipals; i++)
+ to->principals[i] = xstrdup(from->principals[i]);
+ }
+}
+
Key *
key_from_private(const Key *k)
{
Key *n = NULL;
switch (k->type) {
case KEY_DSA:
+ case KEY_DSA_CERT:
n = key_new(k->type);
if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
(BN_copy(n->dsa->q, k->dsa->q) == NULL) ||
@@ -739,6 +937,7 @@ key_from_private(const Key *k)
break;
case KEY_RSA:
case KEY_RSA1:
+ case KEY_RSA_CERT:
n = key_new(k->type);
if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
(BN_copy(n->rsa->e, k->rsa->e) == NULL))
@@ -748,6 +947,8 @@ key_from_private(const Key *k)
fatal("key_from_private: unknown type %d", k->type);
break;
}
+ if (key_is_cert(k))
+ key_cert_copy(k, n);
return n;
}
@@ -764,6 +965,10 @@ key_type_from_name(char *name)
return KEY_RSA;
} else if (strcmp(name, "ssh-dss") == 0) {
return KEY_DSA;
+ } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) {
+ return KEY_RSA_CERT;
+ } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) {
+ return KEY_DSA_CERT;
}
debug2("key_type_from_name: unknown key type '%s'", name);
return KEY_UNSPEC;
@@ -791,6 +996,127 @@ key_names_valid2(const char *names)
return 1;
}
+static int
+cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
+{
+ u_char *principals, *constraints, *sig_key, *sig;
+ u_int signed_len, plen, clen, sklen, slen, kidlen;
+ Buffer tmp;
+ char *principal;
+ int ret = -1;
+
+ buffer_init(&tmp);
+
+ /* Copy the entire key blob for verification and later serialisation */
+ buffer_append(&key->cert->certblob, blob, blen);
+
+ principals = constraints = sig_key = sig = NULL;
+ if (buffer_get_int_ret(&key->cert->type, b) != 0 ||
+ (key->cert->key_id = buffer_get_string_ret(b, &kidlen)) == NULL ||
+ (principals = buffer_get_string_ret(b, &plen)) == NULL ||
+ buffer_get_int64_ret(&key->cert->valid_after, b) != 0 ||
+ buffer_get_int64_ret(&key->cert->valid_before, b) != 0 ||
+ (constraints = buffer_get_string_ret(b, &clen)) == NULL ||
+ /* skip nonce */ buffer_get_string_ptr_ret(b, NULL) == NULL ||
+ /* skip reserved */ buffer_get_string_ptr_ret(b, NULL) == NULL ||
+ (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) {
+ error("%s: parse error", __func__);
+ goto out;
+ }
+
+ if (kidlen != strlen(key->cert->key_id)) {
+ error("%s: key ID contains \\0 character", __func__);
+ goto out;
+ }
+
+ /* Signature is left in the buffer so we can calculate this length */
+ signed_len = buffer_len(&key->cert->certblob) - buffer_len(b);
+
+ if ((sig = buffer_get_string_ret(b, &slen)) == NULL) {
+ error("%s: parse error", __func__);
+ goto out;
+ }
+
+ if (key->cert->type != SSH2_CERT_TYPE_USER &&
+ key->cert->type != SSH2_CERT_TYPE_HOST) {
+ error("Unknown certificate type %u", key->cert->type);
+ goto out;
+ }
+
+ buffer_append(&tmp, principals, plen);
+ while (buffer_len(&tmp) > 0) {
+ if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) {
+ error("%s: Too many principals", __func__);
+ goto out;
+ }
+ if ((principal = buffer_get_string_ret(&tmp, &plen)) == NULL) {
+ error("%s: Principals data invalid", __func__);
+ goto out;
+ }
+ if (strlen(principal) != plen) {
+ error("%s: Principal contains \\0 character",
+ __func__);
+ goto out;
+ }
+ key->cert->principals = xrealloc(key->cert->principals,
+ key->cert->nprincipals + 1, sizeof(*key->cert->principals));
+ key->cert->principals[key->cert->nprincipals++] = principal;
+ }
+
+ buffer_clear(&tmp);
+
+ buffer_append(&key->cert->constraints, constraints, clen);
+ buffer_append(&tmp, constraints, clen);
+ /* validate structure */
+ while (buffer_len(&tmp) != 0) {
+ if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
+ buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
+ error("%s: Constraints data invalid", __func__);
+ goto out;
+ }
+ }
+ buffer_clear(&tmp);
+
+ if ((key->cert->signature_key = key_from_blob(sig_key,
+ sklen)) == NULL) {
+ error("%s: Signature key invalid", __func__);
+ goto out;
+ }
+ if (key->cert->signature_key->type != KEY_RSA &&
+ key->cert->signature_key->type != KEY_DSA) {
+ error("%s: Invalid signature key type %s (%d)", __func__,
+ key_type(key->cert->signature_key),
+ key->cert->signature_key->type);
+ goto out;
+ }
+
+ switch (key_verify(key->cert->signature_key, sig, slen,
+ buffer_ptr(&key->cert->certblob), signed_len)) {
+ case 1:
+ ret = 0;
+ break; /* Good signature */
+ case 0:
+ error("%s: Invalid signature on certificate", __func__);
+ goto out;
+ case -1:
+ error("%s: Certificate signature verification failed",
+ __func__);
+ goto out;
+ }
+
+ out:
+ buffer_free(&tmp);
+ if (principals != NULL)
+ xfree(principals);
+ if (constraints != NULL)
+ xfree(constraints);
+ if (sig_key != NULL)
+ xfree(sig_key);
+ if (sig != NULL)
+ xfree(sig);
+ return ret;
+}
+
Key *
key_from_blob(const u_char *blob, u_int blen)
{
@@ -813,10 +1139,12 @@ key_from_blob(const u_char *blob, u_int blen)
switch (type) {
case KEY_RSA:
+ case KEY_RSA_CERT:
key = key_new(type);
if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 ||
buffer_get_bignum2_ret(&b, key->rsa->n) == -1) {
error("key_from_blob: can't read rsa key");
+ badkey:
key_free(key);
key = NULL;
goto out;
@@ -826,15 +1154,14 @@ key_from_blob(const u_char *blob, u_int blen)
#endif
break;
case KEY_DSA:
+ case KEY_DSA_CERT:
key = key_new(type);
if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 ||
buffer_get_bignum2_ret(&b, key->dsa->q) == -1 ||
buffer_get_bignum2_ret(&b, key->dsa->g) == -1 ||
buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) {
error("key_from_blob: can't read dsa key");
- key_free(key);
- key = NULL;
- goto out;
+ goto badkey;
}
#ifdef DEBUG_PK
DSA_print_fp(stderr, key->dsa, 8);
@@ -847,6 +1174,10 @@ key_from_blob(const u_char *blob, u_int blen)
error("key_from_blob: cannot handle type %s", ktype);
goto out;
}
+ if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) {
+ error("key_from_blob: can't parse cert data");
+ goto badkey;
+ }
rlen = buffer_len(&b);
if (key != NULL && rlen != 0)
error("key_from_blob: remaining bytes in key blob %d", rlen);
@@ -869,6 +1200,12 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
}
buffer_init(&b);
switch (key->type) {
+ case KEY_DSA_CERT:
+ case KEY_RSA_CERT:
+ /* Use the existing blob */
+ buffer_append(&b, buffer_ptr(&key->cert->certblob),
+ buffer_len(&key->cert->certblob));
+ break;
case KEY_DSA:
buffer_put_cstring(&b, key_ssh_name(key));
buffer_put_bignum2(&b, key->dsa->p);
@@ -905,8 +1242,10 @@ key_sign(
const u_char *data, u_int datalen)
{
switch (key->type) {
+ case KEY_DSA_CERT:
case KEY_DSA:
return ssh_dss_sign(key, sigp, lenp, data, datalen);
+ case KEY_RSA_CERT:
case KEY_RSA:
return ssh_rsa_sign(key, sigp, lenp, data, datalen);
default:
@@ -929,8 +1268,10 @@ key_verify(
return -1;
switch (key->type) {
+ case KEY_DSA_CERT:
case KEY_DSA:
return ssh_dss_verify(key, signature, signaturelen, data, datalen);
+ case KEY_RSA_CERT:
case KEY_RSA:
return ssh_rsa_verify(key, signature, signaturelen, data, datalen);
default:
@@ -952,6 +1293,9 @@ key_demote(const Key *k)
pk->rsa = NULL;
switch (k->type) {
+ case KEY_RSA_CERT:
+ key_cert_copy(k, pk);
+ /* FALLTHROUGH */
case KEY_RSA1:
case KEY_RSA:
if ((pk->rsa = RSA_new()) == NULL)
@@ -961,6 +1305,9 @@ key_demote(const Key *k)
if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL)
fatal("key_demote: BN_dup failed");
break;
+ case KEY_DSA_CERT:
+ key_cert_copy(k, pk);
+ /* FALLTHROUGH */
case KEY_DSA:
if ((pk->dsa = DSA_new()) == NULL)
fatal("key_demote: DSA_new failed");
@@ -980,3 +1327,199 @@ key_demote(const Key *k)
return (pk);
}
+
+int
+key_is_cert(const Key *k)
+{
+ return k != NULL &&
+ (k->type == KEY_RSA_CERT || k->type == KEY_DSA_CERT);
+}
+
+/* Return the cert-less equivalent to a certified key type */
+int
+key_type_plain(int type)
+{
+ switch (type) {
+ case KEY_RSA_CERT:
+ return KEY_RSA;
+ case KEY_DSA_CERT:
+ return KEY_DSA;
+ default:
+ return type;
+ }
+}
+
+/* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */
+int
+key_to_certified(Key *k)
+{
+ switch (k->type) {
+ case KEY_RSA:
+ k->cert = cert_new();
+ k->type = KEY_RSA_CERT;
+ return 0;
+ case KEY_DSA:
+ k->cert = cert_new();
+ k->type = KEY_DSA_CERT;
+ return 0;
+ default:
+ error("%s: key has incorrect type %s", __func__, key_type(k));
+ return -1;
+ }
+}
+
+/* Convert a KEY_RSA_CERT or KEY_DSA_CERT to their raw key equivalent */
+int
+key_drop_cert(Key *k)
+{
+ switch (k->type) {
+ case KEY_RSA_CERT:
+ cert_free(k->cert);
+ k->type = KEY_RSA;
+ return 0;
+ case KEY_DSA_CERT:
+ cert_free(k->cert);
+ k->type = KEY_DSA;
+ return 0;
+ default:
+ error("%s: key has incorrect type %s", __func__, key_type(k));
+ return -1;
+ }
+}
+
+/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */
+int
+key_certify(Key *k, Key *ca)
+{
+ Buffer principals;
+ u_char *ca_blob, *sig_blob, nonce[32];
+ u_int i, ca_len, sig_len;
+
+ if (k->cert == NULL) {
+ error("%s: key lacks cert info", __func__);
+ return -1;
+ }
+
+ if (!key_is_cert(k)) {
+ error("%s: certificate has unknown type %d", __func__,
+ k->cert->type);
+ return -1;
+ }
+
+ if (ca->type != KEY_RSA && ca->type != KEY_DSA) {
+ error("%s: CA key has unsupported type %s", __func__,
+ key_type(ca));
+ return -1;
+ }
+
+ key_to_blob(ca, &ca_blob, &ca_len);
+
+ buffer_clear(&k->cert->certblob);
+ buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));
+
+ switch (k->type) {
+ case KEY_DSA_CERT:
+ buffer_put_bignum2(&k->cert->certblob, k->dsa->p);
+ buffer_put_bignum2(&k->cert->certblob, k->dsa->q);
+ buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
+ buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
+ break;
+ case KEY_RSA_CERT:
+ buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
+ buffer_put_bignum2(&k->cert->certblob, k->rsa->n);
+ break;
+ default:
+ error("%s: key has incorrect type %s", __func__, key_type(k));
+ buffer_clear(&k->cert->certblob);
+ xfree(ca_blob);
+ return -1;
+ }
+
+ buffer_put_int(&k->cert->certblob, k->cert->type);
+ buffer_put_cstring(&k->cert->certblob, k->cert->key_id);
+
+ buffer_init(&principals);
+ for (i = 0; i < k->cert->nprincipals; i++)
+ buffer_put_cstring(&principals, k->cert->principals[i]);
+ buffer_put_string(&k->cert->certblob, buffer_ptr(&principals),
+ buffer_len(&principals));
+ buffer_free(&principals);
+
+ buffer_put_int64(&k->cert->certblob, k->cert->valid_after);
+ buffer_put_int64(&k->cert->certblob, k->cert->valid_before);
+ buffer_put_string(&k->cert->certblob,
+ buffer_ptr(&k->cert->constraints),
+ buffer_len(&k->cert->constraints));
+
+ arc4random_buf(&nonce, sizeof(nonce));
+ buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
+ buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
+ buffer_put_string(&k->cert->certblob, ca_blob, ca_len);
+ xfree(ca_blob);
+
+ /* Sign the whole mess */
+ if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob),
+ buffer_len(&k->cert->certblob)) != 0) {
+ error("%s: signature operation failed", __func__);
+ buffer_clear(&k->cert->certblob);
+ return -1;
+ }
+ /* Append signature and we are done */
+ buffer_put_string(&k->cert->certblob, sig_blob, sig_len);
+ xfree(sig_blob);
+
+ return 0;
+}
+
+int
+key_cert_check_authority(const Key *k, int want_host, int require_principal,
+ const char *name, const char **reason)
+{
+ u_int i, principal_matches;
+ time_t now = time(NULL);
+
+ if (want_host) {
+ if (k->cert->type != SSH2_CERT_TYPE_HOST) {
+ *reason = "Certificate invalid: not a host certificate";
+ return -1;
+ }
+ } else {
+ if (k->cert->type != SSH2_CERT_TYPE_USER) {
+ *reason = "Certificate invalid: not a user certificate";
+ return -1;
+ }
+ }
+ if (now < 0) {
+ error("%s: system clock lies before epoch", __func__);
+ *reason = "Certificate invalid: not yet valid";
+ return -1;
+ }
+ if ((u_int64_t)now < k->cert->valid_after) {
+ *reason = "Certificate invalid: not yet valid";
+ return -1;
+ }
+ if ((u_int64_t)now >= k->cert->valid_before) {
+ *reason = "Certificate invalid: expired";
+ return -1;
+ }
+ if (k->cert->nprincipals == 0) {
+ if (require_principal) {
+ *reason = "Certificate lacks principal list";
+ return -1;
+ }
+ } else {
+ principal_matches = 0;
+ for (i = 0; i < k->cert->nprincipals; i++) {
+ if (strcmp(name, k->cert->principals[i]) == 0) {
+ principal_matches = 1;
+ break;
+ }
+ }
+ if (!principal_matches) {
+ *reason = "Certificate invalid: name is not a listed "
+ "principal";
+ return -1;
+ }
+ }
+ return 0;
+}
OpenPOWER on IntegriCloud