version 1.82, 2010/01/13 01:10:56 |
version 1.83, 2010/02/26 20:29:54 |
|
|
#include "uuencode.h" |
#include "uuencode.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "log.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 * |
key_new(int type) |
key_new(int type) |
{ |
{ |
|
|
k->type = type; |
k->type = type; |
k->dsa = NULL; |
k->dsa = NULL; |
k->rsa = NULL; |
k->rsa = NULL; |
|
k->cert = NULL; |
switch (k->type) { |
switch (k->type) { |
case KEY_RSA1: |
case KEY_RSA1: |
case KEY_RSA: |
case KEY_RSA: |
|
case KEY_RSA_CERT: |
if ((rsa = RSA_new()) == NULL) |
if ((rsa = RSA_new()) == NULL) |
fatal("key_new: RSA_new failed"); |
fatal("key_new: RSA_new failed"); |
if ((rsa->n = BN_new()) == NULL) |
if ((rsa->n = BN_new()) == NULL) |
|
|
k->rsa = rsa; |
k->rsa = rsa; |
break; |
break; |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
if ((dsa = DSA_new()) == NULL) |
if ((dsa = DSA_new()) == NULL) |
fatal("key_new: DSA_new failed"); |
fatal("key_new: DSA_new failed"); |
if ((dsa->p = BN_new()) == NULL) |
if ((dsa->p = BN_new()) == NULL) |
|
|
fatal("key_new: bad key type %d", k->type); |
fatal("key_new: bad key type %d", k->type); |
break; |
break; |
} |
} |
|
|
|
if (key_is_cert(k)) |
|
k->cert = cert_new(); |
|
|
return k; |
return k; |
} |
} |
|
|
Key * |
void |
key_new_private(int type) |
key_add_private(Key *k) |
{ |
{ |
Key *k = key_new(type); |
|
switch (k->type) { |
switch (k->type) { |
case KEY_RSA1: |
case KEY_RSA1: |
case KEY_RSA: |
case KEY_RSA: |
|
case KEY_RSA_CERT: |
if ((k->rsa->d = BN_new()) == NULL) |
if ((k->rsa->d = BN_new()) == NULL) |
fatal("key_new_private: BN_new failed"); |
fatal("key_new_private: BN_new failed"); |
if ((k->rsa->iqmp = BN_new()) == NULL) |
if ((k->rsa->iqmp = BN_new()) == NULL) |
|
|
fatal("key_new_private: BN_new failed"); |
fatal("key_new_private: BN_new failed"); |
break; |
break; |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
if ((k->dsa->priv_key = BN_new()) == NULL) |
if ((k->dsa->priv_key = BN_new()) == NULL) |
fatal("key_new_private: BN_new failed"); |
fatal("key_new_private: BN_new failed"); |
break; |
break; |
|
|
default: |
default: |
break; |
break; |
} |
} |
|
} |
|
|
|
Key * |
|
key_new_private(int type) |
|
{ |
|
Key *k = key_new(type); |
|
|
|
key_add_private(k); |
return 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 |
void |
key_free(Key *k) |
key_free(Key *k) |
{ |
{ |
|
|
switch (k->type) { |
switch (k->type) { |
case KEY_RSA1: |
case KEY_RSA1: |
case KEY_RSA: |
case KEY_RSA: |
|
case KEY_RSA_CERT: |
if (k->rsa != NULL) |
if (k->rsa != NULL) |
RSA_free(k->rsa); |
RSA_free(k->rsa); |
k->rsa = NULL; |
k->rsa = NULL; |
break; |
break; |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
if (k->dsa != NULL) |
if (k->dsa != NULL) |
DSA_free(k->dsa); |
DSA_free(k->dsa); |
k->dsa = NULL; |
k->dsa = NULL; |
|
|
fatal("key_free: bad key type %d", k->type); |
fatal("key_free: bad key type %d", k->type); |
break; |
break; |
} |
} |
|
if (key_is_cert(k)) { |
|
if (k->cert != NULL) |
|
cert_free(k->cert); |
|
k->cert = NULL; |
|
} |
|
|
xfree(k); |
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 |
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; |
return 0; |
|
|
switch (a->type) { |
switch (a->type) { |
case KEY_RSA1: |
case KEY_RSA1: |
|
case KEY_RSA_CERT: |
case KEY_RSA: |
case KEY_RSA: |
return a->rsa != NULL && b->rsa != NULL && |
return a->rsa != NULL && b->rsa != NULL && |
BN_cmp(a->rsa->e, b->rsa->e) == 0 && |
BN_cmp(a->rsa->e, b->rsa->e) == 0 && |
BN_cmp(a->rsa->n, b->rsa->n) == 0; |
BN_cmp(a->rsa->n, b->rsa->n) == 0; |
|
case KEY_DSA_CERT: |
case KEY_DSA: |
case KEY_DSA: |
return a->dsa != NULL && b->dsa != NULL && |
return a->dsa != NULL && b->dsa != NULL && |
BN_cmp(a->dsa->p, b->dsa->p) == 0 && |
BN_cmp(a->dsa->p, b->dsa->p) == 0 && |
|
|
/* NOTREACHED */ |
/* 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* |
u_char* |
key_fingerprint_raw(const Key *k, enum fp_type dgst_type, |
key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) |
u_int *dgst_raw_length) |
|
{ |
{ |
const EVP_MD *md = NULL; |
const EVP_MD *md = NULL; |
EVP_MD_CTX ctx; |
EVP_MD_CTX ctx; |
u_char *blob = NULL; |
u_char *blob = NULL; |
u_char *retval = NULL; |
u_char *retval = NULL; |
u_int len = 0; |
u_int len = 0; |
int nlen, elen; |
int nlen, elen, otype; |
|
|
*dgst_raw_length = 0; |
*dgst_raw_length = 0; |
|
|
|
|
case KEY_RSA: |
case KEY_RSA: |
key_to_blob(k, &blob, &len); |
key_to_blob(k, &blob, &len); |
break; |
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: |
case KEY_UNSPEC: |
return retval; |
return retval; |
default: |
default: |
|
|
} |
} |
|
|
char * |
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; |
char *retval = NULL; |
u_char *dgst_raw; |
u_char *dgst_raw; |
|
|
case KEY_UNSPEC: |
case KEY_UNSPEC: |
case KEY_RSA: |
case KEY_RSA: |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
|
case KEY_RSA_CERT: |
space = strchr(cp, ' '); |
space = strchr(cp, ' '); |
if (space == NULL) { |
if (space == NULL) { |
debug3("key_read: missing whitespace"); |
debug3("key_read: missing whitespace"); |
|
|
return -1; |
return -1; |
} |
} |
/*XXXX*/ |
/*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) |
if (ret->rsa != NULL) |
RSA_free(ret->rsa); |
RSA_free(ret->rsa); |
ret->rsa = k->rsa; |
ret->rsa = k->rsa; |
k->rsa = NULL; |
k->rsa = NULL; |
success = 1; |
|
#ifdef DEBUG_PK |
#ifdef DEBUG_PK |
RSA_print_fp(stderr, ret->rsa, 8); |
RSA_print_fp(stderr, ret->rsa, 8); |
#endif |
#endif |
} else { |
} |
|
if (key_type_plain(ret->type) == KEY_DSA) { |
if (ret->dsa != NULL) |
if (ret->dsa != NULL) |
DSA_free(ret->dsa); |
DSA_free(ret->dsa); |
ret->dsa = k->dsa; |
ret->dsa = k->dsa; |
k->dsa = NULL; |
k->dsa = NULL; |
success = 1; |
|
#ifdef DEBUG_PK |
#ifdef DEBUG_PK |
DSA_print_fp(stderr, ret->dsa, 8); |
DSA_print_fp(stderr, ret->dsa, 8); |
#endif |
#endif |
} |
} |
|
success = 1; |
/*XXXX*/ |
/*XXXX*/ |
key_free(k); |
key_free(k); |
if (success != 1) |
if (success != 1) |
|
|
u_char *blob; |
u_char *blob; |
char *uu; |
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' */ |
/* size of modulus 'n' */ |
bits = BN_num_bits(key->rsa->n); |
bits = BN_num_bits(key->rsa->n); |
fprintf(f, "%u", bits); |
fprintf(f, "%u", bits); |
if (write_bignum(f, key->rsa->e) && |
if (write_bignum(f, key->rsa->e) && |
write_bignum(f, key->rsa->n)) { |
write_bignum(f, key->rsa->n)) |
success = 1; |
return 1; |
} else { |
error("key_write: failed for RSA key"); |
error("key_write: failed for RSA key"); |
return 0; |
} |
case KEY_DSA: |
} else if ((key->type == KEY_DSA && key->dsa != NULL) || |
case KEY_DSA_CERT: |
(key->type == KEY_RSA && key->rsa != NULL)) { |
if (key->dsa == NULL) |
key_to_blob(key, &blob, &len); |
return 0; |
uu = xmalloc(2*len); |
break; |
n = uuencode(blob, len, uu, 2*len); |
case KEY_RSA: |
if (n > 0) { |
case KEY_RSA_CERT: |
fprintf(f, "%s %s", key_ssh_name(key), uu); |
if (key->rsa == NULL) |
success = 1; |
return 0; |
} |
break; |
xfree(blob); |
default: |
xfree(uu); |
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; |
return success; |
} |
} |
|
|
|
|
return "RSA"; |
return "RSA"; |
case KEY_DSA: |
case KEY_DSA: |
return "DSA"; |
return "DSA"; |
|
case KEY_RSA_CERT: |
|
return "RSA-CERT"; |
|
case KEY_DSA_CERT: |
|
return "DSA-CERT"; |
} |
} |
return "unknown"; |
return "unknown"; |
} |
} |
|
|
return "ssh-rsa"; |
return "ssh-rsa"; |
case KEY_DSA: |
case KEY_DSA: |
return "ssh-dss"; |
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"; |
return "ssh-unknown"; |
} |
} |
|
|
switch (k->type) { |
switch (k->type) { |
case KEY_RSA1: |
case KEY_RSA1: |
case KEY_RSA: |
case KEY_RSA: |
|
case KEY_RSA_CERT: |
return BN_num_bits(k->rsa->n); |
return BN_num_bits(k->rsa->n); |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
return BN_num_bits(k->dsa->p); |
return BN_num_bits(k->dsa->p); |
} |
} |
return 0; |
return 0; |
|
|
case KEY_RSA1: |
case KEY_RSA1: |
k->rsa = rsa_generate_private_key(bits); |
k->rsa = rsa_generate_private_key(bits); |
break; |
break; |
|
case KEY_RSA_CERT: |
|
case KEY_DSA_CERT: |
|
fatal("key_generate: cert keys cannot be generated directly"); |
default: |
default: |
fatal("key_generate: unknown type %d", type); |
fatal("key_generate: unknown type %d", type); |
} |
} |
|
|
return k; |
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 * |
key_from_private(const Key *k) |
key_from_private(const Key *k) |
{ |
{ |
Key *n = NULL; |
Key *n = NULL; |
switch (k->type) { |
switch (k->type) { |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
n = key_new(k->type); |
n = key_new(k->type); |
if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || |
if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || |
(BN_copy(n->dsa->q, k->dsa->q) == NULL) || |
(BN_copy(n->dsa->q, k->dsa->q) == NULL) || |
|
|
break; |
break; |
case KEY_RSA: |
case KEY_RSA: |
case KEY_RSA1: |
case KEY_RSA1: |
|
case KEY_RSA_CERT: |
n = key_new(k->type); |
n = key_new(k->type); |
if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || |
if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || |
(BN_copy(n->rsa->e, k->rsa->e) == NULL)) |
(BN_copy(n->rsa->e, k->rsa->e) == NULL)) |
|
|
fatal("key_from_private: unknown type %d", k->type); |
fatal("key_from_private: unknown type %d", k->type); |
break; |
break; |
} |
} |
|
if (key_is_cert(k)) |
|
key_cert_copy(k, n); |
return n; |
return n; |
} |
} |
|
|
|
|
return KEY_RSA; |
return KEY_RSA; |
} else if (strcmp(name, "ssh-dss") == 0) { |
} else if (strcmp(name, "ssh-dss") == 0) { |
return KEY_DSA; |
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); |
debug2("key_type_from_name: unknown key type '%s'", name); |
return KEY_UNSPEC; |
return KEY_UNSPEC; |
|
|
return 1; |
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; |
|
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, NULL)) == 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; |
|
} |
|
|
|
/* 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("Too many principals"); |
|
goto out; |
|
} |
|
if ((principal = buffer_get_string_ret(&tmp, NULL)) == NULL) { |
|
error("Principals data invalid"); |
|
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(&tmp, NULL) == NULL || |
|
buffer_get_string_ptr(&tmp, NULL) == NULL) { |
|
error("Constraints data invalid"); |
|
goto out; |
|
} |
|
} |
|
buffer_clear(&tmp); |
|
|
|
if ((key->cert->signature_key = key_from_blob(sig_key, |
|
sklen)) == NULL) { |
|
error("Signature key invalid"); |
|
goto out; |
|
} |
|
if (key->cert->signature_key->type != KEY_RSA && |
|
key->cert->signature_key->type != KEY_DSA) { |
|
error("Invalid signature key type %s (%d)", |
|
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: |
|
break; /* Good signature */ |
|
case 0: |
|
error("Invalid signature on certificate"); |
|
goto out; |
|
case -1: |
|
error("Certificate signature verification failed"); |
|
goto out; |
|
} |
|
|
|
ret = 0; |
|
|
|
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 * |
key_from_blob(const u_char *blob, u_int blen) |
key_from_blob(const u_char *blob, u_int blen) |
{ |
{ |
|
|
|
|
switch (type) { |
switch (type) { |
case KEY_RSA: |
case KEY_RSA: |
|
case KEY_RSA_CERT: |
key = key_new(type); |
key = key_new(type); |
if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || |
if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || |
buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { |
buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { |
error("key_from_blob: can't read rsa key"); |
error("key_from_blob: can't read rsa key"); |
|
badkey: |
key_free(key); |
key_free(key); |
key = NULL; |
key = NULL; |
goto out; |
goto out; |
|
|
#endif |
#endif |
break; |
break; |
case KEY_DSA: |
case KEY_DSA: |
|
case KEY_DSA_CERT: |
key = key_new(type); |
key = key_new(type); |
if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || |
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->q) == -1 || |
buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || |
buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || |
buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { |
buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { |
error("key_from_blob: can't read dsa key"); |
error("key_from_blob: can't read dsa key"); |
key_free(key); |
goto badkey; |
key = NULL; |
|
goto out; |
|
} |
} |
#ifdef DEBUG_PK |
#ifdef DEBUG_PK |
DSA_print_fp(stderr, key->dsa, 8); |
DSA_print_fp(stderr, key->dsa, 8); |
|
|
error("key_from_blob: cannot handle type %s", ktype); |
error("key_from_blob: cannot handle type %s", ktype); |
goto out; |
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); |
rlen = buffer_len(&b); |
if (key != NULL && rlen != 0) |
if (key != NULL && rlen != 0) |
error("key_from_blob: remaining bytes in key blob %d", rlen); |
error("key_from_blob: remaining bytes in key blob %d", rlen); |
|
|
} |
} |
buffer_init(&b); |
buffer_init(&b); |
switch (key->type) { |
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: |
case KEY_DSA: |
buffer_put_cstring(&b, key_ssh_name(key)); |
buffer_put_cstring(&b, key_ssh_name(key)); |
buffer_put_bignum2(&b, key->dsa->p); |
buffer_put_bignum2(&b, key->dsa->p); |
|
|
const u_char *data, u_int datalen) |
const u_char *data, u_int datalen) |
{ |
{ |
switch (key->type) { |
switch (key->type) { |
|
case KEY_DSA_CERT: |
case KEY_DSA: |
case KEY_DSA: |
return ssh_dss_sign(key, sigp, lenp, data, datalen); |
return ssh_dss_sign(key, sigp, lenp, data, datalen); |
|
case KEY_RSA_CERT: |
case KEY_RSA: |
case KEY_RSA: |
return ssh_rsa_sign(key, sigp, lenp, data, datalen); |
return ssh_rsa_sign(key, sigp, lenp, data, datalen); |
default: |
default: |
|
|
return -1; |
return -1; |
|
|
switch (key->type) { |
switch (key->type) { |
|
case KEY_DSA_CERT: |
case KEY_DSA: |
case KEY_DSA: |
return ssh_dss_verify(key, signature, signaturelen, data, datalen); |
return ssh_dss_verify(key, signature, signaturelen, data, datalen); |
|
case KEY_RSA_CERT: |
case KEY_RSA: |
case KEY_RSA: |
return ssh_rsa_verify(key, signature, signaturelen, data, datalen); |
return ssh_rsa_verify(key, signature, signaturelen, data, datalen); |
default: |
default: |
|
|
pk->rsa = NULL; |
pk->rsa = NULL; |
|
|
switch (k->type) { |
switch (k->type) { |
|
case KEY_RSA_CERT: |
|
key_cert_copy(k, pk); |
|
/* FALLTHROUGH */ |
case KEY_RSA1: |
case KEY_RSA1: |
case KEY_RSA: |
case KEY_RSA: |
if ((pk->rsa = RSA_new()) == NULL) |
if ((pk->rsa = RSA_new()) == NULL) |
|
|
if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) |
if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) |
fatal("key_demote: BN_dup failed"); |
fatal("key_demote: BN_dup failed"); |
break; |
break; |
|
case KEY_DSA_CERT: |
|
key_cert_copy(k, pk); |
|
/* FALLTHROUGH */ |
case KEY_DSA: |
case KEY_DSA: |
if ((pk->dsa = DSA_new()) == NULL) |
if ((pk->dsa = DSA_new()) == NULL) |
fatal("key_demote: DSA_new failed"); |
fatal("key_demote: DSA_new failed"); |
|
|
} |
} |
|
|
return (pk); |
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; |
} |
} |