From 6fdcaeb99532e28a69f1a1599fbd540bb15b70a0 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 20 Oct 2014 03:43:01 +0000 Subject: upstream commit whitespace --- hostfile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'hostfile.c') diff --git a/hostfile.c b/hostfile.c index ee2daf45f..ad5acb68e 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.57 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.58 2014/10/20 03:43:01 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -329,7 +329,7 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) debug3("%s: loaded %lu keys", __func__, num_loaded); fclose(f); return; -} +} void free_hostkeys(struct hostkeys *hostkeys) @@ -439,7 +439,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, } return end_return; } - + HostStatus check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key, const struct hostkey_entry **found) -- cgit v1.2.3 From 1129dcfc5a3e508635004bcc05a3574cb7687167 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 15 Jan 2015 09:40:00 +0000 Subject: upstream commit sync ssh-keysign, ssh-keygen and some dependencies to the new buffer/key API; mostly mechanical, ok markus@ --- dns.c | 30 +-- dns.h | 7 +- hostfile.c | 74 +++--- hostfile.h | 11 +- kex.h | 4 +- msg.c | 25 +- msg.h | 7 +- readconf.c | 5 +- readconf.h | 4 +- ssh-keygen.c | 773 +++++++++++++++++++++++++++++++--------------------------- ssh-keysign.c | 120 +++++---- ssh-pkcs11.c | 24 +- ssh-pkcs11.h | 4 +- 13 files changed, 594 insertions(+), 494 deletions(-) (limited to 'hostfile.c') diff --git a/dns.c b/dns.c index 4b8ae44cf..f45bec0bf 100644 --- a/dns.c +++ b/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.32 2014/12/21 22:27:56 djm Exp $ */ +/* $OpenBSD: dns.c,v 1.33 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -38,7 +38,8 @@ #include #include "xmalloc.h" -#include "key.h" +#include "sshkey.h" +#include "ssherr.h" #include "dns.h" #include "log.h" #include "digest.h" @@ -78,9 +79,9 @@ dns_result_totext(unsigned int res) */ static int dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, - u_char **digest, u_int *digest_len, Key *key) + u_char **digest, size_t *digest_len, struct sshkey *key) { - int success = 0; + int r, success = 0; int fp_alg = -1; switch (key->type) { @@ -121,9 +122,10 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, } if (*algorithm && *digest_type) { - *digest = key_fingerprint_raw(key, fp_alg, digest_len); - if (*digest == NULL) - fatal("dns_read_key: null from key_fingerprint_raw()"); + if ((r = sshkey_fingerprint_raw(key, fp_alg, digest, + digest_len)) != 0) + fatal("%s: sshkey_fingerprint_raw: %s", __func__, + ssh_err(r)); success = 1; } else { *digest = NULL; @@ -139,7 +141,7 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, */ static int dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, - u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) + u_char **digest, size_t *digest_len, u_char *rdata, int rdata_len) { int success = 0; @@ -200,7 +202,7 @@ is_numeric_hostname(const char *hostname) */ int verify_host_key_dns(const char *hostname, struct sockaddr *address, - Key *hostkey, int *flags) + struct sshkey *hostkey, int *flags) { u_int counter; int result; @@ -209,12 +211,12 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address, u_int8_t hostkey_algorithm; u_int8_t hostkey_digest_type = SSHFP_HASH_RESERVED; u_char *hostkey_digest; - u_int hostkey_digest_len; + size_t hostkey_digest_len; u_int8_t dnskey_algorithm; u_int8_t dnskey_digest_type; u_char *dnskey_digest; - u_int dnskey_digest_len; + size_t dnskey_digest_len; *flags = 0; @@ -310,13 +312,13 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address, * Export the fingerprint of a key as a DNS resource record */ int -export_dns_rr(const char *hostname, Key *key, FILE *f, int generic) +export_dns_rr(const char *hostname, struct sshkey *key, FILE *f, int generic) { u_int8_t rdata_pubkey_algorithm = 0; u_int8_t rdata_digest_type = SSHFP_HASH_RESERVED; u_int8_t dtype; u_char *rdata_digest; - u_int i, rdata_digest_len; + size_t i, rdata_digest_len; int success = 0; for (dtype = SSHFP_HASH_SHA1; dtype < SSHFP_HASH_MAX; dtype++) { @@ -324,7 +326,7 @@ export_dns_rr(const char *hostname, Key *key, FILE *f, int generic) if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, &rdata_digest, &rdata_digest_len, key)) { if (generic) { - fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", + fprintf(f, "%s IN TYPE%d \\# %zu %02x %02x ", hostname, DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, rdata_pubkey_algorithm, rdata_digest_type); diff --git a/dns.h b/dns.h index b9feae6be..815f073a1 100644 --- a/dns.h +++ b/dns.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.h,v 1.13 2014/04/20 09:24:26 logan Exp $ */ +/* $OpenBSD: dns.h,v 1.14 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -50,7 +50,8 @@ enum sshfp_hashes { #define DNS_VERIFY_MATCH 0x00000002 #define DNS_VERIFY_SECURE 0x00000004 -int verify_host_key_dns(const char *, struct sockaddr *, Key *, int *); -int export_dns_rr(const char *, Key *, FILE *, int); +int verify_host_key_dns(const char *, struct sockaddr *, + struct sshkey *, int *); +int export_dns_rr(const char *, struct sshkey *, FILE *, int); #endif /* DNS_H */ diff --git a/hostfile.c b/hostfile.c index ad5acb68e..40dbbd478 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.58 2014/10/20 03:43:01 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.59 2015/01/15 09:40:00 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -51,10 +51,11 @@ #include "xmalloc.h" #include "match.h" -#include "key.h" +#include "sshkey.h" #include "hostfile.h" #include "log.h" #include "misc.h" +#include "ssherr.h" #include "digest.h" #include "hmac.h" @@ -155,15 +156,16 @@ host_hash(const char *host, const char *name_from_hostfile, u_int src_len) */ int -hostfile_read_key(char **cpp, int *bitsp, Key *ret) +hostfile_read_key(char **cpp, u_int *bitsp, struct sshkey *ret) { char *cp; + int r; /* Skip leading whitespace. */ for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; - if (key_read(ret, &cp) != 1) + if ((r = sshkey_read(ret, &cp)) != 0) return 0; /* Skip trailing whitespace. */ @@ -172,15 +174,13 @@ hostfile_read_key(char **cpp, int *bitsp, Key *ret) /* Return results. */ *cpp = cp; - if (bitsp != NULL) { - if ((*bitsp = key_size(ret)) <= 0) - return 0; - } + if (bitsp != NULL) + *bitsp = sshkey_size(ret); return 1; } static int -hostfile_check_key(int bits, const Key *key, const char *host, +hostfile_check_key(int bits, const struct sshkey *key, const char *host, const char *filename, u_long linenum) { #ifdef WITH_SSH1 @@ -249,8 +249,8 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) u_long linenum = 0, num_loaded = 0; char *cp, *cp2, *hashed_host; HostkeyMarker marker; - Key *key; - int kbits; + struct sshkey *key; + u_int kbits; if ((f = fopen(path, "r")) == NULL) return; @@ -296,13 +296,19 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. */ - key = key_new(KEY_UNSPEC); + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { + error("%s: sshkey_new failed", __func__); + break; + } if (!hostfile_read_key(&cp, &kbits, key)) { - key_free(key); + sshkey_free(key); #ifdef WITH_SSH1 - key = key_new(KEY_RSA1); + if ((key = sshkey_new(KEY_RSA1)) == NULL) { + error("%s: sshkey_new failed", __func__); + break; + } if (!hostfile_read_key(&cp, &kbits, key)) { - key_free(key); + sshkey_free(key); continue; } #else @@ -315,7 +321,7 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) debug3("%s: found %skey type %s in file %s:%lu", __func__, marker == MRK_NONE ? "" : (marker == MRK_CA ? "ca " : "revoked "), - key_type(key), path, linenum); + sshkey_type(key), path, linenum); hostkeys->entries = xrealloc(hostkeys->entries, hostkeys->num_entries + 1, sizeof(*hostkeys->entries)); hostkeys->entries[hostkeys->num_entries].host = xstrdup(host); @@ -339,7 +345,7 @@ free_hostkeys(struct hostkeys *hostkeys) for (i = 0; i < hostkeys->num_entries; i++) { free(hostkeys->entries[i].host); free(hostkeys->entries[i].file); - key_free(hostkeys->entries[i].key); + sshkey_free(hostkeys->entries[i].key); explicit_bzero(hostkeys->entries + i, sizeof(*hostkeys->entries)); } free(hostkeys->entries); @@ -348,18 +354,18 @@ free_hostkeys(struct hostkeys *hostkeys) } static int -check_key_not_revoked(struct hostkeys *hostkeys, Key *k) +check_key_not_revoked(struct hostkeys *hostkeys, struct sshkey *k) { - int is_cert = key_is_cert(k); + int is_cert = sshkey_is_cert(k); u_int i; for (i = 0; i < hostkeys->num_entries; i++) { if (hostkeys->entries[i].marker != MRK_REVOKE) continue; - if (key_equal_public(k, hostkeys->entries[i].key)) + if (sshkey_equal_public(k, hostkeys->entries[i].key)) return -1; if (is_cert && - key_equal_public(k->cert->signature_key, + sshkey_equal_public(k->cert->signature_key, hostkeys->entries[i].key)) return -1; } @@ -383,11 +389,11 @@ check_key_not_revoked(struct hostkeys *hostkeys, Key *k) */ static HostStatus check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, - Key *k, int keytype, const struct hostkey_entry **found) + struct sshkey *k, int keytype, const struct hostkey_entry **found) { u_int i; HostStatus end_return = HOST_NEW; - int want_cert = key_is_cert(k); + int want_cert = sshkey_is_cert(k); HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE; int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2; @@ -411,7 +417,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, break; } if (want_cert) { - if (key_equal_public(k->cert->signature_key, + if (sshkey_equal_public(k->cert->signature_key, hostkeys->entries[i].key)) { /* A matching CA exists */ end_return = HOST_OK; @@ -420,7 +426,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, break; } } else { - if (key_equal(k, hostkeys->entries[i].key)) { + if (sshkey_equal(k, hostkeys->entries[i].key)) { end_return = HOST_OK; if (found != NULL) *found = hostkeys->entries + i; @@ -441,7 +447,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, } HostStatus -check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key, +check_key_in_hostkeys(struct hostkeys *hostkeys, struct sshkey *key, const struct hostkey_entry **found) { if (key == NULL) @@ -463,11 +469,11 @@ lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, */ int -add_host_to_hostfile(const char *filename, const char *host, const Key *key, - int store_hash) +add_host_to_hostfile(const char *filename, const char *host, + const struct sshkey *key, int store_hash) { FILE *f; - int success = 0; + int r, success = 0; char *hashed_host = NULL; if (key == NULL) @@ -485,12 +491,12 @@ add_host_to_hostfile(const char *filename, const char *host, const Key *key, } fprintf(f, "%s ", store_hash ? hashed_host : host); - if (key_write(key, f)) { + if ((r = sshkey_write(key, f)) != 0) { + error("%s: saving key in %s failed: %s", + __func__, filename, ssh_err(r)); + } else success = 1; - } else { - error("add_host_to_hostfile: saving key in %s failed", filename); - } - fprintf(f, "\n"); + fputs("\n", f); fclose(f); return success; } diff --git a/hostfile.h b/hostfile.h index 679c034f3..d90973f42 100644 --- a/hostfile.h +++ b/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.20 2013/07/12 00:19:58 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.21 2015/01/15 09:40:00 djm Exp $ */ /* * Author: Tatu Ylonen @@ -26,7 +26,7 @@ struct hostkey_entry { char *host; char *file; u_long line; - Key *key; + struct sshkey *key; HostkeyMarker marker; }; struct hostkeys; @@ -35,13 +35,14 @@ struct hostkeys *init_hostkeys(void); void load_hostkeys(struct hostkeys *, const char *, const char *); void free_hostkeys(struct hostkeys *); -HostStatus check_key_in_hostkeys(struct hostkeys *, Key *, +HostStatus check_key_in_hostkeys(struct hostkeys *, struct sshkey *, const struct hostkey_entry **); int lookup_key_in_hostkeys_by_type(struct hostkeys *, int, const struct hostkey_entry **); -int hostfile_read_key(char **, int *, Key *); -int add_host_to_hostfile(const char *, const char *, const Key *, int); +int hostfile_read_key(char **, u_int *, struct sshkey *); +int add_host_to_hostfile(const char *, const char *, + const struct sshkey *, int); #define HASH_MAGIC "|1|" #define HASH_DELIM '|' diff --git a/kex.h b/kex.h index dbcc0816f..ef4a1f096 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.65 2015/01/13 19:31:40 markus Exp $ */ +/* $OpenBSD: kex.h,v 1.66 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -27,6 +27,8 @@ #define KEX_H #include "mac.h" +#include "buffer.h" /* XXX for typedef */ +#include "key.h" /* XXX for typedef */ #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) #include diff --git a/msg.c b/msg.c index cd5f98c4f..5a7b8ca91 100644 --- a/msg.c +++ b/msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: msg.c,v 1.15 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: msg.c,v 1.16 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -34,17 +34,18 @@ #include #include -#include "buffer.h" +#include "sshbuf.h" +#include "ssherr.h" #include "log.h" #include "atomicio.h" #include "msg.h" #include "misc.h" int -ssh_msg_send(int fd, u_char type, Buffer *m) +ssh_msg_send(int fd, u_char type, struct sshbuf *m) { u_char buf[5]; - u_int mlen = buffer_len(m); + u_int mlen = sshbuf_len(m); debug3("ssh_msg_send: type %u", (unsigned int)type & 0xff); @@ -54,7 +55,7 @@ ssh_msg_send(int fd, u_char type, Buffer *m) error("ssh_msg_send: write"); return (-1); } - if (atomicio(vwrite, fd, buffer_ptr(m), mlen) != mlen) { + if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(m), mlen) != mlen) { error("ssh_msg_send: write"); return (-1); } @@ -62,10 +63,11 @@ ssh_msg_send(int fd, u_char type, Buffer *m) } int -ssh_msg_recv(int fd, Buffer *m) +ssh_msg_recv(int fd, struct sshbuf *m) { - u_char buf[4]; + u_char buf[4], *p; u_int msg_len; + int r; debug3("ssh_msg_recv entering"); @@ -79,9 +81,12 @@ ssh_msg_recv(int fd, Buffer *m) error("ssh_msg_recv: read: bad msg_len %u", msg_len); return (-1); } - buffer_clear(m); - buffer_append_space(m, msg_len); - if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) { + sshbuf_reset(m); + if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) { + error("%s: buffer error: %s", __func__, ssh_err(r)); + return -1; + } + if (atomicio(read, fd, p, msg_len) != msg_len) { error("ssh_msg_recv: read: %s", strerror(errno)); return (-1); } diff --git a/msg.h b/msg.h index b0cb9b52b..dfb34247c 100644 --- a/msg.h +++ b/msg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: msg.h,v 1.4 2006/03/25 22:22:43 djm Exp $ */ +/* $OpenBSD: msg.h,v 1.5 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -25,7 +25,8 @@ #ifndef SSH_MSG_H #define SSH_MSG_H -int ssh_msg_send(int, u_char, Buffer *); -int ssh_msg_recv(int, Buffer *); +struct sshbuf; +int ssh_msg_send(int, u_char, struct sshbuf *); +int ssh_msg_recv(int, struct sshbuf *); #endif diff --git a/readconf.c b/readconf.c index d7f1cf036..a122d176d 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.226 2015/01/13 07:39:19 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.227 2015/01/15 09:40:00 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -51,11 +51,10 @@ #include "cipher.h" #include "pathnames.h" #include "log.h" -#include "key.h" +#include "sshkey.h" #include "misc.h" #include "readconf.h" #include "match.h" -#include "buffer.h" #include "kex.h" #include "mac.h" #include "uidswap.h" diff --git a/readconf.h b/readconf.h index 11a7332c2..a23da1107 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.105 2014/12/21 22:27:56 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.106 2015/01/15 09:40:00 djm Exp $ */ /* * Author: Tatu Ylonen @@ -93,7 +93,7 @@ typedef struct { int num_identity_files; /* Number of files for RSA/DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; - Key *identity_keys[SSH_MAX_IDENTITY_FILES]; + struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; /* Local TCP/IP forward requests. */ int num_local_forwards; diff --git a/ssh-keygen.c b/ssh-keygen.c index 7f775ff16..c8b05e079 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.251 2014/12/21 22:27:56 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.252 2015/01/15 09:40:00 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -39,11 +39,11 @@ #include #include "xmalloc.h" -#include "key.h" +#include "sshkey.h" #include "rsa.h" #include "authfile.h" #include "uuencode.h" -#include "buffer.h" +#include "sshbuf.h" #include "pathnames.h" #include "log.h" #include "misc.h" @@ -52,6 +52,7 @@ #include "dns.h" #include "ssh.h" #include "ssh2.h" +#include "ssherr.h" #include "ssh-pkcs11.h" #include "atomicio.h" #include "krl.h" @@ -208,7 +209,7 @@ type_bits_valid(int type, u_int32_t *bitsp) fatal("DSA keys must be 1024 bits"); else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 768) fatal("Key must at least be 768 bits"); - else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(*bitsp) == -1) + else if (type == KEY_ECDSA && sshkey_ecdsa_bits_to_nid(*bitsp) == -1) fatal("Invalid ECDSA key length - valid lengths are " "256, 384 or 521 bits"); #endif @@ -223,7 +224,7 @@ ask_filename(struct passwd *pw, const char *prompt) if (key_type_name == NULL) name = _PATH_SSH_CLIENT_ID_RSA; else { - switch (key_type_from_name(key_type_name)) { + switch (sshkey_type_from_name(key_type_name)) { case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; @@ -263,23 +264,26 @@ ask_filename(struct passwd *pw, const char *prompt) have_identity = 1; } -static Key * +static struct sshkey * load_identity(char *filename) { char *pass; - Key *prv; + struct sshkey *prv; + int r; - prv = key_load_private(filename, "", NULL); - if (prv == NULL) { - if (identity_passphrase) - pass = xstrdup(identity_passphrase); - else - pass = read_passphrase("Enter passphrase: ", - RP_ALLOW_STDIN); - prv = key_load_private(filename, pass, NULL); - explicit_bzero(pass, strlen(pass)); - free(pass); - } + if ((r = sshkey_load_private(filename, "", &prv, NULL)) == 0) + return prv; + if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal("Load key \"%s\": %s", filename, ssh_err(r)); + if (identity_passphrase) + pass = xstrdup(identity_passphrase); + else + pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); + r = sshkey_load_private(filename, pass, &prv, NULL); + explicit_bzero(pass, strlen(pass)); + free(pass); + if (r != 0) + fatal("Load key \"%s\": %s", filename, ssh_err(r)); return prv; } @@ -290,39 +294,40 @@ load_identity(char *filename) #ifdef WITH_OPENSSL static void -do_convert_to_ssh2(struct passwd *pw, Key *k) +do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) { - u_int len; + size_t len; u_char *blob; char comment[61]; + int r; if (k->type == KEY_RSA1) { fprintf(stderr, "version 1 keys are not supported\n"); exit(1); } - if (key_to_blob(k, &blob, &len) <= 0) { - fprintf(stderr, "key_to_blob failed\n"); + if ((r = sshkey_to_blob(k, &blob, &len)) != 0) { + fprintf(stderr, "key_to_blob failed: %s\n", ssh_err(r)); exit(1); } /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ snprintf(comment, sizeof(comment), "%u-bit %s, converted by %s@%s from OpenSSH", - key_size(k), key_type(k), + sshkey_size(k), sshkey_type(k), pw->pw_name, hostname); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); fprintf(stdout, "Comment: \"%s\"\n", comment); dump_base64(stdout, blob, len); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); - key_free(k); + sshkey_free(k); free(blob); exit(0); } static void -do_convert_to_pkcs8(Key *k) +do_convert_to_pkcs8(struct sshkey *k) { - switch (key_type_plain(k->type)) { + switch (sshkey_type_plain(k->type)) { case KEY_RSA1: case KEY_RSA: if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) @@ -339,15 +344,15 @@ do_convert_to_pkcs8(Key *k) break; #endif default: - fatal("%s: unsupported key type %s", __func__, key_type(k)); + fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } exit(0); } static void -do_convert_to_pem(Key *k) +do_convert_to_pem(struct sshkey *k) { - switch (key_type_plain(k->type)) { + switch (sshkey_type_plain(k->type)) { case KEY_RSA1: case KEY_RSA: if (!PEM_write_RSAPublicKey(stdout, k->rsa)) @@ -361,7 +366,7 @@ do_convert_to_pem(Key *k) #endif /* XXX ECDSA? */ default: - fatal("%s: unsupported key type %s", __func__, key_type(k)); + fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } exit(0); } @@ -369,20 +374,16 @@ do_convert_to_pem(Key *k) static void do_convert_to(struct passwd *pw) { - Key *k; + struct sshkey *k; struct stat st; + int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); - if ((k = key_load_public(identity_file, NULL)) == NULL) { - if ((k = load_identity(identity_file)) == NULL) { - fprintf(stderr, "load failed\n"); - exit(1); - } - } - + if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) + k = load_identity(identity_file); switch (convert_format) { case FMT_RFC4716: do_convert_to_ssh2(pw, k); @@ -399,51 +400,63 @@ do_convert_to(struct passwd *pw) exit(0); } +/* + * This is almost exactly the bignum1 encoding, but with 32 bit for length + * instead of 16. + */ static void -buffer_get_bignum_bits(Buffer *b, BIGNUM *value) +buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value) { - u_int bignum_bits = buffer_get_int(b); - u_int bytes = (bignum_bits + 7) / 8; - - if (buffer_len(b) < bytes) - fatal("buffer_get_bignum_bits: input buffer too small: " - "need %d have %d", bytes, buffer_len(b)); - if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) - fatal("buffer_get_bignum_bits: BN_bin2bn failed"); - buffer_consume(b, bytes); + u_int bytes, bignum_bits; + int r; + + if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + bytes = (bignum_bits + 7) / 8; + if (sshbuf_len(b) < bytes) + fatal("%s: input buffer too small: need %d have %zu", + __func__, bytes, sshbuf_len(b)); + if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL) + fatal("%s: BN_bin2bn failed", __func__); + if ((r = sshbuf_consume(b, bytes)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } -static Key * +static struct sshkey * do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) { - Buffer b; - Key *key = NULL; + struct sshbuf *b; + struct sshkey *key = NULL; char *type, *cipher; - u_char *sig = NULL, data[] = "abcde12345"; - int magic, rlen, ktype, i1, i2, i3, i4; - u_int slen; + u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; + int r, rlen, ktype; + u_int magic, i1, i2, i3, i4; + size_t slen; u_long e; - buffer_init(&b); - buffer_append(&b, blob, blen); + if ((b = sshbuf_from(blob, blen)) == NULL) + fatal("%s: sshbuf_from failed", __func__); + if ((r = sshbuf_get_u32(b, &magic)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); - magic = buffer_get_int(&b); if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { - error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); - buffer_free(&b); + error("bad magic 0x%x != 0x%x", magic, + SSH_COM_PRIVATE_KEY_MAGIC); + sshbuf_free(b); return NULL; } - i1 = buffer_get_int(&b); - type = buffer_get_string(&b, NULL); - cipher = buffer_get_string(&b, NULL); - i2 = buffer_get_int(&b); - i3 = buffer_get_int(&b); - i4 = buffer_get_int(&b); + if ((r = sshbuf_get_u32(b, &i1)) != 0 || + (r = sshbuf_get_cstring(b, &type, NULL)) != 0 || + (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 || + (r = sshbuf_get_u32(b, &i2)) != 0 || + (r = sshbuf_get_u32(b, &i3)) != 0 || + (r = sshbuf_get_u32(b, &i4)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug("ignore (%d %d %d %d)", i1, i2, i3, i4); if (strcmp(cipher, "none") != 0) { error("unsupported cipher %s", cipher); free(cipher); - buffer_free(&b); + sshbuf_free(b); free(type); return NULL; } @@ -454,56 +467,64 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) } else if (strstr(type, "rsa")) { ktype = KEY_RSA; } else { - buffer_free(&b); + sshbuf_free(b); free(type); return NULL; } - key = key_new_private(ktype); + if ((key = sshkey_new_private(ktype)) == NULL) + fatal("key_new_private failed"); free(type); switch (key->type) { case KEY_DSA: - buffer_get_bignum_bits(&b, key->dsa->p); - buffer_get_bignum_bits(&b, key->dsa->g); - buffer_get_bignum_bits(&b, key->dsa->q); - buffer_get_bignum_bits(&b, key->dsa->pub_key); - buffer_get_bignum_bits(&b, key->dsa->priv_key); + buffer_get_bignum_bits(b, key->dsa->p); + buffer_get_bignum_bits(b, key->dsa->g); + buffer_get_bignum_bits(b, key->dsa->q); + buffer_get_bignum_bits(b, key->dsa->pub_key); + buffer_get_bignum_bits(b, key->dsa->priv_key); break; case KEY_RSA: - e = buffer_get_char(&b); + if ((r = sshbuf_get_u8(b, &e1)) != 0 || + (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || + (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + e = e1; debug("e %lx", e); if (e < 30) { e <<= 8; - e += buffer_get_char(&b); + e += e2; debug("e %lx", e); e <<= 8; - e += buffer_get_char(&b); + e += e3; debug("e %lx", e); } if (!BN_set_word(key->rsa->e, e)) { - buffer_free(&b); - key_free(key); + sshbuf_free(b); + sshkey_free(key); return NULL; } - buffer_get_bignum_bits(&b, key->rsa->d); - buffer_get_bignum_bits(&b, key->rsa->n); - buffer_get_bignum_bits(&b, key->rsa->iqmp); - buffer_get_bignum_bits(&b, key->rsa->q); - buffer_get_bignum_bits(&b, key->rsa->p); - if (rsa_generate_additional_parameters(key->rsa) != 0) - fatal("%s: rsa_generate_additional_parameters " - "error", __func__); + buffer_get_bignum_bits(b, key->rsa->d); + buffer_get_bignum_bits(b, key->rsa->n); + buffer_get_bignum_bits(b, key->rsa->iqmp); + buffer_get_bignum_bits(b, key->rsa->q); + buffer_get_bignum_bits(b, key->rsa->p); + if ((r = rsa_generate_additional_parameters(key->rsa)) != 0) + fatal("generate RSA parameters failed: %s", ssh_err(r)); break; } - rlen = buffer_len(&b); + rlen = sshbuf_len(b); if (rlen != 0) error("do_convert_private_ssh2_from_blob: " "remaining bytes in key blob %d", rlen); - buffer_free(&b); + sshbuf_free(b); /* try the key */ - key_sign(key, &sig, &slen, data, sizeof(data)); - key_verify(key, sig, slen, data, sizeof(data)); + if (sshkey_sign(key, &sig, &slen, data, sizeof(data), 0) != 0 || + sshkey_verify(key, sig, slen, data, sizeof(data), 0) != 0) { + sshkey_free(key); + free(sig); + return NULL; + } free(sig); return key; } @@ -539,14 +560,13 @@ get_line(FILE *fp, char *line, size_t len) } static void -do_convert_from_ssh2(struct passwd *pw, Key **k, int *private) +do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) { - int blen; + int r, blen, escaped = 0; u_int len; char line[1024]; u_char blob[8096]; char encoded[8096]; - int escaped = 0; FILE *fp; if ((fp = fopen(identity_file, "r")) == NULL) @@ -583,18 +603,17 @@ do_convert_from_ssh2(struct passwd *pw, Key **k, int *private) fprintf(stderr, "uudecode failed.\n"); exit(1); } - *k = *private ? - do_convert_private_ssh2_from_blob(blob, blen) : - key_from_blob(blob, blen); - if (*k == NULL) { - fprintf(stderr, "decode blob failed.\n"); + if (*private) + *k = do_convert_private_ssh2_from_blob(blob, blen); + else if ((r = sshkey_from_blob(blob, blen, k)) != 0) { + fprintf(stderr, "decode blob failed: %s\n", ssh_err(r)); exit(1); } fclose(fp); } static void -do_convert_from_pkcs8(Key **k, int *private) +do_convert_from_pkcs8(struct sshkey **k, int *private) { EVP_PKEY *pubkey; FILE *fp; @@ -608,21 +627,24 @@ do_convert_from_pkcs8(Key **k, int *private) fclose(fp); switch (EVP_PKEY_type(pubkey->type)) { case EVP_PKEY_RSA: - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); break; case EVP_PKEY_DSA: - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; #ifdef OPENSSL_HAS_ECC case EVP_PKEY_EC: - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_ECDSA; (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); - (*k)->ecdsa_nid = key_ecdsa_key_to_nid((*k)->ecdsa); + (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa); break; #endif default: @@ -634,7 +656,7 @@ do_convert_from_pkcs8(Key **k, int *private) } static void -do_convert_from_pem(Key **k, int *private) +do_convert_from_pem(struct sshkey **k, int *private) { FILE *fp; RSA *rsa; @@ -645,7 +667,8 @@ do_convert_from_pem(Key **k, int *private) if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = rsa; fclose(fp); @@ -654,7 +677,8 @@ do_convert_from_pem(Key **k, int *private) #if notyet /* OpenSSH 0.9.8 lacks this function */ rewind(fp); if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = dsa; fclose(fp); @@ -668,8 +692,8 @@ do_convert_from_pem(Key **k, int *private) static void do_convert_from(struct passwd *pw) { - Key *k = NULL; - int private = 0, ok = 0; + struct sshkey *k = NULL; + int r, private = 0, ok = 0; struct stat st; if (!have_identity) @@ -692,7 +716,8 @@ do_convert_from(struct passwd *pw) } if (!private) - ok = key_write(k, stdout); + if ((r = sshkey_write(k, stdout)) == 0) + ok = 1; if (ok) fprintf(stdout, "\n"); else { @@ -713,7 +738,7 @@ do_convert_from(struct passwd *pw) break; default: fatal("%s: unsupported key type %s", __func__, - key_type(k)); + sshkey_type(k)); } } @@ -721,7 +746,7 @@ do_convert_from(struct passwd *pw) fprintf(stderr, "key write failed\n"); exit(1); } - key_free(k); + sshkey_free(k); exit(0); } #endif @@ -729,8 +754,9 @@ do_convert_from(struct passwd *pw) static void do_print_public(struct passwd *pw) { - Key *prv; + struct sshkey *prv; struct stat st; + int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -739,13 +765,9 @@ do_print_public(struct passwd *pw) exit(1); } prv = load_identity(identity_file); - if (prv == NULL) { - fprintf(stderr, "load failed\n"); - exit(1); - } - if (!key_write(prv, stdout)) - fprintf(stderr, "key_write failed"); - key_free(prv); + if ((r = sshkey_write(prv, stdout)) != 0) + fprintf(stderr, "key_write failed: %s", ssh_err(r)); + sshkey_free(prv); fprintf(stdout, "\n"); exit(0); } @@ -754,9 +776,9 @@ static void do_download(struct passwd *pw) { #ifdef ENABLE_PKCS11 - Key **keys = NULL; + struct sshkey **keys = NULL; int i, nkeys; - enum fp_rep rep; + enum sshkey_fp_rep rep; int fptype; char *fp, *ra; @@ -769,20 +791,20 @@ do_download(struct passwd *pw) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { if (print_fingerprint) { - fp = key_fingerprint(keys[i], fptype, rep); - ra = key_fingerprint(keys[i], fingerprint_hash, + fp = sshkey_fingerprint(keys[i], fptype, rep); + ra = sshkey_fingerprint(keys[i], fingerprint_hash, SSH_FP_RANDOMART); - printf("%u %s %s (PKCS11 key)\n", key_size(keys[i]), - fp, key_type(keys[i])); + printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]), + fp, sshkey_type(keys[i])); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } else { - key_write(keys[i], stdout); + (void) sshkey_write(keys[i], stdout); /* XXX check */ fprintf(stdout, "\n"); } - key_free(keys[i]); + sshkey_free(keys[i]); } free(keys); pkcs11_terminate(); @@ -796,10 +818,10 @@ static void do_fingerprint(struct passwd *pw) { FILE *f; - Key *public; + struct sshkey *public; char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra; - int i, skip = 0, num = 0, invalid = 1; - enum fp_rep rep; + int r, i, skip = 0, num = 0, invalid = 1; + enum sshkey_fp_rep rep; int fptype; struct stat st; @@ -811,16 +833,18 @@ do_fingerprint(struct passwd *pw) perror(identity_file); exit(1); } - public = key_load_public(identity_file, &comment); - if (public != NULL) { - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fingerprint_hash, + if ((r = sshkey_load_public(identity_file, &public, &comment)) != 0) + error("Error loading public key \"%s\": %s", + identity_file, ssh_err(r)); + else { + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, comment, - key_type(public)); + printf("%u %s %s (%s)\n", sshkey_size(public), fp, comment, + sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); - key_free(public); + sshkey_free(public); free(comment); free(ra); free(fp); @@ -869,27 +893,29 @@ do_fingerprint(struct passwd *pw) *cp++ = '\0'; } ep = cp; - public = key_new(KEY_RSA1); - if (key_read(public, &cp) != 1) { + if ((public = sshkey_new(KEY_RSA1)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(public, &cp)) != 0) { cp = ep; - key_free(public); - public = key_new(KEY_UNSPEC); - if (key_read(public, &cp) != 1) { - key_free(public); + sshkey_free(public); + if ((public = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(public, &cp)) != 0) { + sshkey_free(public); continue; } } comment = *cp ? cp : comment; - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fingerprint_hash, + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, - comment ? comment : "no comment", key_type(public)); + printf("%u %s %s (%s)\n", sshkey_size(public), fp, + comment ? comment : "no comment", sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); - key_free(public); + sshkey_free(public); invalid = 0; } fclose(f); @@ -921,9 +947,9 @@ do_gen_all_hostkeys(struct passwd *pw) int first = 0; struct stat st; - Key *private, *public; + struct sshkey *private, *public; char comment[1024]; - int i, type, fd; + int i, type, fd, r; FILE *f; for (i = 0; key_types[i].key_type; i++) { @@ -942,34 +968,36 @@ do_gen_all_hostkeys(struct passwd *pw) } printf("%s ", key_types[i].key_type_display); fflush(stdout); - type = key_type_from_name(key_types[i].key_type); + type = sshkey_type_from_name(key_types[i].key_type); strlcpy(identity_file, key_types[i].path, sizeof(identity_file)); bits = 0; type_bits_valid(type, &bits); - private = key_generate(type, bits); - if (private == NULL) { - fprintf(stderr, "key_generate failed\n"); + if ((r = sshkey_generate(type, bits, &private)) != 0) { + fprintf(stderr, "key_generate failed: %s\n", + ssh_err(r)); first = 0; continue; } - public = key_from_private(private); + if ((r = sshkey_from_private(private, &public)) != 0) + fatal("sshkey_from_private failed: %s", ssh_err(r)); snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); - if (!key_save_private(private, identity_file, "", comment, - use_new_format, new_format_cipher, rounds)) { - printf("Saving the key failed: %s.\n", identity_file); - key_free(private); - key_free(public); + if ((r = sshkey_save_private(private, identity_file, "", + comment, use_new_format, new_format_cipher, rounds)) != 0) { + printf("Saving key \"%s\" failed: %s\n", identity_file, + ssh_err(r)); + sshkey_free(private); + sshkey_free(public); first = 0; continue; } - key_free(private); + sshkey_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { printf("Could not save your public key in %s\n", identity_file); - key_free(public); + sshkey_free(public); first = 0; continue; } @@ -977,20 +1005,20 @@ do_gen_all_hostkeys(struct passwd *pw) if (f == NULL) { printf("fdopen %s failed\n", identity_file); close(fd); - key_free(public); + sshkey_free(public); first = 0; continue; } - if (!key_write(public, f)) { + if (!sshkey_write(public, f)) { fprintf(stderr, "write key failed\n"); fclose(f); - key_free(public); + sshkey_free(public); first = 0; continue; } fprintf(f, " %s\n", comment); fclose(f); - key_free(public); + sshkey_free(public); } if (first != 0) @@ -998,32 +1026,35 @@ do_gen_all_hostkeys(struct passwd *pw) } static void -printhost(FILE *f, const char *name, Key *public, int ca, int revoked, int hash) +printhost(FILE *f, const char *name, struct sshkey *public, + int ca, int revoked, int hash) { if (print_fingerprint) { - enum fp_rep rep; + enum sshkey_fp_rep rep; int fptype; char *fp, *ra; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fingerprint_hash, + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, name, - key_type(public)); + printf("%u %s %s (%s)\n", sshkey_size(public), fp, name, + sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } else { + int r; + if (hash && (name = host_hash(name, NULL, 0)) == NULL) fatal("hash_host failed"); fprintf(f, "%s%s%s ", ca ? CA_MARKER " " : "", revoked ? REVOKE_MARKER " " : "" , name); - if (!key_write(public, f)) - fatal("key_write failed"); + if ((r = sshkey_write(public, f)) != 0) + fatal("key_write failed: %s", ssh_err(r)); fprintf(f, "\n"); } } @@ -1032,11 +1063,11 @@ static void do_known_hosts(struct passwd *pw, const char *name) { FILE *in, *out = stdout; - Key *pub; + struct sshkey *pub; char *cp, *cp2, *kp, *kp2; char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; - int ca, revoked; + int r, ca, revoked; int found_key = 0; if (!have_identity) { @@ -1106,7 +1137,7 @@ do_known_hosts(struct passwd *pw, const char *name) sizeof(REVOKE_MARKER) - 1) == 0 && (cp[sizeof(REVOKE_MARKER) - 1] == ' ' || cp[sizeof(REVOKE_MARKER) - 1] == '\t')) { - revoked = 1; + revoked = 1; cp += sizeof(REVOKE_MARKER); } else revoked = 0; @@ -1124,15 +1155,17 @@ do_known_hosts(struct passwd *pw, const char *name) *kp++ = '\0'; kp2 = kp; - pub = key_new(KEY_RSA1); - if (key_read(pub, &kp) != 1) { + if ((pub = sshkey_new(KEY_RSA1)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(pub, &kp)) != 0) { kp = kp2; - key_free(pub); - pub = key_new(KEY_UNSPEC); - if (key_read(pub, &kp) != 1) { + sshkey_free(pub); + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(pub, &kp)) != 0) { error("line %d invalid key: %.40s...", num, line); - key_free(pub); + sshkey_free(pub); invalid = 1; continue; } @@ -1152,7 +1185,7 @@ do_known_hosts(struct passwd *pw, const char *name) if (!quiet) printf("# Host %s found: " "line %d type %s%s\n", name, - num, key_type(pub), + num, sshkey_type(pub), ca ? " (CA key)" : revoked? " (revoked)" : ""); printhost(out, cp, pub, ca, revoked, 0); @@ -1165,7 +1198,7 @@ do_known_hosts(struct passwd *pw, const char *name) } else { printf("# Host %s found: " "line %d type %s\n", name, - num, key_type(pub)); + num, sshkey_type(pub)); } } } else if (hash_hosts) @@ -1178,7 +1211,7 @@ do_known_hosts(struct passwd *pw, const char *name) if (!quiet) printf("# Host %s found: " "line %d type %s%s\n", name, - num, key_type(pub), + num, sshkey_type(pub), ca ? " (CA key)" : ""); printhost(out, name, pub, ca, revoked, hash_hosts && !(ca || revoked)); @@ -1191,7 +1224,7 @@ do_known_hosts(struct passwd *pw, const char *name) } else { printf("# Host %s found: " "line %d type %s\n", name, - num, key_type(pub)); + num, sshkey_type(pub)); } } } else if (hash_hosts && (ca || revoked)) { @@ -1219,7 +1252,7 @@ do_known_hosts(struct passwd *pw, const char *name) } } } - key_free(pub); + sshkey_free(pub); } fclose(in); @@ -1276,7 +1309,8 @@ do_change_passphrase(struct passwd *pw) char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; - Key *private; + struct sshkey *private; + int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -1285,22 +1319,25 @@ do_change_passphrase(struct passwd *pw) exit(1); } /* Try to load the file with empty passphrase. */ - private = key_load_private(identity_file, "", &comment); - if (private == NULL) { + r = sshkey_load_private(identity_file, "", &private, &comment); + if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", RP_ALLOW_STDIN); - private = key_load_private(identity_file, old_passphrase, - &comment); + r = sshkey_load_private(identity_file, old_passphrase, + &private, &comment); explicit_bzero(old_passphrase, strlen(old_passphrase)); free(old_passphrase); - if (private == NULL) { - printf("Bad passphrase.\n"); - exit(1); - } + if (r != 0) + goto badkey; + } else if (r != 0) { + badkey: + fprintf(stderr, "Failed to load key \"%s\": %s\n", + identity_file, ssh_err(r)); + exit(1); } printf("Key has comment '%s'\n", comment); @@ -1330,19 +1367,20 @@ do_change_passphrase(struct passwd *pw) } /* Save the file using the new passphrase. */ - if (!key_save_private(private, identity_file, passphrase1, comment, - use_new_format, new_format_cipher, rounds)) { - printf("Saving the key failed: %s.\n", identity_file); + if ((r = sshkey_save_private(private, identity_file, passphrase1, + comment, use_new_format, new_format_cipher, rounds)) != 0) { + printf("Saving key \"%s\" failed: %s.\n", + identity_file, ssh_err(r)); explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); - key_free(private); + sshkey_free(private); free(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); - key_free(private); /* Destroys contents */ + sshkey_free(private); /* Destroys contents */ free(comment); printf("Your identification has been saved with the new passphrase.\n"); @@ -1355,9 +1393,10 @@ do_change_passphrase(struct passwd *pw) static int do_print_resource_record(struct passwd *pw, char *fname, char *hname) { - Key *public; + struct sshkey *public; char *comment = NULL; struct stat st; + int r; if (fname == NULL) fatal("%s: no filename", __func__); @@ -1367,18 +1406,15 @@ do_print_resource_record(struct passwd *pw, char *fname, char *hname) perror(fname); exit(1); } - public = key_load_public(fname, &comment); - if (public != NULL) { - export_dns_rr(hname, public, stdout, print_generic); - key_free(public); - free(comment); - return 1; + if ((r = sshkey_load_public(fname, &public, &comment)) != 0) { + printf("Failed to read v2 public key from \"%s\": %s.\n", + fname, ssh_err(r)); + exit(1); } - if (comment) - free(comment); - - printf("failed to read v2 public key from %s.\n", fname); - exit(1); + export_dns_rr(hname, public, stdout, print_generic); + sshkey_free(public); + free(comment); + return 1; } /* @@ -1388,11 +1424,11 @@ static void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment, *passphrase; - Key *private; - Key *public; + struct sshkey *private; + struct sshkey *public; struct stat st; FILE *f; - int fd; + int r, fd; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -1400,8 +1436,14 @@ do_change_comment(struct passwd *pw) perror(identity_file); exit(1); } - private = key_load_private(identity_file, "", &comment); - if (private == NULL) { + if ((r = sshkey_load_private(identity_file, "", + &private, &comment)) == 0) + passphrase = xstrdup(""); + else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { + printf("Cannot load private key \"%s\": %s.\n", + identity_file, ssh_err(r)); + exit(1); + } else { if (identity_passphrase) passphrase = xstrdup(identity_passphrase); else if (identity_new_passphrase) @@ -1410,19 +1452,18 @@ do_change_comment(struct passwd *pw) passphrase = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); /* Try to load using the passphrase. */ - private = key_load_private(identity_file, passphrase, &comment); - if (private == NULL) { + if ((r = sshkey_load_private(identity_file, passphrase, + &private, &comment)) != 0) { explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); - printf("Bad passphrase.\n"); + printf("Cannot load private key \"%s\": %s.\n", + identity_file, ssh_err(r)); exit(1); } - } else { - passphrase = xstrdup(""); } if (private->type != KEY_RSA1) { fprintf(stderr, "Comments are only supported for RSA1 keys.\n"); - key_free(private); + sshkey_free(private); exit(1); } printf("Key now has comment '%s'\n", comment); @@ -1434,26 +1475,28 @@ do_change_comment(struct passwd *pw) fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { explicit_bzero(passphrase, strlen(passphrase)); - key_free(private); + sshkey_free(private); exit(1); } new_comment[strcspn(new_comment, "\n")] = '\0'; } /* Save the file using the new passphrase. */ - if (!key_save_private(private, identity_file, passphrase, new_comment, - use_new_format, new_format_cipher, rounds)) { - printf("Saving the key failed: %s.\n", identity_file); + if ((r = sshkey_save_private(private, identity_file, passphrase, + new_comment, use_new_format, new_format_cipher, rounds)) != 0) { + printf("Saving key \"%s\" failed: %s\n", + identity_file, ssh_err(r)); explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); - key_free(private); + sshkey_free(private); free(comment); exit(1); } explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); - public = key_from_private(private); - key_free(private); + if ((r = sshkey_from_private(private, &public)) != 0) + fatal("key_from_private failed: %s", ssh_err(r)); + sshkey_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); @@ -1466,9 +1509,9 @@ do_change_comment(struct passwd *pw) printf("fdopen %s failed\n", identity_file); exit(1); } - if (!key_write(public, f)) - fprintf(stderr, "write key failed\n"); - key_free(public); + if ((r = sshkey_write(public, f)) != 0) + fprintf(stderr, "write key failed: %s\n", ssh_err(r)); + sshkey_free(public); fprintf(f, " %s\n", new_comment); fclose(f); @@ -1517,34 +1560,39 @@ fmt_validity(u_int64_t valid_from, u_int64_t valid_to) } static void -add_flag_option(Buffer *c, const char *name) +add_flag_option(struct sshbuf *c, const char *name) { + int r; + debug3("%s: %s", __func__, name); - buffer_put_cstring(c, name); - buffer_put_string(c, NULL, 0); + if ((r = sshbuf_put_cstring(c, name)) != 0 || + (r = sshbuf_put_string(c, NULL, 0)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } static void -add_string_option(Buffer *c, const char *name, const char *value) +add_string_option(struct sshbuf *c, const char *name, const char *value) { - Buffer b; + struct sshbuf *b; + int r; debug3("%s: %s=%s", __func__, name, value); - buffer_init(&b); - buffer_put_cstring(&b, value); - - buffer_put_cstring(c, name); - buffer_put_string(c, buffer_ptr(&b), buffer_len(&b)); - - buffer_free(&b); + if ((b = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshbuf_put_cstring(b, value)) != 0 || + (r = sshbuf_put_cstring(c, name)) != 0 || + (r = sshbuf_put_stringb(c, b)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + sshbuf_free(b); } #define OPTIONS_CRITICAL 1 #define OPTIONS_EXTENSIONS 2 static void -prepare_options_buf(Buffer *c, int which) +prepare_options_buf(struct sshbuf *c, int which) { - buffer_clear(c); + sshbuf_reset(c); if ((which & OPTIONS_CRITICAL) != 0 && certflags_command != NULL) add_string_option(c, "force-command", certflags_command); @@ -1568,29 +1616,30 @@ prepare_options_buf(Buffer *c, int which) add_string_option(c, "source-address", certflags_src_addr); } -static Key * +static struct sshkey * load_pkcs11_key(char *path) { #ifdef ENABLE_PKCS11 - Key **keys = NULL, *public, *private = NULL; - int i, nkeys; + struct sshkey **keys = NULL, *public, *private = NULL; + int r, i, nkeys; - if ((public = key_load_public(path, NULL)) == NULL) - fatal("Couldn't load CA public key \"%s\"", path); + if ((r = sshkey_load_public(path, &public, NULL)) != 0) + fatal("Couldn't load CA public key \"%s\": %s", + path, ssh_err(r)); nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys); debug3("%s: %d keys", __func__, nkeys); if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { - if (key_equal_public(public, keys[i])) { + if (sshkey_equal_public(public, keys[i])) { private = keys[i]; continue; } - key_free(keys[i]); + sshkey_free(keys[i]); } free(keys); - key_free(public); + sshkey_free(public); return private; #else fatal("no pkcs11 support"); @@ -1600,15 +1649,15 @@ load_pkcs11_key(char *path) static void do_ca_sign(struct passwd *pw, int argc, char **argv) { - int i, fd; + int r, i, fd; u_int n; - Key *ca, *public; + struct sshkey *ca, *public; char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; int v00 = 0; /* legacy keys */ if (key_type_name != NULL) { - switch (key_type_from_name(key_type_name)) { + switch (sshkey_type_from_name(key_type_name)) { case KEY_RSA_CERT_V00: case KEY_DSA_CERT_V00: v00 = 1; @@ -1633,8 +1682,8 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) if (pkcs11provider != NULL) { if ((ca = load_pkcs11_key(tmp)) == NULL) fatal("No PKCS#11 key matching %s found", ca_key_path); - } else if ((ca = load_identity(tmp)) == NULL) - fatal("Couldn't load CA key \"%s\"", tmp); + } else + ca = load_identity(tmp); free(tmp); for (i = 0; i < argc; i++) { @@ -1652,16 +1701,18 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) } tmp = tilde_expand_filename(argv[i], pw->pw_uid); - if ((public = key_load_public(tmp, &comment)) == NULL) - fatal("%s: unable to open \"%s\"", __func__, tmp); + if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) + fatal("%s: unable to open \"%s\": %s", + __func__, tmp, ssh_err(r)); if (public->type != KEY_RSA && public->type != KEY_DSA && public->type != KEY_ECDSA && public->type != KEY_ED25519) fatal("%s: key \"%s\" type %s cannot be certified", - __func__, tmp, key_type(public)); + __func__, tmp, sshkey_type(public)); /* Prepare certificate to sign */ - if (key_to_certified(public, v00) != 0) - fatal("Could not upgrade key %s to certificate", tmp); + if ((r = sshkey_to_certified(public, v00)) != 0) + fatal("Could not upgrade key %s to certificate: %s", + tmp, ssh_err(r)); public->cert->type = cert_key_type; public->cert->serial = (u_int64_t)cert_serial; public->cert->key_id = xstrdup(cert_key_id); @@ -1678,9 +1729,11 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) prepare_options_buf(public->cert->extensions, OPTIONS_EXTENSIONS); } - public->cert->signature_key = key_from_private(ca); + if ((r = sshkey_from_private(ca, + &public->cert->signature_key)) != 0) + fatal("key_from_private (ca key): %s", ssh_err(r)); - if (key_certify(public, ca) != 0) + if (sshkey_certify(public, ca) != 0) fatal("Couldn't not certify key %s", tmp); if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) @@ -1693,14 +1746,15 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) strerror(errno)); if ((f = fdopen(fd, "w")) == NULL) fatal("%s: fdopen: %s", __func__, strerror(errno)); - if (!key_write(public, f)) - fatal("Could not write certified key to %s", out); + if ((r = sshkey_write(public, f)) != 0) + fatal("Could not write certified key to %s: %s", + out, ssh_err(r)); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { logit("Signed %s key %s: id \"%s\" serial %llu%s%s " - "valid %s", key_cert_type(public), + "valid %s", sshkey_cert_type(public), out, public->cert->key_id, (unsigned long long)public->cert->serial, cert_principals != NULL ? " for " : "", @@ -1708,7 +1762,7 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) fmt_validity(cert_valid_from, cert_valid_to)); } - key_free(public); + sshkey_free(public); free(out); } #ifdef ENABLE_PKCS11 @@ -1859,21 +1913,20 @@ add_cert_option(char *opt) } static void -show_options(const Buffer *optbuf, int v00, int in_critical) +show_options(struct sshbuf *optbuf, int v00, int in_critical) { char *name, *arg; - const u_char *data; - u_int dlen; - Buffer options, option; - - buffer_init(&options); - buffer_append(&options, buffer_ptr(optbuf), buffer_len(optbuf)); - - 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); + struct sshbuf *options, *option = NULL; + int r; + + if ((options = sshbuf_fromb(optbuf)) == NULL) + fatal("%s: sshbuf_fromb failed", __func__); + while (sshbuf_len(options) != 0) { + sshbuf_free(option); + option = NULL; + if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 || + (r = sshbuf_froms(options, &option)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); printf(" %s", name); if ((v00 || !in_critical) && (strcmp(name, "permit-X11-forwarding") == 0 || @@ -1885,50 +1938,54 @@ show_options(const Buffer *optbuf, int v00, int in_critical) else if ((v00 || in_critical) && (strcmp(name, "force-command") == 0 || strcmp(name, "source-address") == 0)) { - arg = buffer_get_cstring(&option, NULL); + if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); printf(" %s\n", arg); free(arg); } else { - printf(" UNKNOWN OPTION (len %u)\n", - buffer_len(&option)); - buffer_clear(&option); + printf(" UNKNOWN OPTION (len %zu)\n", + sshbuf_len(option)); + sshbuf_reset(option); } free(name); - if (buffer_len(&option) != 0) + if (sshbuf_len(option) != 0) fatal("Option corrupt: extra data at end"); } - buffer_free(&option); - buffer_free(&options); + sshbuf_free(option); + sshbuf_free(options); } static void do_show_cert(struct passwd *pw) { - Key *key; + struct sshkey *key; struct stat st; char *key_fp, *ca_fp; u_int i, v00; + int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); - if ((key = key_load_public(identity_file, NULL)) == NULL) - fatal("%s is not a public key", identity_file); - if (!key_is_cert(key)) + if ((r = sshkey_load_public(identity_file, &key, NULL)) != 0) + fatal("Cannot load public key \"%s\": %s", + identity_file, ssh_err(r)); + if (!sshkey_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, fingerprint_hash, SSH_FP_DEFAULT); - ca_fp = key_fingerprint(key->cert->signature_key, + key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT); + ca_fp = sshkey_fingerprint(key->cert->signature_key, fingerprint_hash, SSH_FP_DEFAULT); printf("%s:\n", identity_file); - 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(" Type: %s %s certificate\n", sshkey_ssh_name(key), + sshkey_cert_type(key)); + printf(" Public key: %s %s\n", sshkey_type(key), key_fp); printf(" Signing CA: %s %s\n", - key_type(key->cert->signature_key), ca_fp); + sshkey_type(key->cert->signature_key), ca_fp); printf(" Key ID: \"%s\"\n", key->cert->key_id); if (!v00) { printf(" Serial: %llu\n", @@ -1946,7 +2003,7 @@ do_show_cert(struct passwd *pw) printf("\n"); } printf(" Critical Options: "); - if (buffer_len(key->cert->critical) == 0) + if (sshbuf_len(key->cert->critical) == 0) printf("(none)\n"); else { printf("\n"); @@ -1954,7 +2011,7 @@ do_show_cert(struct passwd *pw) } if (!v00) { printf(" Extensions: "); - if (buffer_len(key->cert->extensions) == 0) + if (sshbuf_len(key->cert->extensions) == 0) printf("(none)\n"); else { printf("\n"); @@ -1967,27 +2024,28 @@ do_show_cert(struct passwd *pw) static void load_krl(const char *path, struct ssh_krl **krlp) { - Buffer krlbuf; - int fd; + struct sshbuf *krlbuf; + int r, fd; - buffer_init(&krlbuf); + if ((krlbuf = sshbuf_new()) == NULL) + fatal("sshbuf_new failed"); if ((fd = open(path, O_RDONLY)) == -1) fatal("open %s: %s", path, strerror(errno)); - if (!key_load_file(fd, path, &krlbuf)) - fatal("Unable to load KRL"); + if ((r = sshkey_load_file(fd, krlbuf)) != 0) + fatal("Unable to load KRL: %s", ssh_err(r)); close(fd); /* XXX check sigs */ - if (ssh_krl_from_blob(&krlbuf, krlp, NULL, 0) != 0 || + if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 || *krlp == NULL) - fatal("Invalid KRL file"); - buffer_free(&krlbuf); + fatal("Invalid KRL file: %s", ssh_err(r)); + sshbuf_free(krlbuf); } static void -update_krl_from_file(struct passwd *pw, const char *file, const Key *ca, - struct ssh_krl *krl) +update_krl_from_file(struct passwd *pw, const char *file, + const struct sshkey *ca, struct ssh_krl *krl) { - Key *key = NULL; + struct sshkey *key = NULL; u_long lnum = 0; char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES]; unsigned long long serial, serial2; @@ -2086,10 +2144,11 @@ update_krl_from_file(struct passwd *pw, const char *file, const Key *ca, * Parsing will fail if it isn't. */ } - if ((key = key_new(KEY_UNSPEC)) == NULL) + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) fatal("key_new"); - if (key_read(key, &cp) != 1) - fatal("%s:%lu: invalid key", path, lnum); + if ((r = sshkey_read(key, &cp)) != 0) + fatal("%s:%lu: invalid key: %s", + path, lnum, ssh_err(r)); if (was_explicit_key) r = ssh_krl_revoke_key_explicit(krl, key); else if (was_sha1) @@ -2097,8 +2156,9 @@ update_krl_from_file(struct passwd *pw, const char *file, const Key *ca, else r = ssh_krl_revoke_key(krl, key); if (r != 0) - fatal("%s: revoke key failed", __func__); - key_free(key); + fatal("%s: revoke key failed: %s", + __func__, ssh_err(r)); + sshkey_free(key); } } if (strcmp(path, "-") != 0) @@ -2111,10 +2171,10 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) { struct ssh_krl *krl; struct stat sb; - Key *ca = NULL; - int fd, i; + struct sshkey *ca = NULL; + int fd, i, r; char *tmp; - Buffer kbuf; + struct sshbuf *kbuf; if (*identity_file == '\0') fatal("KRL generation requires an output file"); @@ -2127,8 +2187,9 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) } if (ca_key_path != NULL) { tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); - if ((ca = key_load_public(tmp, NULL)) == NULL) - fatal("Cannot load CA public key %s", tmp); + if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) + fatal("Cannot load CA public key %s: %s", + tmp, ssh_err(r)); free(tmp); } @@ -2145,19 +2206,20 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) for (i = 0; i < argc; i++) update_krl_from_file(pw, argv[i], ca, krl); - buffer_init(&kbuf); - if (ssh_krl_to_blob(krl, &kbuf, NULL, 0) != 0) + if ((kbuf = sshbuf_new()) == NULL) + fatal("sshbuf_new failed"); + if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0) fatal("Couldn't generate KRL"); if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) fatal("open %s: %s", identity_file, strerror(errno)); - if (atomicio(vwrite, fd, buffer_ptr(&kbuf), buffer_len(&kbuf)) != - buffer_len(&kbuf)) + if (atomicio(vwrite, fd, (void *)sshbuf_ptr(kbuf), sshbuf_len(kbuf)) != + sshbuf_len(kbuf)) fatal("write %s: %s", identity_file, strerror(errno)); close(fd); - buffer_free(&kbuf); + sshbuf_free(kbuf); ssh_krl_free(krl); if (ca != NULL) - key_free(ca); + sshkey_free(ca); } static void @@ -2166,21 +2228,22 @@ do_check_krl(struct passwd *pw, int argc, char **argv) int i, r, ret = 0; char *comment; struct ssh_krl *krl; - Key *k; + struct sshkey *k; if (*identity_file == '\0') fatal("KRL checking requires an input file"); load_krl(identity_file, &krl); for (i = 0; i < argc; i++) { - if ((k = key_load_public(argv[i], &comment)) == NULL) - fatal("Cannot load public key %s", argv[i]); + if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0) + fatal("Cannot load public key %s: %s", + argv[i], ssh_err(r)); r = ssh_krl_check_key(krl, k); printf("%s%s%s%s: %s\n", argv[i], *comment ? " (" : "", comment, *comment ? ")" : "", r == 0 ? "ok" : "REVOKED"); if (r != 0) ret = 1; - key_free(k); + sshkey_free(k); free(comment); } ssh_krl_free(krl); @@ -2230,11 +2293,11 @@ main(int argc, char **argv) { char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; char *checkpoint = NULL; - char out_file[MAXPATHLEN], *ep, *rr_hostname = NULL; - Key *private, *public; + char out_file[MAXPATHLEN], *rr_hostname = NULL, *ep; + struct sshkey *private, *public; struct passwd *pw; struct stat st; - int opt, type, fd; + int r, opt, type, fd; u_int32_t memory = 0, generator_wanted = 0; int do_gen_candidates = 0, do_screen_candidates = 0; int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; @@ -2607,17 +2670,20 @@ main(int argc, char **argv) if (key_type_name == NULL) key_type_name = "rsa"; - type = key_type_from_name(key_type_name); + type = sshkey_type_from_name(key_type_name); type_bits_valid(type, &bits); if (!quiet) - printf("Generating public/private %s key pair.\n", key_type_name); - private = key_generate(type, bits); - if (private == NULL) { + printf("Generating public/private %s key pair.\n", + key_type_name); + if ((r = sshkey_generate(type, bits, &private)) != 0) { fprintf(stderr, "key_generate failed\n"); exit(1); } - public = key_from_private(private); + if ((r = sshkey_from_private(private, &public)) != 0) { + fprintf(stderr, "key_from_private failed: %s\n", ssh_err(r)); + exit(1); + } if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); @@ -2685,9 +2751,10 @@ passphrase_again: } /* Save the key with the given passphrase and comment. */ - if (!key_save_private(private, identity_file, passphrase1, comment, - use_new_format, new_format_cipher, rounds)) { - printf("Saving the key failed: %s.\n", identity_file); + if ((r = sshkey_save_private(private, identity_file, passphrase1, + comment, use_new_format, new_format_cipher, rounds)) != 0) { + printf("Saving key \"%s\" failed: %s\n", + identity_file, ssh_err(r)); explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); exit(1); @@ -2697,7 +2764,7 @@ passphrase_again: free(passphrase1); /* Clear the private key and the random number generator. */ - key_free(private); + sshkey_free(private); if (!quiet) printf("Your identification has been saved in %s.\n", identity_file); @@ -2713,15 +2780,15 @@ passphrase_again: printf("fdopen %s failed\n", identity_file); exit(1); } - if (!key_write(public, f)) - fprintf(stderr, "write key failed\n"); + if ((r = sshkey_write(public, f)) != 0) + fprintf(stderr, "write key failed: %s\n", ssh_err(r)); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { - char *fp = key_fingerprint(public, fingerprint_hash, + char *fp = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_DEFAULT); - char *ra = key_fingerprint(public, fingerprint_hash, + char *ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); printf("Your public key has been saved in %s.\n", identity_file); @@ -2733,6 +2800,6 @@ passphrase_again: free(fp); } - key_free(public); + sshkey_free(public); exit(0); } diff --git a/ssh-keysign.c b/ssh-keysign.c index 821939997..8af13fa89 100644 --- a/ssh-keysign.c +++ b/ssh-keysign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keysign.c,v 1.45 2015/01/08 10:14:08 djm Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.46 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -43,11 +43,11 @@ #include "xmalloc.h" #include "log.h" -#include "key.h" +#include "sshkey.h" #include "ssh.h" #include "ssh2.h" #include "misc.h" -#include "buffer.h" +#include "sshbuf.h" #include "authfile.h" #include "msg.h" #include "canohost.h" @@ -63,64 +63,73 @@ uid_t original_real_uid; extern char *__progname; static int -valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, - u_int datalen) +valid_request(struct passwd *pw, char *host, struct sshkey **ret, + u_char *data, size_t datalen) { - Buffer b; - Key *key = NULL; - u_char *pkblob; - u_int blen, len; - char *pkalg, *p; - int pktype, fail; + struct sshbuf *b; + struct sshkey *key = NULL; + u_char type, *pkblob; + char *p; + size_t blen, len; + char *pkalg, *luser; + int r, pktype, fail; if (ret != NULL) *ret = NULL; fail = 0; - buffer_init(&b); - buffer_append(&b, data, datalen); + if ((b = sshbuf_from(data, datalen)) == NULL) + fatal("%s: sshbuf_from failed", __func__); /* session id, currently limited to SHA1 (20 bytes) or SHA256 (32) */ - p = buffer_get_string(&b, &len); + if ((r = sshbuf_get_string(b, NULL, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (len != 20 && len != 32) fail++; - free(p); - if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) + if ((r = sshbuf_get_u8(b, &type)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (type != SSH2_MSG_USERAUTH_REQUEST) fail++; /* server user */ - buffer_skip_string(&b); + if ((r = sshbuf_skip_string(b)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); /* service */ - p = buffer_get_string(&b, NULL); + if ((r = sshbuf_get_cstring(b, &p, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (strcmp("ssh-connection", p) != 0) fail++; free(p); /* method */ - p = buffer_get_string(&b, NULL); + if ((r = sshbuf_get_cstring(b, &p, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (strcmp("hostbased", p) != 0) fail++; free(p); /* pubkey */ - pkalg = buffer_get_string(&b, NULL); - pkblob = buffer_get_string(&b, &blen); + if ((r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0 || + (r = sshbuf_get_string(b, &pkblob, &blen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); - pktype = key_type_from_name(pkalg); + pktype = sshkey_type_from_name(pkalg); if (pktype == KEY_UNSPEC) fail++; - else if ((key = key_from_blob(pkblob, blen)) == NULL) + else if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error("%s: bad key blob: %s", __func__, ssh_err(r)); fail++; - else if (key->type != pktype) + } else if (key->type != pktype) fail++; free(pkalg); free(pkblob); /* client host name, handle trailing dot */ - p = buffer_get_string(&b, &len); - debug2("valid_request: check expect chost %s got %s", host, p); + if ((r = sshbuf_get_cstring(b, &p, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + debug2("%s: check expect chost %s got %s", __func__, host, p); if (strlen(host) != len - 1) fail++; else if (p[len - 1] != '.') @@ -130,21 +139,22 @@ valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, free(p); /* local user */ - p = buffer_get_string(&b, NULL); + if ((r = sshbuf_get_cstring(b, &luser, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); - if (strcmp(pw->pw_name, p) != 0) + if (strcmp(pw->pw_name, luser) != 0) fail++; - free(p); + free(luser); /* end of message */ - if (buffer_len(&b) != 0) + if (sshbuf_len(b) != 0) fail++; - buffer_free(&b); + sshbuf_free(b); - debug3("valid_request: fail %d", fail); + debug3("%s: fail %d", __func__, fail); if (fail && key != NULL) - key_free(key); + sshkey_free(key); else *ret = key; @@ -154,15 +164,15 @@ valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, int main(int argc, char **argv) { - Buffer b; + struct sshbuf *b; Options options; #define NUM_KEYTYPES 4 - Key *keys[NUM_KEYTYPES], *key = NULL; + struct sshkey *keys[NUM_KEYTYPES], *key = NULL; struct passwd *pw; int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; - u_char *signature, *data; + u_char *signature, *data, rver; char *host, *fp; - u_int slen, dlen; + size_t slen, dlen; #ifdef WITH_OPENSSL u_int32_t rnd[256]; #endif @@ -232,18 +242,23 @@ main(int argc, char **argv) if (!found) fatal("no hostkey found"); - buffer_init(&b); - if (ssh_msg_recv(STDIN_FILENO, &b) < 0) + if ((b = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if (ssh_msg_recv(STDIN_FILENO, b) < 0) fatal("ssh_msg_recv failed"); - if (buffer_get_char(&b) != version) - fatal("bad version"); - fd = buffer_get_int(&b); - if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) + if ((r = sshbuf_get_u8(b, &rver)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (rver != version) + fatal("bad version: received %d, expected %d", rver, version); + if ((r = sshbuf_get_u32(b, (u_int *)&fd)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (fd < 0 || fd == STDIN_FILENO || fd == STDOUT_FILENO) fatal("bad fd"); if ((host = get_local_name(fd)) == NULL) fatal("cannot get local name for fd"); - data = buffer_get_string(&b, &dlen); + if ((r = sshbuf_get_string(b, &data, &dlen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (valid_request(pw, host, &key, data, dlen) < 0) fatal("not a valid request"); free(host); @@ -251,26 +266,27 @@ main(int argc, char **argv) found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { if (keys[i] != NULL && - key_equal_public(key, keys[i])) { + sshkey_equal_public(key, keys[i])) { found = 1; break; } } if (!found) { - fp = key_fingerprint(key, options.fingerprint_hash, + fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); fatal("no matching hostkey found for key %s %s", - key_type(key), fp); + sshkey_type(key), fp ? fp : ""); } - if (key_sign(keys[i], &signature, &slen, data, dlen) != 0) - fatal("key_sign failed"); + if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, 0)) != 0) + fatal("sshkey_sign failed: %s", ssh_err(r)); free(data); /* send reply */ - buffer_clear(&b); - buffer_put_string(&b, signature, slen); - if (ssh_msg_send(STDOUT_FILENO, version, &b) == -1) + sshbuf_reset(b); + if ((r = sshbuf_put_string(b, signature, slen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (ssh_msg_send(STDOUT_FILENO, version, b) == -1) fatal("ssh_msg_send failed"); return (0); diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c index c96be3bd2..e91df8bb1 100644 --- a/ssh-pkcs11.c +++ b/ssh-pkcs11.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11.c,v 1.14 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: ssh-pkcs11.c,v 1.15 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * @@ -38,7 +38,7 @@ #include "log.h" #include "misc.h" -#include "key.h" +#include "sshkey.h" #include "ssh-pkcs11.h" #include "xmalloc.h" @@ -385,12 +385,12 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) * keysp points to an (possibly empty) array with *nkeys keys. */ static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, - CK_ATTRIBUTE [], CK_ATTRIBUTE [3], Key ***, int *) + CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); static int pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, - Key ***keysp, int *nkeys) + struct sshkey ***keysp, int *nkeys) { CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; @@ -422,12 +422,12 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, } static int -pkcs11_key_included(Key ***keysp, int *nkeys, Key *key) +pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) { int i; for (i = 0; i < *nkeys; i++) - if (key_equal(key, (*keysp)[i])) + if (sshkey_equal(key, (*keysp)[i])) return (1); return (0); } @@ -435,9 +435,9 @@ pkcs11_key_included(Key ***keysp, int *nkeys, Key *key) static int pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], - Key ***keysp, int *nkeys) + struct sshkey ***keysp, int *nkeys) { - Key *key; + struct sshkey *key; RSA *rsa; X509 *x509; EVP_PKEY *evp; @@ -517,16 +517,16 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, } if (rsa && rsa->n && rsa->e && pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { - key = key_new(KEY_UNSPEC); + key = sshkey_new(KEY_UNSPEC); key->rsa = rsa; key->type = KEY_RSA; key->flags |= SSHKEY_FLAG_EXT; if (pkcs11_key_included(keysp, nkeys, key)) { - key_free(key); + sshkey_free(key); } else { /* expand key array and add key */ *keysp = xrealloc(*keysp, *nkeys + 1, - sizeof(Key *)); + sizeof(struct sshkey *)); (*keysp)[*nkeys] = key; *nkeys = *nkeys + 1; debug("have %d keys", *nkeys); @@ -544,7 +544,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, /* register a new provider, fails if provider already exists */ int -pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) +pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) { int nkeys, need_finalize = 0; struct pkcs11_provider *p = NULL; diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h index 4d2efda13..0ced74f29 100644 --- a/ssh-pkcs11.h +++ b/ssh-pkcs11.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11.h,v 1.3 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: ssh-pkcs11.h,v 1.4 2015/01/15 09:40:00 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * @@ -16,7 +16,7 @@ */ int pkcs11_init(int); void pkcs11_terminate(void); -int pkcs11_add_provider(char *, char *, Key ***); +int pkcs11_add_provider(char *, char *, struct sshkey ***); int pkcs11_del_provider(char *); #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) -- cgit v1.2.3 From c29811cc480a260e42fd88849fc86a80c1e91038 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sun, 18 Jan 2015 21:40:23 +0000 Subject: upstream commit introduce hostkeys_foreach() to allow iteration over a known_hosts file or controlled subset thereof. This will allow us to pull out some ugly and duplicated code, and will be used to implement hostkey rotation later. feedback and ok markus --- hostfile.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- hostfile.h | 43 +++++++++++++++++- 2 files changed, 187 insertions(+), 3 deletions(-) (limited to 'hostfile.c') diff --git a/hostfile.c b/hostfile.c index 40dbbd478..5f0366310 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.59 2015/01/15 09:40:00 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.60 2015/01/18 21:40:23 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -42,6 +42,7 @@ #include +#include #include #include #include @@ -64,6 +65,8 @@ struct hostkeys { u_int num_entries; }; +/* XXX hmac is too easy to dictionary attack; use bcrypt? */ + static int extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len) { @@ -496,7 +499,147 @@ add_host_to_hostfile(const char *filename, const char *host, __func__, filename, ssh_err(r)); } else success = 1; - fputs("\n", f); + fputc('\n', f); fclose(f); return success; } + +static int +match_maybe_hashed(const char *host, const char *names, int *was_hashed) +{ + int hashed = *names == HASH_DELIM; + const char *hashed_host; + size_t nlen = strlen(names); + + if (was_hashed != NULL) + *was_hashed = hashed; + if (hashed) { + if ((hashed_host = host_hash(host, names, nlen)) == NULL) + return -1; + return nlen == strlen(hashed_host) && + strncmp(hashed_host, names, nlen) == 0; + } + return match_hostname(host, names, nlen) == 1; +} + +int +hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, + const char *host, u_int options) +{ + FILE *f; + char line[8192], oline[8192]; + u_long linenum = 0; + char *cp, *cp2; + u_int kbits; + int s, r = 0; + struct hostkey_foreach_line lineinfo; + + memset(&lineinfo, 0, sizeof(lineinfo)); + if (host == NULL && (options & HKF_WANT_MATCH_HOST) != 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((f = fopen(path, "r")) == NULL) + return SSH_ERR_SYSTEM_ERROR; + + debug3("%s: reading file \"%s\"", __func__, path); + while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { + line[strcspn(line, "\n")] = '\0'; + strlcpy(oline, line, sizeof(oline)); + + sshkey_free(lineinfo.key); + memset(&lineinfo, 0, sizeof(lineinfo)); + lineinfo.path = path; + lineinfo.linenum = linenum; + lineinfo.line = oline; + lineinfo.status = HKF_STATUS_OK; + + /* Skip any leading whitespace, comments and empty lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') { + if ((options & HKF_WANT_MATCH_HOST) == 0) { + lineinfo.status = HKF_STATUS_COMMENT; + if ((r = callback(&lineinfo, ctx)) != 0) + break; + } + continue; + } + + if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) { + verbose("%s: invalid marker at %s:%lu", + __func__, path, linenum); + if ((options & HKF_WANT_MATCH_HOST) == 0) + goto bad; + continue; + } + + /* Find the end of the host name portion. */ + for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) + ; + lineinfo.hosts = cp; + *cp2++ = '\0'; + + /* Check if the host name matches. */ + if (host != NULL) { + s = match_maybe_hashed(host, lineinfo.hosts, + &lineinfo.was_hashed); + if (s == 1) + lineinfo.status = HKF_STATUS_HOST_MATCHED; + else if ((options & HKF_WANT_MATCH_HOST) != 0) + continue; + else if (s == -1) { + debug2("%s: %s:%ld: bad host hash \"%.32s\"", + __func__, path, linenum, lineinfo.hosts); + goto bad; + } + } + + /* Got a match. Skip host name and any following whitespace */ + for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) + ; + if (*cp2 == '\0' || *cp2 == '#') { + debug2("%s:%ld: truncated before key", path, linenum); + goto bad; + } + lineinfo.rawkey = cp = cp2; + + if ((options & HKF_WANT_PARSE_KEY) != 0) { + /* + * Extract the key from the line. This will skip + * any leading whitespace. Ignore badly formatted + * lines. + */ + if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) { + error("%s: sshkey_new failed", __func__); + return SSH_ERR_ALLOC_FAIL; + } + if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) { +#ifdef WITH_SSH1 + sshkey_free(lineinfo.key); + lineinfo.key = sshkey_new(KEY_RSA1); + if (lineinfo.key == NULL) { + error("%s: sshkey_new fail", __func__); + return SSH_ERR_ALLOC_FAIL; + } + if (!hostfile_read_key(&cp, &kbits, + lineinfo.key)) + goto bad; +#else + goto bad; +#endif + } + if (!hostfile_check_key(kbits, lineinfo.key, host, + path, linenum)) { + bad: + lineinfo.status = HKF_STATUS_INVALID; + if ((r = callback(&lineinfo, ctx)) != 0) + break; + continue; + } + } + if ((r = callback(&lineinfo, ctx)) != 0) + break; + } + sshkey_free(lineinfo.key); + fclose(f); + return r; +} diff --git a/hostfile.h b/hostfile.h index d90973f42..24c3813aa 100644 --- a/hostfile.h +++ b/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.21 2015/01/15 09:40:00 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.22 2015/01/18 21:40:24 djm Exp $ */ /* * Author: Tatu Ylonen @@ -52,4 +52,45 @@ int add_host_to_hostfile(const char *, const char *, char *host_hash(const char *, const char *, u_int); +/* + * Iterate through a hostkeys file, optionally parsing keys and matching + * hostnames. Allows access to the raw keyfile lines to allow + * streaming edits to the file to take place. + */ +#define HKF_WANT_MATCH_HOST (1) /* return only matching hosts */ +#define HKF_WANT_PARSE_KEY (1<<1) /* need key parsed */ + +#define HKF_STATUS_OK 1 /* Line parsed, didn't match host */ +#define HKF_STATUS_INVALID 2 /* line had parse error */ +#define HKF_STATUS_COMMENT 3 /* valid line contained no key */ +#define HKF_STATUS_HOST_MATCHED 4 /* hostname matched */ + +/* + * The callback function receives this as an argument for each matching + * hostkey line. The callback may "steal" the 'key' field by setting it to NULL. + * If a parse error occurred, then "hosts" and subsequent options may be NULL. + */ +struct hostkey_foreach_line { + const char *path; /* Path of file */ + u_long linenum; /* Line number */ + int status; /* One of HKF_STATUS_* */ + char *line; /* Entire key line; mutable by callback */ + int marker; /* CA/revocation markers; indicated by MRK_* value */ + const char *hosts; /* Raw hosts text, may be hashed or list multiple */ + int was_hashed; /* Non-zero if hostname was hashed */ + const char *rawkey; /* Text of key and any comment following it */ + struct sshkey *key; /* Key, if parsed ok and HKF_WANT_MATCH_HOST set */ + const char *comment; /* Any comment following the key */ +}; + +/* + * Callback fires for each line (or matching line if a HKF_WANT_* option + * is set). The foreach loop will terminate if the callback returns a non- + * zero exit status. + */ +typedef int hostkeys_foreach_fn(struct hostkey_foreach_line *l, void *ctx); + +int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, + const char *host, u_int options); + #endif -- cgit v1.2.3 From ec3d065df3a9557ea96b02d061fd821a18c1a0b9 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sun, 18 Jan 2015 21:48:09 +0000 Subject: upstream commit convert load_hostkeys() (hostkey ordering and known_host matching) to use the new hostkey_foreach() iterator; ok markus --- hostfile.c | 141 +++++++++++++++++++++++-------------------------------------- 1 file changed, 52 insertions(+), 89 deletions(-) (limited to 'hostfile.c') diff --git a/hostfile.c b/hostfile.c index 5f0366310..ccb2af920 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.60 2015/01/18 21:40:23 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.61 2015/01/18 21:48:09 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -244,100 +244,64 @@ init_hostkeys(void) return ret; } -void -load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) -{ - FILE *f; - char line[8192]; - u_long linenum = 0, num_loaded = 0; - char *cp, *cp2, *hashed_host; - HostkeyMarker marker; - struct sshkey *key; - u_int kbits; - - if ((f = fopen(path, "r")) == NULL) - return; - debug3("%s: loading entries for host \"%.100s\" from file \"%s\"", - __func__, host, path); - while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { - cp = line; +struct load_callback_ctx { + const char *host; + u_long num_loaded; + struct hostkeys *hostkeys; +}; - /* Skip any leading whitespace, comments and empty lines. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '#' || *cp == '\n') - continue; +static int +record_hostkey(struct hostkey_foreach_line *l, void *_ctx) +{ + struct load_callback_ctx *ctx = (struct load_callback_ctx *)_ctx; + struct hostkeys *hostkeys = ctx->hostkeys; + struct hostkey_entry *tmp; - if ((marker = check_markers(&cp)) == MRK_ERROR) { - verbose("%s: invalid marker at %s:%lu", - __func__, path, linenum); - continue; - } + if (l->status == HKF_STATUS_INVALID) { + error("%s:%ld: parse error in hostkeys file", + l->path, l->linenum); + return 0; + } - /* Find the end of the host name portion. */ - for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) - ; + debug3("%s: found %skey type %s in file %s:%lu", __func__, + l->marker == MRK_NONE ? "" : + (l->marker == MRK_CA ? "ca " : "revoked "), + sshkey_type(l->key), l->path, l->linenum); + if ((tmp = reallocarray(hostkeys->entries, + hostkeys->num_entries + 1, sizeof(*hostkeys->entries))) == NULL) + return SSH_ERR_ALLOC_FAIL; + hostkeys->entries = tmp; + hostkeys->entries[hostkeys->num_entries].host = xstrdup(ctx->host); + hostkeys->entries[hostkeys->num_entries].file = xstrdup(l->path); + hostkeys->entries[hostkeys->num_entries].line = l->linenum; + hostkeys->entries[hostkeys->num_entries].key = l->key; + l->key = NULL; /* steal it */ + hostkeys->entries[hostkeys->num_entries].marker = l->marker; + hostkeys->num_entries++; + ctx->num_loaded++; - /* Check if the host name matches. */ - if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) { - if (*cp != HASH_DELIM) - continue; - hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); - if (hashed_host == NULL) { - debug("Invalid hashed host line %lu of %s", - linenum, path); - continue; - } - if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) - continue; - } + return 0; +} - /* Got a match. Skip host name. */ - cp = cp2; +void +load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) +{ + int r; + struct load_callback_ctx ctx; - /* - * Extract the key from the line. This will skip any leading - * whitespace. Ignore badly formatted lines. - */ - if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { - error("%s: sshkey_new failed", __func__); - break; - } - if (!hostfile_read_key(&cp, &kbits, key)) { - sshkey_free(key); -#ifdef WITH_SSH1 - if ((key = sshkey_new(KEY_RSA1)) == NULL) { - error("%s: sshkey_new failed", __func__); - break; - } - if (!hostfile_read_key(&cp, &kbits, key)) { - sshkey_free(key); - continue; - } -#else - continue; -#endif - } - if (!hostfile_check_key(kbits, key, host, path, linenum)) - continue; + ctx.host = host; + ctx.num_loaded = 0; + ctx.hostkeys = hostkeys; - debug3("%s: found %skey type %s in file %s:%lu", __func__, - marker == MRK_NONE ? "" : - (marker == MRK_CA ? "ca " : "revoked "), - sshkey_type(key), path, linenum); - hostkeys->entries = xrealloc(hostkeys->entries, - hostkeys->num_entries + 1, sizeof(*hostkeys->entries)); - hostkeys->entries[hostkeys->num_entries].host = xstrdup(host); - hostkeys->entries[hostkeys->num_entries].file = xstrdup(path); - hostkeys->entries[hostkeys->num_entries].line = linenum; - hostkeys->entries[hostkeys->num_entries].key = key; - hostkeys->entries[hostkeys->num_entries].marker = marker; - hostkeys->num_entries++; - num_loaded++; + if ((r = hostkeys_foreach(path, record_hostkey, &ctx, host, + HKF_WANT_MATCH_HOST|HKF_WANT_PARSE_KEY)) != 0) { + if (r != SSH_ERR_SYSTEM_ERROR && errno != ENOENT) + debug("%s: hostkeys_foreach failed for %s: %s", + __func__, path, ssh_err(r)); } - debug3("%s: loaded %lu keys", __func__, num_loaded); - fclose(f); - return; + if (ctx.num_loaded != 0) + debug3("%s: loaded %lu keys from %s", __func__, + ctx.num_loaded, host); } void @@ -470,7 +434,6 @@ lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, * Appends an entry to the host file. Returns false if the entry could not * be appended. */ - int add_host_to_hostfile(const char *filename, const char *host, const struct sshkey *key, int store_hash) @@ -487,7 +450,7 @@ add_host_to_hostfile(const char *filename, const char *host, if (store_hash) { if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { - error("add_host_to_hostfile: host_hash failed"); + error("%s: host_hash failed", __func__); fclose(f); return 0; } -- cgit v1.2.3 From 8d4f87258f31cb6def9b3b55b6a7321d84728ff2 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 26 Jan 2015 03:04:45 +0000 Subject: upstream commit Host key rotation support. Add a hostkeys@openssh.com protocol extension (global request) for a server to inform a client of all its available host key after authentication has completed. The client may record the keys in known_hosts, allowing it to upgrade to better host key algorithms and a server to gracefully rotate its keys. The client side of this is controlled by a UpdateHostkeys config option (default on). ok markus@ --- PROTOCOL | 24 ++++++- clientloop.c | 94 ++++++++++++++++++++++++++- hostfile.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- hostfile.h | 5 +- readconf.c | 13 +++- readconf.h | 6 +- ssh_config.5 | 26 +++++++- sshconnect.c | 11 +++- sshd.c | 44 ++++++++++++- 9 files changed, 401 insertions(+), 28 deletions(-) (limited to 'hostfile.c') diff --git a/PROTOCOL b/PROTOCOL index aa59f584e..8150c577b 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -282,6 +282,28 @@ by the client cancel the forwarding of a Unix domain socket. boolean FALSE string socket path +2.5. connection: hostkey update and rotation "hostkeys@openssh.com" + +OpenSSH supports a protocol extension allowing a server to inform +a client of all its protocol v.2 hostkeys after user-authentication +has completed. + + byte SSH_MSG_GLOBAL_REQUEST + string "hostkeys@openssh.com" + string[] hostkeys + +Upon receiving this message, a client may update its known_hosts +file, adding keys that it has not seen before and deleting keys +for the server host that are no longer offered. + +This extension allows a client to learn key types that it had +not previously encountered, thereby allowing it to potentially +upgrade from weaker key algorithms to better ones. It also +supports graceful key rotation: a server may offer multiple keys +of the same type for a period (to give clients an opportunity to +learn them using this extension) before removing the deprecated +key from those offered. + 3. SFTP protocol changes 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK @@ -406,4 +428,4 @@ respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". -$OpenBSD: PROTOCOL,v 1.24 2014/07/15 15:54:14 millert Exp $ +$OpenBSD: PROTOCOL,v 1.25 2015/01/26 03:04:45 djm Exp $ diff --git a/clientloop.c b/clientloop.c index 4522a6332..7b54b6eb0 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.266 2015/01/20 23:14:00 deraadt Exp $ */ +/* $OpenBSD: clientloop.c,v 1.267 2015/01/26 03:04:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -112,6 +112,7 @@ #include "msg.h" #include "roaming.h" #include "ssherr.h" +#include "hostfile.h" /* import options */ extern Options options; @@ -1781,6 +1782,7 @@ client_input_exit_status(int type, u_int32_t seq, void *ctxt) quit_pending = 1; return 0; } + static int client_input_agent_open(int type, u_int32_t seq, void *ctxt) { @@ -2038,6 +2040,7 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) free(ctype); return 0; } + static int client_input_channel_req(int type, u_int32_t seq, void *ctxt) { @@ -2085,6 +2088,91 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) free(rtype); return 0; } + +/* + * Handle hostkeys@openssh.com global request to inform the client of all + * the server's hostkeys. The keys are checked against the user's + * HostkeyAlgorithms preference before they are accepted. + */ +static int +client_input_hostkeys(void) +{ + const u_char *blob = NULL; + u_int i, len = 0, nkeys = 0; + struct sshbuf *buf = NULL; + struct sshkey *key = NULL, **tmp, **keys = NULL; + int r, success = 1; + char *fp, *host_str = NULL; + static int hostkeys_seen = 0; /* XXX use struct ssh */ + + /* + * NB. Return success for all cases other than protocol error. The + * server doesn't need to know what the client does with its hosts + * file. + */ + + blob = packet_get_string_ptr(&len); + packet_check_eom(); + + if (hostkeys_seen) + fatal("%s: server already sent hostkeys", __func__); + if (!options.update_hostkeys || options.num_user_hostfiles <= 0) + return 1; + if ((buf = sshbuf_from(blob, len)) == NULL) + fatal("%s: sshbuf_from failed", __func__); + while (sshbuf_len(buf) > 0) { + sshkey_free(key); + key = NULL; + if ((r = sshkey_froms(buf, &key)) != 0) + fatal("%s: parse key: %s", __func__, ssh_err(r)); + fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); + debug3("%s: received %s key %s", __func__, + sshkey_type(key), fp); + free(fp); + /* Check that the key is accepted in HostkeyAlgorithms */ + if (options.hostkeyalgorithms != NULL && + match_pattern_list(sshkey_ssh_name(key), + options.hostkeyalgorithms, + strlen(options.hostkeyalgorithms), 0) != 1) { + debug3("%s: %s key not permitted by HostkeyAlgorithms", + __func__, sshkey_ssh_name(key)); + continue; + } + if ((tmp = reallocarray(keys, nkeys + 1, + sizeof(*keys))) == NULL) + fatal("%s: reallocarray failed nkeys = %u", + __func__, nkeys); + keys = tmp; + keys[nkeys++] = key; + key = NULL; + } + + debug3("%s: received %u keys from server", __func__, nkeys); + if (nkeys == 0) { + error("%s: server sent no hostkeys", __func__); + goto out; + } + + get_hostfile_hostname_ipaddr(host, NULL, options.port, &host_str, NULL); + + if ((r = hostfile_replace_entries(options.user_hostfiles[0], host_str, + keys, nkeys, options.hash_known_hosts, 1)) != 0) { + error("%s: hostfile_replace_entries failed: %s", + __func__, ssh_err(r)); + goto out; + } + + /* Success */ + out: + free(host_str); + sshkey_free(key); + for (i = 0; i < nkeys; i++) + sshkey_free(keys[i]); + sshbuf_free(buf); + return success; +} + static int client_input_global_request(int type, u_int32_t seq, void *ctxt) { @@ -2092,10 +2180,12 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) int want_reply; int success = 0; - rtype = packet_get_string(NULL); + rtype = packet_get_cstring(NULL); want_reply = packet_get_char(); debug("client_input_global_request: rtype %s want_reply %d", rtype, want_reply); + if (strcmp(rtype, "hostkeys@openssh.com") == 0) + success = client_input_hostkeys(); if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); diff --git a/hostfile.c b/hostfile.c index ccb2af920..9de1b383b 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.61 2015/01/18 21:48:09 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.62 2015/01/26 03:04:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -39,6 +39,7 @@ #include "includes.h" #include +#include #include @@ -49,6 +50,7 @@ #include #include #include +#include #include "xmalloc.h" #include "match.h" @@ -430,6 +432,29 @@ lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, found) == HOST_FOUND); } +static int +write_host_entry(FILE *f, const char *host, + const struct sshkey *key, int store_hash) +{ + int r, success = 0; + char *hashed_host = NULL; + + if (store_hash) { + if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { + error("%s: host_hash failed", __func__); + return 0; + } + } + fprintf(f, "%s ", store_hash ? hashed_host : host); + + if ((r = sshkey_write(key, f)) == 0) + success = 1; + else + error("%s: sshkey_write failed: %s", __func__, ssh_err(r)); + fputc('\n', f); + return success; +} + /* * Appends an entry to the host file. Returns false if the entry could not * be appended. @@ -439,32 +464,181 @@ add_host_to_hostfile(const char *filename, const char *host, const struct sshkey *key, int store_hash) { FILE *f; - int r, success = 0; - char *hashed_host = NULL; + int success; if (key == NULL) return 1; /* XXX ? */ f = fopen(filename, "a"); if (!f) return 0; + success = write_host_entry(f, host, key, store_hash); + fclose(f); + return success; +} - if (store_hash) { - if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { - error("%s: host_hash failed", __func__); - fclose(f); +struct host_delete_ctx { + FILE *out; + int quiet; + const char *host; + int *skip_keys; + struct sshkey * const *keys; + size_t nkeys; +}; + +static int +host_delete(struct hostkey_foreach_line *l, void *_ctx) +{ + struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx; + int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO; + size_t i; + + if (l->status == HKF_STATUS_HOST_MATCHED) { + if (l->marker != MRK_NONE) { + /* Don't remove CA and revocation lines */ + fprintf(ctx->out, "%s\n", l->line); + return 0; + } + + /* XXX might need a knob for this later */ + /* Don't remove RSA1 keys */ + if (l->key->type == KEY_RSA1) { + fprintf(ctx->out, "%s\n", l->line); return 0; } + + /* + * If this line contains one of the keys that we will be + * adding later, then don't change it and mark the key for + * skipping. + */ + for (i = 0; i < ctx->nkeys; i++) { + if (sshkey_equal(ctx->keys[i], l->key)) { + ctx->skip_keys[i] = 1; + fprintf(ctx->out, "%s\n", l->line); + debug3("%s: %s key already at %s:%ld", __func__, + sshkey_type(l->key), l->path, l->linenum); + return 0; + } + } + + /* + * Hostname matches and has no CA/revoke marker, delete it + * by *not* writing the line to ctx->out. + */ + do_log2(loglevel, "%s%s%s:%ld: Host %s removed", + ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", + l->path, l->linenum, ctx->host); + return 0; } - fprintf(f, "%s ", store_hash ? hashed_host : host); + /* Retain non-matching hosts and invalid lines when deleting */ + if (l->status == HKF_STATUS_INVALID) { + do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry", + ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", + l->path, l->linenum); + } + fprintf(ctx->out, "%s\n", l->line); + return 0; +} - if ((r = sshkey_write(key, f)) != 0) { - error("%s: saving key in %s failed: %s", - __func__, filename, ssh_err(r)); - } else - success = 1; - fputc('\n', f); - fclose(f); - return success; +int +hostfile_replace_entries(const char *filename, const char *host, + struct sshkey **keys, size_t nkeys, int store_hash, int quiet) +{ + int r, fd, oerrno = 0; + int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO; + struct host_delete_ctx ctx; + char *temp = NULL, *back = NULL; + mode_t omask; + size_t i; + + memset(&ctx, 0, sizeof(ctx)); + ctx.host = host; + ctx.quiet = quiet; + if ((ctx.skip_keys = calloc(nkeys, sizeof(*ctx.skip_keys))) == NULL) + return SSH_ERR_ALLOC_FAIL; + ctx.keys = keys; + ctx.nkeys = nkeys; + + /* + * Prepare temporary file for in-place deletion. + */ + if ((r = asprintf(&temp, "%s.XXXXXXXXXXX", filename)) < 0 || + (r = asprintf(&back, "%s.old", filename)) < 0) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + + omask = umask(077); + if ((fd = mkstemp(temp)) == -1) { + oerrno = errno; + error("%s: mkstemp: %s", __func__, strerror(oerrno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if ((ctx.out = fdopen(fd, "w")) == NULL) { + oerrno = errno; + close(fd); + error("%s: fdopen: %s", __func__, strerror(oerrno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + + /* Remove all entries for the specified host from the file */ + if ((r = hostkeys_foreach(filename, host_delete, &ctx, host, + HKF_WANT_PARSE_KEY)) != 0) { + error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); + goto fail; + } + + /* Add the requested keys */ + for (i = 0; i < nkeys; i++) { + if (ctx.skip_keys[i]) + continue; + do_log2(loglevel, "%s%sadd %s key to %s", + quiet ? __func__ : "", quiet ? ": " : NULL, + sshkey_type(keys[i]), filename); + if (!write_host_entry(ctx.out, host, keys[i], store_hash)) { + r = SSH_ERR_INTERNAL_ERROR; + goto fail; + } + } + fclose(ctx.out); + ctx.out = NULL; + + /* Backup the original file and replace it with the temporary */ + if (unlink(back) == -1 && errno != ENOENT) { + oerrno = errno; + error("%s: unlink %.100s: %s", __func__, back, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if (link(filename, back) == -1) { + oerrno = errno; + error("%s: link %.100s to %.100s: %s", __func__, filename, back, + strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if (rename(temp, filename) == -1) { + oerrno = errno; + error("%s: rename \"%s\" to \"%s\": %s", __func__, + temp, filename, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + /* success */ + r = 0; + fail: + if (temp != NULL && r != 0) + unlink(temp); + free(temp); + free(back); + if (ctx.out != NULL) + fclose(ctx.out); + free(ctx.skip_keys); + if (r == SSH_ERR_SYSTEM_ERROR) + errno = oerrno; + return r; } static int diff --git a/hostfile.h b/hostfile.h index 24c3813aa..9080b5edb 100644 --- a/hostfile.h +++ b/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.22 2015/01/18 21:40:24 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.23 2015/01/26 03:04:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -44,6 +44,9 @@ int hostfile_read_key(char **, u_int *, struct sshkey *); int add_host_to_hostfile(const char *, const char *, const struct sshkey *, int); +int hostfile_replace_entries(const char *filename, const char *host, + struct sshkey **keys, size_t nkeys, int store_hash, int quiet); + #define HASH_MAGIC "|1|" #define HASH_DELIM '|' diff --git a/readconf.c b/readconf.c index cb61a5af0..401f3430d 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.228 2015/01/16 06:40:12 deraadt Exp $ */ +/* $OpenBSD: readconf.c,v 1.229 2015/01/26 03:04:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -156,7 +156,7 @@ typedef enum { oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, - oFingerprintHash, + oFingerprintHash, oUpdateHostkeys, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; @@ -273,6 +273,7 @@ static struct { { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "revokedhostkeys", oRevokedHostKeys }, { "fingerprinthash", oFingerprintHash }, + { "updatehostkeys", oUpdateHostkeys }, { "ignoreunknown", oIgnoreUnknown }, { NULL, oBadOption } @@ -1476,6 +1477,10 @@ parse_int: *intptr = value; break; + case oUpdateHostkeys: + intptr = &options->update_hostkeys; + goto parse_flag; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1654,6 +1659,7 @@ initialize_options(Options * options) options->canonicalize_hostname = -1; options->revoked_host_keys = NULL; options->fingerprint_hash = -1; + options->update_hostkeys = -1; } /* @@ -1833,6 +1839,8 @@ fill_default_options(Options * options) options->canonicalize_hostname = SSH_CANONICALISE_NO; if (options->fingerprint_hash == -1) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; + if (options->update_hostkeys == -1) + options->update_hostkeys = 1; #define CLEAR_ON_NONE(v) \ do { \ @@ -2256,6 +2264,7 @@ dump_client_config(Options *o, const char *host) dump_cfg_fmtint(oUsePrivilegedPort, o->use_privileged_port); dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns); dump_cfg_fmtint(oVisualHostKey, o->visual_host_key); + dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys); /* Integer options */ dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots); diff --git a/readconf.h b/readconf.h index a23da1107..7a8ae17c0 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.106 2015/01/15 09:40:00 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.107 2015/01/26 03:04:45 djm Exp $ */ /* * Author: Tatu Ylonen @@ -146,7 +146,9 @@ typedef struct { char *revoked_host_keys; - int fingerprint_hash; + int fingerprint_hash; + + int update_hostkeys; char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ } Options; diff --git a/ssh_config.5 b/ssh_config.5 index 361c32288..0d4cdf4c6 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.199 2014/12/22 09:24:59 jmc Exp $ -.Dd $Mdocdate: December 22 2014 $ +.\" $OpenBSD: ssh_config.5,v 1.200 2015/01/26 03:04:45 djm Exp $ +.Dd $Mdocdate: January 26 2015 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -1492,6 +1492,28 @@ is not specified, it defaults to .Dq any . The default is .Dq any:any . +.It Cm UpdateHostkeys +Specifies whether +.Xr ssh 1 +should accept notifications of additional hostkeys from the server sent +after authentication has completed and add them to +.Cm UserKnownHostsFile . +The argument must be +.Dq yes +(the default) +or +.Dq no . +Enabling this option allows learning alternate hostkeys for a server +and supports graceful key rotation by allowing a server to public replacement +keys before old ones are removed. +Additional hostkeys are only accepted if the key used to authenticate the +host was already trusted or explicity accepted by the user. +.Pp +Presently, only +.Xr sshd 8 +from OpenSSH 6.8 and greater support the +.Dq hostkeys@openssh.com +protocol extension used to inform the client of all the server's hostkeys. .It Cm UsePrivilegedPort Specifies whether to use a privileged port for outgoing connections. The argument must be diff --git a/sshconnect.c b/sshconnect.c index 6fc3fa520..ae3b642cb 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.256 2015/01/20 23:14:00 deraadt Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.257 2015/01/26 03:04:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -818,6 +818,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, int len, cancelled_forwarding = 0; int local = sockaddr_is_local(hostaddr); int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; + int hostkey_trusted = 0; /* Known or explicitly accepted by user */ struct hostkeys *host_hostkeys, *ip_hostkeys; u_int i; @@ -926,6 +927,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, free(ra); free(fp); } + hostkey_trusted = 1; break; case HOST_NEW: if (options.host_key_alias == NULL && port != 0 && @@ -989,6 +991,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, free(fp); if (!confirm(msg)) goto fail; + hostkey_trusted = 1; /* user explicitly confirmed */ } /* * If not in strict mode, add the key automatically to the @@ -1187,6 +1190,12 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, } } + if (!hostkey_trusted && options.update_hostkeys) { + debug("%s: hostkey not known or explicitly trusted: " + "disabling UpdateHostkeys", __func__); + options.update_hostkeys = 0; + } + free(ip); free(host); if (host_hostkeys != NULL) diff --git a/sshd.c b/sshd.c index ef63bd1e8..f2ee10d2c 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.438 2015/01/20 23:14:00 deraadt Exp $ */ +/* $OpenBSD: sshd.c,v 1.439 2015/01/26 03:04:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -911,6 +911,42 @@ get_hostkey_index(Key *key, struct ssh *ssh) return (-1); } +/* Inform the client of all hostkeys */ +static void +notify_hostkeys(struct ssh *ssh) +{ + struct sshbuf *buf; + struct sshkey *key; + int i, nkeys, r; + char *fp; + + if ((buf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + for (i = nkeys = 0; i < options.num_host_key_files; i++) { + key = get_hostkey_public_by_index(i, ssh); + if (key == NULL || key->type == KEY_UNSPEC || + key->type == KEY_RSA1 || sshkey_is_cert(key)) + continue; + fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); + debug3("%s: key %d: %s %s", __func__, i, + sshkey_ssh_name(key), fp); + free(fp); + if ((r = sshkey_puts(key, buf)) != 0) + fatal("%s: couldn't put hostkey %d: %s", + __func__, i, ssh_err(r)); + nkeys++; + } + if (nkeys == 0) + fatal("%s: no hostkeys", __func__); + debug3("%s: send %d hostkeys", __func__, nkeys); + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("hostkeys@openssh.com"); + packet_put_char(0); /* want-reply */ + packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf)); + packet_send(); +} + /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability @@ -1722,6 +1758,8 @@ main(int ac, char **av) continue; key = key_load_private(options.host_key_files[i], "", NULL); pubkey = key_load_public(options.host_key_files[i], NULL); + if (pubkey == NULL && key != NULL) + pubkey = key_demote(key); sensitive_data.host_keys[i] = key; sensitive_data.host_pubkeys[i] = pubkey; @@ -2185,6 +2223,10 @@ main(int ac, char **av) packet_set_timeout(options.client_alive_interval, options.client_alive_count_max); + /* Try to send all our hostkeys to the client */ + if (compat20) + notify_hostkeys(active_state); + /* Start session. */ do_authenticated(authctxt); -- cgit v1.2.3 From 3076ee7d530d5b16842fac7a6229706c7e5acd26 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 26 Jan 2015 13:36:53 +0000 Subject: upstream commit properly restore umask --- hostfile.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'hostfile.c') diff --git a/hostfile.c b/hostfile.c index 9de1b383b..ea6bc6fc8 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.62 2015/01/26 03:04:45 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.63 2015/01/26 13:36:53 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -551,6 +551,8 @@ hostfile_replace_entries(const char *filename, const char *host, mode_t omask; size_t i; + omask = umask(077); + memset(&ctx, 0, sizeof(ctx)); ctx.host = host; ctx.quiet = quiet; @@ -568,7 +570,6 @@ hostfile_replace_entries(const char *filename, const char *host, goto fail; } - omask = umask(077); if ((fd = mkstemp(temp)) == -1) { oerrno = errno; error("%s: mkstemp: %s", __func__, strerror(oerrno)); @@ -636,6 +637,7 @@ hostfile_replace_entries(const char *filename, const char *host, if (ctx.out != NULL) fclose(ctx.out); free(ctx.skip_keys); + umask(omask); if (r == SSH_ERR_SYSTEM_ERROR) errno = oerrno; return r; -- cgit v1.2.3 From 6c5c949782d86a6e7d58006599c7685bfcd01685 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 16 Feb 2015 22:08:57 +0000 Subject: upstream commit Refactor hostkeys_foreach() and dependent code Deal with IP addresses (i.e. CheckHostIP) Don't clobber known_hosts when nothing changed ok markus@ as part of larger commit --- clientloop.c | 22 ++++-- hostfile.c | 219 ++++++++++++++++++++++++++++++++++++++--------------------- hostfile.h | 31 ++++++--- ssh-keygen.c | 71 ++++++++++--------- 4 files changed, 218 insertions(+), 125 deletions(-) (limited to 'hostfile.c') diff --git a/clientloop.c b/clientloop.c index 7b54b6eb0..c6f8e9dc1 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.267 2015/01/26 03:04:45 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.268 2015/02/16 22:08:57 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2102,8 +2102,9 @@ client_input_hostkeys(void) struct sshbuf *buf = NULL; struct sshkey *key = NULL, **tmp, **keys = NULL; int r, success = 1; - char *fp, *host_str = NULL; + char *fp, *host_str = NULL, *ip_str = NULL; static int hostkeys_seen = 0; /* XXX use struct ssh */ + extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ /* * NB. Return success for all cases other than protocol error. The @@ -2148,16 +2149,24 @@ client_input_hostkeys(void) key = NULL; } - debug3("%s: received %u keys from server", __func__, nkeys); if (nkeys == 0) { error("%s: server sent no hostkeys", __func__); goto out; } - get_hostfile_hostname_ipaddr(host, NULL, options.port, &host_str, NULL); + get_hostfile_hostname_ipaddr(host, + options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, + options.port, &host_str, options.check_host_ip ? &ip_str : NULL); - if ((r = hostfile_replace_entries(options.user_hostfiles[0], host_str, - keys, nkeys, options.hash_known_hosts, 1)) != 0) { + debug3("%s: update known hosts for %s%s%s with %u keys from server", + __func__, host_str, + options.check_host_ip ? " " : "", + options.check_host_ip ? ip_str : "", nkeys); + + if ((r = hostfile_replace_entries(options.user_hostfiles[0], + host_str, options.check_host_ip ? ip_str : NULL, + keys, nkeys, options.hash_known_hosts, 0, + options.fingerprint_hash)) != 0) { error("%s: hostfile_replace_entries failed: %s", __func__, ssh_err(r)); goto out; @@ -2166,6 +2175,7 @@ client_input_hostkeys(void) /* Success */ out: free(host_str); + free(ip_str); sshkey_free(key); for (i = 0; i < nkeys; i++) sshkey_free(keys[i]); diff --git a/hostfile.c b/hostfile.c index ea6bc6fc8..b235795e6 100644 --- a/hostfile.c +++ b/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.63 2015/01/26 13:36:53 djm Exp $ */ +/* $OpenBSD: hostfile.c,v 1.64 2015/02/16 22:08:57 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -184,24 +184,6 @@ hostfile_read_key(char **cpp, u_int *bitsp, struct sshkey *ret) return 1; } -static int -hostfile_check_key(int bits, const struct sshkey *key, const char *host, - const char *filename, u_long linenum) -{ -#ifdef WITH_SSH1 - if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) - return 1; - if (bits != BN_num_bits(key->rsa->n)) { - logit("Warning: %s, line %lu: keysize mismatch for host %s: " - "actual %d vs. announced %d.", - filename, linenum, host, BN_num_bits(key->rsa->n), bits); - logit("Warning: replace %d with %d in %s, line %lu.", - bits, BN_num_bits(key->rsa->n), filename, linenum); - } -#endif - return 1; -} - static HostkeyMarker check_markers(char **cpp) { @@ -295,8 +277,8 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) ctx.num_loaded = 0; ctx.hostkeys = hostkeys; - if ((r = hostkeys_foreach(path, record_hostkey, &ctx, host, - HKF_WANT_MATCH_HOST|HKF_WANT_PARSE_KEY)) != 0) { + if ((r = hostkeys_foreach(path, record_hostkey, &ctx, host, NULL, + HKF_WANT_MATCH|HKF_WANT_PARSE_KEY)) != 0) { if (r != SSH_ERR_SYSTEM_ERROR && errno != ENOENT) debug("%s: hostkeys_foreach failed for %s: %s", __func__, path, ssh_err(r)); @@ -433,7 +415,7 @@ lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, } static int -write_host_entry(FILE *f, const char *host, +write_host_entry(FILE *f, const char *host, const char *ip, const struct sshkey *key, int store_hash) { int r, success = 0; @@ -444,8 +426,11 @@ write_host_entry(FILE *f, const char *host, error("%s: host_hash failed", __func__); return 0; } - } - fprintf(f, "%s ", store_hash ? hashed_host : host); + fprintf(f, "%s ", hashed_host); + } else if (ip != NULL) + fprintf(f, "%s,%s ", host, ip); + else + fprintf(f, "%s ", host); if ((r = sshkey_write(key, f)) == 0) success = 1; @@ -471,7 +456,7 @@ add_host_to_hostfile(const char *filename, const char *host, f = fopen(filename, "a"); if (!f) return 0; - success = write_host_entry(f, host, key, store_hash); + success = write_host_entry(f, host, NULL, key, store_hash); fclose(f); return success; } @@ -480,19 +465,20 @@ struct host_delete_ctx { FILE *out; int quiet; const char *host; - int *skip_keys; + int *skip_keys; /* XXX split for host/ip? might want to ensure both */ struct sshkey * const *keys; size_t nkeys; + int modified; }; static int host_delete(struct hostkey_foreach_line *l, void *_ctx) { struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx; - int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO; + int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; size_t i; - if (l->status == HKF_STATUS_HOST_MATCHED) { + if (l->status == HKF_STATUS_MATCHED) { if (l->marker != MRK_NONE) { /* Don't remove CA and revocation lines */ fprintf(ctx->out, "%s\n", l->line); @@ -525,9 +511,10 @@ host_delete(struct hostkey_foreach_line *l, void *_ctx) * Hostname matches and has no CA/revoke marker, delete it * by *not* writing the line to ctx->out. */ - do_log2(loglevel, "%s%s%s:%ld: Host %s removed", + do_log2(loglevel, "%s%s%s:%ld: Removed %s key for host %s", ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", - l->path, l->linenum, ctx->host); + l->path, l->linenum, sshkey_type(l->key), ctx->host); + ctx->modified = 1; return 0; } /* Retain non-matching hosts and invalid lines when deleting */ @@ -541,13 +528,13 @@ host_delete(struct hostkey_foreach_line *l, void *_ctx) } int -hostfile_replace_entries(const char *filename, const char *host, - struct sshkey **keys, size_t nkeys, int store_hash, int quiet) +hostfile_replace_entries(const char *filename, const char *host, const char *ip, + struct sshkey **keys, size_t nkeys, int store_hash, int quiet, int hash_alg) { int r, fd, oerrno = 0; - int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO; + int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; struct host_delete_ctx ctx; - char *temp = NULL, *back = NULL; + char *fp, *temp = NULL, *back = NULL; mode_t omask; size_t i; @@ -560,6 +547,7 @@ hostfile_replace_entries(const char *filename, const char *host, return SSH_ERR_ALLOC_FAIL; ctx.keys = keys; ctx.nkeys = nkeys; + ctx.modified = 0; /* * Prepare temporary file for in-place deletion. @@ -585,7 +573,7 @@ hostfile_replace_entries(const char *filename, const char *host, } /* Remove all entries for the specified host from the file */ - if ((r = hostkeys_foreach(filename, host_delete, &ctx, host, + if ((r = hostkeys_foreach(filename, host_delete, &ctx, host, ip, HKF_WANT_PARSE_KEY)) != 0) { error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); goto fail; @@ -595,38 +583,54 @@ hostfile_replace_entries(const char *filename, const char *host, for (i = 0; i < nkeys; i++) { if (ctx.skip_keys[i]) continue; - do_log2(loglevel, "%s%sadd %s key to %s", - quiet ? __func__ : "", quiet ? ": " : NULL, - sshkey_type(keys[i]), filename); - if (!write_host_entry(ctx.out, host, keys[i], store_hash)) { + if ((fp = sshkey_fingerprint(keys[i], hash_alg, + SSH_FP_DEFAULT)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + do_log2(loglevel, "%s%sAdding new key for %s to %s: %s %s", + quiet ? __func__ : "", quiet ? ": " : "", host, filename, + sshkey_ssh_name(keys[i]), fp); + free(fp); + if (!write_host_entry(ctx.out, host, ip, keys[i], store_hash)) { r = SSH_ERR_INTERNAL_ERROR; goto fail; } + ctx.modified = 1; } fclose(ctx.out); ctx.out = NULL; - /* Backup the original file and replace it with the temporary */ - if (unlink(back) == -1 && errno != ENOENT) { - oerrno = errno; - error("%s: unlink %.100s: %s", __func__, back, strerror(errno)); - r = SSH_ERR_SYSTEM_ERROR; - goto fail; - } - if (link(filename, back) == -1) { - oerrno = errno; - error("%s: link %.100s to %.100s: %s", __func__, filename, back, - strerror(errno)); - r = SSH_ERR_SYSTEM_ERROR; - goto fail; - } - if (rename(temp, filename) == -1) { - oerrno = errno; - error("%s: rename \"%s\" to \"%s\": %s", __func__, - temp, filename, strerror(errno)); - r = SSH_ERR_SYSTEM_ERROR; - goto fail; + if (ctx.modified) { + /* Backup the original file and replace it with the temporary */ + if (unlink(back) == -1 && errno != ENOENT) { + oerrno = errno; + error("%s: unlink %.100s: %s", __func__, + back, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if (link(filename, back) == -1) { + oerrno = errno; + error("%s: link %.100s to %.100s: %s", __func__, + filename, back, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if (rename(temp, filename) == -1) { + oerrno = errno; + error("%s: rename \"%s\" to \"%s\": %s", __func__, + temp, filename, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + } else { + /* No changes made; just delete the temporary file */ + if (unlink(temp) != 0) + error("%s: unlink \"%s\": %s", __func__, + temp, strerror(errno)); } + /* success */ r = 0; fail: @@ -663,18 +667,20 @@ match_maybe_hashed(const char *host, const char *names, int *was_hashed) int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, - const char *host, u_int options) + const char *host, const char *ip, u_int options) { FILE *f; - char line[8192], oline[8192]; + char line[8192], oline[8192], ktype[128]; u_long linenum = 0; char *cp, *cp2; u_int kbits; + int hashed; int s, r = 0; struct hostkey_foreach_line lineinfo; + size_t l; memset(&lineinfo, 0, sizeof(lineinfo)); - if (host == NULL && (options & HKF_WANT_MATCH_HOST) != 0) + if (host == NULL && (options & HKF_WANT_MATCH) != 0) return SSH_ERR_INVALID_ARGUMENT; if ((f = fopen(path, "r")) == NULL) return SSH_ERR_SYSTEM_ERROR; @@ -689,13 +695,15 @@ hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, lineinfo.path = path; lineinfo.linenum = linenum; lineinfo.line = oline; + lineinfo.marker = MRK_NONE; lineinfo.status = HKF_STATUS_OK; + lineinfo.keytype = KEY_UNSPEC; /* Skip any leading whitespace, comments and empty lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') { - if ((options & HKF_WANT_MATCH_HOST) == 0) { + if ((options & HKF_WANT_MATCH) == 0) { lineinfo.status = HKF_STATUS_COMMENT; if ((r = callback(&lineinfo, ctx)) != 0) break; @@ -706,7 +714,7 @@ hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) { verbose("%s: invalid marker at %s:%lu", __func__, path, linenum); - if ((options & HKF_WANT_MATCH_HOST) == 0) + if ((options & HKF_WANT_MATCH) == 0) goto bad; continue; } @@ -719,24 +727,47 @@ hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, /* Check if the host name matches. */ if (host != NULL) { - s = match_maybe_hashed(host, lineinfo.hosts, - &lineinfo.was_hashed); - if (s == 1) - lineinfo.status = HKF_STATUS_HOST_MATCHED; - else if ((options & HKF_WANT_MATCH_HOST) != 0) - continue; - else if (s == -1) { + if ((s = match_maybe_hashed(host, lineinfo.hosts, + &hashed)) == -1) { debug2("%s: %s:%ld: bad host hash \"%.32s\"", __func__, path, linenum, lineinfo.hosts); goto bad; } + if (s == 1) { + lineinfo.status = HKF_STATUS_MATCHED; + lineinfo.match |= HKF_MATCH_HOST | + (hashed ? HKF_MATCH_HOST_HASHED : 0); + } + /* Try matching IP address if supplied */ + if (ip != NULL) { + if ((s = match_maybe_hashed(ip, lineinfo.hosts, + &hashed)) == -1) { + debug2("%s: %s:%ld: bad ip hash " + "\"%.32s\"", __func__, path, + linenum, lineinfo.hosts); + goto bad; + } + if (s == 1) { + lineinfo.status = HKF_STATUS_MATCHED; + lineinfo.match |= HKF_MATCH_IP | + (hashed ? HKF_MATCH_IP_HASHED : 0); + } + } + /* + * Skip this line if host matching requested and + * neither host nor address matched. + */ + if ((options & HKF_WANT_MATCH) != 0 && + lineinfo.status != HKF_STATUS_MATCHED) + continue; } /* Got a match. Skip host name and any following whitespace */ for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; if (*cp2 == '\0' || *cp2 == '#') { - debug2("%s:%ld: truncated before key", path, linenum); + debug2("%s:%ld: truncated before key type", + path, linenum); goto bad; } lineinfo.rawkey = cp = cp2; @@ -749,7 +780,8 @@ hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, */ if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) { error("%s: sshkey_new failed", __func__); - return SSH_ERR_ALLOC_FAIL; + r = SSH_ERR_ALLOC_FAIL; + break; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) { #ifdef WITH_SSH1 @@ -757,7 +789,8 @@ hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, lineinfo.key = sshkey_new(KEY_RSA1); if (lineinfo.key == NULL) { error("%s: sshkey_new fail", __func__); - return SSH_ERR_ALLOC_FAIL; + r = SSH_ERR_ALLOC_FAIL; + break; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) @@ -766,9 +799,43 @@ hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, goto bad; #endif } - if (!hostfile_check_key(kbits, lineinfo.key, host, - path, linenum)) { + lineinfo.keytype = lineinfo.key->type; + lineinfo.comment = cp; + } else { + /* Extract and parse key type */ + l = strcspn(lineinfo.rawkey, " \t"); + if (l <= 1 || l >= sizeof(ktype) || + lineinfo.rawkey[l] == '\0') + goto bad; + memcpy(ktype, lineinfo.rawkey, l); + ktype[l] = '\0'; + lineinfo.keytype = sshkey_type_from_name(ktype); +#ifdef WITH_SSH1 + /* + * Assume RSA1 if the first component is a short + * decimal number. + */ + if (lineinfo.keytype == KEY_UNSPEC && l < 8 && + strspn(ktype, "0123456789") == l) + lineinfo.keytype = KEY_RSA1; +#endif + /* + * Check that something other than whitespace follows + * the key type. This won't catch all corruption, but + * it does catch trivial truncation. + */ + cp2 += l; /* Skip past key type */ + for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) + ; + if (*cp2 == '\0' || *cp2 == '#') { + debug2("%s:%ld: truncated after key type", + path, linenum); + lineinfo.keytype = KEY_UNSPEC; + } + if (lineinfo.keytype == KEY_UNSPEC) { bad: + sshkey_free(lineinfo.key); + lineinfo.key = NULL; lineinfo.status = HKF_STATUS_INVALID; if ((r = callback(&lineinfo, ctx)) != 0) break; diff --git a/hostfile.h b/hostfile.h index 9080b5edb..bd2104373 100644 --- a/hostfile.h +++ b/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.23 2015/01/26 03:04:45 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.24 2015/02/16 22:08:57 djm Exp $ */ /* * Author: Tatu Ylonen @@ -44,8 +44,9 @@ int hostfile_read_key(char **, u_int *, struct sshkey *); int add_host_to_hostfile(const char *, const char *, const struct sshkey *, int); -int hostfile_replace_entries(const char *filename, const char *host, - struct sshkey **keys, size_t nkeys, int store_hash, int quiet); +int hostfile_replace_entries(const char *filename, + const char *host, const char *ip, struct sshkey **keys, size_t nkeys, + int store_hash, int quiet, int hash_alg); #define HASH_MAGIC "|1|" #define HASH_DELIM '|' @@ -60,13 +61,19 @@ char *host_hash(const char *, const char *, u_int); * hostnames. Allows access to the raw keyfile lines to allow * streaming edits to the file to take place. */ -#define HKF_WANT_MATCH_HOST (1) /* return only matching hosts */ +#define HKF_WANT_MATCH (1) /* return only matching hosts/addrs */ #define HKF_WANT_PARSE_KEY (1<<1) /* need key parsed */ -#define HKF_STATUS_OK 1 /* Line parsed, didn't match host */ -#define HKF_STATUS_INVALID 2 /* line had parse error */ -#define HKF_STATUS_COMMENT 3 /* valid line contained no key */ -#define HKF_STATUS_HOST_MATCHED 4 /* hostname matched */ +#define HKF_STATUS_OK 0 /* Line parsed, didn't match host */ +#define HKF_STATUS_INVALID 1 /* line had parse error */ +#define HKF_STATUS_COMMENT 2 /* valid line contained no key */ +#define HKF_STATUS_MATCHED 3 /* hostname or IP matched */ + +#define HKF_MATCH_HOST (1) /* hostname matched */ +#define HKF_MATCH_IP (1<<1) /* address matched */ +#define HKF_MATCH_HOST_HASHED (1<<2) /* hostname was hashed */ +#define HKF_MATCH_IP_HASHED (1<<3) /* address was hashed */ +/* XXX HKF_MATCH_KEY_TYPE? */ /* * The callback function receives this as an argument for each matching @@ -76,12 +83,13 @@ char *host_hash(const char *, const char *, u_int); struct hostkey_foreach_line { const char *path; /* Path of file */ u_long linenum; /* Line number */ - int status; /* One of HKF_STATUS_* */ + u_int status; /* One of HKF_STATUS_* */ + u_int match; /* Zero or more of HKF_MATCH_* OR'd together */ char *line; /* Entire key line; mutable by callback */ int marker; /* CA/revocation markers; indicated by MRK_* value */ const char *hosts; /* Raw hosts text, may be hashed or list multiple */ - int was_hashed; /* Non-zero if hostname was hashed */ const char *rawkey; /* Text of key and any comment following it */ + int keytype; /* Type of key; KEY_UNSPEC for invalid/comment lines */ struct sshkey *key; /* Key, if parsed ok and HKF_WANT_MATCH_HOST set */ const char *comment; /* Any comment following the key */ }; @@ -93,7 +101,8 @@ struct hostkey_foreach_line { */ typedef int hostkeys_foreach_fn(struct hostkey_foreach_line *l, void *ctx); +/* Iterate over a hostkeys file */ int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, - const char *host, u_int options); + const char *host, const char *ip, u_int options); #endif diff --git a/ssh-keygen.c b/ssh-keygen.c index 2c6a56839..9b2068254 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.261 2015/01/30 01:10:33 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.262 2015/02/16 22:08:57 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -1052,40 +1052,47 @@ known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) char *hashed, *cp, *hosts, *ohosts; int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); - /* Retain invalid lines when hashing, but mark file as invalid. */ - if (l->status == HKF_STATUS_INVALID) { + switch (l->status) { + case HKF_STATUS_OK: + case HKF_STATUS_MATCHED: + /* + * Don't hash hosts already already hashed, with wildcard + * characters or a CA/revocation marker. + */ + if ((l->match & HKF_MATCH_HOST_HASHED) != 0 || + has_wild || l->marker != MRK_NONE) { + fprintf(ctx->out, "%s\n", l->line); + if (has_wild && !find_host) { + fprintf(stderr, "%s:%ld: ignoring host name " + "with wildcard: %.64s\n", l->path, + l->linenum, l->hosts); + } + return 0; + } + /* + * Split any comma-separated hostnames from the host list, + * hash and store separately. + */ + ohosts = hosts = xstrdup(l->hosts); + while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { + if ((hashed = host_hash(cp, NULL, 0)) == NULL) + fatal("hash_host failed"); + fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); + ctx->has_unhashed = 1; + } + free(ohosts); + return 0; + case HKF_STATUS_INVALID: + /* Retain invalid lines, but mark file as invalid. */ ctx->invalid = 1; fprintf(stderr, "%s:%ld: invalid line\n", l->path, l->linenum); + /* FALLTHROUGH */ + default: fprintf(ctx->out, "%s\n", l->line); return 0; } - - /* - * Don't hash hosts already already hashed, with wildcard characters - * or a CA/revocation marker. - */ - if (l->was_hashed || has_wild || l->marker != MRK_NONE) { - fprintf(ctx->out, "%s\n", l->line); - if (has_wild && !find_host) { - fprintf(stderr, "%s:%ld: ignoring host name " - "with wildcard: %.64s\n", l->path, - l->linenum, l->hosts); - } - return 0; - } - /* - * Split any comma-separated hostnames from the host list, - * hash and store separately. - */ - ohosts = hosts = xstrdup(l->hosts); - while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { - if ((hashed = host_hash(cp, NULL, 0)) == NULL) - fatal("hash_host failed"); - fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); - ctx->has_unhashed = 1; - } - free(ohosts); - return 0; + /* NOTREACHED */ + return -1; } static int @@ -1093,7 +1100,7 @@ known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) { struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; - if (l->status == HKF_STATUS_HOST_MATCHED) { + if (l->status == HKF_STATUS_MATCHED) { if (delete_host) { if (l->marker != MRK_NONE) { /* Don't remove CA and revocation lines */ @@ -1180,7 +1187,7 @@ do_known_hosts(struct passwd *pw, const char *name) /* XXX support identity_file == "-" for stdin */ if ((r = hostkeys_foreach(identity_file, hash_hosts ? known_hosts_hash : known_hosts_find_delete, &ctx, - name, find_host ? HKF_WANT_MATCH_HOST : 0)) != 0) + name, NULL, find_host ? HKF_WANT_MATCH : 0)) != 0) fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); if (inplace) -- cgit v1.2.3