diff options
author | des <des@FreeBSD.org> | 2010-03-09 19:16:43 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2010-03-09 19:16:43 +0000 |
commit | c3510f9e73156eaabbbfc18da7d796b9f9ff7ea9 (patch) | |
tree | 286162d33ef4c84bb329fd42641bbe0dd68cc42c /crypto/openssh/key.c | |
parent | 68f48e51f9a4d71d7fb9c332592215895a206ff6 (diff) | |
parent | 57baac6b030508c9bcd74f3b995bd6a4a4f79211 (diff) | |
download | FreeBSD-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.c | 613 |
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; +} |