diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2010-04-16 01:47:27 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2010-04-16 01:47:27 +0000 |
commit | cb70d06f2e2985efc7f12c04e8b76a3c84454947 (patch) | |
tree | ce550248fe33e17436f009faf54260f85b647350 /usr.bin/ssh | |
parent | c31709835d764b0d04ae5e93159494f65427c697 (diff) |
revised certificate format ssh-{dss,rsa}-cert-v01@openssh.com with the
following changes:
move the nonce field to the beginning of the certificate where it can
better protect against chosen-prefix attacks on the signature hash
Rename "constraints" field to "critical options"
Add a new non-critical "extensions" field
Add a serial number
The older format is still support for authentication and cert generation
(use "ssh-keygen -t v00 -s ca_key ..." to generate a v00 certificate)
ok markus@
Diffstat (limited to 'usr.bin/ssh')
-rw-r--r-- | usr.bin/ssh/PROTOCOL.certkeys | 73 | ||||
-rw-r--r-- | usr.bin/ssh/auth-options.c | 21 | ||||
-rw-r--r-- | usr.bin/ssh/auth-options.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/auth-rsa.c | 4 | ||||
-rw-r--r-- | usr.bin/ssh/auth2-pubkey.c | 9 | ||||
-rw-r--r-- | usr.bin/ssh/authfd.c | 6 | ||||
-rw-r--r-- | usr.bin/ssh/key.c | 177 | ||||
-rw-r--r-- | usr.bin/ssh/key.h | 11 | ||||
-rw-r--r-- | usr.bin/ssh/myproposal.h | 7 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-add.c | 4 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-agent.c | 5 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-dss.c | 12 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-keygen.1 | 27 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-keygen.c | 235 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-rsa.c | 12 | ||||
-rw-r--r-- | usr.bin/ssh/sshconnect.c | 8 | ||||
-rw-r--r-- | usr.bin/ssh/sshconnect2.c | 7 | ||||
-rw-r--r-- | usr.bin/ssh/sshd.c | 15 |
18 files changed, 426 insertions, 211 deletions
diff --git a/usr.bin/ssh/PROTOCOL.certkeys b/usr.bin/ssh/PROTOCOL.certkeys index 1ed9e206410..a2069f54767 100644 --- a/usr.bin/ssh/PROTOCOL.certkeys +++ b/usr.bin/ssh/PROTOCOL.certkeys @@ -16,7 +16,7 @@ These protocol extensions build on the simple public key authentication system already in SSH to allow certificate-based authentication. The certificates used are not traditional X.509 certificates, with numerous options and complex encoding rules, but something rather -more minimal: a key, some identity information and usage constraints +more minimal: a key, some identity information and usage options that have been signed with some other trusted key. A sshd server may be configured to allow authentication via certified @@ -27,7 +27,7 @@ of acceptance of certified host keys, by adding a similar ability to specify CA keys in ~/.ssh/known_hosts. Certified keys are represented using two new key types: -ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com that +ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com that include certification information along with the public key that is used to sign challenges. ssh-keygen performs the CA signing operation. @@ -47,7 +47,7 @@ in RFC4252 section 7. New public key formats ---------------------- -The ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com key +The ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com key types take a similar high-level format (note: data types and encoding are as per RFC4251 section 5). The serialised wire encoding of these certificates is also used for storing them on disk. @@ -57,42 +57,55 @@ these certificates is also used for storing them on disk. RSA certificate - string "ssh-rsa-cert-v00@openssh.com" + string "ssh-rsa-cert-v01@openssh.com" + string nonce mpint e mpint n + uint64 serial uint32 type string key id string valid principals uint64 valid after uint64 valid before - string constraints - string nonce + string critical options + string extensions string reserved string signature key string signature DSA certificate - string "ssh-dss-cert-v00@openssh.com" + string "ssh-dss-cert-v01@openssh.com" + string nonce mpint p mpint q mpint g mpint y + uint64 serial uint32 type string key id string valid principals uint64 valid after uint64 valid before - string constraints - string nonce + string critical options + string extensions string reserved string signature key string signature +The nonce field is a CA-provided random bitstring of arbitrary length +(but typically 16 or 32 bytes) included to make attacks that depend on +inducing collisions in the signature hash infeasible. + e and n are the RSA exponent and public modulus respectively. p, q, g, y are the DSA parameters as described in FIPS-186-2. +serial is an optional certificate serial number set by the CA to +provide an abbreviated way to refer to certificates from that CA. +If a CA does not with to number its certificates it must set this +field to zero. + type specifies whether this certificate is for identification of a user or a host using a SSH_CERT_TYPE_... value. @@ -112,13 +125,15 @@ certificate. Each represents a time in seconds since 1970-01-01 00:00:00. A certificate is considered valid if: valid after <= current time < valid before -constraints is a set of zero or more key constraints encoded as below. +criticial options is a set of zero or more key options encoded as +below. All such options are "critical" in the sense that an implementation +must refuse to authorise a key that has an unrecognised option. -The nonce field is a CA-provided random bitstring of arbitrary length -(but typically 16 or 32 bytes) included to make attacks that depend on -inducing collisions in the signature hash infeasible. +extensions is a set of zero or more optional extensions. These extensions +are not critical, and an implementation that encounters one that it does +not recognise may safely ignore it. No extensions are defined at present. -The reserved field is current unused and is ignored in this version of +The reserved field is currently unused and is ignored in this version of the protocol. signature key contains the CA key used to sign the certificate. @@ -132,22 +147,22 @@ up to, and including the signature key. Signatures are computed and encoded according to the rules defined for the CA's public key algorithm (RFC4253 section 6.6 for ssh-rsa and ssh-dss). -Constraints ------------ +Critical options +---------------- -The constraints section of the certificate specifies zero or more -constraints on the certificates validity. The format of this field +The critical options section of the certificate specifies zero or more +options on the certificates validity. The format of this field is a sequence of zero or more tuples: string name string data -The name field identifies the constraint and the data field encodes -constraint-specific information (see below). All constraints are -"critical", if an implementation does not recognise a constraint +The name field identifies the option and the data field encodes +option-specific information (see below). All options are +"critical", if an implementation does not recognise a option then the validating party should refuse to accept the certificate. -The supported constraints and the contents and structure of their +The supported options and the contents and structure of their data fields are: Name Format Description @@ -159,35 +174,35 @@ force-command string Specifies a command that is executed permit-X11-forwarding empty Flag indicating that X11 forwarding should be permitted. X11 forwarding will - be refused if this constraint is absent. + be refused if this option is absent. permit-agent-forwarding empty Flag indicating that agent forwarding should be allowed. Agent forwarding must not be permitted unless this - constraint is present. + option is present. permit-port-forwarding empty Flag indicating that port-forwarding - should be allowed. If this constraint is + should be allowed. If this option is not present then no port forwarding will be allowed. permit-pty empty Flag indicating that PTY allocation should be permitted. In the absence of - this constraint PTY allocation will be + this option PTY allocation will be disabled. permit-user-rc empty Flag indicating that execution of ~/.ssh/rc should be permitted. Execution of this script will not be permitted if - this constraint is not present. + this option is not present. source-address string Comma-separated list of source addresses from which this certificate is accepted for authentication. Addresses are specified in CIDR format (nn.nn.nn.nn/nn or hhhh::hhhh/nn). - If this constraint is not present then + If this option is not present then certificates may be presented from any source address. -$OpenBSD: PROTOCOL.certkeys,v 1.3 2010/03/03 22:50:40 djm Exp $ +$OpenBSD: PROTOCOL.certkeys,v 1.4 2010/04/16 01:47:25 djm Exp $ diff --git a/usr.bin/ssh/auth-options.c b/usr.bin/ssh/auth-options.c index e6819d98510..e2d4b532766 100644 --- a/usr.bin/ssh/auth-options.c +++ b/usr.bin/ssh/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.49 2010/03/16 15:46:52 stevesk Exp $ */ +/* $OpenBSD: auth-options.c,v 1.50 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -25,10 +25,10 @@ #include "canohost.h" #include "buffer.h" #include "channels.h" -#include "auth-options.h" #include "servconf.h" #include "misc.h" #include "key.h" +#include "auth-options.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI @@ -375,11 +375,11 @@ bad_option: } /* - * Set options from certificate constraints. These supersede user key options - * so this must be called after auth_parse_options(). + * Set options from critical certificate options. These supersede user key + * options so this must be called after auth_parse_options(). */ int -auth_cert_constraints(Buffer *c_orig, struct passwd *pw) +auth_cert_options(Key *k, struct passwd *pw) { u_char *name = NULL, *data_blob = NULL; u_int nlen, dlen, clen; @@ -398,12 +398,13 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) /* Make copy to avoid altering original */ buffer_init(&c); - buffer_append(&c, buffer_ptr(c_orig), buffer_len(c_orig)); + buffer_append(&c, + buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); while (buffer_len(&c) > 0) { if ((name = buffer_get_string_ret(&c, &nlen)) == NULL || (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { - error("Certificate constraints corrupt"); + error("Certificate options corrupt"); goto out; } buffer_append(&data, data_blob, dlen); @@ -437,7 +438,7 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) } if (cert_forced_command != NULL) { error("Certificate has multiple " - "force-command constraints"); + "force-command options"); xfree(command); goto out; } @@ -457,7 +458,7 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) } if (cert_source_address_done++) { error("Certificate has multiple " - "source-address constraints"); + "source-address options"); xfree(allowed); goto out; } @@ -500,7 +501,7 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) name = data_blob = NULL; } - /* successfully parsed all constraints */ + /* successfully parsed all options */ ret = 0; no_port_forwarding_flag |= cert_no_port_forwarding_flag; diff --git a/usr.bin/ssh/auth-options.h b/usr.bin/ssh/auth-options.h index 694edc842b3..20f0dbe3e0b 100644 --- a/usr.bin/ssh/auth-options.h +++ b/usr.bin/ssh/auth-options.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.h,v 1.18 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.19 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -34,6 +34,6 @@ extern int key_is_cert_authority; int auth_parse_options(struct passwd *, char *, char *, u_long); void auth_clear_options(void); -int auth_cert_constraints(Buffer *, struct passwd *); +int auth_cert_options(Key *, struct passwd *); #endif diff --git a/usr.bin/ssh/auth-rsa.c b/usr.bin/ssh/auth-rsa.c index f136e65961c..2c8277dcd7e 100644 --- a/usr.bin/ssh/auth-rsa.c +++ b/usr.bin/ssh/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.74 2010/03/04 10:36:03 djm Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.75 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -31,11 +31,11 @@ #include "uidswap.h" #include "match.h" #include "buffer.h" -#include "auth-options.h" #include "pathnames.h" #include "log.h" #include "servconf.h" #include "key.h" +#include "auth-options.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI diff --git a/usr.bin/ssh/auth2-pubkey.c b/usr.bin/ssh/auth2-pubkey.c index 4f0694885b8..56c6cc1083a 100644 --- a/usr.bin/ssh/auth2-pubkey.c +++ b/usr.bin/ssh/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.22 2010/03/10 23:27:17 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.23 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -234,7 +234,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) } if (auth_parse_options(pw, key_options, file, linenum) != 1) continue; - if (key->type == KEY_RSA_CERT || key->type == KEY_DSA_CERT) { + if (key_is_cert(key)) { if (!key_is_cert_authority) continue; if (!key_equal(found, key->cert->signature_key)) @@ -250,8 +250,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) auth_debug_add("%s", reason); continue; } - if (auth_cert_constraints(&key->cert->constraints, - pw) != 0) { + if (auth_cert_options(key, pw) != 0) { xfree(fp); continue; } @@ -306,7 +305,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) auth_debug_add("%s", reason); goto out; } - if (auth_cert_constraints(&key->cert->constraints, pw) != 0) + if (auth_cert_options(key, pw) != 0) goto out; verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s", diff --git a/usr.bin/ssh/authfd.c b/usr.bin/ssh/authfd.c index cae89b047d1..2d4399a91cc 100644 --- a/usr.bin/ssh/authfd.c +++ b/usr.bin/ssh/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.82 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.83 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -481,6 +481,7 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) buffer_put_bignum2(b, key->rsa->p); buffer_put_bignum2(b, key->rsa->q); break; + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) fatal("%s: no cert/certblob", __func__); @@ -498,6 +499,7 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) buffer_put_bignum2(b, key->dsa->pub_key); buffer_put_bignum2(b, key->dsa->priv_key); break; + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) fatal("%s: no cert/certblob", __func__); @@ -533,8 +535,10 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, break; case KEY_RSA: case KEY_RSA_CERT: + case KEY_RSA_CERT_V00: case KEY_DSA: case KEY_DSA_CERT: + case KEY_DSA_CERT_V00: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; diff --git a/usr.bin/ssh/key.c b/usr.bin/ssh/key.c index a2e57a1e24a..75677f8c112 100644 --- a/usr.bin/ssh/key.c +++ b/usr.bin/ssh/key.c @@ -1,4 +1,4 @@ -/* $OpenBSD: key.c,v 1.86 2010/03/15 19:40:02 stevesk Exp $ */ +/* $OpenBSD: key.c,v 1.87 2010/04/16 01:47:26 djm Exp $ */ /* * read_bignum(): * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -57,7 +57,8 @@ cert_new(void) cert = xcalloc(1, sizeof(*cert)); buffer_init(&cert->certblob); - buffer_init(&cert->constraints); + buffer_init(&cert->critical); + buffer_init(&cert->extensions); cert->key_id = NULL; cert->principals = NULL; cert->signature_key = NULL; @@ -78,6 +79,7 @@ key_new(int type) switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: if ((rsa = RSA_new()) == NULL) fatal("key_new: RSA_new failed"); @@ -88,6 +90,7 @@ key_new(int type) k->rsa = rsa; break; case KEY_DSA: + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: if ((dsa = DSA_new()) == NULL) fatal("key_new: DSA_new failed"); @@ -120,6 +123,7 @@ key_add_private(Key *k) switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: if ((k->rsa->d = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); @@ -135,6 +139,7 @@ key_add_private(Key *k) fatal("key_new_private: BN_new failed"); break; case KEY_DSA: + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: if ((k->dsa->priv_key = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); @@ -161,7 +166,8 @@ cert_free(struct KeyCert *cert) u_int i; buffer_free(&cert->certblob); - buffer_free(&cert->constraints); + buffer_free(&cert->critical); + buffer_free(&cert->extensions); if (cert->key_id != NULL) xfree(cert->key_id); for (i = 0; i < cert->nprincipals; i++) @@ -180,12 +186,14 @@ key_free(Key *k) switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: if (k->rsa != NULL) RSA_free(k->rsa); k->rsa = NULL; break; case KEY_DSA: + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: if (k->dsa != NULL) DSA_free(k->dsa); @@ -234,11 +242,13 @@ key_equal_public(const Key *a, const Key *b) switch (a->type) { case KEY_RSA1: + case KEY_RSA_CERT_V00: 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_V00: case KEY_DSA_CERT: case KEY_DSA: return a->dsa != NULL && b->dsa != NULL && @@ -300,6 +310,8 @@ key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) case KEY_RSA: key_to_blob(k, &blob, &len); break; + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: case KEY_DSA_CERT: case KEY_RSA_CERT: /* We want a fingerprint of the _key_ not of the cert */ @@ -627,6 +639,8 @@ key_read(Key *ret, char **cpp) case KEY_UNSPEC: case KEY_RSA: case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: case KEY_DSA_CERT: case KEY_RSA_CERT: space = strchr(cp, ' '); @@ -753,11 +767,13 @@ key_write(const Key *key, FILE *f) error("key_write: failed for RSA key"); return 0; case KEY_DSA: + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: if (key->dsa == NULL) return 0; break; case KEY_RSA: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: if (key->rsa == NULL) return 0; @@ -789,6 +805,10 @@ key_type(const Key *k) return "RSA"; case KEY_DSA: return "DSA"; + case KEY_RSA_CERT_V00: + return "RSA-CERT-V00"; + case KEY_DSA_CERT_V00: + return "DSA-CERT-V00"; case KEY_RSA_CERT: return "RSA-CERT"; case KEY_DSA_CERT: @@ -818,10 +838,14 @@ key_ssh_name(const Key *k) return "ssh-rsa"; case KEY_DSA: return "ssh-dss"; - case KEY_RSA_CERT: + case KEY_RSA_CERT_V00: return "ssh-rsa-cert-v00@openssh.com"; - case KEY_DSA_CERT: + case KEY_DSA_CERT_V00: return "ssh-dss-cert-v00@openssh.com"; + case KEY_RSA_CERT: + return "ssh-rsa-cert-v01@openssh.com"; + case KEY_DSA_CERT: + return "ssh-dss-cert-v01@openssh.com"; } return "ssh-unknown"; } @@ -832,9 +856,11 @@ key_size(const Key *k) switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: return BN_num_bits(k->rsa->n); case KEY_DSA: + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: return BN_num_bits(k->dsa->p); } @@ -878,6 +904,8 @@ key_generate(int type, u_int bits) case KEY_RSA1: k->rsa = rsa_generate_private_key(bits); break; + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: case KEY_RSA_CERT: case KEY_DSA_CERT: fatal("key_generate: cert keys cannot be generated directly"); @@ -908,9 +936,12 @@ key_cert_copy(const Key *from_key, struct Key *to_key) buffer_append(&to->certblob, buffer_ptr(&from->certblob), buffer_len(&from->certblob)); - buffer_append(&to->constraints, buffer_ptr(&from->constraints), - buffer_len(&from->constraints)); + buffer_append(&to->critical, + buffer_ptr(&from->critical), buffer_len(&from->critical)); + buffer_append(&to->extensions, + buffer_ptr(&from->extensions), buffer_len(&from->extensions)); + to->serial = from->serial; to->type = from->type; to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id); to->valid_after = from->valid_after; @@ -936,6 +967,7 @@ key_from_private(const Key *k) Key *n = NULL; switch (k->type) { case KEY_DSA: + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: n = key_new(k->type); if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || @@ -946,6 +978,7 @@ key_from_private(const Key *k) break; case KEY_RSA: case KEY_RSA1: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: n = key_new(k->type); if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || @@ -975,8 +1008,12 @@ key_type_from_name(char *name) } 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; + return KEY_RSA_CERT_V00; } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) { + return KEY_DSA_CERT_V00; + } else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) { + return KEY_RSA_CERT; + } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) { return KEY_DSA_CERT; } debug2("key_type_from_name: unknown key type '%s'", name); @@ -1008,26 +1045,31 @@ key_names_valid2(const char *names) 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; + u_char *principals, *critical, *exts, *sig_key, *sig; + u_int signed_len, plen, clen, sklen, slen, kidlen, elen; Buffer tmp; char *principal; int ret = -1; + int v00 = key->type == KEY_DSA_CERT_V00 || + key->type == KEY_RSA_CERT_V00; 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 || + elen = 0; /* Not touched for v00 certs */ + principals = exts = critical = sig_key = sig = NULL; + if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) || + 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 || + (critical = buffer_get_string_ret(b, &clen)) == NULL || + (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) || + (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */ + buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */ (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) { error("%s: parse error", __func__); goto out; @@ -1074,13 +1116,25 @@ cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) buffer_clear(&tmp); - buffer_append(&key->cert->constraints, constraints, clen); - buffer_append(&tmp, constraints, clen); + buffer_append(&key->cert->critical, critical, clen); + buffer_append(&tmp, critical, 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__); + error("%s: critical option data invalid", __func__); + goto out; + } + } + buffer_clear(&tmp); + + buffer_append(&key->cert->extensions, exts, elen); + buffer_append(&tmp, exts, elen); + /* 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: extension data invalid", __func__); goto out; } } @@ -1117,8 +1171,10 @@ cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) buffer_free(&tmp); if (principals != NULL) xfree(principals); - if (constraints != NULL) - xfree(constraints); + if (critical != NULL) + xfree(critical); + if (exts != NULL) + xfree(exts); if (sig_key != NULL) xfree(sig_key); if (sig != NULL) @@ -1147,8 +1203,11 @@ key_from_blob(const u_char *blob, u_int blen) type = key_type_from_name(ktype); switch (type) { - case KEY_RSA: case KEY_RSA_CERT: + (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + /* FALLTHROUGH */ + case KEY_RSA: + case KEY_RSA_CERT_V00: key = key_new(type); if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { @@ -1162,8 +1221,11 @@ key_from_blob(const u_char *blob, u_int blen) RSA_print_fp(stderr, key->rsa, 8); #endif break; - case KEY_DSA: case KEY_DSA_CERT: + (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + /* FALLTHROUGH */ + case KEY_DSA: + case KEY_DSA_CERT_V00: key = key_new(type); if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || buffer_get_bignum2_ret(&b, key->dsa->q) == -1 || @@ -1209,6 +1271,8 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp) } buffer_init(&b); switch (key->type) { + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: case KEY_DSA_CERT: case KEY_RSA_CERT: /* Use the existing blob */ @@ -1251,9 +1315,11 @@ key_sign( const u_char *data, u_int datalen) { switch (key->type) { + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_sign(key, sigp, lenp, data, datalen); + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_sign(key, sigp, lenp, data, datalen); @@ -1277,9 +1343,11 @@ key_verify( return -1; switch (key->type) { + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_verify(key, signature, signaturelen, data, datalen); + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_verify(key, signature, signaturelen, data, datalen); @@ -1302,6 +1370,7 @@ key_demote(const Key *k) pk->rsa = NULL; switch (k->type) { + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: key_cert_copy(k, pk); /* FALLTHROUGH */ @@ -1314,6 +1383,7 @@ 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_V00: case KEY_DSA_CERT: key_cert_copy(k, pk); /* FALLTHROUGH */ @@ -1340,8 +1410,17 @@ key_demote(const Key *k) int key_is_cert(const Key *k) { - return k != NULL && - (k->type == KEY_RSA_CERT || k->type == KEY_DSA_CERT); + if (k == NULL) + return 0; + switch (k->type) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_DSA_CERT: + return 1; + default: + return 0; + } } /* Return the cert-less equivalent to a certified key type */ @@ -1349,8 +1428,10 @@ int key_type_plain(int type) { switch (type) { + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: return KEY_RSA; + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: return KEY_DSA; default: @@ -1360,16 +1441,16 @@ key_type_plain(int type) /* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */ int -key_to_certified(Key *k) +key_to_certified(Key *k, int legacy) { switch (k->type) { case KEY_RSA: k->cert = cert_new(); - k->type = KEY_RSA_CERT; + k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT; return 0; case KEY_DSA: k->cert = cert_new(); - k->type = KEY_DSA_CERT; + k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT; return 0; default: error("%s: key has incorrect type %s", __func__, key_type(k)); @@ -1382,10 +1463,12 @@ int key_drop_cert(Key *k) { switch (k->type) { + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: cert_free(k->cert); k->type = KEY_RSA; return 0; + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: cert_free(k->cert); k->type = KEY_DSA; @@ -1426,13 +1509,21 @@ key_certify(Key *k, Key *ca) buffer_clear(&k->cert->certblob); buffer_put_cstring(&k->cert->certblob, key_ssh_name(k)); + /* -v01 certs put nonce first */ + if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) { + arc4random_buf(&nonce, sizeof(nonce)); + buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); + } + switch (k->type) { + case KEY_DSA_CERT_V00: 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_V00: case KEY_RSA_CERT: buffer_put_bignum2(&k->cert->certblob, k->rsa->e); buffer_put_bignum2(&k->cert->certblob, k->rsa->n); @@ -1444,6 +1535,10 @@ key_certify(Key *k, Key *ca) return -1; } + /* -v01 certs have a serial number next */ + if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) + buffer_put_int64(&k->cert->certblob, k->cert->serial); + buffer_put_int(&k->cert->certblob, k->cert->type); buffer_put_cstring(&k->cert->certblob, k->cert->key_id); @@ -1457,11 +1552,19 @@ key_certify(Key *k, Key *ca) 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)); + buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); + + /* -v01 certs have non-critical options here */ + if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) { + buffer_put_string(&k->cert->certblob, + buffer_ptr(&k->cert->extensions), + buffer_len(&k->cert->extensions)); + } + + /* -v00 certs put the nonce at the end */ + if (k->type == KEY_DSA_CERT_V00 || k->type == KEY_RSA_CERT_V00) + buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); - 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); @@ -1532,3 +1635,15 @@ key_cert_check_authority(const Key *k, int want_host, int require_principal, } return 0; } + +int +key_cert_is_legacy(Key *k) +{ + switch (k->type) { + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + return 1; + default: + return 0; + } +} diff --git a/usr.bin/ssh/key.h b/usr.bin/ssh/key.h index 4f17777c088..11d30eae685 100644 --- a/usr.bin/ssh/key.h +++ b/usr.bin/ssh/key.h @@ -1,4 +1,4 @@ -/* $OpenBSD: key.h,v 1.29 2010/03/15 19:40:02 stevesk Exp $ */ +/* $OpenBSD: key.h,v 1.30 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -37,6 +37,8 @@ enum types { KEY_DSA, KEY_RSA_CERT, KEY_DSA_CERT, + KEY_RSA_CERT_V00, + KEY_DSA_CERT_V00, KEY_UNSPEC }; enum fp_type { @@ -56,11 +58,13 @@ enum fp_rep { struct KeyCert { Buffer certblob; /* Kept around for use on wire */ u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ + u_int64_t serial; char *key_id; u_int nprincipals; char **principals; u_int64_t valid_after, valid_before; - Buffer constraints; + Buffer critical; + Buffer extensions; Key *signature_key; }; @@ -92,12 +96,13 @@ Key *key_from_private(const Key *); int key_type_from_name(char *); int key_is_cert(const Key *); int key_type_plain(int); -int key_to_certified(Key *); +int key_to_certified(Key *, int); int key_drop_cert(Key *); int key_certify(Key *, Key *); void key_cert_copy(const Key *, struct Key *); int key_cert_check_authority(const Key *, int, int, const char *, const char **); +int key_cert_is_legacy(Key *); Key *key_from_blob(const u_char *, u_int); int key_to_blob(const Key *, u_char **, u_int *); diff --git a/usr.bin/ssh/myproposal.h b/usr.bin/ssh/myproposal.h index 1b4247962c3..a5502e39886 100644 --- a/usr.bin/ssh/myproposal.h +++ b/usr.bin/ssh/myproposal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: myproposal.h,v 1.24 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: myproposal.h,v 1.25 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -28,7 +28,10 @@ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ "diffie-hellman-group1-sha1" -#define KEX_DEFAULT_PK_ALG "ssh-rsa-cert-v00@openssh.com," \ +#define KEX_DEFAULT_PK_ALG \ + "ssh-rsa-cert-v01@openssh.com," \ + "ssh-dss-cert-v01@openssh.com," \ + "ssh-rsa-cert-v00@openssh.com," \ "ssh-dss-cert-v00@openssh.com," \ "ssh-rsa,ssh-dss" diff --git a/usr.bin/ssh/ssh-add.c b/usr.bin/ssh/ssh-add.c index 929d6f66828..f3113c7697d 100644 --- a/usr.bin/ssh/ssh-add.c +++ b/usr.bin/ssh/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.94 2010/03/01 11:07:06 otto Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.95 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -200,7 +200,7 @@ add_file(AuthenticationConnection *ac, const char *filename) xasprintf(&certpath, "%s-cert.pub", filename); if ((cert = key_load_public(certpath, NULL)) != NULL) { /* Graft with private bits */ - if (key_to_certified(private) != 0) + if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) fatal("%s: key_to_certified failed", __func__); key_cert_copy(cert, private); key_free(cert); diff --git a/usr.bin/ssh/ssh-agent.c b/usr.bin/ssh/ssh-agent.c index c4e5a770c43..327e3e01d4f 100644 --- a/usr.bin/ssh/ssh-agent.c +++ b/usr.bin/ssh/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.165 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.166 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -486,6 +486,7 @@ process_add_identity(SocketEntry *e, int version) buffer_get_bignum2(&e->request, k->dsa->pub_key); buffer_get_bignum2(&e->request, k->dsa->priv_key); break; + case KEY_DSA_CERT_V00: case KEY_DSA_CERT: cert = buffer_get_string(&e->request, &len); if ((k = key_from_blob(cert, len)) == NULL) @@ -506,6 +507,7 @@ process_add_identity(SocketEntry *e, int version) /* Generate additional parameters */ rsa_generate_additional_parameters(k->rsa); break; + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: cert = buffer_get_string(&e->request, &len); if ((k = key_from_blob(cert, len)) == NULL) @@ -526,6 +528,7 @@ process_add_identity(SocketEntry *e, int version) /* enable blinding */ switch (k->type) { case KEY_RSA: + case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA1: if (RSA_blinding_on(k->rsa, NULL) != 1) { diff --git a/usr.bin/ssh/ssh-dss.c b/usr.bin/ssh/ssh-dss.c index a35130d7862..719f350f29b 100644 --- a/usr.bin/ssh/ssh-dss.c +++ b/usr.bin/ssh/ssh-dss.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-dss.c,v 1.25 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: ssh-dss.c,v 1.26 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -50,9 +50,8 @@ ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp, u_int rlen, slen, len, dlen; Buffer b; - if (key == NULL || - (key->type != KEY_DSA && key->type != KEY_DSA_CERT) || - key->dsa == NULL) { + if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && + key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { error("ssh_dss_sign: no DSA key"); return -1; } @@ -115,9 +114,8 @@ ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen, int rlen, ret; Buffer b; - if (key == NULL || - (key->type != KEY_DSA && key->type != KEY_DSA_CERT) || - key->dsa == NULL) { + if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && + key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { error("ssh_dss_verify: no DSA key"); return -1; } diff --git a/usr.bin/ssh/ssh-keygen.1 b/usr.bin/ssh/ssh-keygen.1 index 3e03a9bd0cd..aacd4d3dc24 100644 --- a/usr.bin/ssh/ssh-keygen.1 +++ b/usr.bin/ssh/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.92 2010/03/13 23:38:13 jmc Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.93 2010/04/16 01:47:26 djm Exp $ .\" .\" -*- nroff -*- .\" @@ -37,7 +37,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: March 13 2010 $ +.Dd $Mdocdate: April 16 2010 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME @@ -110,8 +110,9 @@ .Fl I Ar certificate_identity .Op Fl h .Op Fl n Ar principals -.Op Fl O Ar constraint +.Op Fl O Ar option .Op Fl V Ar validity_interval +.Op Fl z Ar serial_number .Ar .Nm ssh-keygen .Fl L @@ -299,13 +300,13 @@ Multiple principals may be specified, separated by commas. Please see the .Sx CERTIFICATES section for details. -.It Fl O Ar constraint -Specify a certificate constraint when signing a key. +.It Fl O Ar option +Specify a certificate option when signing a key. This option may be specified multiple times. Please see the .Sx CERTIFICATES section for details. -The constraints that are valid for user certificates are: +The options that are valid for user certificates are: .Bl -tag -width Ds .It Ic clear Clear all enabled permissions. @@ -355,7 +356,7 @@ is a comma-separated list of one or more address/netmask pairs in CIDR format. .El .Pp -At present, no constraints are valid for host keys. +At present, no options are valid for host keys. .It Fl P Ar passphrase Provides the (old) passphrase. .It Fl p @@ -441,6 +442,10 @@ Specify desired generator when testing candidate moduli for DH-GEX. .It Fl y This option will read a private OpenSSH format file and print an OpenSSH public key to stdout. +.It Fl z Ar serial_number +Specifies a serial number to be embedded in the certificate to distinguish +this certificate from others from the same CA. +The default serial number is zero. .El .Sh MODULI GENERATION .Nm @@ -501,7 +506,7 @@ that both ends of a connection share common moduli. supports signing of keys to produce certificates that may be used for user or host authentication. Certificates consist of a public key, some identity information, zero or -more principal (user or host) names and an optional set of constraints that +more principal (user or host) names and an optional set of options that are signed by a Certification Authority (CA) key. Clients or servers may then trust only the CA key and verify its signature on a certificate rather than trusting many user/host keys. @@ -541,11 +546,11 @@ To generate a certificate for a specified set of principals: .Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain user_key.pub" .Pp Additional limitations on the validity and use of user certificates may -be specified through certificate constraints. -A constrained certificate may disable features of the SSH session, may be +be specified through certificate options.. +A certificate option may disable features of the SSH session, may be valid only when presented from particular source addresses or may force the use of a specific command. -For a list of valid certificate constraints, see the documentation for the +For a list of valid certificate options, see the documentation for the .Fl O option above. .Pp diff --git a/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c index fbd8695a0b5..58b1e400eb8 100644 --- a/usr.bin/ssh/ssh-keygen.c +++ b/usr.bin/ssh/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.185 2010/03/15 19:40:02 stevesk Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.186 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -97,6 +97,9 @@ char *identity_comment = NULL; /* Path to CA key when certifying keys. */ char *ca_key_path = NULL; +/* Certificate serial number */ +long long cert_serial = 0; + /* Key type when certifying */ u_int cert_key_type = SSH2_CERT_TYPE_USER; @@ -110,18 +113,18 @@ char *cert_principals = NULL; u_int64_t cert_valid_from = 0; u_int64_t cert_valid_to = ~0ULL; -/* Certificate constraints */ -#define CONSTRAINT_X_FWD (1) -#define CONSTRAINT_AGENT_FWD (1<<1) -#define CONSTRAINT_PORT_FWD (1<<2) -#define CONSTRAINT_PTY (1<<3) -#define CONSTRAINT_USER_RC (1<<4) -#define CONSTRAINT_DEFAULT (CONSTRAINT_X_FWD|CONSTRAINT_AGENT_FWD| \ - CONSTRAINT_PORT_FWD|CONSTRAINT_PTY| \ - CONSTRAINT_USER_RC) -u_int32_t constraint_flags = CONSTRAINT_DEFAULT; -char *constraint_command = NULL; -char *constraint_src_addr = NULL; +/* Certificate options */ +#define CRITOPT_X_FWD (1) +#define CRITOPT_AGENT_FWD (1<<1) +#define CRITOPT_PORT_FWD (1<<2) +#define CRITOPT_PTY (1<<3) +#define CRITOPT_USER_RC (1<<4) +#define CRITOPT_DEFAULT (CRITOPT_X_FWD|CRITOPT_AGENT_FWD| \ + CRITOPT_PORT_FWD|CRITOPT_PTY| \ + CRITOPT_USER_RC) +u_int32_t critical_flags = CRITOPT_DEFAULT; +char *critical_command = NULL; +char *critical_src_addr = NULL; /* Dump public key file in format used by real and the original SSH 2 */ int convert_to_ssh2 = 0; @@ -153,9 +156,13 @@ ask_filename(struct passwd *pw, const char *prompt) case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; + case KEY_DSA_CERT: + case KEY_DSA_CERT_V00: case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; + case KEY_RSA_CERT: + case KEY_RSA_CERT_V00: case KEY_RSA: name = _PATH_SSH_CLIENT_ID_RSA; break; @@ -1096,7 +1103,7 @@ fmt_validity(u_int64_t valid_from, u_int64_t valid_to) } static void -add_flag_constraint(Buffer *c, const char *name) +add_flag_option(Buffer *c, const char *name) { debug3("%s: %s", __func__, name); buffer_put_cstring(c, name); @@ -1104,7 +1111,7 @@ add_flag_constraint(Buffer *c, const char *name) } static void -add_string_constraint(Buffer *c, const char *name, const char *value) +add_string_option(Buffer *c, const char *name, const char *value) { Buffer b; @@ -1119,24 +1126,23 @@ add_string_constraint(Buffer *c, const char *name, const char *value) } static void -prepare_constraint_buf(Buffer *c) +prepare_options_buf(Buffer *c) { - buffer_clear(c); - if ((constraint_flags & CONSTRAINT_X_FWD) != 0) - add_flag_constraint(c, "permit-X11-forwarding"); - if ((constraint_flags & CONSTRAINT_AGENT_FWD) != 0) - add_flag_constraint(c, "permit-agent-forwarding"); - if ((constraint_flags & CONSTRAINT_PORT_FWD) != 0) - add_flag_constraint(c, "permit-port-forwarding"); - if ((constraint_flags & CONSTRAINT_PTY) != 0) - add_flag_constraint(c, "permit-pty"); - if ((constraint_flags & CONSTRAINT_USER_RC) != 0) - add_flag_constraint(c, "permit-user-rc"); - if (constraint_command != NULL) - add_string_constraint(c, "force-command", constraint_command); - if (constraint_src_addr != NULL) - add_string_constraint(c, "source-address", constraint_src_addr); + if ((critical_flags & CRITOPT_X_FWD) != 0) + add_flag_option(c, "permit-X11-forwarding"); + if ((critical_flags & CRITOPT_AGENT_FWD) != 0) + add_flag_option(c, "permit-agent-forwarding"); + if ((critical_flags & CRITOPT_PORT_FWD) != 0) + add_flag_option(c, "permit-port-forwarding"); + if ((critical_flags & CRITOPT_PTY) != 0) + add_flag_option(c, "permit-pty"); + if ((critical_flags & CRITOPT_USER_RC) != 0) + add_flag_option(c, "permit-user-rc"); + if (critical_command != NULL) + add_string_option(c, "force-command", critical_command); + if (critical_src_addr != NULL) + add_string_option(c, "source-address", critical_src_addr); } static void @@ -1147,12 +1153,32 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) Key *ca, *public; char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; + int v00 = 0; /* legacy keys */ tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if ((ca = load_identity(tmp)) == NULL) fatal("Couldn't load CA key \"%s\"", tmp); xfree(tmp); + if (key_type_name != NULL) { + switch (key_type_from_name(key_type_name)) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + v00 = 1; + break; + case KEY_UNSPEC: + if (strcasecmp(key_type_name, "v00") == 0) { + v00 = 1; + break; + } else if (strcasecmp(key_type_name, "v01") == 0) + break; + /* FALLTHROUGH */ + default: + fprintf(stderr, "unknown key type %s\n", key_type_name); + exit(1); + } + } + for (i = 0; i < argc; i++) { /* Split list of principals */ n = 0; @@ -1175,15 +1201,16 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) __func__, tmp, key_type(public)); /* Prepare certificate to sign */ - if (key_to_certified(public) != 0) + if (key_to_certified(public, v00) != 0) fatal("Could not upgrade key %s to certificate", tmp); public->cert->type = cert_key_type; + public->cert->serial = (u_int64_t)cert_serial; public->cert->key_id = xstrdup(cert_key_id); public->cert->nprincipals = n; public->cert->principals = plist; public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; - prepare_constraint_buf(&public->cert->constraints); + prepare_options_buf(&public->cert->critical); public->cert->signature_key = key_from_private(ca); if (key_certify(public, ca) != 0) @@ -1204,13 +1231,14 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) fprintf(f, " %s\n", comment); fclose(f); - if (!quiet) - logit("Signed %s key %s: id \"%s\"%s%s valid %s", - cert_key_type == SSH2_CERT_TYPE_USER?"user":"host", - out, cert_key_id, + if (!quiet) { + logit("Signed %s key %s: id \"%s\" serial %llu%s%s " + "valid %s", key_cert_type(public), + out, public->cert->key_id, public->cert->serial, cert_principals != NULL ? " for " : "", cert_principals != NULL ? cert_principals : "", fmt_validity(cert_valid_from, cert_valid_to)); + } key_free(public); xfree(out); @@ -1313,50 +1341,50 @@ parse_cert_times(char *timespec) } static void -add_cert_constraint(char *opt) +add_cert_option(char *opt) { char *val; if (strcmp(opt, "clear") == 0) - constraint_flags = 0; + critical_flags = 0; else if (strcasecmp(opt, "no-x11-forwarding") == 0) - constraint_flags &= ~CONSTRAINT_X_FWD; + critical_flags &= ~CRITOPT_X_FWD; else if (strcasecmp(opt, "permit-x11-forwarding") == 0) - constraint_flags |= CONSTRAINT_X_FWD; + critical_flags |= CRITOPT_X_FWD; else if (strcasecmp(opt, "no-agent-forwarding") == 0) - constraint_flags &= ~CONSTRAINT_AGENT_FWD; + critical_flags &= ~CRITOPT_AGENT_FWD; else if (strcasecmp(opt, "permit-agent-forwarding") == 0) - constraint_flags |= CONSTRAINT_AGENT_FWD; + critical_flags |= CRITOPT_AGENT_FWD; else if (strcasecmp(opt, "no-port-forwarding") == 0) - constraint_flags &= ~CONSTRAINT_PORT_FWD; + critical_flags &= ~CRITOPT_PORT_FWD; else if (strcasecmp(opt, "permit-port-forwarding") == 0) - constraint_flags |= CONSTRAINT_PORT_FWD; + critical_flags |= CRITOPT_PORT_FWD; else if (strcasecmp(opt, "no-pty") == 0) - constraint_flags &= ~CONSTRAINT_PTY; + critical_flags &= ~CRITOPT_PTY; else if (strcasecmp(opt, "permit-pty") == 0) - constraint_flags |= CONSTRAINT_PTY; + critical_flags |= CRITOPT_PTY; else if (strcasecmp(opt, "no-user-rc") == 0) - constraint_flags &= ~CONSTRAINT_USER_RC; + critical_flags &= ~CRITOPT_USER_RC; else if (strcasecmp(opt, "permit-user-rc") == 0) - constraint_flags |= CONSTRAINT_USER_RC; + critical_flags |= CRITOPT_USER_RC; else if (strncasecmp(opt, "force-command=", 14) == 0) { val = opt + 14; if (*val == '\0') - fatal("Empty force-command constraint"); - if (constraint_command != NULL) + fatal("Empty force-command option"); + if (critical_command != NULL) fatal("force-command already specified"); - constraint_command = xstrdup(val); + critical_command = xstrdup(val); } else if (strncasecmp(opt, "source-address=", 15) == 0) { val = opt + 15; if (*val == '\0') - fatal("Empty source-address constraint"); - if (constraint_src_addr != NULL) + fatal("Empty source-address option"); + if (critical_src_addr != NULL) fatal("source-address already specified"); if (addr_match_cidr_list(NULL, val) != 0) fatal("Invalid source-address list"); - constraint_src_addr = xstrdup(val); + critical_src_addr = xstrdup(val); } else - fatal("Unsupported certificate constraint \"%s\"", opt); + fatal("Unsupported certificate option \"%s\"", opt); } static void @@ -1365,9 +1393,9 @@ do_show_cert(struct passwd *pw) Key *key; struct stat st; char *key_fp, *ca_fp; - Buffer constraints, constraint; + Buffer options, option; u_char *name, *data; - u_int i, dlen; + u_int i, dlen, v00; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -1379,17 +1407,21 @@ do_show_cert(struct passwd *pw) fatal("%s is not a public key", identity_file); if (!key_is_cert(key)) fatal("%s is not a certificate", identity_file); - + v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00; + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); ca_fp = key_fingerprint(key->cert->signature_key, SSH_FP_MD5, SSH_FP_HEX); printf("%s:\n", identity_file); - printf(" %s %s certificate %s\n", key_type(key), - key_cert_type(key), key_fp); - printf(" Signed by %s CA %s\n", + printf(" Type: %s %s certificate\n", key_ssh_name(key), + key_cert_type(key)); + printf(" Public key: %s %s\n", key_type(key), key_fp); + printf(" Signing CA: %s %s\n", key_type(key->cert->signature_key), ca_fp); - printf(" Key ID \"%s\"\n", key->cert->key_id); + printf(" Key ID: \"%s\"\n", key->cert->key_id); + if (!v00) + printf(" Serial: %llu\n", key->cert->serial); printf(" Valid: %s\n", fmt_validity(key->cert->valid_after, key->cert->valid_before)); printf(" Principals: "); @@ -1401,20 +1433,20 @@ do_show_cert(struct passwd *pw) key->cert->principals[i]); printf("\n"); } - printf(" Constraints: "); - if (buffer_len(&key->cert->constraints) == 0) + printf(" Critical Options: "); + if (buffer_len(&key->cert->critical) == 0) printf("(none)\n"); else { printf("\n"); - buffer_init(&constraints); - buffer_append(&constraints, - buffer_ptr(&key->cert->constraints), - buffer_len(&key->cert->constraints)); - buffer_init(&constraint); - while (buffer_len(&constraints) != 0) { - name = buffer_get_string(&constraints, NULL); - data = buffer_get_string_ptr(&constraints, &dlen); - buffer_append(&constraint, data, dlen); + buffer_init(&options); + buffer_append(&options, + buffer_ptr(&key->cert->critical), + buffer_len(&key->cert->critical)); + buffer_init(&option); + while (buffer_len(&options) != 0) { + name = buffer_get_string(&options, NULL); + data = buffer_get_string_ptr(&options, &dlen); + buffer_append(&option, data, dlen); printf(" %s", name); if (strcmp(name, "permit-X11-forwarding") == 0 || strcmp(name, "permit-agent-forwarding") == 0 || @@ -1424,22 +1456,43 @@ do_show_cert(struct passwd *pw) printf("\n"); else if (strcmp(name, "force-command") == 0 || strcmp(name, "source-address") == 0) { - data = buffer_get_string(&constraint, NULL); + data = buffer_get_string(&option, NULL); printf(" %s\n", data); xfree(data); } else { - printf(" UNKNOWN CONSTRAINT (len %u)\n", - buffer_len(&constraint)); - buffer_clear(&constraint); + printf(" UNKNOWN OPTION (len %u)\n", + buffer_len(&option)); + buffer_clear(&option); } xfree(name); - if (buffer_len(&constraint) != 0) - fatal("Constraint corrupt: extra data at end"); + if (buffer_len(&option) != 0) + fatal("Option corrupt: extra data at end"); + } + buffer_free(&option); + buffer_free(&options); + } + if (!v00) { + printf(" Extensions: "); + if (buffer_len(&key->cert->extensions) == 0) + printf("(none)\n"); + else { + printf("\n"); + buffer_init(&options); + buffer_append(&options, + buffer_ptr(&key->cert->extensions), + buffer_len(&key->cert->extensions)); + buffer_init(&option); + while (buffer_len(&options) != 0) { + name = buffer_get_string(&options, NULL); + (void)buffer_get_string_ptr(&options, &dlen); + printf(" %s UNKNOWN OPTION " + "(len %u)\n", name, dlen); + xfree(name); + } + buffer_free(&option); + buffer_free(&options); } - buffer_free(&constraint); - buffer_free(&constraints); } - exit(0); } @@ -1470,7 +1523,7 @@ usage(void) fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); fprintf(stderr, " -n name,... User/host principal names to include in certificate\n"); fprintf(stderr, " -N phrase Provide new passphrase.\n"); - fprintf(stderr, " -O cnstr Specify a certificate constraint.\n"); + fprintf(stderr, " -O cnstr Specify a certificate option.\n"); fprintf(stderr, " -P phrase Provide old passphrase.\n"); fprintf(stderr, " -p Change passphrase of private key file.\n"); fprintf(stderr, " -q Quiet.\n"); @@ -1528,7 +1581,7 @@ main(int argc, char **argv) } while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:N:n:" - "O:C:r:g:R:T:G:M:S:s:a:V:W:")) != -1) { + "O:C:r:g:R:T:G:M:S:s:a:V:W:z:")) != -1) { switch (opt) { case 'b': bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); @@ -1584,7 +1637,7 @@ main(int argc, char **argv) identity_new_passphrase = optarg; break; case 'O': - add_cert_constraint(optarg); + add_cert_option(optarg); break; case 'C': identity_comment = optarg; @@ -1599,7 +1652,7 @@ main(int argc, char **argv) break; case 'h': cert_key_type = SSH2_CERT_TYPE_HOST; - constraint_flags = 0; + critical_flags = 0; break; case 'i': case 'X': @@ -1648,9 +1701,8 @@ main(int argc, char **argv) break; case 'M': memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); - if (errstr) { + if (errstr) fatal("Memory limit is %s: %s", errstr, optarg); - } break; case 'G': do_gen_candidates = 1; @@ -1672,6 +1724,11 @@ main(int argc, char **argv) case 'V': parse_cert_times(optarg); break; + case 'z': + cert_serial = strtonum(optarg, 0, LLONG_MAX, &errstr); + if (errstr) + fatal("Invalid serial number: %s", errstr); + break; case '?': default: usage(); diff --git a/usr.bin/ssh/ssh-rsa.c b/usr.bin/ssh/ssh-rsa.c index ce3111b9ca6..e98533cbe18 100644 --- a/usr.bin/ssh/ssh-rsa.c +++ b/usr.bin/ssh/ssh-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-rsa.c,v 1.40 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: ssh-rsa.c,v 1.41 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> * @@ -43,9 +43,8 @@ ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, int ok, nid; Buffer b; - if (key == NULL || - (key->type != KEY_RSA && key->type != KEY_RSA_CERT) || - key->rsa == NULL) { + if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && + key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { error("ssh_rsa_sign: no RSA key"); return -1; } @@ -112,9 +111,8 @@ ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, u_int len, dlen, modlen; int rlen, ret, nid; - if (key == NULL || - (key->type != KEY_RSA && key->type != KEY_RSA_CERT) || - key->rsa == NULL) { + if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && + key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { error("ssh_rsa_verify: no RSA key"); return -1; } diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index 3becf8fd524..12d9803df0b 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.222 2010/04/14 22:27:42 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.223 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -577,9 +577,9 @@ check_host_cert(const char *host, const Key *host_key) error("%s", reason); return 0; } - if (buffer_len(&host_key->cert->constraints) != 0) { - error("Certificate for %s contains unsupported constraint(s)", - host); + if (buffer_len(&host_key->cert->critical) != 0) { + error("Certificate for %s contains unsupported " + "critical options(s)", host); return 0; } return 1; diff --git a/usr.bin/ssh/sshconnect2.c b/usr.bin/ssh/sshconnect2.c index ba5875ed9d1..b680493d35d 100644 --- a/usr.bin/ssh/sshconnect2.c +++ b/usr.bin/ssh/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.181 2010/04/10 02:10:56 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.182 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -1134,8 +1134,11 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) u_int skip = 0; int ret = -1; int have_sig = 1; + char *fp; - debug3("sign_and_send_pubkey"); + fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); + xfree(fp); if (key_to_blob(id->key, &blob, &bloblen) == 0) { /* we cannot handle this key */ diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index adb2aec90bd..e8748af1513 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.374 2010/03/07 11:57:13 dtucker Exp $ */ +/* $OpenBSD: sshd.c,v 1.375 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -727,6 +727,8 @@ list_hostkey_types(void) if (key == NULL) continue; switch (key->type) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: case KEY_RSA_CERT: case KEY_DSA_CERT: if (buffer_len(&b) > 0) @@ -750,10 +752,17 @@ get_hostkey_by_type(int type, int need_private) Key *key; for (i = 0; i < options.num_host_key_files; i++) { - if (type == KEY_RSA_CERT || type == KEY_DSA_CERT) + switch (type) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_DSA_CERT: key = sensitive_data.host_certificates[i]; - else + break; + default: key = sensitive_data.host_keys[i]; + break; + } if (key != NULL && key->type == type) return need_private ? sensitive_data.host_keys[i] : key; |