From 128343bcdb0b60fc826f2733df8cf979ec1627b4 Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Tue, 13 Jan 2015 19:31:40 +0000 Subject: upstream commit adapt mac.c to ssherr.h return codes (de-fatal) and simplify dependencies ok djm@ --- kex.h | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'kex.h') diff --git a/kex.h b/kex.h index 4c40ec851..dbcc0816f 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.64 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.65 2015/01/13 19:31:40 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -26,10 +26,9 @@ #ifndef KEX_H #define KEX_H -#include -#include -#include -#ifdef OPENSSL_HAS_ECC +#include "mac.h" + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) #include #endif @@ -82,8 +81,8 @@ enum kex_exchange { #define KEX_INIT_SENT 0x0001 typedef struct Kex Kex; -typedef struct Mac Mac; typedef struct Comp Comp; +typedef struct sshmac Mac; typedef struct Enc Enc; typedef struct Newkeys Newkeys; @@ -97,17 +96,6 @@ struct Enc { u_char *key; u_char *iv; }; -struct Mac { - char *name; - int enabled; - u_int mac_len; - u_char *key; - u_int key_len; - int type; - int etm; /* Encrypt-then-MAC */ - struct ssh_hmac_ctx *hmac_ctx; - struct umac_ctx *umac_ctx; -}; struct Comp { int type; int enabled; -- 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 'kex.h') 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 091c302829210c41e7f57c3f094c7b9c054306f0 Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Mon, 19 Jan 2015 19:52:16 +0000 Subject: upstream commit update packet.c & isolate, introduce struct ssh a) switch packet.c to buffer api and isolate per-connection info into struct ssh b) (de)serialization of the state is moved from monitor to packet.c c) the old packet.c API is implemented in opacket.[ch] d) compress.c/h is removed and integrated into packet.c with and ok djm@ --- Makefile.in | 4 +- clientloop.c | 16 +- compress.c | 167 ---- compress.h | 25 - deattack.c | 82 +- deattack.h | 11 +- kex.c | 87 +- kex.h | 39 +- kexc25519c.c | 6 +- kexc25519s.c | 6 +- kexdhc.c | 6 +- kexdhs.c | 6 +- kexecdhc.c | 6 +- kexecdhs.c | 6 +- kexgexc.c | 6 +- kexgexs.c | 6 +- monitor.c | 289 ++---- monitor.h | 4 +- monitor_wrap.c | 244 +---- monitor_wrap.h | 7 +- opacket.c | 279 ++++++ opacket.h | 173 ++++ packet.c | 2675 +++++++++++++++++++++++++++++++++++-------------------- packet.h | 240 ++--- roaming_dummy.c | 13 +- serverloop.c | 11 +- sshconnect2.c | 7 +- sshd.c | 13 +- 28 files changed, 2542 insertions(+), 1892 deletions(-) delete mode 100644 compress.c delete mode 100644 compress.h create mode 100644 opacket.c create mode 100644 opacket.h (limited to 'kex.h') diff --git a/Makefile.in b/Makefile.in index ebb0c516b..13256c2af 100644 --- a/Makefile.in +++ b/Makefile.in @@ -78,8 +78,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ authfd.o authfile.o bufaux.o bufbn.o buffer.o \ canohost.o channels.o cipher.o cipher-aes.o cipher-aesctr.o \ cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ - compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \ - log.o match.o md-sha256.o moduli.o nchan.o packet.o \ + compat.o crc32.o deattack.o fatal.o hostfile.o \ + log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ diff --git a/clientloop.c b/clientloop.c index 2137a81ce..3b9700aa4 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.262 2015/01/14 20:05:27 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.263 2015/01/19 19:52:16 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -192,9 +192,6 @@ TAILQ_HEAD(global_confirms, global_confirm); static struct global_confirms global_confirms = TAILQ_HEAD_INITIALIZER(global_confirms); -/*XXX*/ -extern Kex *xxx_kex; - void ssh_process_session2_setup(int, int, int, Buffer *); /* Restores stdin to blocking mode. */ @@ -1416,7 +1413,7 @@ static void client_process_buffered_input_packets(void) { dispatch_run(DISPATCH_NONBLOCK, &quit_pending, - compat20 ? xxx_kex : NULL); + compat20 ? active_state->kex : NULL); } /* scan buf[] for '~' before sending data to the peer */ @@ -1555,7 +1552,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) if (compat20 && session_closed && !channel_still_open()) break; - rekeying = (xxx_kex != NULL && !xxx_kex->done); + rekeying = (active_state->kex != NULL && !active_state->kex->done); if (rekeying) { debug("rekeying in progress"); @@ -1599,8 +1596,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) channel_after_select(readset, writeset); if (need_rekeying || packet_need_rekeying()) { debug("need rekeying"); - xxx_kex->done = 0; - kex_send_kexinit(xxx_kex); + active_state->kex->done = 0; + kex_send_kexinit(active_state->kex); need_rekeying = 0; } } @@ -1729,8 +1726,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /* Report bytes transferred, and transfer rates. */ total_time = get_current_time() - start_time; - packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); - packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); + packet_get_bytes(&ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", (unsigned long long)obytes, (unsigned long long)ibytes, total_time); if (total_time > 0) diff --git a/compress.c b/compress.c deleted file mode 100644 index 24778e524..000000000 --- a/compress.c +++ /dev/null @@ -1,167 +0,0 @@ -/* $OpenBSD: compress.c,v 1.26 2010/09/08 04:13:31 deraadt Exp $ */ -/* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * Interface to packet compression for ssh. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" - -#include - -#include - -#include "log.h" -#include "buffer.h" -#include "compress.h" - -#include - -z_stream incoming_stream; -z_stream outgoing_stream; -static int compress_init_send_called = 0; -static int compress_init_recv_called = 0; -static int inflate_failed = 0; -static int deflate_failed = 0; - -/* - * Initializes compression; level is compression level from 1 to 9 - * (as in gzip). - */ - -void -buffer_compress_init_send(int level) -{ - if (compress_init_send_called == 1) - deflateEnd(&outgoing_stream); - compress_init_send_called = 1; - debug("Enabling compression at level %d.", level); - if (level < 1 || level > 9) - fatal("Bad compression level %d.", level); - deflateInit(&outgoing_stream, level); -} -void -buffer_compress_init_recv(void) -{ - if (compress_init_recv_called == 1) - inflateEnd(&incoming_stream); - compress_init_recv_called = 1; - inflateInit(&incoming_stream); -} - -/* Frees any data structures allocated for compression. */ - -void -buffer_compress_uninit(void) -{ - debug("compress outgoing: raw data %llu, compressed %llu, factor %.2f", - (unsigned long long)outgoing_stream.total_in, - (unsigned long long)outgoing_stream.total_out, - outgoing_stream.total_in == 0 ? 0.0 : - (double) outgoing_stream.total_out / outgoing_stream.total_in); - debug("compress incoming: raw data %llu, compressed %llu, factor %.2f", - (unsigned long long)incoming_stream.total_out, - (unsigned long long)incoming_stream.total_in, - incoming_stream.total_out == 0 ? 0.0 : - (double) incoming_stream.total_in / incoming_stream.total_out); - if (compress_init_recv_called == 1 && inflate_failed == 0) - inflateEnd(&incoming_stream); - if (compress_init_send_called == 1 && deflate_failed == 0) - deflateEnd(&outgoing_stream); -} - -/* - * Compresses the contents of input_buffer into output_buffer. All packets - * compressed using this function will form a single compressed data stream; - * however, data will be flushed at the end of every call so that each - * output_buffer can be decompressed independently (but in the appropriate - * order since they together form a single compression stream) by the - * receiver. This appends the compressed data to the output buffer. - */ - -void -buffer_compress(Buffer * input_buffer, Buffer * output_buffer) -{ - u_char buf[4096]; - int status; - - /* This case is not handled below. */ - if (buffer_len(input_buffer) == 0) - return; - - /* Input is the contents of the input buffer. */ - outgoing_stream.next_in = buffer_ptr(input_buffer); - outgoing_stream.avail_in = buffer_len(input_buffer); - - /* Loop compressing until deflate() returns with avail_out != 0. */ - do { - /* Set up fixed-size output buffer. */ - outgoing_stream.next_out = buf; - outgoing_stream.avail_out = sizeof(buf); - - /* Compress as much data into the buffer as possible. */ - status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); - switch (status) { - case Z_OK: - /* Append compressed data to output_buffer. */ - buffer_append(output_buffer, buf, - sizeof(buf) - outgoing_stream.avail_out); - break; - default: - deflate_failed = 1; - fatal("buffer_compress: deflate returned %d", status); - /* NOTREACHED */ - } - } while (outgoing_stream.avail_out == 0); -} - -/* - * Uncompresses the contents of input_buffer into output_buffer. All packets - * uncompressed using this function will form a single compressed data - * stream; however, data will be flushed at the end of every call so that - * each output_buffer. This must be called for the same size units that the - * buffer_compress was called, and in the same order that buffers compressed - * with that. This appends the uncompressed data to the output buffer. - */ - -void -buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) -{ - u_char buf[4096]; - int status; - - incoming_stream.next_in = buffer_ptr(input_buffer); - incoming_stream.avail_in = buffer_len(input_buffer); - - for (;;) { - /* Set up fixed-size output buffer. */ - incoming_stream.next_out = buf; - incoming_stream.avail_out = sizeof(buf); - - status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); - switch (status) { - case Z_OK: - buffer_append(output_buffer, buf, - sizeof(buf) - incoming_stream.avail_out); - break; - case Z_BUF_ERROR: - /* - * Comments in zlib.h say that we should keep calling - * inflate() until we get an error. This appears to - * be the error that we get. - */ - return; - default: - inflate_failed = 1; - fatal("buffer_uncompress: inflate returned %d", status); - /* NOTREACHED */ - } - } -} diff --git a/compress.h b/compress.h deleted file mode 100644 index 418d6fd2c..000000000 --- a/compress.h +++ /dev/null @@ -1,25 +0,0 @@ -/* $OpenBSD: compress.h,v 1.12 2006/03/25 22:22:43 djm Exp $ */ - -/* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * Interface to packet compression for ssh. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#ifndef COMPRESS_H -#define COMPRESS_H - -void buffer_compress_init_send(int); -void buffer_compress_init_recv(void); -void buffer_compress_uninit(void); -void buffer_compress(Buffer *, Buffer *); -void buffer_uncompress(Buffer *, Buffer *); - -#endif /* COMPRESS_H */ diff --git a/deattack.c b/deattack.c index 1b37e4dab..b102401e7 100644 --- a/deattack.c +++ b/deattack.c @@ -1,4 +1,4 @@ -/* $OpenBSD: deattack.c,v 1.30 2006/09/16 19:53:37 djm Exp $ */ +/* $OpenBSD: deattack.c,v 1.31 2015/01/19 19:52:16 markus Exp $ */ /* * Cryptographic attack detector for ssh - source code * @@ -20,16 +20,14 @@ #include "includes.h" -#include - +#include #include #include -#include +#include -#include "xmalloc.h" #include "deattack.h" -#include "log.h" #include "crc32.h" +#include "sshbuf.h" #include "misc.h" /* @@ -66,7 +64,7 @@ /* Hash function (Input keys are cipher results) */ -#define HASH(x) get_u32(x) +#define HASH(x) PEEK_U32(x) #define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE)) @@ -79,10 +77,10 @@ crc_update(u_int32_t *a, u_int32_t b) /* detect if a block is used in a particular pattern */ static int -check_crc(u_char *S, u_char *buf, u_int32_t len) +check_crc(const u_char *S, const u_char *buf, u_int32_t len) { u_int32_t crc; - u_char *c; + const u_char *c; crc = 0; for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { @@ -94,36 +92,44 @@ check_crc(u_char *S, u_char *buf, u_int32_t len) crc_update(&crc, 0); } } - return (crc == 0); + return crc == 0; } +void +deattack_init(struct deattack_ctx *dctx) +{ + bzero(dctx, sizeof(*dctx)); + dctx->n = HASH_MINSIZE / HASH_ENTRYSIZE; +} /* Detect a crc32 compensation attack on a packet */ int -detect_attack(u_char *buf, u_int32_t len) +detect_attack(struct deattack_ctx *dctx, const u_char *buf, u_int32_t len) { - static u_int16_t *h = (u_int16_t *) NULL; - static u_int32_t n = HASH_MINSIZE / HASH_ENTRYSIZE; - u_int32_t i, j; - u_int32_t l, same; - u_char *c; - u_char *d; + u_int32_t i, j, l, same; + u_int16_t *tmp; + const u_char *c, *d; if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || - len % SSH_BLOCKSIZE != 0) { - fatal("detect_attack: bad length %d", len); - } - for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) + len % SSH_BLOCKSIZE != 0) + return DEATTACK_ERROR; + for (l = dctx->n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) ; - if (h == NULL) { - debug("Installing crc compensation attack detector."); - h = (u_int16_t *) xcalloc(l, HASH_ENTRYSIZE); - n = l; + if (dctx->h == NULL) { + if ((dctx->h = calloc(l, HASH_ENTRYSIZE)) == NULL) + return DEATTACK_ERROR; + dctx->n = l; } else { - if (l > n) { - h = (u_int16_t *)xrealloc(h, l, HASH_ENTRYSIZE); - n = l; + if (l > dctx->n) { + if ((tmp = reallocarray(dctx->h, l, HASH_ENTRYSIZE)) + == NULL) { + free(dctx->h); + dctx->h = NULL; + return DEATTACK_ERROR; + } + dctx->h = tmp; + dctx->n = l; } } @@ -132,29 +138,29 @@ detect_attack(u_char *buf, u_int32_t len) for (d = buf; d < c; d += SSH_BLOCKSIZE) { if (!CMP(c, d)) { if ((check_crc(c, buf, len))) - return (DEATTACK_DETECTED); + return DEATTACK_DETECTED; else break; } } } - return (DEATTACK_OK); + return DEATTACK_OK; } - memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); + memset(dctx->h, HASH_UNUSEDCHAR, dctx->n * HASH_ENTRYSIZE); for (c = buf, same = j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) { - for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; - i = (i + 1) & (n - 1)) { - if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) { + for (i = HASH(c) & (dctx->n - 1); dctx->h[i] != HASH_UNUSED; + i = (i + 1) & (dctx->n - 1)) { + if (!CMP(c, buf + dctx->h[i] * SSH_BLOCKSIZE)) { if (++same > MAX_IDENTICAL) - return (DEATTACK_DOS_DETECTED); + return DEATTACK_DOS_DETECTED; if (check_crc(c, buf, len)) - return (DEATTACK_DETECTED); + return DEATTACK_DETECTED; else break; } } - h[i] = j; + dctx->h[i] = j; } - return (DEATTACK_OK); + return DEATTACK_OK; } diff --git a/deattack.h b/deattack.h index 0316fb285..ce67a30ff 100644 --- a/deattack.h +++ b/deattack.h @@ -1,4 +1,4 @@ -/* $OpenBSD: deattack.h,v 1.10 2006/09/16 19:53:37 djm Exp $ */ +/* $OpenBSD: deattack.h,v 1.11 2015/01/19 19:52:16 markus Exp $ */ /* * Cryptographic attack detector for ssh - Header file @@ -26,6 +26,13 @@ #define DEATTACK_OK 0 #define DEATTACK_DETECTED 1 #define DEATTACK_DOS_DETECTED 2 +#define DEATTACK_ERROR 3 -int detect_attack(u_char *, u_int32_t); +struct deattack_ctx { + u_int16_t *h; + u_int32_t n; +}; + +void deattack_init(struct deattack_ctx *); +int detect_attack(struct deattack_ctx *, const u_char *, u_int32_t); #endif diff --git a/kex.c b/kex.c index ce0bf8809..7c4dd7a90 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.99 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: kex.c,v 1.100 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -239,8 +239,8 @@ kex_finish(Kex *kex) debug("SSH2_MSG_NEWKEYS received"); kex->done = 1; - buffer_clear(&kex->peer); - /* buffer_clear(&kex->my); */ + buffer_clear(kex->peer); + /* buffer_clear(kex->my); */ kex->flags &= ~KEX_INIT_SENT; free(kex->name); kex->name = NULL; @@ -264,9 +264,9 @@ kex_send_kexinit(Kex *kex) kex->done = 0; /* generate a random cookie */ - if (buffer_len(&kex->my) < KEX_COOKIE_LEN) + if (buffer_len(kex->my) < KEX_COOKIE_LEN) fatal("kex_send_kexinit: kex proposal too short"); - cookie = buffer_ptr(&kex->my); + cookie = buffer_ptr(kex->my); for (i = 0; i < KEX_COOKIE_LEN; i++) { if (i % 4 == 0) rnd = arc4random(); @@ -274,7 +274,7 @@ kex_send_kexinit(Kex *kex) rnd >>= 8; } packet_start(SSH2_MSG_KEXINIT); - packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my)); + packet_put_raw(buffer_ptr(kex->my), buffer_len(kex->my)); packet_send(); debug("SSH2_MSG_KEXINIT sent"); kex->flags |= KEX_INIT_SENT; @@ -284,8 +284,9 @@ kex_send_kexinit(Kex *kex) void kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { - char *ptr; - u_int i, dlen; + const char *ptr; + u_int i; + size_t dlen; Kex *kex = (Kex *)ctxt; debug("SSH2_MSG_KEXINIT received"); @@ -293,7 +294,7 @@ kex_input_kexinit(int type, u_int32_t seq, void *ctxt) fatal("kex_input_kexinit: no kex, cannot rekey"); ptr = packet_get_raw(&dlen); - buffer_append(&kex->peer, ptr, dlen); + buffer_append(kex->peer, ptr, dlen); /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) @@ -317,15 +318,49 @@ kex_input_kexinit(int type, u_int32_t seq, void *ctxt) kex_kexinit_finish(kex); } +void +kex_free_newkeys(struct newkeys *newkeys) +{ + if (newkeys == NULL) + return; + if (newkeys->enc.key) { + explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); + free(newkeys->enc.key); + newkeys->enc.key = NULL; + } + if (newkeys->enc.iv) { + explicit_bzero(newkeys->enc.iv, newkeys->enc.block_size); + free(newkeys->enc.iv); + newkeys->enc.iv = NULL; + } + free(newkeys->enc.name); + explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); + free(newkeys->comp.name); + explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); + mac_clear(&newkeys->mac); + if (newkeys->mac.key) { + explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); + free(newkeys->mac.key); + newkeys->mac.key = NULL; + } + free(newkeys->mac.name); + explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); + explicit_bzero(newkeys, sizeof(*newkeys)); + free(newkeys); +} + Kex * kex_setup(char *proposal[PROPOSAL_MAX]) { - Kex *kex; + struct kex *kex; - kex = xcalloc(1, sizeof(*kex)); - buffer_init(&kex->peer); - buffer_init(&kex->my); - kex_prop2buf(&kex->my, proposal); + if ((kex = calloc(1, sizeof(*kex))) == NULL) + fatal("%s: calloc", __func__); + if ((kex->peer = sshbuf_new()) == NULL || + (kex->my = sshbuf_new()) == NULL) { + fatal("%s: sshbuf_new", __func__); + } + kex_prop2buf(kex->my, proposal); kex->done = 0; kex_send_kexinit(kex); /* we start */ @@ -464,8 +499,8 @@ kex_choose_conf(Kex *kex) u_int mode, ctos, need, dh_need, authlen; int first_kex_follows, type; - my = kex_buf2prop(&kex->my, NULL); - peer = kex_buf2prop(&kex->peer, &first_kex_follows); + my = kex_buf2prop(kex->my, NULL); + peer = kex_buf2prop(kex->peer, &first_kex_follows); if (kex->server) { cprop=peer; @@ -591,8 +626,6 @@ derive_key(Kex *kex, int id, u_int need, u_char *hash, u_int hashlen, return digest; } -Newkeys *current_keys[MODE_MAX]; - #define NKEYS 6 void kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen, @@ -608,13 +641,11 @@ kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen, debug2("kex_derive_keys"); for (mode = 0; mode < MODE_MAX; mode++) { - current_keys[mode] = kex->newkeys[mode]; - kex->newkeys[mode] = NULL; ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); - current_keys[mode]->enc.iv = keys[ctos ? 0 : 1]; - current_keys[mode]->enc.key = keys[ctos ? 2 : 3]; - current_keys[mode]->mac.key = keys[ctos ? 4 : 5]; + kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; + kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; + kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; } } @@ -632,16 +663,6 @@ kex_derive_keys_bn(Kex *kex, u_char *hash, u_int hashlen, const BIGNUM *secret) } #endif -Newkeys * -kex_get_newkeys(int mode) -{ - Newkeys *ret; - - ret = current_keys[mode]; - current_keys[mode] = NULL; - return ret; -} - #ifdef WITH_SSH1 void derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, diff --git a/kex.h b/kex.h index ef4a1f096..ffceb9fe4 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.66 2015/01/15 09:40:00 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.67 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -82,15 +82,15 @@ enum kex_exchange { #define KEX_INIT_SENT 0x0001 -typedef struct Kex Kex; -typedef struct Comp Comp; +typedef struct kex Kex; +typedef struct sshcomp Comp; typedef struct sshmac Mac; -typedef struct Enc Enc; -typedef struct Newkeys Newkeys; +typedef struct sshenc Enc; +typedef struct newkeys Newkeys; -struct Enc { +struct sshenc { char *name; - const Cipher *cipher; + const struct sshcipher *cipher; int enabled; u_int key_len; u_int iv_len; @@ -98,20 +98,20 @@ struct Enc { u_char *key; u_char *iv; }; -struct Comp { - int type; +struct sshcomp { + u_int type; int enabled; char *name; }; -struct Newkeys { - Enc enc; - Mac mac; - Comp comp; +struct newkeys { + struct sshenc enc; + struct sshmac mac; + struct sshcomp comp; }; -struct Kex { +struct kex { u_char *session_id; - u_int session_id_len; - Newkeys *newkeys[MODE_MAX]; + size_t session_id_len; + struct newkeys *newkeys[MODE_MAX]; u_int we_need; u_int dh_need; int server; @@ -119,8 +119,8 @@ struct Kex { int hostkey_type; int kex_type; int roaming; - Buffer my; - Buffer peer; + struct sshbuf *my; + struct sshbuf *peer; sig_atomic_t done; int flags; int hash_alg; @@ -140,14 +140,13 @@ char *kex_alg_list(char); Kex *kex_setup(char *[PROPOSAL_MAX]); void kex_finish(Kex *); +void kex_free_newkeys(struct newkeys *); void kex_send_kexinit(Kex *); void kex_input_kexinit(int, u_int32_t, void *); void kex_derive_keys(Kex *, u_char *, u_int, const u_char *, u_int); void kex_derive_keys_bn(Kex *, u_char *, u_int, const BIGNUM *); -Newkeys *kex_get_newkeys(int); - void kexdh_client(Kex *); void kexdh_server(Kex *); void kexgex_client(Kex *); diff --git a/kexc25519c.c b/kexc25519c.c index a80678af6..ffb537ef6 100644 --- a/kexc25519c.c +++ b/kexc25519c.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519c.c,v 1.4 2014/01/12 08:13:13 djm Exp $ */ +/* $OpenBSD: kexc25519c.c,v 1.5 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -101,8 +101,8 @@ kexc25519_client(Kex *kex) kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->my), buffer_len(&kex->my), - buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), server_host_key_blob, sbloblen, client_pubkey, server_pubkey, diff --git a/kexc25519s.c b/kexc25519s.c index 2b8e8efa1..ba6f546f4 100644 --- a/kexc25519s.c +++ b/kexc25519s.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519s.c,v 1.4 2014/01/12 08:13:13 djm Exp $ */ +/* $OpenBSD: kexc25519s.c,v 1.5 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -85,8 +85,8 @@ kexc25519_server(Kex *kex) kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->peer), buffer_len(&kex->peer), - buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), server_host_key_blob, sbloblen, client_pubkey, server_pubkey, diff --git a/kexdhc.c b/kexdhc.c index 53c3d9bcd..cd12df33d 100644 --- a/kexdhc.c +++ b/kexdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhc.c,v 1.15 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: kexdhc.c,v 1.16 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -133,8 +133,8 @@ kexdh_client(Kex *kex) kex_dh_hash( kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->my), buffer_len(&kex->my), - buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), server_host_key_blob, sbloblen, dh->pub_key, dh_server_pub, diff --git a/kexdhs.c b/kexdhs.c index 56aa5d031..34a215f8c 100644 --- a/kexdhs.c +++ b/kexdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhs.c,v 1.18 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: kexdhs.c,v 1.19 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -121,8 +121,8 @@ kexdh_server(Kex *kex) kex_dh_hash( kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->peer), buffer_len(&kex->peer), - buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), server_host_key_blob, sbloblen, dh_client_pub, dh->pub_key, diff --git a/kexecdhc.c b/kexecdhc.c index 2019940e5..df811c1c8 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhc.c,v 1.7 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: kexecdhc.c,v 1.8 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -128,8 +128,8 @@ kexecdh_client(Kex *kex) group, kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->my), buffer_len(&kex->my), - buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), server_host_key_blob, sbloblen, EC_KEY_get0_public_key(client_key), server_public, diff --git a/kexecdhs.c b/kexecdhs.c index 48bc56dc6..6bfad04cf 100644 --- a/kexecdhs.c +++ b/kexecdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhs.c,v 1.10 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: kexecdhs.c,v 1.11 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -113,8 +113,8 @@ kexecdh_server(Kex *kex) group, kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->peer), buffer_len(&kex->peer), - buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), server_host_key_blob, sbloblen, client_public, EC_KEY_get0_public_key(server_key), diff --git a/kexgexc.c b/kexgexc.c index a21a1d957..18d09cfb0 100644 --- a/kexgexc.c +++ b/kexgexc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexc.c,v 1.17 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: kexgexc.c,v 1.18 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -175,8 +175,8 @@ kexgex_client(Kex *kex) kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->my), buffer_len(&kex->my), - buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), server_host_key_blob, sbloblen, min, nbits, max, dh->p, dh->g, diff --git a/kexgexs.c b/kexgexs.c index ab90a9da1..1021e0bf6 100644 --- a/kexgexs.c +++ b/kexgexs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexs.c,v 1.19 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: kexgexs.c,v 1.20 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -165,8 +165,8 @@ kexgex_server(Kex *kex) kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(&kex->peer), buffer_len(&kex->peer), - buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(kex->peer), buffer_len(kex->peer), + buffer_ptr(kex->my), buffer_len(kex->my), server_host_key_blob, sbloblen, omin, onbits, omax, dh->p, dh->g, diff --git a/monitor.c b/monitor.c index 6858478ca..6ee291128 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.138 2015/01/14 20:05:27 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.139 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -110,38 +110,13 @@ static Gssctxt *gsscontext = NULL; /* Imports */ extern ServerOptions options; extern u_int utmp_len; -extern Newkeys *current_keys[]; -extern z_stream incoming_stream; -extern z_stream outgoing_stream; extern u_char session_id[]; extern Buffer auth_debug; extern int auth_debug_init; extern Buffer loginmsg; /* State exported from the child */ - -struct { - z_stream incoming; - z_stream outgoing; - u_char *keyin; - u_int keyinlen; - u_char *keyout; - u_int keyoutlen; - u_char *ivin; - u_int ivinlen; - u_char *ivout; - u_int ivoutlen; - u_char *ssh1key; - u_int ssh1keylen; - int ssh1cipher; - int ssh1protoflags; - u_char *input; - u_int ilen; - u_char *output; - u_int olen; - u_int64_t sent_bytes; - u_int64_t recv_bytes; -} child_state; +static struct sshbuf *child_state; /* Functions on the monitor that answer unprivileged requests */ @@ -506,6 +481,27 @@ monitor_sync(struct monitor *pmonitor) } } +/* Allocation functions for zlib */ +static void * +mm_zalloc(struct mm_master *mm, u_int ncount, u_int size) +{ + size_t len = (size_t) size * ncount; + void *address; + + if (len == 0 || ncount > SIZE_T_MAX / size) + fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size); + + address = mm_malloc(mm, len); + + return (address); +} + +static void +mm_zfree(struct mm_master *mm, void *address) +{ + mm_free(mm, address); +} + static int monitor_read_log(struct monitor *pmonitor) { @@ -1807,105 +1803,41 @@ mm_answer_audit_command(int socket, Buffer *m) void monitor_apply_keystate(struct monitor *pmonitor) { - if (compat20) { - set_newkeys(MODE_IN); - set_newkeys(MODE_OUT); - } else { - packet_set_protocol_flags(child_state.ssh1protoflags); - packet_set_encryption_key(child_state.ssh1key, - child_state.ssh1keylen, child_state.ssh1cipher); - free(child_state.ssh1key); - } - - /* for rc4 and other stateful ciphers */ - packet_set_keycontext(MODE_OUT, child_state.keyout); - free(child_state.keyout); - packet_set_keycontext(MODE_IN, child_state.keyin); - free(child_state.keyin); - - if (!compat20) { - packet_set_iv(MODE_OUT, child_state.ivout); - free(child_state.ivout); - packet_set_iv(MODE_IN, child_state.ivin); - free(child_state.ivin); + struct ssh *ssh = active_state; /* XXX */ + struct kex *kex; + int r; + + debug3("%s: packet_set_state", __func__); + if ((r = ssh_packet_set_state(ssh, child_state)) != 0) + fatal("%s: packet_set_state: %s", __func__, ssh_err(r)); + sshbuf_free(child_state); + child_state = NULL; + + if ((kex = ssh->kex) != 0) { + /* XXX set callbacks */ + kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; + kex->kex[KEX_ECDH_SHA2] = kexecdh_server; + kex->kex[KEX_C25519_SHA256] = kexc25519_server; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; + kex->host_key_index=&get_hostkey_index; + kex->sign = sshd_hostkey_sign; } - memcpy(&incoming_stream, &child_state.incoming, - sizeof(incoming_stream)); - memcpy(&outgoing_stream, &child_state.outgoing, - sizeof(outgoing_stream)); - /* Update with new address */ - if (options.compression) - mm_init_compression(pmonitor->m_zlib); - - packet_set_postauth(); + if (options.compression) { + ssh_packet_set_compress_hooks(ssh, pmonitor->m_zlib, + (ssh_packet_comp_alloc_func *)mm_zalloc, + (ssh_packet_comp_free_func *)mm_zfree); + } if (options.rekey_limit || options.rekey_interval) - packet_set_rekey_limits((u_int32_t)options.rekey_limit, + ssh_packet_set_rekey_limits(ssh, + (u_int32_t)options.rekey_limit, (time_t)options.rekey_interval); - - /* Network I/O buffers */ - /* XXX inefficient for large buffers, need: buffer_init_from_string */ - buffer_clear(packet_get_input()); - buffer_append(packet_get_input(), child_state.input, child_state.ilen); - explicit_bzero(child_state.input, child_state.ilen); - free(child_state.input); - - buffer_clear(packet_get_output()); - buffer_append(packet_get_output(), child_state.output, - child_state.olen); - explicit_bzero(child_state.output, child_state.olen); - free(child_state.output); - - /* Roaming */ - if (compat20) - roam_set_bytes(child_state.sent_bytes, child_state.recv_bytes); -} - -static Kex * -mm_get_kex(Buffer *m) -{ - Kex *kex; - void *blob; - u_int bloblen; - - kex = xcalloc(1, sizeof(*kex)); - kex->session_id = buffer_get_string(m, &kex->session_id_len); - if (session_id2 == NULL || - kex->session_id_len != session_id2_len || - timingsafe_bcmp(kex->session_id, session_id2, session_id2_len) != 0) - fatal("mm_get_get: internal error: bad session id"); - kex->we_need = buffer_get_int(m); -#ifdef WITH_OPENSSL - kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; - kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; - kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; - kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; - kex->kex[KEX_ECDH_SHA2] = kexecdh_server; -#endif - kex->kex[KEX_C25519_SHA256] = kexc25519_server; - kex->server = 1; - kex->hostkey_type = buffer_get_int(m); - kex->kex_type = buffer_get_int(m); - blob = buffer_get_string(m, &bloblen); - buffer_init(&kex->my); - buffer_append(&kex->my, blob, bloblen); - free(blob); - blob = buffer_get_string(m, &bloblen); - buffer_init(&kex->peer); - buffer_append(&kex->peer, blob, bloblen); - free(blob); - kex->done = 1; - kex->flags = buffer_get_int(m); - kex->client_version_string = buffer_get_string(m, NULL); - kex->server_version_string = buffer_get_string(m, NULL); - kex->load_host_public_key=&get_hostkey_public_by_type; - kex->load_host_private_key=&get_hostkey_private_by_type; - kex->host_key_index=&get_hostkey_index; - kex->sign = sshd_hostkey_sign; - - return (kex); } /* This function requries careful sanity checking */ @@ -1913,117 +1845,15 @@ mm_get_kex(Buffer *m) void mm_get_keystate(struct monitor *pmonitor) { - Buffer m; - u_char *blob, *p; - u_int bloblen, plen; - u_int32_t seqnr, packets; - u_int64_t blocks, bytes; - debug3("%s: Waiting for new keys", __func__); - buffer_init(&m); - mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, &m); - if (!compat20) { - child_state.ssh1protoflags = buffer_get_int(&m); - child_state.ssh1cipher = buffer_get_int(&m); - child_state.ssh1key = buffer_get_string(&m, - &child_state.ssh1keylen); - child_state.ivout = buffer_get_string(&m, - &child_state.ivoutlen); - child_state.ivin = buffer_get_string(&m, &child_state.ivinlen); - goto skip; - } else { - /* Get the Kex for rekeying */ - *pmonitor->m_pkex = mm_get_kex(&m); - } - - blob = buffer_get_string(&m, &bloblen); - current_keys[MODE_OUT] = mm_newkeys_from_blob(blob, bloblen); - free(blob); - - debug3("%s: Waiting for second key", __func__); - blob = buffer_get_string(&m, &bloblen); - current_keys[MODE_IN] = mm_newkeys_from_blob(blob, bloblen); - free(blob); - - /* Now get sequence numbers for the packets */ - seqnr = buffer_get_int(&m); - blocks = buffer_get_int64(&m); - packets = buffer_get_int(&m); - bytes = buffer_get_int64(&m); - packet_set_state(MODE_OUT, seqnr, blocks, packets, bytes); - seqnr = buffer_get_int(&m); - blocks = buffer_get_int64(&m); - packets = buffer_get_int(&m); - bytes = buffer_get_int64(&m); - packet_set_state(MODE_IN, seqnr, blocks, packets, bytes); - - skip: - /* Get the key context */ - child_state.keyout = buffer_get_string(&m, &child_state.keyoutlen); - child_state.keyin = buffer_get_string(&m, &child_state.keyinlen); - - debug3("%s: Getting compression state", __func__); - /* Get compression state */ - p = buffer_get_string(&m, &plen); - if (plen != sizeof(child_state.outgoing)) - fatal("%s: bad request size", __func__); - memcpy(&child_state.outgoing, p, sizeof(child_state.outgoing)); - free(p); - - p = buffer_get_string(&m, &plen); - if (plen != sizeof(child_state.incoming)) - fatal("%s: bad request size", __func__); - memcpy(&child_state.incoming, p, sizeof(child_state.incoming)); - free(p); - - /* Network I/O buffers */ - debug3("%s: Getting Network I/O buffers", __func__); - child_state.input = buffer_get_string(&m, &child_state.ilen); - child_state.output = buffer_get_string(&m, &child_state.olen); - - /* Roaming */ - if (compat20) { - child_state.sent_bytes = buffer_get_int64(&m); - child_state.recv_bytes = buffer_get_int64(&m); - } - - buffer_free(&m); -} - - -/* Allocation functions for zlib */ -void * -mm_zalloc(struct mm_master *mm, u_int ncount, u_int size) -{ - size_t len = (size_t) size * ncount; - void *address; - - if (len == 0 || ncount > SIZE_T_MAX / size) - fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size); - - address = mm_malloc(mm, len); - - return (address); + if ((child_state = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, + child_state); + debug3("%s: GOT new keys", __func__); } -void -mm_zfree(struct mm_master *mm, void *address) -{ - mm_free(mm, address); -} - -void -mm_init_compression(struct mm_master *mm) -{ - outgoing_stream.zalloc = (alloc_func)mm_zalloc; - outgoing_stream.zfree = (free_func)mm_zfree; - outgoing_stream.opaque = mm; - - incoming_stream.zalloc = (alloc_func)mm_zalloc; - incoming_stream.zfree = (free_func)mm_zfree; - incoming_stream.opaque = mm; -} /* XXX */ @@ -2060,6 +1890,7 @@ monitor_openfds(struct monitor *mon, int do_logfds) struct monitor * monitor_init(void) { + struct ssh *ssh = active_state; /* XXX */ struct monitor *mon; mon = xcalloc(1, sizeof(*mon)); @@ -2072,7 +1903,9 @@ monitor_init(void) mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE); /* Compression needs to share state across borders */ - mm_init_compression(mon->m_zlib); + ssh_packet_set_compress_hooks(ssh, mon->m_zlib, + (ssh_packet_comp_alloc_func *)mm_zalloc, + (ssh_packet_comp_free_func *)mm_zfree); } return mon; diff --git a/monitor.h b/monitor.h index 5bc41b513..93b8b66dd 100644 --- a/monitor.h +++ b/monitor.h @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.h,v 1.18 2014/01/29 06:18:35 djm Exp $ */ +/* $OpenBSD: monitor.h,v 1.19 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright 2002 Niels Provos @@ -75,7 +75,7 @@ struct monitor { int m_log_sendfd; struct mm_master *m_zback; struct mm_master *m_zlib; - struct Kex **m_pkex; + struct kex **m_pkex; pid_t m_pid; }; diff --git a/monitor_wrap.c b/monitor_wrap.c index b0dbb3f70..84df481d8 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.81 2015/01/13 19:31:40 markus Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.82 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -82,6 +82,8 @@ #include "servconf.h" #include "roaming.h" +#include "ssherr.h" + /* Imports */ extern int compat20; extern z_stream incoming_stream; @@ -470,239 +472,21 @@ mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) return (verified); } -/* Export key state after authentication */ -Newkeys * -mm_newkeys_from_blob(u_char *blob, int blen) -{ - Buffer b; - u_int len; - Newkeys *newkey = NULL; - Enc *enc; - Mac *mac; - Comp *comp; - - debug3("%s: %p(%d)", __func__, blob, blen); -#ifdef DEBUG_PK - dump_base64(stderr, blob, blen); -#endif - buffer_init(&b); - buffer_append(&b, blob, blen); - - newkey = xcalloc(1, sizeof(*newkey)); - enc = &newkey->enc; - mac = &newkey->mac; - comp = &newkey->comp; - - /* Enc structure */ - enc->name = buffer_get_string(&b, NULL); - buffer_get(&b, &enc->cipher, sizeof(enc->cipher)); - enc->enabled = buffer_get_int(&b); - enc->block_size = buffer_get_int(&b); - enc->key = buffer_get_string(&b, &enc->key_len); - enc->iv = buffer_get_string(&b, &enc->iv_len); - - if (enc->name == NULL || cipher_by_name(enc->name) != enc->cipher) - fatal("%s: bad cipher name %s or pointer %p", __func__, - enc->name, enc->cipher); - - /* Mac structure */ - if (cipher_authlen(enc->cipher) == 0) { - mac->name = buffer_get_string(&b, NULL); - if (mac->name == NULL || mac_setup(mac, mac->name) != 0) - fatal("%s: can not setup mac %s", __func__, mac->name); - mac->enabled = buffer_get_int(&b); - mac->key = buffer_get_string(&b, &len); - if (len > mac->key_len) - fatal("%s: bad mac key length: %u > %d", __func__, len, - mac->key_len); - mac->key_len = len; - } - - /* Comp structure */ - comp->type = buffer_get_int(&b); - comp->enabled = buffer_get_int(&b); - comp->name = buffer_get_string(&b, NULL); - - len = buffer_len(&b); - if (len != 0) - error("newkeys_from_blob: remaining bytes in blob %u", len); - buffer_free(&b); - return (newkey); -} - -int -mm_newkeys_to_blob(int mode, u_char **blobp, u_int *lenp) -{ - Buffer b; - int len; - Enc *enc; - Mac *mac; - Comp *comp; - Newkeys *newkey = (Newkeys *)packet_get_newkeys(mode); - - debug3("%s: converting %p", __func__, newkey); - - if (newkey == NULL) { - error("%s: newkey == NULL", __func__); - return 0; - } - enc = &newkey->enc; - mac = &newkey->mac; - comp = &newkey->comp; - - buffer_init(&b); - /* Enc structure */ - buffer_put_cstring(&b, enc->name); - /* The cipher struct is constant and shared, you export pointer */ - buffer_append(&b, &enc->cipher, sizeof(enc->cipher)); - buffer_put_int(&b, enc->enabled); - buffer_put_int(&b, enc->block_size); - buffer_put_string(&b, enc->key, enc->key_len); - packet_get_keyiv(mode, enc->iv, enc->iv_len); - buffer_put_string(&b, enc->iv, enc->iv_len); - - /* Mac structure */ - if (cipher_authlen(enc->cipher) == 0) { - buffer_put_cstring(&b, mac->name); - buffer_put_int(&b, mac->enabled); - buffer_put_string(&b, mac->key, mac->key_len); - } - - /* Comp structure */ - buffer_put_int(&b, comp->type); - buffer_put_int(&b, comp->enabled); - buffer_put_cstring(&b, comp->name); - - len = buffer_len(&b); - if (lenp != NULL) - *lenp = len; - if (blobp != NULL) { - *blobp = xmalloc(len); - memcpy(*blobp, buffer_ptr(&b), len); - } - explicit_bzero(buffer_ptr(&b), len); - buffer_free(&b); - return len; -} - -static void -mm_send_kex(Buffer *m, Kex *kex) -{ - buffer_put_string(m, kex->session_id, kex->session_id_len); - buffer_put_int(m, kex->we_need); - buffer_put_int(m, kex->hostkey_type); - buffer_put_int(m, kex->kex_type); - buffer_put_string(m, buffer_ptr(&kex->my), buffer_len(&kex->my)); - buffer_put_string(m, buffer_ptr(&kex->peer), buffer_len(&kex->peer)); - buffer_put_int(m, kex->flags); - buffer_put_cstring(m, kex->client_version_string); - buffer_put_cstring(m, kex->server_version_string); -} - void mm_send_keystate(struct monitor *monitor) { - Buffer m, *input, *output; - u_char *blob, *p; - u_int bloblen, plen; - u_int32_t seqnr, packets; - u_int64_t blocks, bytes; - - buffer_init(&m); - - if (!compat20) { - u_char iv[24]; - u_char *key; - u_int ivlen, keylen; - - buffer_put_int(&m, packet_get_protocol_flags()); - - buffer_put_int(&m, packet_get_ssh1_cipher()); - - debug3("%s: Sending ssh1 KEY+IV", __func__); - keylen = packet_get_encryption_key(NULL); - key = xmalloc(keylen+1); /* add 1 if keylen == 0 */ - keylen = packet_get_encryption_key(key); - buffer_put_string(&m, key, keylen); - explicit_bzero(key, keylen); - free(key); - - ivlen = packet_get_keyiv_len(MODE_OUT); - packet_get_keyiv(MODE_OUT, iv, ivlen); - buffer_put_string(&m, iv, ivlen); - ivlen = packet_get_keyiv_len(MODE_IN); - packet_get_keyiv(MODE_IN, iv, ivlen); - buffer_put_string(&m, iv, ivlen); - goto skip; - } else { - /* Kex for rekeying */ - mm_send_kex(&m, *monitor->m_pkex); - } - - debug3("%s: Sending new keys: %p %p", - __func__, packet_get_newkeys(MODE_OUT), - packet_get_newkeys(MODE_IN)); - - /* Keys from Kex */ - if (!mm_newkeys_to_blob(MODE_OUT, &blob, &bloblen)) - fatal("%s: conversion of newkeys failed", __func__); - - buffer_put_string(&m, blob, bloblen); - free(blob); - - if (!mm_newkeys_to_blob(MODE_IN, &blob, &bloblen)) - fatal("%s: conversion of newkeys failed", __func__); - - buffer_put_string(&m, blob, bloblen); - free(blob); - - packet_get_state(MODE_OUT, &seqnr, &blocks, &packets, &bytes); - buffer_put_int(&m, seqnr); - buffer_put_int64(&m, blocks); - buffer_put_int(&m, packets); - buffer_put_int64(&m, bytes); - packet_get_state(MODE_IN, &seqnr, &blocks, &packets, &bytes); - buffer_put_int(&m, seqnr); - buffer_put_int64(&m, blocks); - buffer_put_int(&m, packets); - buffer_put_int64(&m, bytes); - - debug3("%s: New keys have been sent", __func__); - skip: - /* More key context */ - plen = packet_get_keycontext(MODE_OUT, NULL); - p = xmalloc(plen+1); - packet_get_keycontext(MODE_OUT, p); - buffer_put_string(&m, p, plen); - free(p); - - plen = packet_get_keycontext(MODE_IN, NULL); - p = xmalloc(plen+1); - packet_get_keycontext(MODE_IN, p); - buffer_put_string(&m, p, plen); - free(p); - - /* Compression state */ - debug3("%s: Sending compression state", __func__); - buffer_put_string(&m, &outgoing_stream, sizeof(outgoing_stream)); - buffer_put_string(&m, &incoming_stream, sizeof(incoming_stream)); - - /* Network I/O buffers */ - input = (Buffer *)packet_get_input(); - output = (Buffer *)packet_get_output(); - buffer_put_string(&m, buffer_ptr(input), buffer_len(input)); - buffer_put_string(&m, buffer_ptr(output), buffer_len(output)); - - /* Roaming */ - if (compat20) { - buffer_put_int64(&m, get_sent_bytes()); - buffer_put_int64(&m, get_recv_bytes()); - } - - mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, &m); + struct ssh *ssh = active_state; /* XXX */ + struct sshbuf *m; + int r; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = ssh_packet_get_state(ssh, m)) != 0) + fatal("%s: get_state failed: %s", + __func__, ssh_err(r)); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, m); debug3("%s: Finished sending state", __func__); - - buffer_free(&m); + sshbuf_free(m); } int diff --git a/monitor_wrap.h b/monitor_wrap.h index 18c25010d..d97e8db1e 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.h,v 1.24 2014/01/29 06:18:35 djm Exp $ */ +/* $OpenBSD: monitor_wrap.h,v 1.25 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright 2002 Niels Provos @@ -87,7 +87,7 @@ void mm_ssh1_session_id(u_char *); int mm_ssh1_session_key(BIGNUM *); /* Key export functions */ -struct Newkeys *mm_newkeys_from_blob(u_char *, int); +struct newkeys *mm_newkeys_from_blob(u_char *, int); int mm_newkeys_to_blob(int, u_char **, u_int *); void monitor_apply_keystate(struct monitor *); @@ -103,9 +103,6 @@ int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); int mm_skey_respond(void *, u_int, char **); /* zlib allocation hooks */ - -void *mm_zalloc(struct mm_master *, u_int, u_int); -void mm_zfree(struct mm_master *, void *); void mm_init_compression(struct mm_master *); #endif /* _MM_WRAP_H_ */ diff --git a/opacket.c b/opacket.c new file mode 100644 index 000000000..63b419d5c --- /dev/null +++ b/opacket.c @@ -0,0 +1,279 @@ +/* Written by Markus Friedl. Placed in the public domain. */ + +#include "includes.h" + +#include "ssherr.h" +#include "packet.h" +#include "log.h" + +struct ssh *active_state, *backup_state; + +/* Map old to new API */ + +void +ssh_packet_start(struct ssh *ssh, u_char type) +{ + int r; + + if ((r = sshpkt_start(ssh, type)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_char(struct ssh *ssh, int value) +{ + u_char ch = value; + int r; + + if ((r = sshpkt_put_u8(ssh, ch)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_int(struct ssh *ssh, u_int value) +{ + int r; + + if ((r = sshpkt_put_u32(ssh, value)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_int64(struct ssh *ssh, u_int64_t value) +{ + int r; + + if ((r = sshpkt_put_u64(ssh, value)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_string(struct ssh *ssh, const void *buf, u_int len) +{ + int r; + + if ((r = sshpkt_put_string(ssh, buf, len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_cstring(struct ssh *ssh, const char *str) +{ + int r; + + if ((r = sshpkt_put_cstring(ssh, str)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_raw(struct ssh *ssh, const void *buf, u_int len) +{ + int r; + + if ((r = sshpkt_put(ssh, buf, len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +#ifdef WITH_OPENSSL +void +ssh_packet_put_bignum(struct ssh *ssh, BIGNUM * value) +{ + int r; + + if ((r = sshpkt_put_bignum1(ssh, value)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_bignum2(struct ssh *ssh, BIGNUM * value) +{ + int r; + + if ((r = sshpkt_put_bignum2(ssh, value)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_put_ecpoint(struct ssh *ssh, const EC_GROUP *curve, + const EC_POINT *point) +{ + int r; + + if ((r = sshpkt_put_ec(ssh, point, curve)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} +#endif /* WITH_OPENSSL */ + +void +ssh_packet_send(struct ssh *ssh) +{ + int r; + + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +u_int +ssh_packet_get_char(struct ssh *ssh) +{ + u_char ch; + int r; + + if ((r = sshpkt_get_u8(ssh, &ch)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + return ch; +} + +u_int +ssh_packet_get_int(struct ssh *ssh) +{ + u_int val; + int r; + + if ((r = sshpkt_get_u32(ssh, &val)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + return val; +} + +u_int64_t +ssh_packet_get_int64(struct ssh *ssh) +{ + u_int64_t val; + int r; + + if ((r = sshpkt_get_u64(ssh, &val)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + return val; +} + +#ifdef WITH_OPENSSL +void +ssh_packet_get_bignum(struct ssh *ssh, BIGNUM * value) +{ + int r; + + if ((r = sshpkt_get_bignum1(ssh, value)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_get_bignum2(struct ssh *ssh, BIGNUM * value) +{ + int r; + + if ((r = sshpkt_get_bignum2(ssh, value)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} + +void +ssh_packet_get_ecpoint(struct ssh *ssh, const EC_GROUP *curve, EC_POINT *point) +{ + int r; + + if ((r = sshpkt_get_ec(ssh, point, curve)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} +#endif /* WITH_OPENSSL */ + +void * +ssh_packet_get_string(struct ssh *ssh, u_int *length_ptr) +{ + int r; + size_t len; + u_char *val; + + if ((r = sshpkt_get_string(ssh, &val, &len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + if (length_ptr != NULL) + *length_ptr = (u_int)len; + return val; +} + +const void * +ssh_packet_get_string_ptr(struct ssh *ssh, u_int *length_ptr) +{ + int r; + size_t len; + const u_char *val; + + if ((r = sshpkt_get_string_direct(ssh, &val, &len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + if (length_ptr != NULL) + *length_ptr = (u_int)len; + return val; +} + +char * +ssh_packet_get_cstring(struct ssh *ssh, u_int *length_ptr) +{ + int r; + size_t len; + char *val; + + if ((r = sshpkt_get_cstring(ssh, &val, &len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + if (length_ptr != NULL) + *length_ptr = (u_int)len; + return val; +} + +/* Old API, that had to be reimplemented */ + +void +packet_set_connection(int fd_in, int fd_out) +{ + active_state = ssh_packet_set_connection(active_state, fd_in, fd_out); +} + +void +packet_backup_state(void) +{ + ssh_packet_backup_state(active_state, backup_state); +} + +void +packet_restore_state(void) +{ + ssh_packet_restore_state(active_state, backup_state); +} + +u_int +packet_get_char(void) +{ + return (ssh_packet_get_char(active_state)); +} + +u_int +packet_get_int(void) +{ + return (ssh_packet_get_int(active_state)); +} + +int +packet_read_seqnr(u_int32_t *seqnr) +{ + u_char type; + int r; + + if ((r = ssh_packet_read_seqnr(active_state, &type, seqnr))) + fatal("%s: %s", __func__, ssh_err(r)); + return type; +} + +int +packet_read_poll_seqnr(u_int32_t *seqnr) +{ + u_char type; + int r; + + if ((r = ssh_packet_read_poll_seqnr(active_state, &type, seqnr))) + fatal("%s: %s", __func__, ssh_err(r)); + return type; +} + +void +packet_close(void) +{ + ssh_packet_close(active_state); + active_state = NULL; +} diff --git a/opacket.h b/opacket.h new file mode 100644 index 000000000..1e15626eb --- /dev/null +++ b/opacket.h @@ -0,0 +1,173 @@ +#ifndef _OPACKET_H +/* Written by Markus Friedl. Placed in the public domain. */ + +/* Map old to new API */ +void ssh_packet_start(struct ssh *, u_char); +void ssh_packet_put_char(struct ssh *, int ch); +void ssh_packet_put_int(struct ssh *, u_int value); +void ssh_packet_put_int64(struct ssh *, u_int64_t value); +void ssh_packet_put_bignum(struct ssh *, BIGNUM * value); +void ssh_packet_put_bignum2(struct ssh *, BIGNUM * value); +void ssh_packet_put_ecpoint(struct ssh *, const EC_GROUP *, const EC_POINT *); +void ssh_packet_put_string(struct ssh *, const void *buf, u_int len); +void ssh_packet_put_cstring(struct ssh *, const char *str); +void ssh_packet_put_raw(struct ssh *, const void *buf, u_int len); +void ssh_packet_send(struct ssh *); + +u_int ssh_packet_get_char(struct ssh *); +u_int ssh_packet_get_int(struct ssh *); +u_int64_t ssh_packet_get_int64(struct ssh *); +void ssh_packet_get_bignum(struct ssh *, BIGNUM * value); +void ssh_packet_get_bignum2(struct ssh *, BIGNUM * value); +void ssh_packet_get_ecpoint(struct ssh *, const EC_GROUP *, EC_POINT *); +void *ssh_packet_get_string(struct ssh *, u_int *length_ptr); +char *ssh_packet_get_cstring(struct ssh *, u_int *length_ptr); + +/* don't allow remaining bytes after the end of the message */ +#define ssh_packet_check_eom(ssh) \ +do { \ + int _len = ssh_packet_remaining(ssh); \ + if (_len > 0) { \ + logit("Packet integrity error (%d bytes remaining) at %s:%d", \ + _len ,__FILE__, __LINE__); \ + ssh_packet_disconnect(ssh, \ + "Packet integrity error."); \ + } \ +} while (0) + +/* old API */ +void packet_close(void); +u_int packet_get_char(void); +u_int packet_get_int(void); +void packet_backup_state(void); +void packet_restore_state(void); +void packet_set_connection(int, int); +int packet_read_seqnr(u_int32_t *); +int packet_read_poll_seqnr(u_int32_t *); +#define packet_set_timeout(timeout, count) \ + ssh_packet_set_timeout(active_state, (timeout), (count)) +#define packet_connection_is_on_socket() \ + ssh_packet_connection_is_on_socket(active_state) +#define packet_set_nonblocking() \ + ssh_packet_set_nonblocking(active_state) +#define packet_get_connection_in() \ + ssh_packet_get_connection_in(active_state) +#define packet_get_connection_out() \ + ssh_packet_get_connection_out(active_state) +#define packet_set_protocol_flags(protocol_flags) \ + ssh_packet_set_protocol_flags(active_state, (protocol_flags)) +#define packet_get_protocol_flags() \ + ssh_packet_get_protocol_flags(active_state) +#define packet_start_compression(level) \ + ssh_packet_start_compression(active_state, (level)) +#define packet_set_encryption_key(key, keylen, number) \ + ssh_packet_set_encryption_key(active_state, (key), (keylen), (number)) +#define packet_start(type) \ + ssh_packet_start(active_state, (type)) +#define packet_put_char(value) \ + ssh_packet_put_char(active_state, (value)) +#define packet_put_int(value) \ + ssh_packet_put_int(active_state, (value)) +#define packet_put_int64(value) \ + ssh_packet_put_int64(active_state, (value)) +#define packet_put_string( buf, len) \ + ssh_packet_put_string(active_state, (buf), (len)) +#define packet_put_cstring(str) \ + ssh_packet_put_cstring(active_state, (str)) +#define packet_put_raw(buf, len) \ + ssh_packet_put_raw(active_state, (buf), (len)) +#define packet_put_bignum(value) \ + ssh_packet_put_bignum(active_state, (value)) +#define packet_put_bignum2(value) \ + ssh_packet_put_bignum2(active_state, (value)) +#define packet_send() \ + ssh_packet_send(active_state) +#define packet_read() \ + ssh_packet_read(active_state) +#define packet_read_expect(expected_type) \ + ssh_packet_read_expect(active_state, (expected_type)) +#define packet_process_incoming(buf, len) \ + ssh_packet_process_incoming(active_state, (buf), (len)) +#define packet_get_int64() \ + ssh_packet_get_int64(active_state) +#define packet_get_bignum(value) \ + ssh_packet_get_bignum(active_state, (value)) +#define packet_get_bignum2(value) \ + ssh_packet_get_bignum2(active_state, (value)) +#define packet_remaining() \ + ssh_packet_remaining(active_state) +#define packet_get_string(length_ptr) \ + ssh_packet_get_string(active_state, (length_ptr)) +#define packet_get_string_ptr(length_ptr) \ + ssh_packet_get_string_ptr(active_state, (length_ptr)) +#define packet_get_cstring(length_ptr) \ + ssh_packet_get_cstring(active_state, (length_ptr)) +#define packet_send_debug(fmt, args...) \ + ssh_packet_send_debug(active_state, (fmt), ##args) +#define packet_disconnect(fmt, args...) \ + ssh_packet_disconnect(active_state, (fmt), ##args) +#define packet_write_poll() \ + ssh_packet_write_poll(active_state) +#define packet_write_wait() \ + ssh_packet_write_wait(active_state) +#define packet_have_data_to_write() \ + ssh_packet_have_data_to_write(active_state) +#define packet_not_very_much_data_to_write() \ + ssh_packet_not_very_much_data_to_write(active_state) +#define packet_set_interactive(interactive, qos_interactive, qos_bulk) \ + ssh_packet_set_interactive(active_state, (interactive), (qos_interactive), (qos_bulk)) +#define packet_is_interactive() \ + ssh_packet_is_interactive(active_state) +#define packet_set_maxsize(s) \ + ssh_packet_set_maxsize(active_state, (s)) +#define packet_inc_alive_timeouts() \ + ssh_packet_inc_alive_timeouts(active_state) +#define packet_set_alive_timeouts(ka) \ + ssh_packet_set_alive_timeouts(active_state, (ka)) +#define packet_get_maxsize() \ + ssh_packet_get_maxsize(active_state) +#define packet_add_padding(pad) \ + sshpkt_add_padding(active_state, (pad)) +#define packet_send_ignore(nbytes) \ + ssh_packet_send_ignore(active_state, (nbytes)) +#define packet_need_rekeying() \ + ssh_packet_need_rekeying(active_state) +#define packet_set_rekey_limit(bytes) \ + ssh_packet_set_rekey_limit(active_state, (bytes)) +#define packet_set_server() \ + ssh_packet_set_server(active_state) +#define packet_set_authenticated() \ + ssh_packet_set_authenticated(active_state) +#define packet_get_input() \ + ssh_packet_get_input(active_state) +#define packet_get_output() \ + ssh_packet_get_output(active_state) +#define packet_set_compress_hooks(ctx, allocfunc, freefunc) \ + ssh_packet_set_compress_hooks(active_state, ctx, \ + allocfunc, freefunc); +#define packet_check_eom() \ + ssh_packet_check_eom(active_state) +#define set_newkeys(mode) \ + ssh_set_newkeys(active_state, (mode)) +#define packet_get_state(m) \ + ssh_packet_get_state(active_state, m) +#define packet_set_state(m) \ + ssh_packet_set_state(active_state, m) +#if 0 +#define get_remote_ipaddr() \ + ssh_remote_ipaddr(active_state) +#endif +#define packet_get_raw(lenp) \ + sshpkt_ptr(active_state, lenp) +#define packet_get_ecpoint(c,p) \ + ssh_packet_get_ecpoint(active_state, c, p) +#define packet_put_ecpoint(c,p) \ + ssh_packet_put_ecpoint(active_state, c, p) +#define packet_get_rekey_timeout() \ + ssh_packet_get_rekey_timeout(active_state) +#define packet_set_rekey_limits(x,y) \ + ssh_packet_set_rekey_limits(active_state, x, y) +#define packet_get_bytes(x,y) \ + ssh_packet_get_bytes(active_state, x, y) + +#endif /* _OPACKET_H */ diff --git a/packet.c b/packet.c index d6f9ff36b..3306e4d6c 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.200 2015/01/13 19:31:40 markus Exp $ */ +/* $OpenBSD: packet.c,v 1.201 2015/01/19 19:52:16 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -60,17 +60,19 @@ #include #include +#include + +#include "buffer.h" /* typedefs XXX */ +#include "key.h" /* typedefs XXX */ + #include "xmalloc.h" -#include "buffer.h" -#include "packet.h" #include "crc32.h" -#include "compress.h" #include "deattack.h" #include "compat.h" #include "ssh1.h" #include "ssh2.h" #include "cipher.h" -#include "key.h" +#include "sshkey.h" #include "kex.h" #include "digest.h" #include "mac.h" @@ -79,8 +81,10 @@ #include "misc.h" #include "channels.h" #include "ssh.h" -#include "ssherr.h" +#include "packet.h" #include "roaming.h" +#include "ssherr.h" +#include "sshbuf.h" #ifdef PACKET_DEBUG #define DBG(x) x @@ -100,7 +104,7 @@ struct packet_state { struct packet { TAILQ_ENTRY(packet) next; u_char type; - Buffer payload; + struct sshbuf *payload; }; struct session_state { @@ -117,26 +121,33 @@ struct session_state { u_int remote_protocol_flags; /* Encryption context for receiving data. Only used for decryption. */ - CipherContext receive_context; + struct sshcipher_ctx receive_context; /* Encryption context for sending data. Only used for encryption. */ - CipherContext send_context; + struct sshcipher_ctx send_context; /* Buffer for raw input data from the socket. */ - Buffer input; + struct sshbuf *input; /* Buffer for raw output data going to the socket. */ - Buffer output; + struct sshbuf *output; /* Buffer for the partial outgoing packet being constructed. */ - Buffer outgoing_packet; + struct sshbuf *outgoing_packet; /* Buffer for the incoming packet currently being processed. */ - Buffer incoming_packet; + struct sshbuf *incoming_packet; /* Scratch buffer for packet compression/decompression. */ - Buffer compression_buffer; - int compression_buffer_ready; + struct sshbuf *compression_buffer; + + /* Incoming/outgoing compression dictionaries */ + z_stream compression_in_stream; + z_stream compression_out_stream; + int compression_in_started; + int compression_out_started; + int compression_in_failures; + int compression_out_failures; /* * Flag indicating whether packet compression/decompression is @@ -165,7 +176,7 @@ struct session_state { int packet_timeout_ms; /* Session key information for Encryption and MAC */ - Newkeys *newkeys[MODE_MAX]; + struct newkeys *newkeys[MODE_MAX]; struct packet_state p_read, p_send; /* Volume-based rekeying */ @@ -185,7 +196,7 @@ struct session_state { /* XXX discard incoming data after MAC error */ u_int packet_discard; - Mac *packet_discard_mac; + struct sshmac *packet_discard_mac; /* Used in packet_read_poll2() */ u_int packlen; @@ -199,121 +210,164 @@ struct session_state { /* Used in packet_set_maxsize */ int set_maxsize_called; - TAILQ_HEAD(, packet) outgoing; -}; + /* One-off warning about weak ciphers */ + int cipher_warning_done; -static struct session_state *active_state, *backup_state; + /* SSH1 CRC compensation attack detector */ + struct deattack_ctx deattack; -static struct session_state * -alloc_session_state(void) -{ - struct session_state *s = xcalloc(1, sizeof(*s)); + TAILQ_HEAD(, packet) outgoing; +}; - s->connection_in = -1; - s->connection_out = -1; - s->max_packet_size = 32768; - s->packet_timeout_ms = -1; - return s; +struct ssh * +ssh_alloc_session_state(void) +{ + struct ssh *ssh = NULL; + struct session_state *state = NULL; + + if ((ssh = calloc(1, sizeof(*ssh))) == NULL || + (state = calloc(1, sizeof(*state))) == NULL || + (state->input = sshbuf_new()) == NULL || + (state->output = sshbuf_new()) == NULL || + (state->outgoing_packet = sshbuf_new()) == NULL || + (state->incoming_packet = sshbuf_new()) == NULL) + goto fail; + TAILQ_INIT(&state->outgoing); + state->connection_in = -1; + state->connection_out = -1; + state->max_packet_size = 32768; + state->packet_timeout_ms = -1; + state->p_send.packets = state->p_read.packets = 0; + state->initialized = 1; + /* + * ssh_packet_send2() needs to queue packets until + * we've done the initial key exchange. + */ + state->rekeying = 1; + ssh->state = state; + return ssh; + fail: + if (state) { + sshbuf_free(state->input); + sshbuf_free(state->output); + sshbuf_free(state->incoming_packet); + sshbuf_free(state->outgoing_packet); + free(state); + } + free(ssh); + return NULL; } /* * Sets the descriptors used for communication. Disables encryption until * packet_set_encryption_key is called. */ -void -packet_set_connection(int fd_in, int fd_out) +struct ssh * +ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out) { - const Cipher *none = cipher_by_name("none"); + struct session_state *state; + const struct sshcipher *none = cipher_by_name("none"); int r; if (none == NULL) - fatal("packet_set_connection: cannot load cipher 'none'"); - if (active_state == NULL) - active_state = alloc_session_state(); - active_state->connection_in = fd_in; - active_state->connection_out = fd_out; - if ((r = cipher_init(&active_state->send_context, none, + fatal("%s: cannot load cipher 'none'", __func__); + if (ssh == NULL) + ssh = ssh_alloc_session_state(); + if (ssh == NULL) + fatal("%s: cound not allocate state", __func__); + state = ssh->state; + state->connection_in = fd_in; + state->connection_out = fd_out; + if ((r = cipher_init(&state->send_context, none, (const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || - (r = cipher_init(&active_state->receive_context, none, + (r = cipher_init(&state->receive_context, none, (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) - fatal("%s: cipher_init: %s", __func__, ssh_err(r)); - active_state->newkeys[MODE_IN] = active_state->newkeys[MODE_OUT] = NULL; - if (!active_state->initialized) { - active_state->initialized = 1; - buffer_init(&active_state->input); - buffer_init(&active_state->output); - buffer_init(&active_state->outgoing_packet); - buffer_init(&active_state->incoming_packet); - TAILQ_INIT(&active_state->outgoing); - active_state->p_send.packets = active_state->p_read.packets = 0; - } + fatal("%s: cipher_init failed: %s", __func__, ssh_err(r)); + state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; + deattack_init(&state->deattack); + return ssh; } void -packet_set_timeout(int timeout, int count) +ssh_packet_set_timeout(struct ssh *ssh, int timeout, int count) { + struct session_state *state = ssh->state; + if (timeout <= 0 || count <= 0) { - active_state->packet_timeout_ms = -1; + state->packet_timeout_ms = -1; return; } if ((INT_MAX / 1000) / count < timeout) - active_state->packet_timeout_ms = INT_MAX; + state->packet_timeout_ms = INT_MAX; else - active_state->packet_timeout_ms = timeout * count * 1000; + state->packet_timeout_ms = timeout * count * 1000; } -static void -packet_stop_discard(void) +int +ssh_packet_stop_discard(struct ssh *ssh) { - if (active_state->packet_discard_mac) { + struct session_state *state = ssh->state; + int r; + + if (state->packet_discard_mac) { char buf[1024]; - + memset(buf, 'a', sizeof(buf)); - while (buffer_len(&active_state->incoming_packet) < + while (sshbuf_len(state->incoming_packet) < PACKET_MAX_SIZE) - buffer_append(&active_state->incoming_packet, buf, - sizeof(buf)); - (void) mac_compute(active_state->packet_discard_mac, - active_state->p_read.seqnr, - buffer_ptr(&active_state->incoming_packet), - PACKET_MAX_SIZE, NULL, 0); - } - logit("Finished discarding for %.200s", get_remote_ipaddr()); - cleanup_exit(255); + if ((r = sshbuf_put(state->incoming_packet, buf, + sizeof(buf))) != 0) + return r; + (void) mac_compute(state->packet_discard_mac, + state->p_read.seqnr, + sshbuf_ptr(state->incoming_packet), PACKET_MAX_SIZE, + NULL, 0); + } + logit("Finished discarding for %.200s", ssh_remote_ipaddr(ssh)); + return SSH_ERR_MAC_INVALID; } -static void -packet_start_discard(Enc *enc, Mac *mac, u_int packet_length, u_int discard) +static int +ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, + struct sshmac *mac, u_int packet_length, u_int discard) { - if (enc == NULL || !cipher_is_cbc(enc->cipher) || (mac && mac->etm)) - packet_disconnect("Packet corrupt"); + struct session_state *state = ssh->state; + int r; + + if (enc == NULL || !cipher_is_cbc(enc->cipher) || (mac && mac->etm)) { + if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) + return r; + return SSH_ERR_MAC_INVALID; + } if (packet_length != PACKET_MAX_SIZE && mac && mac->enabled) - active_state->packet_discard_mac = mac; - if (buffer_len(&active_state->input) >= discard) - packet_stop_discard(); - active_state->packet_discard = discard - - buffer_len(&active_state->input); + state->packet_discard_mac = mac; + if (sshbuf_len(state->input) >= discard && + (r = ssh_packet_stop_discard(ssh)) != 0) + return r; + state->packet_discard = discard - sshbuf_len(state->input); + return 0; } /* Returns 1 if remote host is connected via socket, 0 if not. */ int -packet_connection_is_on_socket(void) +ssh_packet_connection_is_on_socket(struct ssh *ssh) { + struct session_state *state = ssh->state; struct sockaddr_storage from, to; socklen_t fromlen, tolen; /* filedescriptors in and out are the same, so it's a socket */ - if (active_state->connection_in == active_state->connection_out) + if (state->connection_in == state->connection_out) return 1; fromlen = sizeof(from); memset(&from, 0, sizeof(from)); - if (getpeername(active_state->connection_in, (struct sockaddr *)&from, + if (getpeername(state->connection_in, (struct sockaddr *)&from, &fromlen) < 0) return 0; tolen = sizeof(to); memset(&to, 0, sizeof(to)); - if (getpeername(active_state->connection_out, (struct sockaddr *)&to, + if (getpeername(state->connection_out, (struct sockaddr *)&to, &tolen) < 0) return 0; if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0) @@ -323,127 +377,23 @@ packet_connection_is_on_socket(void) return 1; } -/* - * Exports an IV from the CipherContext required to export the key - * state back from the unprivileged child to the privileged parent - * process. - */ - -void -packet_get_keyiv(int mode, u_char *iv, u_int len) -{ - CipherContext *cc; - int r; - - if (mode == MODE_OUT) - cc = &active_state->send_context; - else - cc = &active_state->receive_context; - - if ((r = cipher_get_keyiv(cc, iv, len)) != 0) - fatal("%s: cipher_get_keyiv: %s", __func__, ssh_err(r)); -} - -int -packet_get_keycontext(int mode, u_char *dat) -{ - CipherContext *cc; - - if (mode == MODE_OUT) - cc = &active_state->send_context; - else - cc = &active_state->receive_context; - - return (cipher_get_keycontext(cc, dat)); -} - void -packet_set_keycontext(int mode, u_char *dat) +ssh_packet_get_bytes(struct ssh *ssh, u_int64_t *ibytes, u_int64_t *obytes) { - CipherContext *cc; - - if (mode == MODE_OUT) - cc = &active_state->send_context; - else - cc = &active_state->receive_context; - - cipher_set_keycontext(cc, dat); + if (ibytes) + *ibytes = ssh->state->p_read.bytes; + if (obytes) + *obytes = ssh->state->p_send.bytes; } int -packet_get_keyiv_len(int mode) -{ - CipherContext *cc; - - if (mode == MODE_OUT) - cc = &active_state->send_context; - else - cc = &active_state->receive_context; - - return (cipher_get_keyiv_len(cc)); -} - -void -packet_set_iv(int mode, u_char *dat) -{ - CipherContext *cc; - int r; - - if (mode == MODE_OUT) - cc = &active_state->send_context; - else - cc = &active_state->receive_context; - - if ((r = cipher_set_keyiv(cc, dat)) != 0) - fatal("%s: cipher_set_keyiv: %s", __func__, ssh_err(r)); -} - -int -packet_get_ssh1_cipher(void) -{ - return (cipher_get_number(active_state->receive_context.cipher)); -} - -void -packet_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks, - u_int32_t *packets, u_int64_t *bytes) -{ - struct packet_state *state; - - state = (mode == MODE_IN) ? - &active_state->p_read : &active_state->p_send; - if (seqnr) - *seqnr = state->seqnr; - if (blocks) - *blocks = state->blocks; - if (packets) - *packets = state->packets; - if (bytes) - *bytes = state->bytes; -} - -void -packet_set_state(int mode, u_int32_t seqnr, u_int64_t blocks, u_int32_t packets, - u_int64_t bytes) -{ - struct packet_state *state; - - state = (mode == MODE_IN) ? - &active_state->p_read : &active_state->p_send; - state->seqnr = seqnr; - state->blocks = blocks; - state->packets = packets; - state->bytes = bytes; -} - -static int -packet_connection_af(void) +ssh_packet_connection_af(struct ssh *ssh) { struct sockaddr_storage to; socklen_t tolen = sizeof(to); memset(&to, 0, sizeof(to)); - if (getsockname(active_state->connection_out, (struct sockaddr *)&to, + if (getsockname(ssh->state->connection_out, (struct sockaddr *)&to, &tolen) < 0) return 0; #ifdef IPV4_IN_IPV6 @@ -457,72 +407,125 @@ packet_connection_af(void) /* Sets the connection into non-blocking mode. */ void -packet_set_nonblocking(void) +ssh_packet_set_nonblocking(struct ssh *ssh) { /* Set the socket into non-blocking mode. */ - set_nonblock(active_state->connection_in); + set_nonblock(ssh->state->connection_in); - if (active_state->connection_out != active_state->connection_in) - set_nonblock(active_state->connection_out); + if (ssh->state->connection_out != ssh->state->connection_in) + set_nonblock(ssh->state->connection_out); } /* Returns the socket used for reading. */ int -packet_get_connection_in(void) +ssh_packet_get_connection_in(struct ssh *ssh) { - return active_state->connection_in; + return ssh->state->connection_in; } /* Returns the descriptor used for writing. */ int -packet_get_connection_out(void) +ssh_packet_get_connection_out(struct ssh *ssh) +{ + return ssh->state->connection_out; +} + +/* + * Returns the IP-address of the remote host as a string. The returned + * string must not be freed. + */ + +const char * +ssh_remote_ipaddr(struct ssh *ssh) { - return active_state->connection_out; + /* Check whether we have cached the ipaddr. */ + if (ssh->remote_ipaddr == NULL) + ssh->remote_ipaddr = ssh_packet_connection_is_on_socket(ssh) ? + get_peer_ipaddr(ssh->state->connection_in) : + strdup("UNKNOWN"); + if (ssh->remote_ipaddr == NULL) + return "UNKNOWN"; + return ssh->remote_ipaddr; } /* Closes the connection and clears and frees internal data structures. */ void -packet_close(void) +ssh_packet_close(struct ssh *ssh) { - if (!active_state->initialized) + struct session_state *state = ssh->state; + int r; + u_int mode; + + if (!state->initialized) return; - active_state->initialized = 0; - if (active_state->connection_in == active_state->connection_out) { - shutdown(active_state->connection_out, SHUT_RDWR); - close(active_state->connection_out); + state->initialized = 0; + if (state->connection_in == state->connection_out) { + shutdown(state->connection_out, SHUT_RDWR); + close(state->connection_out); } else { - close(active_state->connection_in); - close(active_state->connection_out); + close(state->connection_in); + close(state->connection_out); + } + sshbuf_free(state->input); + sshbuf_free(state->output); + sshbuf_free(state->outgoing_packet); + sshbuf_free(state->incoming_packet); + for (mode = 0; mode < MODE_MAX; mode++) + kex_free_newkeys(state->newkeys[mode]); + if (state->compression_buffer) { + sshbuf_free(state->compression_buffer); + if (state->compression_out_started) { + z_streamp stream = &state->compression_out_stream; + debug("compress outgoing: " + "raw data %llu, compressed %llu, factor %.2f", + (unsigned long long)stream->total_in, + (unsigned long long)stream->total_out, + stream->total_in == 0 ? 0.0 : + (double) stream->total_out / stream->total_in); + if (state->compression_out_failures == 0) + deflateEnd(stream); + } + if (state->compression_in_started) { + z_streamp stream = &state->compression_out_stream; + debug("compress incoming: " + "raw data %llu, compressed %llu, factor %.2f", + (unsigned long long)stream->total_out, + (unsigned long long)stream->total_in, + stream->total_out == 0 ? 0.0 : + (double) stream->total_in / stream->total_out); + if (state->compression_in_failures == 0) + inflateEnd(stream); + } } - buffer_free(&active_state->input); - buffer_free(&active_state->output); - buffer_free(&active_state->outgoing_packet); - buffer_free(&active_state->incoming_packet); - if (active_state->compression_buffer_ready) { - buffer_free(&active_state->compression_buffer); - buffer_compress_uninit(); + if ((r = cipher_cleanup(&state->send_context)) != 0) + error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); + if ((r = cipher_cleanup(&state->receive_context)) != 0) + error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); + if (ssh->remote_ipaddr) { + free(ssh->remote_ipaddr); + ssh->remote_ipaddr = NULL; } - cipher_cleanup(&active_state->send_context); - cipher_cleanup(&active_state->receive_context); + free(ssh->state); + ssh->state = NULL; } /* Sets remote side protocol flags. */ void -packet_set_protocol_flags(u_int protocol_flags) +ssh_packet_set_protocol_flags(struct ssh *ssh, u_int protocol_flags) { - active_state->remote_protocol_flags = protocol_flags; + ssh->state->remote_protocol_flags = protocol_flags; } /* Returns the remote protocol flags set earlier by the above function. */ u_int -packet_get_protocol_flags(void) +ssh_packet_get_protocol_flags(struct ssh *ssh) { - return active_state->remote_protocol_flags; + return ssh->state->remote_protocol_flags; } /* @@ -530,135 +533,275 @@ packet_get_protocol_flags(void) * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ -static void -packet_init_compression(void) +static int +ssh_packet_init_compression(struct ssh *ssh) { - if (active_state->compression_buffer_ready == 1) - return; - active_state->compression_buffer_ready = 1; - buffer_init(&active_state->compression_buffer); + if (!ssh->state->compression_buffer && + ((ssh->state->compression_buffer = sshbuf_new()) == NULL)) + return SSH_ERR_ALLOC_FAIL; + return 0; } -void -packet_start_compression(int level) -{ - if (active_state->packet_compression && !compat20) - fatal("Compression already enabled."); - active_state->packet_compression = 1; - packet_init_compression(); - buffer_compress_init_send(level); - buffer_compress_init_recv(); +static int +start_compression_out(struct ssh *ssh, int level) +{ + if (level < 1 || level > 9) + return SSH_ERR_INVALID_ARGUMENT; + debug("Enabling compression at level %d.", level); + if (ssh->state->compression_out_started == 1) + deflateEnd(&ssh->state->compression_out_stream); + switch (deflateInit(&ssh->state->compression_out_stream, level)) { + case Z_OK: + ssh->state->compression_out_started = 1; + break; + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + default: + return SSH_ERR_INTERNAL_ERROR; + } + return 0; } -/* - * Causes any further packets to be encrypted using the given key. The same - * key is used for both sending and reception. However, both directions are - * encrypted independently of each other. - */ +static int +start_compression_in(struct ssh *ssh) +{ + if (ssh->state->compression_in_started == 1) + inflateEnd(&ssh->state->compression_in_stream); + switch (inflateInit(&ssh->state->compression_in_stream)) { + case Z_OK: + ssh->state->compression_in_started = 1; + break; + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + default: + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} -void -packet_set_encryption_key(const u_char *key, u_int keylen, int number) +int +ssh_packet_start_compression(struct ssh *ssh, int level) { - const Cipher *cipher = cipher_by_number(number); int r; - if (cipher == NULL) - fatal("packet_set_encryption_key: unknown cipher number %d", number); - if (keylen < 20) - fatal("packet_set_encryption_key: keylen too small: %d", keylen); - if (keylen > SSH_SESSION_KEY_LENGTH) - fatal("packet_set_encryption_key: keylen too big: %d", keylen); - memcpy(active_state->ssh1_key, key, keylen); - active_state->ssh1_keylen = keylen; - if ((r = cipher_init(&active_state->send_context, cipher, - key, keylen, NULL, 0, CIPHER_ENCRYPT)) != 0 || - (r = cipher_init(&active_state->receive_context, cipher, - key, keylen, NULL, 0, CIPHER_DECRYPT)) != 0) - fatal("%s: cipher_init: %s", __func__, ssh_err(r)); + if (ssh->state->packet_compression && !compat20) + return SSH_ERR_INTERNAL_ERROR; + ssh->state->packet_compression = 1; + if ((r = ssh_packet_init_compression(ssh)) != 0 || + (r = start_compression_in(ssh)) != 0 || + (r = start_compression_out(ssh, level)) != 0) + return r; + return 0; } -u_int -packet_get_encryption_key(u_char *key) +/* XXX remove need for separate compression buffer */ +static int +compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { - if (key == NULL) - return (active_state->ssh1_keylen); - memcpy(key, active_state->ssh1_key, active_state->ssh1_keylen); - return (active_state->ssh1_keylen); -} + u_char buf[4096]; + int r, status; -/* Start constructing a packet to send. */ -void -packet_start(u_char type) -{ - u_char buf[9]; - int len; + if (ssh->state->compression_out_started != 1) + return SSH_ERR_INTERNAL_ERROR; - DBG(debug("packet_start[%d]", type)); - len = compat20 ? 6 : 9; - memset(buf, 0, len - 1); - buf[len - 1] = type; - buffer_clear(&active_state->outgoing_packet); - buffer_append(&active_state->outgoing_packet, buf, len); + /* This case is not handled below. */ + if (sshbuf_len(in) == 0) + return 0; + + /* Input is the contents of the input buffer. */ + if ((ssh->state->compression_out_stream.next_in = + sshbuf_mutable_ptr(in)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + ssh->state->compression_out_stream.avail_in = sshbuf_len(in); + + /* Loop compressing until deflate() returns with avail_out != 0. */ + do { + /* Set up fixed-size output buffer. */ + ssh->state->compression_out_stream.next_out = buf; + ssh->state->compression_out_stream.avail_out = sizeof(buf); + + /* Compress as much data into the buffer as possible. */ + status = deflate(&ssh->state->compression_out_stream, + Z_PARTIAL_FLUSH); + switch (status) { + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + case Z_OK: + /* Append compressed data to output_buffer. */ + if ((r = sshbuf_put(out, buf, sizeof(buf) - + ssh->state->compression_out_stream.avail_out)) != 0) + return r; + break; + case Z_STREAM_ERROR: + default: + ssh->state->compression_out_failures++; + return SSH_ERR_INVALID_FORMAT; + } + } while (ssh->state->compression_out_stream.avail_out == 0); + return 0; } -/* Append payload. */ -void -packet_put_char(int value) +static int +uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { - char ch = value; + u_char buf[4096]; + int r, status; - buffer_append(&active_state->outgoing_packet, &ch, 1); -} + if (ssh->state->compression_in_started != 1) + return SSH_ERR_INTERNAL_ERROR; -void -packet_put_int(u_int value) -{ - buffer_put_int(&active_state->outgoing_packet, value); -} + if ((ssh->state->compression_in_stream.next_in = + sshbuf_mutable_ptr(in)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + ssh->state->compression_in_stream.avail_in = sshbuf_len(in); -void -packet_put_int64(u_int64_t value) -{ - buffer_put_int64(&active_state->outgoing_packet, value); + for (;;) { + /* Set up fixed-size output buffer. */ + ssh->state->compression_in_stream.next_out = buf; + ssh->state->compression_in_stream.avail_out = sizeof(buf); + + status = inflate(&ssh->state->compression_in_stream, + Z_PARTIAL_FLUSH); + switch (status) { + case Z_OK: + if ((r = sshbuf_put(out, buf, sizeof(buf) - + ssh->state->compression_in_stream.avail_out)) != 0) + return r; + break; + case Z_BUF_ERROR: + /* + * Comments in zlib.h say that we should keep calling + * inflate() until we get an error. This appears to + * be the error that we get. + */ + return 0; + case Z_DATA_ERROR: + return SSH_ERR_INVALID_FORMAT; + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + case Z_STREAM_ERROR: + default: + ssh->state->compression_in_failures++; + return SSH_ERR_INTERNAL_ERROR; + } + } + /* NOTREACHED */ } -void -packet_put_string(const void *buf, u_int len) +/* Serialise compression state into a blob for privsep */ +static int +ssh_packet_get_compress_state(struct sshbuf *m, struct ssh *ssh) { - buffer_put_string(&active_state->outgoing_packet, buf, len); -} + struct session_state *state = ssh->state; + struct sshbuf *b; + int r; -void -packet_put_cstring(const char *str) + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (state->compression_in_started) { + if ((r = sshbuf_put_string(b, &state->compression_in_stream, + sizeof(state->compression_in_stream))) != 0) + goto out; + } else if ((r = sshbuf_put_string(b, NULL, 0)) != 0) + goto out; + if (state->compression_out_started) { + if ((r = sshbuf_put_string(b, &state->compression_out_stream, + sizeof(state->compression_out_stream))) != 0) + goto out; + } else if ((r = sshbuf_put_string(b, NULL, 0)) != 0) + goto out; + r = sshbuf_put_stringb(m, b); + out: + sshbuf_free(b); + return r; +} + +/* Deserialise compression state from a blob for privsep */ +static int +ssh_packet_set_compress_state(struct ssh *ssh, struct sshbuf *m) { - buffer_put_cstring(&active_state->outgoing_packet, str); + struct session_state *state = ssh->state; + struct sshbuf *b = NULL; + int r; + const u_char *inblob, *outblob; + size_t inl, outl; + + if ((r = sshbuf_froms(m, &b)) != 0) + goto out; + if ((r = sshbuf_get_string_direct(b, &inblob, &inl)) != 0 || + (r = sshbuf_get_string_direct(b, &outblob, &outl)) != 0) + goto out; + if (inl == 0) + state->compression_in_started = 0; + else if (inl != sizeof(state->compression_in_stream)) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } else { + state->compression_in_started = 1; + memcpy(&state->compression_in_stream, inblob, inl); + } + if (outl == 0) + state->compression_out_started = 0; + else if (outl != sizeof(state->compression_out_stream)) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } else { + state->compression_out_started = 1; + memcpy(&state->compression_out_stream, outblob, outl); + } + r = 0; + out: + sshbuf_free(b); + return r; } void -packet_put_raw(const void *buf, u_int len) +ssh_packet_set_compress_hooks(struct ssh *ssh, void *ctx, + void *(*allocfunc)(void *, u_int, u_int), + void (*freefunc)(void *, void *)) { - buffer_append(&active_state->outgoing_packet, buf, len); + ssh->state->compression_out_stream.zalloc = (alloc_func)allocfunc; + ssh->state->compression_out_stream.zfree = (free_func)freefunc; + ssh->state->compression_out_stream.opaque = ctx; + ssh->state->compression_in_stream.zalloc = (alloc_func)allocfunc; + ssh->state->compression_in_stream.zfree = (free_func)freefunc; + ssh->state->compression_in_stream.opaque = ctx; } -#ifdef WITH_OPENSSL -void -packet_put_bignum(BIGNUM * value) -{ - buffer_put_bignum(&active_state->outgoing_packet, value); -} +/* + * Causes any further packets to be encrypted using the given key. The same + * key is used for both sending and reception. However, both directions are + * encrypted independently of each other. + */ +#ifdef WITH_OPENSSL void -packet_put_bignum2(BIGNUM * value) +ssh_packet_set_encryption_key(struct ssh *ssh, const u_char *key, u_int keylen, int number) { - buffer_put_bignum2(&active_state->outgoing_packet, value); -} -#endif + struct session_state *state = ssh->state; + const struct sshcipher *cipher = cipher_by_number(number); + int r; + const char *wmsg; -#ifdef OPENSSL_HAS_ECC -void -packet_put_ecpoint(const EC_GROUP *curve, const EC_POINT *point) -{ - buffer_put_ecpoint(&active_state->outgoing_packet, curve, point); + if (cipher == NULL) + fatal("%s: unknown cipher number %d", __func__, number); + if (keylen < 20) + fatal("%s: keylen too small: %d", __func__, keylen); + if (keylen > SSH_SESSION_KEY_LENGTH) + fatal("%s: keylen too big: %d", __func__, keylen); + memcpy(state->ssh1_key, key, keylen); + state->ssh1_keylen = keylen; + if ((r = cipher_init(&state->send_context, cipher, key, keylen, + NULL, 0, CIPHER_ENCRYPT)) != 0 || + (r = cipher_init(&state->receive_context, cipher, key, keylen, + NULL, 0, CIPHER_DECRYPT) != 0)) + fatal("%s: cipher_init failed: %s", __func__, ssh_err(r)); + if (!state->cipher_warning_done && + ((wmsg = cipher_warning_message(&state->send_context)) != NULL || + (wmsg = cipher_warning_message(&state->send_context)) != NULL)) { + error("Warning: %s", wmsg); + state->cipher_warning_done = 1; + } } #endif @@ -667,114 +810,126 @@ packet_put_ecpoint(const EC_GROUP *curve, const EC_POINT *point) * encrypts the packet before sending. */ -static void -packet_send1(void) +int +ssh_packet_send1(struct ssh *ssh) { + struct session_state *state = ssh->state; u_char buf[8], *cp; - int i, padding, len; + int r, padding, len; u_int checksum; - u_int32_t rnd = 0; /* * If using packet compression, compress the payload of the outgoing * packet. */ - if (active_state->packet_compression) { - buffer_clear(&active_state->compression_buffer); + if (state->packet_compression) { + sshbuf_reset(state->compression_buffer); /* Skip padding. */ - buffer_consume(&active_state->outgoing_packet, 8); + if ((r = sshbuf_consume(state->outgoing_packet, 8)) != 0) + goto out; /* padding */ - buffer_append(&active_state->compression_buffer, - "\0\0\0\0\0\0\0\0", 8); - buffer_compress(&active_state->outgoing_packet, - &active_state->compression_buffer); - buffer_clear(&active_state->outgoing_packet); - buffer_append(&active_state->outgoing_packet, - buffer_ptr(&active_state->compression_buffer), - buffer_len(&active_state->compression_buffer)); + if ((r = sshbuf_put(state->compression_buffer, + "\0\0\0\0\0\0\0\0", 8)) != 0) + goto out; + if ((r = compress_buffer(ssh, state->outgoing_packet, + state->compression_buffer)) != 0) + goto out; + sshbuf_reset(state->outgoing_packet); + if ((r = sshbuf_putb(state->outgoing_packet, + state->compression_buffer)) != 0) + goto out; } /* Compute packet length without padding (add checksum, remove padding). */ - len = buffer_len(&active_state->outgoing_packet) + 4 - 8; + len = sshbuf_len(state->outgoing_packet) + 4 - 8; /* Insert padding. Initialized to zero in packet_start1() */ padding = 8 - len % 8; - if (!active_state->send_context.plaintext) { - cp = buffer_ptr(&active_state->outgoing_packet); - for (i = 0; i < padding; i++) { - if (i % 4 == 0) - rnd = arc4random(); - cp[7 - i] = rnd & 0xff; - rnd >>= 8; + if (!state->send_context.plaintext) { + cp = sshbuf_mutable_ptr(state->outgoing_packet); + if (cp == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; } + arc4random_buf(cp + 8 - padding, padding); } - buffer_consume(&active_state->outgoing_packet, 8 - padding); + if ((r = sshbuf_consume(state->outgoing_packet, 8 - padding)) != 0) + goto out; /* Add check bytes. */ - checksum = ssh_crc32(buffer_ptr(&active_state->outgoing_packet), - buffer_len(&active_state->outgoing_packet)); - put_u32(buf, checksum); - buffer_append(&active_state->outgoing_packet, buf, 4); + checksum = ssh_crc32(sshbuf_ptr(state->outgoing_packet), + sshbuf_len(state->outgoing_packet)); + POKE_U32(buf, checksum); + if ((r = sshbuf_put(state->outgoing_packet, buf, 4)) != 0) + goto out; #ifdef PACKET_DEBUG fprintf(stderr, "packet_send plain: "); - buffer_dump(&active_state->outgoing_packet); + sshbuf_dump(state->outgoing_packet, stderr); #endif /* Append to output. */ - put_u32(buf, len); - buffer_append(&active_state->output, buf, 4); - cp = buffer_append_space(&active_state->output, - buffer_len(&active_state->outgoing_packet)); - if (cipher_crypt(&active_state->send_context, 0, cp, - buffer_ptr(&active_state->outgoing_packet), - buffer_len(&active_state->outgoing_packet), 0, 0) != 0) - fatal("%s: cipher_crypt failed", __func__); + POKE_U32(buf, len); + if ((r = sshbuf_put(state->output, buf, 4)) != 0) + goto out; + if ((r = sshbuf_reserve(state->output, + sshbuf_len(state->outgoing_packet), &cp)) != 0) + goto out; + if ((r = cipher_crypt(&state->send_context, 0, cp, + sshbuf_ptr(state->outgoing_packet), + sshbuf_len(state->outgoing_packet), 0, 0)) != 0) + goto out; #ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); - buffer_dump(&active_state->output); + sshbuf_dump(state->output, stderr); #endif - active_state->p_send.packets++; - active_state->p_send.bytes += len + - buffer_len(&active_state->outgoing_packet); - buffer_clear(&active_state->outgoing_packet); + state->p_send.packets++; + state->p_send.bytes += len + + sshbuf_len(state->outgoing_packet); + sshbuf_reset(state->outgoing_packet); /* * Note that the packet is now only buffered in output. It won't be * actually sent until packet_write_wait or packet_write_poll is * called. */ + r = 0; + out: + return r; } -void -set_newkeys(int mode) +int +ssh_set_newkeys(struct ssh *ssh, int mode) { - Enc *enc; - Mac *mac; - Comp *comp; - CipherContext *cc; + struct session_state *state = ssh->state; + struct sshenc *enc; + struct sshmac *mac; + struct sshcomp *comp; + struct sshcipher_ctx *cc; u_int64_t *max_blocks; + const char *wmsg; int r, crypt_type; debug2("set_newkeys: mode %d", mode); if (mode == MODE_OUT) { - cc = &active_state->send_context; + cc = &state->send_context; crypt_type = CIPHER_ENCRYPT; - active_state->p_send.packets = active_state->p_send.blocks = 0; - max_blocks = &active_state->max_blocks_out; + state->p_send.packets = state->p_send.blocks = 0; + max_blocks = &state->max_blocks_out; } else { - cc = &active_state->receive_context; + cc = &state->receive_context; crypt_type = CIPHER_DECRYPT; - active_state->p_read.packets = active_state->p_read.blocks = 0; - max_blocks = &active_state->max_blocks_in; + state->p_read.packets = state->p_read.blocks = 0; + max_blocks = &state->max_blocks_in; } - if (active_state->newkeys[mode] != NULL) { + if (state->newkeys[mode] != NULL) { debug("set_newkeys: rekeying"); - cipher_cleanup(cc); - enc = &active_state->newkeys[mode]->enc; - mac = &active_state->newkeys[mode]->mac; - comp = &active_state->newkeys[mode]->comp; + if ((r = cipher_cleanup(cc)) != 0) + return r; + enc = &state->newkeys[mode]->enc; + mac = &state->newkeys[mode]->mac; + comp = &state->newkeys[mode]->comp; mac_clear(mac); explicit_bzero(enc->iv, enc->iv_len); explicit_bzero(enc->key, enc->key_len); @@ -785,32 +940,45 @@ set_newkeys(int mode) free(mac->name); free(mac->key); free(comp->name); - free(active_state->newkeys[mode]); - } - active_state->newkeys[mode] = kex_get_newkeys(mode); - if (active_state->newkeys[mode] == NULL) - fatal("newkeys: no keys for mode %d", mode); - enc = &active_state->newkeys[mode]->enc; - mac = &active_state->newkeys[mode]->mac; - comp = &active_state->newkeys[mode]->comp; - if (cipher_authlen(enc->cipher) == 0 && mac_init(mac) == 0) - mac->enabled = 1; + free(state->newkeys[mode]); + } + /* move newkeys from kex to state */ + if ((state->newkeys[mode] = ssh->kex->newkeys[mode]) == NULL) + return SSH_ERR_INTERNAL_ERROR; + ssh->kex->newkeys[mode] = NULL; + enc = &state->newkeys[mode]->enc; + mac = &state->newkeys[mode]->mac; + comp = &state->newkeys[mode]->comp; + if (cipher_authlen(enc->cipher) == 0) { + if ((r = mac_init(mac)) != 0) + return r; + } + mac->enabled = 1; DBG(debug("cipher_init_context: %d", mode)); if ((r = cipher_init(cc, enc->cipher, enc->key, enc->key_len, enc->iv, enc->iv_len, crypt_type)) != 0) - fatal("%s: cipher_init: %s", __func__, ssh_err(r)); + return r; + if (!state->cipher_warning_done && + (wmsg = cipher_warning_message(cc)) != NULL) { + error("Warning: %s", wmsg); + state->cipher_warning_done = 1; + } /* Deleting the keys does not gain extra security */ /* explicit_bzero(enc->iv, enc->block_size); explicit_bzero(enc->key, enc->key_len); explicit_bzero(mac->key, mac->key_len); */ if ((comp->type == COMP_ZLIB || (comp->type == COMP_DELAYED && - active_state->after_authentication)) && comp->enabled == 0) { - packet_init_compression(); - if (mode == MODE_OUT) - buffer_compress_init_send(6); - else - buffer_compress_init_recv(); + state->after_authentication)) && comp->enabled == 0) { + if ((r = ssh_packet_init_compression(ssh)) < 0) + return r; + if (mode == MODE_OUT) { + if ((r = start_compression_out(ssh, 6)) != 0) + return r; + } else { + if ((r = start_compression_in(ssh)) != 0) + return r; + } comp->enabled = 1; } /* @@ -821,9 +989,10 @@ set_newkeys(int mode) *max_blocks = (u_int64_t)1 << (enc->block_size*2); else *max_blocks = ((u_int64_t)1 << 30) / enc->block_size; - if (active_state->rekey_limit) + if (state->rekey_limit) *max_blocks = MIN(*max_blocks, - active_state->rekey_limit / enc->block_size); + state->rekey_limit / enc->block_size); + return 0; } /* @@ -831,53 +1000,59 @@ set_newkeys(int mode) * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent, * and on the client side after a SSH2_MSG_USERAUTH_SUCCESS is received. */ -static void -packet_enable_delayed_compress(void) +static int +ssh_packet_enable_delayed_compress(struct ssh *ssh) { - Comp *comp = NULL; - int mode; + struct session_state *state = ssh->state; + struct sshcomp *comp = NULL; + int r, mode; /* * Remember that we are past the authentication step, so rekeying * with COMP_DELAYED will turn on compression immediately. */ - active_state->after_authentication = 1; + state->after_authentication = 1; for (mode = 0; mode < MODE_MAX; mode++) { /* protocol error: USERAUTH_SUCCESS received before NEWKEYS */ - if (active_state->newkeys[mode] == NULL) + if (state->newkeys[mode] == NULL) continue; - comp = &active_state->newkeys[mode]->comp; + comp = &state->newkeys[mode]->comp; if (comp && !comp->enabled && comp->type == COMP_DELAYED) { - packet_init_compression(); - if (mode == MODE_OUT) - buffer_compress_init_send(6); - else - buffer_compress_init_recv(); + if ((r = ssh_packet_init_compression(ssh)) != 0) + return r; + if (mode == MODE_OUT) { + if ((r = start_compression_out(ssh, 6)) != 0) + return r; + } else { + if ((r = start_compression_in(ssh)) != 0) + return r; + } comp->enabled = 1; } } + return 0; } /* * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) */ -static void -packet_send2_wrapped(void) +int +ssh_packet_send2_wrapped(struct ssh *ssh) { + struct session_state *state = ssh->state; u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; u_char padlen, pad = 0; - u_int i, len, authlen = 0, aadlen = 0; - u_int32_t rnd = 0; - Enc *enc = NULL; - Mac *mac = NULL; - Comp *comp = NULL; - int block_size; - int r; - - if (active_state->newkeys[MODE_OUT] != NULL) { - enc = &active_state->newkeys[MODE_OUT]->enc; - mac = &active_state->newkeys[MODE_OUT]->mac; - comp = &active_state->newkeys[MODE_OUT]->comp; + u_int authlen = 0, aadlen = 0; + u_int len; + struct sshenc *enc = NULL; + struct sshmac *mac = NULL; + struct sshcomp *comp = NULL; + int r, block_size; + + if (state->newkeys[MODE_OUT] != NULL) { + enc = &state->newkeys[MODE_OUT]->enc; + mac = &state->newkeys[MODE_OUT]->mac; + comp = &state->newkeys[MODE_OUT]->comp; /* disable mac for authenticated encryption */ if ((authlen = cipher_authlen(enc->cipher)) != 0) mac = NULL; @@ -885,32 +1060,34 @@ packet_send2_wrapped(void) block_size = enc ? enc->block_size : 8; aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; - cp = buffer_ptr(&active_state->outgoing_packet); - type = cp[5]; + type = (sshbuf_ptr(state->outgoing_packet))[5]; #ifdef PACKET_DEBUG fprintf(stderr, "plain: "); - buffer_dump(&active_state->outgoing_packet); + sshbuf_dump(state->outgoing_packet, stderr); #endif if (comp && comp->enabled) { - len = buffer_len(&active_state->outgoing_packet); + len = sshbuf_len(state->outgoing_packet); /* skip header, compress only payload */ - buffer_consume(&active_state->outgoing_packet, 5); - buffer_clear(&active_state->compression_buffer); - buffer_compress(&active_state->outgoing_packet, - &active_state->compression_buffer); - buffer_clear(&active_state->outgoing_packet); - buffer_append(&active_state->outgoing_packet, "\0\0\0\0\0", 5); - buffer_append(&active_state->outgoing_packet, - buffer_ptr(&active_state->compression_buffer), - buffer_len(&active_state->compression_buffer)); - DBG(debug("compression: raw %d compressed %d", len, - buffer_len(&active_state->outgoing_packet))); + if ((r = sshbuf_consume(state->outgoing_packet, 5)) != 0) + goto out; + sshbuf_reset(state->compression_buffer); + if ((r = compress_buffer(ssh, state->outgoing_packet, + state->compression_buffer)) != 0) + goto out; + sshbuf_reset(state->outgoing_packet); + if ((r = sshbuf_put(state->outgoing_packet, + "\0\0\0\0\0", 5)) != 0 || + (r = sshbuf_putb(state->outgoing_packet, + state->compression_buffer)) != 0) + goto out; + DBG(debug("compression: raw %d compressed %zd", len, + sshbuf_len(state->outgoing_packet))); } /* sizeof (packet_len + pad_len + payload) */ - len = buffer_len(&active_state->outgoing_packet); + len = sshbuf_len(state->outgoing_packet); /* * calc size of padding, alloc space, get random data, @@ -920,143 +1097,145 @@ packet_send2_wrapped(void) padlen = block_size - (len % block_size); if (padlen < 4) padlen += block_size; - if (active_state->extra_pad) { + if (state->extra_pad) { /* will wrap if extra_pad+padlen > 255 */ - active_state->extra_pad = - roundup(active_state->extra_pad, block_size); - pad = active_state->extra_pad - - ((len + padlen) % active_state->extra_pad); + state->extra_pad = + roundup(state->extra_pad, block_size); + pad = state->extra_pad - + ((len + padlen) % state->extra_pad); DBG(debug3("%s: adding %d (len %d padlen %d extra_pad %d)", - __func__, pad, len, padlen, active_state->extra_pad)); + __func__, pad, len, padlen, state->extra_pad)); padlen += pad; - active_state->extra_pad = 0; + state->extra_pad = 0; } - cp = buffer_append_space(&active_state->outgoing_packet, padlen); - if (enc && !active_state->send_context.plaintext) { + if ((r = sshbuf_reserve(state->outgoing_packet, padlen, &cp)) != 0) + goto out; + if (enc && !state->send_context.plaintext) { /* random padding */ - for (i = 0; i < padlen; i++) { - if (i % 4 == 0) - rnd = arc4random(); - cp[i] = rnd & 0xff; - rnd >>= 8; - } + arc4random_buf(cp, padlen); } else { /* clear padding */ explicit_bzero(cp, padlen); } /* sizeof (packet_len + pad_len + payload + padding) */ - len = buffer_len(&active_state->outgoing_packet); - cp = buffer_ptr(&active_state->outgoing_packet); + len = sshbuf_len(state->outgoing_packet); + cp = sshbuf_mutable_ptr(state->outgoing_packet); + if (cp == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } /* packet_length includes payload, padding and padding length field */ - put_u32(cp, len - 4); + POKE_U32(cp, len - 4); cp[4] = padlen; DBG(debug("send: len %d (includes padlen %d, aadlen %d)", len, padlen, aadlen)); /* compute MAC over seqnr and packet(length fields, payload, padding) */ if (mac && mac->enabled && !mac->etm) { - if ((r = mac_compute(mac, active_state->p_send.seqnr, - buffer_ptr(&active_state->outgoing_packet), len, + if ((r = mac_compute(mac, state->p_send.seqnr, + sshbuf_ptr(state->outgoing_packet), len, macbuf, sizeof(macbuf))) != 0) - fatal("%s: mac_compute: %s", __func__, ssh_err(r)); - DBG(debug("done calc MAC out #%d", active_state->p_send.seqnr)); + goto out; + DBG(debug("done calc MAC out #%d", state->p_send.seqnr)); } /* encrypt packet and append to output buffer. */ - cp = buffer_append_space(&active_state->output, len + authlen); - if (cipher_crypt(&active_state->send_context, active_state->p_send.seqnr, - cp, buffer_ptr(&active_state->outgoing_packet), - len - aadlen, aadlen, authlen) != 0) - fatal("%s: cipher_crypt failed", __func__); + if ((r = sshbuf_reserve(state->output, + sshbuf_len(state->outgoing_packet) + authlen, &cp)) != 0) + goto out; + if ((r = cipher_crypt(&state->send_context, state->p_send.seqnr, cp, + sshbuf_ptr(state->outgoing_packet), + len - aadlen, aadlen, authlen)) != 0) + goto out; /* append unencrypted MAC */ if (mac && mac->enabled) { if (mac->etm) { /* EtM: compute mac over aadlen + cipher text */ - if ((r = mac_compute(mac, - active_state->p_send.seqnr, cp, len, - macbuf, sizeof(macbuf))) != 0) - fatal("%s: mac_compute: %s", __func__, ssh_err(r)); + if ((r = mac_compute(mac, state->p_send.seqnr, + cp, len, macbuf, sizeof(macbuf))) != 0) + goto out; DBG(debug("done calc MAC(EtM) out #%d", - active_state->p_send.seqnr)); + state->p_send.seqnr)); } - buffer_append(&active_state->output, macbuf, mac->mac_len); + if ((r = sshbuf_put(state->output, macbuf, mac->mac_len)) != 0) + goto out; } #ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); - buffer_dump(&active_state->output); + sshbuf_dump(state->output, stderr); #endif /* increment sequence number for outgoing packets */ - if (++active_state->p_send.seqnr == 0) + if (++state->p_send.seqnr == 0) logit("outgoing seqnr wraps around"); - if (++active_state->p_send.packets == 0) - if (!(datafellows & SSH_BUG_NOREKEY)) - fatal("XXX too many packets with same key"); - active_state->p_send.blocks += len / block_size; - active_state->p_send.bytes += len; - buffer_clear(&active_state->outgoing_packet); + if (++state->p_send.packets == 0) + if (!(ssh->compat & SSH_BUG_NOREKEY)) + return SSH_ERR_NEED_REKEY; + state->p_send.blocks += len / block_size; + state->p_send.bytes += len; + sshbuf_reset(state->outgoing_packet); if (type == SSH2_MSG_NEWKEYS) - set_newkeys(MODE_OUT); - else if (type == SSH2_MSG_USERAUTH_SUCCESS && active_state->server_side) - packet_enable_delayed_compress(); + r = ssh_set_newkeys(ssh, MODE_OUT); + else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) + r = ssh_packet_enable_delayed_compress(ssh); + else + r = 0; + out: + return r; } -static void -packet_send2(void) +int +ssh_packet_send2(struct ssh *ssh) { + struct session_state *state = ssh->state; struct packet *p; - u_char type, *cp; + u_char type; + int r; - cp = buffer_ptr(&active_state->outgoing_packet); - type = cp[5]; + type = sshbuf_ptr(state->outgoing_packet)[5]; /* during rekeying we can only send key exchange messages */ - if (active_state->rekeying) { + if (state->rekeying) { if ((type < SSH2_MSG_TRANSPORT_MIN) || (type > SSH2_MSG_TRANSPORT_MAX) || (type == SSH2_MSG_SERVICE_REQUEST) || (type == SSH2_MSG_SERVICE_ACCEPT)) { debug("enqueue packet: %u", type); - p = xcalloc(1, sizeof(*p)); + p = calloc(1, sizeof(*p)); + if (p == NULL) + return SSH_ERR_ALLOC_FAIL; p->type = type; - memcpy(&p->payload, &active_state->outgoing_packet, - sizeof(Buffer)); - buffer_init(&active_state->outgoing_packet); - TAILQ_INSERT_TAIL(&active_state->outgoing, p, next); - return; + p->payload = state->outgoing_packet; + TAILQ_INSERT_TAIL(&state->outgoing, p, next); + state->outgoing_packet = sshbuf_new(); + if (state->outgoing_packet == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; } } /* rekeying starts with sending KEXINIT */ if (type == SSH2_MSG_KEXINIT) - active_state->rekeying = 1; + state->rekeying = 1; - packet_send2_wrapped(); + if ((r = ssh_packet_send2_wrapped(ssh)) != 0) + return r; /* after a NEWKEYS message we can send the complete queue */ if (type == SSH2_MSG_NEWKEYS) { - active_state->rekeying = 0; - active_state->rekey_time = monotime(); - while ((p = TAILQ_FIRST(&active_state->outgoing))) { + state->rekeying = 0; + state->rekey_time = monotime(); + while ((p = TAILQ_FIRST(&state->outgoing))) { type = p->type; debug("dequeue packet: %u", type); - buffer_free(&active_state->outgoing_packet); - memcpy(&active_state->outgoing_packet, &p->payload, - sizeof(Buffer)); - TAILQ_REMOVE(&active_state->outgoing, p, next); + sshbuf_free(state->outgoing_packet); + state->outgoing_packet = p->payload; + TAILQ_REMOVE(&state->outgoing, p, next); free(p); - packet_send2_wrapped(); + if ((r = ssh_packet_send2_wrapped(ssh)) != 0) + return r; } } -} - -void -packet_send(void) -{ - if (compat20) - packet_send2(); - else - packet_send1(); - DBG(debug("packet_send done")); + return 0; } /* @@ -1066,95 +1245,106 @@ packet_send(void) */ int -packet_read_seqnr(u_int32_t *seqnr_p) +ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { - int type, len, ret, cont, ms_remain = 0; + struct session_state *state = ssh->state; + int len, r, ms_remain, cont; fd_set *setp; char buf[8192]; struct timeval timeout, start, *timeoutp = NULL; DBG(debug("packet_read()")); - setp = (fd_set *)xcalloc(howmany(active_state->connection_in + 1, + setp = (fd_set *)calloc(howmany(state->connection_in + 1, NFDBITS), sizeof(fd_mask)); + if (setp == NULL) + return SSH_ERR_ALLOC_FAIL; /* Since we are blocking, ensure that all written packets have been sent. */ - packet_write_wait(); + ssh_packet_write_wait(ssh); /* Stay in the loop until we have received a complete packet. */ for (;;) { /* Try to read a packet from the buffer. */ - type = packet_read_poll_seqnr(seqnr_p); + r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); + if (r != 0) + break; if (!compat20 && ( - type == SSH_SMSG_SUCCESS - || type == SSH_SMSG_FAILURE - || type == SSH_CMSG_EOF - || type == SSH_CMSG_EXIT_CONFIRMATION)) - packet_check_eom(); + *typep == SSH_SMSG_SUCCESS + || *typep == SSH_SMSG_FAILURE + || *typep == SSH_CMSG_EOF + || *typep == SSH_CMSG_EXIT_CONFIRMATION)) + if ((r = sshpkt_get_end(ssh)) != 0) + break; /* If we got a packet, return it. */ - if (type != SSH_MSG_NONE) { - free(setp); - return type; - } + if (*typep != SSH_MSG_NONE) + break; /* * Otherwise, wait for some data to arrive, add it to the * buffer, and try again. */ - memset(setp, 0, howmany(active_state->connection_in + 1, + memset(setp, 0, howmany(state->connection_in + 1, NFDBITS) * sizeof(fd_mask)); - FD_SET(active_state->connection_in, setp); + FD_SET(state->connection_in, setp); - if (active_state->packet_timeout_ms > 0) { - ms_remain = active_state->packet_timeout_ms; + if (state->packet_timeout_ms > 0) { + ms_remain = state->packet_timeout_ms; timeoutp = &timeout; } /* Wait for some data to arrive. */ for (;;) { - if (active_state->packet_timeout_ms != -1) { + if (state->packet_timeout_ms != -1) { ms_to_timeval(&timeout, ms_remain); gettimeofday(&start, NULL); } - if ((ret = select(active_state->connection_in + 1, setp, + if ((r = select(state->connection_in + 1, setp, NULL, NULL, timeoutp)) >= 0) break; if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) break; - if (active_state->packet_timeout_ms == -1) + if (state->packet_timeout_ms == -1) continue; ms_subtract_diff(&start, &ms_remain); if (ms_remain <= 0) { - ret = 0; + r = 0; break; } } - if (ret == 0) { + if (r == 0) { logit("Connection to %.200s timed out while " - "waiting to read", get_remote_ipaddr()); + "waiting to read", ssh_remote_ipaddr(ssh)); cleanup_exit(255); } /* Read data from the socket. */ do { cont = 0; - len = roaming_read(active_state->connection_in, buf, + len = roaming_read(state->connection_in, buf, sizeof(buf), &cont); } while (len == 0 && cont); if (len == 0) { - logit("Connection closed by %.200s", get_remote_ipaddr()); + logit("Connection closed by %.200s", + ssh_remote_ipaddr(ssh)); cleanup_exit(255); } if (len < 0) fatal("Read from socket failed: %.100s", strerror(errno)); /* Append it to the buffer. */ - packet_process_incoming(buf, len); + ssh_packet_process_incoming(ssh, buf, len); } - /* NOTREACHED */ + free(setp); + return r; } int -packet_read(void) +ssh_packet_read(struct ssh *ssh) { - return packet_read_seqnr(NULL); + u_char type; + int r; + + if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + return type; } /* @@ -1163,13 +1353,14 @@ packet_read(void) */ void -packet_read_expect(int expected_type) +ssh_packet_read_expect(struct ssh *ssh, int expected_type) { int type; - type = packet_read(); + type = ssh_packet_read(ssh); if (type != expected_type) - packet_disconnect("Protocol error: expected packet type %d, got %d", + ssh_packet_disconnect(ssh, + "Protocol error: expected packet type %d, got %d", expected_type, type); } @@ -1182,116 +1373,141 @@ packet_read_expect(int expected_type) * to higher levels. */ -static int -packet_read_poll1(void) +int +ssh_packet_read_poll1(struct ssh *ssh, u_char *typep) { + struct session_state *state = ssh->state; u_int len, padded_len; - u_char *cp, type; + const u_char *cp; + u_char *p; u_int checksum, stored_checksum; + int r; + + *typep = SSH_MSG_NONE; /* Check if input size is less than minimum packet size. */ - if (buffer_len(&active_state->input) < 4 + 8) - return SSH_MSG_NONE; + if (sshbuf_len(state->input) < 4 + 8) + return 0; /* Get length of incoming packet. */ - cp = buffer_ptr(&active_state->input); - len = get_u32(cp); + len = PEEK_U32(sshbuf_ptr(state->input)); if (len < 1 + 2 + 2 || len > 256 * 1024) - packet_disconnect("Bad packet length %u.", len); + ssh_packet_disconnect(ssh, "Bad packet length %u.", + len); padded_len = (len + 8) & ~7; /* Check if the packet has been entirely received. */ - if (buffer_len(&active_state->input) < 4 + padded_len) - return SSH_MSG_NONE; + if (sshbuf_len(state->input) < 4 + padded_len) + return 0; /* The entire packet is in buffer. */ /* Consume packet length. */ - buffer_consume(&active_state->input, 4); + if ((r = sshbuf_consume(state->input, 4)) != 0) + goto out; /* * Cryptographic attack detector for ssh * (C)1998 CORE-SDI, Buenos Aires Argentina * Ariel Futoransky(futo@core-sdi.com) */ - if (!active_state->receive_context.plaintext) { - switch (detect_attack(buffer_ptr(&active_state->input), - padded_len)) { + if (!state->receive_context.plaintext) { + switch (detect_attack(&state->deattack, + sshbuf_ptr(state->input), padded_len)) { + case DEATTACK_OK: + break; case DEATTACK_DETECTED: - packet_disconnect("crc32 compensation attack: " - "network attack detected"); + ssh_packet_disconnect(ssh, + "crc32 compensation attack: network attack detected" + ); case DEATTACK_DOS_DETECTED: - packet_disconnect("deattack denial of " - "service detected"); + ssh_packet_disconnect(ssh, + "deattack denial of service detected"); + default: + ssh_packet_disconnect(ssh, "deattack error"); } } /* Decrypt data to incoming_packet. */ - buffer_clear(&active_state->incoming_packet); - cp = buffer_append_space(&active_state->incoming_packet, padded_len); - if (cipher_crypt(&active_state->receive_context, 0, cp, - buffer_ptr(&active_state->input), padded_len, 0, 0) != 0) - fatal("%s: cipher_crypt failed", __func__); + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_reserve(state->incoming_packet, padded_len, &p)) != 0) + goto out; + if ((r = cipher_crypt(&state->receive_context, 0, p, + sshbuf_ptr(state->input), padded_len, 0, 0)) != 0) + goto out; - buffer_consume(&active_state->input, padded_len); + if ((r = sshbuf_consume(state->input, padded_len)) != 0) + goto out; #ifdef PACKET_DEBUG fprintf(stderr, "read_poll plain: "); - buffer_dump(&active_state->incoming_packet); + sshbuf_dump(state->incoming_packet, stderr); #endif /* Compute packet checksum. */ - checksum = ssh_crc32(buffer_ptr(&active_state->incoming_packet), - buffer_len(&active_state->incoming_packet) - 4); + checksum = ssh_crc32(sshbuf_ptr(state->incoming_packet), + sshbuf_len(state->incoming_packet) - 4); /* Skip padding. */ - buffer_consume(&active_state->incoming_packet, 8 - len % 8); + if ((r = sshbuf_consume(state->incoming_packet, 8 - len % 8)) != 0) + goto out; /* Test check bytes. */ - if (len != buffer_len(&active_state->incoming_packet)) - packet_disconnect("packet_read_poll1: len %d != buffer_len %d.", - len, buffer_len(&active_state->incoming_packet)); + if (len != sshbuf_len(state->incoming_packet)) + ssh_packet_disconnect(ssh, + "packet_read_poll1: len %d != sshbuf_len %zd.", + len, sshbuf_len(state->incoming_packet)); - cp = (u_char *)buffer_ptr(&active_state->incoming_packet) + len - 4; - stored_checksum = get_u32(cp); + cp = sshbuf_ptr(state->incoming_packet) + len - 4; + stored_checksum = PEEK_U32(cp); if (checksum != stored_checksum) - packet_disconnect("Corrupted check bytes on input."); - buffer_consume_end(&active_state->incoming_packet, 4); - - if (active_state->packet_compression) { - buffer_clear(&active_state->compression_buffer); - buffer_uncompress(&active_state->incoming_packet, - &active_state->compression_buffer); - buffer_clear(&active_state->incoming_packet); - buffer_append(&active_state->incoming_packet, - buffer_ptr(&active_state->compression_buffer), - buffer_len(&active_state->compression_buffer)); - } - active_state->p_read.packets++; - active_state->p_read.bytes += padded_len + 4; - type = buffer_get_char(&active_state->incoming_packet); - if (type < SSH_MSG_MIN || type > SSH_MSG_MAX) - packet_disconnect("Invalid ssh1 packet type: %d", type); - return type; + ssh_packet_disconnect(ssh, + "Corrupted check bytes on input."); + if ((r = sshbuf_consume_end(state->incoming_packet, 4)) < 0) + goto out; + + if (state->packet_compression) { + sshbuf_reset(state->compression_buffer); + if ((r = uncompress_buffer(ssh, state->incoming_packet, + state->compression_buffer)) != 0) + goto out; + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_putb(state->incoming_packet, + state->compression_buffer)) != 0) + goto out; + } + state->p_read.packets++; + state->p_read.bytes += padded_len + 4; + if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) + goto out; + if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX) + ssh_packet_disconnect(ssh, + "Invalid ssh1 packet type: %d", *typep); + r = 0; + out: + return r; } -static int -packet_read_poll2(u_int32_t *seqnr_p) +int +ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { + struct session_state *state = ssh->state; u_int padlen, need; - u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; - u_int maclen, authlen = 0, aadlen = 0, block_size; + u_char *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; + u_int maclen, aadlen = 0, authlen = 0, block_size; + struct sshenc *enc = NULL; + struct sshmac *mac = NULL; + struct sshcomp *comp = NULL; int r; - Enc *enc = NULL; - Mac *mac = NULL; - Comp *comp = NULL; - if (active_state->packet_discard) - return SSH_MSG_NONE; + *typep = SSH_MSG_NONE; + + if (state->packet_discard) + return 0; - if (active_state->newkeys[MODE_IN] != NULL) { - enc = &active_state->newkeys[MODE_IN]->enc; - mac = &active_state->newkeys[MODE_IN]->mac; - comp = &active_state->newkeys[MODE_IN]->comp; + if (state->newkeys[MODE_IN] != NULL) { + enc = &state->newkeys[MODE_IN]->enc; + mac = &state->newkeys[MODE_IN]->mac; + comp = &state->newkeys[MODE_IN]->comp; /* disable mac for authenticated encryption */ if ((authlen = cipher_authlen(enc->cipher)) != 0) mac = NULL; @@ -1300,69 +1516,71 @@ packet_read_poll2(u_int32_t *seqnr_p) block_size = enc ? enc->block_size : 8; aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; - if (aadlen && active_state->packlen == 0) { - if (cipher_get_length(&active_state->receive_context, - &active_state->packlen, - active_state->p_read.seqnr, - buffer_ptr(&active_state->input), - buffer_len(&active_state->input)) != 0) - return SSH_MSG_NONE; - if (active_state->packlen < 1 + 4 || - active_state->packlen > PACKET_MAX_SIZE) { + if (aadlen && state->packlen == 0) { + if (cipher_get_length(&state->receive_context, + &state->packlen, state->p_read.seqnr, + sshbuf_ptr(state->input), sshbuf_len(state->input)) != 0) + return 0; + if (state->packlen < 1 + 4 || + state->packlen > PACKET_MAX_SIZE) { #ifdef PACKET_DEBUG - buffer_dump(&active_state->input); + sshbuf_dump(state->input, stderr); #endif - logit("Bad packet length %u.", active_state->packlen); - packet_disconnect("Packet corrupt"); + logit("Bad packet length %u.", state->packlen); + if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) + return r; } - buffer_clear(&active_state->incoming_packet); - } else if (active_state->packlen == 0) { + sshbuf_reset(state->incoming_packet); + } else if (state->packlen == 0) { /* * check if input size is less than the cipher block size, * decrypt first block and extract length of incoming packet */ - if (buffer_len(&active_state->input) < block_size) - return SSH_MSG_NONE; - buffer_clear(&active_state->incoming_packet); - cp = buffer_append_space(&active_state->incoming_packet, - block_size); - if (cipher_crypt(&active_state->receive_context, - active_state->p_read.seqnr, cp, - buffer_ptr(&active_state->input), block_size, 0, 0) != 0) - fatal("Decryption integrity check failed"); - cp = buffer_ptr(&active_state->incoming_packet); - active_state->packlen = get_u32(cp); - if (active_state->packlen < 1 + 4 || - active_state->packlen > PACKET_MAX_SIZE) { + if (sshbuf_len(state->input) < block_size) + return 0; + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_reserve(state->incoming_packet, block_size, + &cp)) != 0) + goto out; + if ((r = cipher_crypt(&state->receive_context, + state->p_send.seqnr, cp, sshbuf_ptr(state->input), + block_size, 0, 0)) != 0) + goto out; + state->packlen = PEEK_U32(sshbuf_ptr(state->incoming_packet)); + if (state->packlen < 1 + 4 || + state->packlen > PACKET_MAX_SIZE) { #ifdef PACKET_DEBUG - buffer_dump(&active_state->incoming_packet); + fprintf(stderr, "input: \n"); + sshbuf_dump(state->input, stderr); + fprintf(stderr, "incoming_packet: \n"); + sshbuf_dump(state->incoming_packet, stderr); #endif - logit("Bad packet length %u.", active_state->packlen); - packet_start_discard(enc, mac, active_state->packlen, - PACKET_MAX_SIZE); - return SSH_MSG_NONE; + logit("Bad packet length %u.", state->packlen); + return ssh_packet_start_discard(ssh, enc, mac, + state->packlen, PACKET_MAX_SIZE); } - buffer_consume(&active_state->input, block_size); + if ((r = sshbuf_consume(state->input, block_size)) != 0) + goto out; } - DBG(debug("input: packet len %u", active_state->packlen+4)); + DBG(debug("input: packet len %u", state->packlen+4)); + if (aadlen) { /* only the payload is encrypted */ - need = active_state->packlen; + need = state->packlen; } else { /* * the payload size and the payload are encrypted, but we * have a partial packet of block_size bytes */ - need = 4 + active_state->packlen - block_size; + need = 4 + state->packlen - block_size; } DBG(debug("partial packet: block %d, need %d, maclen %d, authlen %d," " aadlen %d", block_size, need, maclen, authlen, aadlen)); if (need % block_size != 0) { logit("padding error: need %d block %d mod %d", need, block_size, need % block_size); - packet_start_discard(enc, mac, active_state->packlen, - PACKET_MAX_SIZE - block_size); - return SSH_MSG_NONE; + return ssh_packet_start_discard(ssh, enc, mac, + state->packlen, PACKET_MAX_SIZE - block_size); } /* * check if the entire packet has been received and @@ -1372,171 +1590,190 @@ packet_read_poll2(u_int32_t *seqnr_p) * 'authlen' bytes of authentication tag or * 'maclen' bytes of message authentication code. */ - if (buffer_len(&active_state->input) < aadlen + need + authlen + maclen) - return SSH_MSG_NONE; + if (sshbuf_len(state->input) < aadlen + need + authlen + maclen) + return 0; #ifdef PACKET_DEBUG fprintf(stderr, "read_poll enc/full: "); - buffer_dump(&active_state->input); + sshbuf_dump(state->input, stderr); #endif /* EtM: compute mac over encrypted input */ - if (mac && mac->enabled && mac->etm) - if ((r = mac_compute(mac, active_state->p_read.seqnr, - buffer_ptr(&active_state->input), aadlen + need, + if (mac && mac->enabled && mac->etm) { + if ((r = mac_compute(mac, state->p_read.seqnr, + sshbuf_ptr(state->input), aadlen + need, macbuf, sizeof(macbuf))) != 0) - fatal("%s: mac_compute: %s", __func__, ssh_err(r)); - cp = buffer_append_space(&active_state->incoming_packet, aadlen + need); - if (cipher_crypt(&active_state->receive_context, - active_state->p_read.seqnr, cp, - buffer_ptr(&active_state->input), need, aadlen, authlen) != 0) - fatal("Decryption integrity check failed"); - buffer_consume(&active_state->input, aadlen + need + authlen); + goto out; + } + if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need, + &cp)) != 0) + goto out; + if ((r = cipher_crypt(&state->receive_context, state->p_read.seqnr, cp, + sshbuf_ptr(state->input), need, aadlen, authlen)) != 0) + goto out; + if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0) + goto out; /* * compute MAC over seqnr and packet, * increment sequence number for incoming packet */ if (mac && mac->enabled) { if (!mac->etm) - if ((r = mac_compute(mac, active_state->p_read.seqnr, - buffer_ptr(&active_state->incoming_packet), - buffer_len(&active_state->incoming_packet), + if ((r = mac_compute(mac, state->p_read.seqnr, + sshbuf_ptr(state->incoming_packet), + sshbuf_len(state->incoming_packet), macbuf, sizeof(macbuf))) != 0) - fatal("%s: mac_compute: %s", __func__, ssh_err(r)); - if (timingsafe_bcmp(macbuf, buffer_ptr(&active_state->input), + goto out; + if (timingsafe_bcmp(macbuf, sshbuf_ptr(state->input), mac->mac_len) != 0) { logit("Corrupted MAC on input."); if (need > PACKET_MAX_SIZE) - fatal("internal error need %d", need); - packet_start_discard(enc, mac, active_state->packlen, - PACKET_MAX_SIZE - need); - return SSH_MSG_NONE; + return SSH_ERR_INTERNAL_ERROR; + return ssh_packet_start_discard(ssh, enc, mac, + state->packlen, PACKET_MAX_SIZE - need); } - - DBG(debug("MAC #%d ok", active_state->p_read.seqnr)); - buffer_consume(&active_state->input, mac->mac_len); + + DBG(debug("MAC #%d ok", state->p_read.seqnr)); + if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) + goto out; } /* XXX now it's safe to use fatal/packet_disconnect */ if (seqnr_p != NULL) - *seqnr_p = active_state->p_read.seqnr; - if (++active_state->p_read.seqnr == 0) + *seqnr_p = state->p_read.seqnr; + if (++state->p_read.seqnr == 0) logit("incoming seqnr wraps around"); - if (++active_state->p_read.packets == 0) - if (!(datafellows & SSH_BUG_NOREKEY)) - fatal("XXX too many packets with same key"); - active_state->p_read.blocks += (active_state->packlen + 4) / block_size; - active_state->p_read.bytes += active_state->packlen + 4; + if (++state->p_read.packets == 0) + if (!(ssh->compat & SSH_BUG_NOREKEY)) + return SSH_ERR_NEED_REKEY; + state->p_read.blocks += (state->packlen + 4) / block_size; + state->p_read.bytes += state->packlen + 4; /* get padlen */ - cp = buffer_ptr(&active_state->incoming_packet); - padlen = cp[4]; + padlen = sshbuf_ptr(state->incoming_packet)[4]; DBG(debug("input: padlen %d", padlen)); if (padlen < 4) - packet_disconnect("Corrupted padlen %d on input.", padlen); + ssh_packet_disconnect(ssh, + "Corrupted padlen %d on input.", padlen); /* skip packet size + padlen, discard padding */ - buffer_consume(&active_state->incoming_packet, 4 + 1); - buffer_consume_end(&active_state->incoming_packet, padlen); + if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 || + ((r = sshbuf_consume_end(state->incoming_packet, padlen)) != 0)) + goto out; - DBG(debug("input: len before de-compress %d", - buffer_len(&active_state->incoming_packet))); + DBG(debug("input: len before de-compress %zd", + sshbuf_len(state->incoming_packet))); if (comp && comp->enabled) { - buffer_clear(&active_state->compression_buffer); - buffer_uncompress(&active_state->incoming_packet, - &active_state->compression_buffer); - buffer_clear(&active_state->incoming_packet); - buffer_append(&active_state->incoming_packet, - buffer_ptr(&active_state->compression_buffer), - buffer_len(&active_state->compression_buffer)); - DBG(debug("input: len after de-compress %d", - buffer_len(&active_state->incoming_packet))); + sshbuf_reset(state->compression_buffer); + if ((r = uncompress_buffer(ssh, state->incoming_packet, + state->compression_buffer)) != 0) + goto out; + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_putb(state->incoming_packet, + state->compression_buffer)) != 0) + goto out; + DBG(debug("input: len after de-compress %zd", + sshbuf_len(state->incoming_packet))); } /* * get packet type, implies consume. * return length of payload (without type field) */ - type = buffer_get_char(&active_state->incoming_packet); - if (type < SSH2_MSG_MIN || type >= SSH2_MSG_LOCAL_MIN) - packet_disconnect("Invalid ssh2 packet type: %d", type); - if (type == SSH2_MSG_NEWKEYS) - set_newkeys(MODE_IN); - else if (type == SSH2_MSG_USERAUTH_SUCCESS && - !active_state->server_side) - packet_enable_delayed_compress(); + if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) + goto out; + if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) + ssh_packet_disconnect(ssh, + "Invalid ssh2 packet type: %d", *typep); + if (*typep == SSH2_MSG_NEWKEYS) + r = ssh_set_newkeys(ssh, MODE_IN); + else if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side) + r = ssh_packet_enable_delayed_compress(ssh); + else + r = 0; #ifdef PACKET_DEBUG - fprintf(stderr, "read/plain[%d]:\r\n", type); - buffer_dump(&active_state->incoming_packet); + fprintf(stderr, "read/plain[%d]:\r\n", *typep); + sshbuf_dump(state->incoming_packet, stderr); #endif /* reset for next packet */ - active_state->packlen = 0; - return type; + state->packlen = 0; + out: + return r; } int -packet_read_poll_seqnr(u_int32_t *seqnr_p) +ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { + struct session_state *state = ssh->state; u_int reason, seqnr; - u_char type; - char *msg; + int r; + u_char *msg; for (;;) { + msg = NULL; if (compat20) { - type = packet_read_poll2(seqnr_p); - if (type) { - active_state->keep_alive_timeouts = 0; - DBG(debug("received packet type %d", type)); + r = ssh_packet_read_poll2(ssh, typep, seqnr_p); + if (r != 0) + return r; + if (*typep) { + state->keep_alive_timeouts = 0; + DBG(debug("received packet type %d", *typep)); } - switch (type) { + switch (*typep) { case SSH2_MSG_IGNORE: debug3("Received SSH2_MSG_IGNORE"); break; case SSH2_MSG_DEBUG: - packet_get_char(); - msg = packet_get_string(NULL); + if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || + (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || + (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { + if (msg) + free(msg); + return r; + } debug("Remote: %.900s", msg); free(msg); - msg = packet_get_string(NULL); - free(msg); break; case SSH2_MSG_DISCONNECT: - reason = packet_get_int(); - msg = packet_get_string(NULL); + if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || + (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) + return r; /* Ignore normal client exit notifications */ - do_log2(active_state->server_side && + do_log2(ssh->state->server_side && reason == SSH2_DISCONNECT_BY_APPLICATION ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, "Received disconnect from %s: %u: %.400s", - get_remote_ipaddr(), reason, msg); + ssh_remote_ipaddr(ssh), reason, msg); free(msg); - cleanup_exit(255); - break; + return SSH_ERR_DISCONNECTED; case SSH2_MSG_UNIMPLEMENTED: - seqnr = packet_get_int(); + if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) + return r; debug("Received SSH2_MSG_UNIMPLEMENTED for %u", seqnr); break; default: - return type; + return 0; } } else { - type = packet_read_poll1(); - switch (type) { + r = ssh_packet_read_poll1(ssh, typep); + switch (*typep) { case SSH_MSG_NONE: return SSH_MSG_NONE; case SSH_MSG_IGNORE: break; case SSH_MSG_DEBUG: - msg = packet_get_string(NULL); + if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0) + return r; debug("Remote: %.900s", msg); free(msg); break; case SSH_MSG_DISCONNECT: - msg = packet_get_string(NULL); + if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0) + return r; error("Received disconnect from %s: %.400s", - get_remote_ipaddr(), msg); - cleanup_exit(255); - break; + ssh_remote_ipaddr(ssh), msg); + free(msg); + return SSH_ERR_DISCONNECTED; default: - DBG(debug("received packet type %d", type)); - return type; + DBG(debug("received packet type %d", *typep)); + return 0; } } } @@ -1548,112 +1785,29 @@ packet_read_poll_seqnr(u_int32_t *seqnr_p) */ void -packet_process_incoming(const char *buf, u_int len) +ssh_packet_process_incoming(struct ssh *ssh, const char *buf, u_int len) { - if (active_state->packet_discard) { - active_state->keep_alive_timeouts = 0; /* ?? */ - if (len >= active_state->packet_discard) - packet_stop_discard(); - active_state->packet_discard -= len; + struct session_state *state = ssh->state; + int r; + + if (state->packet_discard) { + state->keep_alive_timeouts = 0; /* ?? */ + if (len >= state->packet_discard) { + if ((r = ssh_packet_stop_discard(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + cleanup_exit(255); + } + state->packet_discard -= len; return; } - buffer_append(&active_state->input, buf, len); -} - -/* Returns a character from the packet. */ - -u_int -packet_get_char(void) -{ - char ch; - - buffer_get(&active_state->incoming_packet, &ch, 1); - return (u_char) ch; -} - -/* Returns an integer from the packet data. */ - -u_int -packet_get_int(void) -{ - return buffer_get_int(&active_state->incoming_packet); -} - -/* Returns an 64 bit integer from the packet data. */ - -u_int64_t -packet_get_int64(void) -{ - return buffer_get_int64(&active_state->incoming_packet); -} - -/* - * Returns an arbitrary precision integer from the packet data. The integer - * must have been initialized before this call. - */ - -#ifdef WITH_OPENSSL -void -packet_get_bignum(BIGNUM * value) -{ - buffer_get_bignum(&active_state->incoming_packet, value); -} - -void -packet_get_bignum2(BIGNUM * value) -{ - buffer_get_bignum2(&active_state->incoming_packet, value); -} - -#ifdef OPENSSL_HAS_ECC -void -packet_get_ecpoint(const EC_GROUP *curve, EC_POINT *point) -{ - buffer_get_ecpoint(&active_state->incoming_packet, curve, point); -} -#endif -#endif - -void * -packet_get_raw(u_int *length_ptr) -{ - u_int bytes = buffer_len(&active_state->incoming_packet); - - if (length_ptr != NULL) - *length_ptr = bytes; - return buffer_ptr(&active_state->incoming_packet); + if ((r = sshbuf_put(ssh->state->input, buf, len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } int -packet_remaining(void) +ssh_packet_remaining(struct ssh *ssh) { - return buffer_len(&active_state->incoming_packet); -} - -/* - * Returns a string from the packet data. The string is allocated using - * xmalloc; it is the responsibility of the calling program to free it when - * no longer needed. The length_ptr argument may be NULL, or point to an - * integer into which the length of the string is stored. - */ - -void * -packet_get_string(u_int *length_ptr) -{ - return buffer_get_string(&active_state->incoming_packet, length_ptr); -} - -const void * -packet_get_string_ptr(u_int *length_ptr) -{ - return buffer_get_string_ptr(&active_state->incoming_packet, length_ptr); -} - -/* Ensures the returned string has no embedded \0 characters in it. */ -char * -packet_get_cstring(u_int *length_ptr) -{ - return buffer_get_cstring(&active_state->incoming_packet, length_ptr); + return sshbuf_len(ssh->state->incoming_packet); } /* @@ -1666,12 +1820,13 @@ packet_get_cstring(u_int *length_ptr) */ void -packet_send_debug(const char *fmt,...) +ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...) { char buf[1024]; va_list args; + int r; - if (compat20 && (datafellows & SSH_BUG_DEBUG)) + if (compat20 && (ssh->compat & SSH_BUG_DEBUG)) return; va_start(args, fmt); @@ -1679,16 +1834,19 @@ packet_send_debug(const char *fmt,...) va_end(args); if (compat20) { - packet_start(SSH2_MSG_DEBUG); - packet_put_char(0); /* bool: always display */ - packet_put_cstring(buf); - packet_put_cstring(""); + if ((r = sshpkt_start(ssh, SSH2_MSG_DEBUG)) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || /* always display */ + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } else { - packet_start(SSH_MSG_DEBUG); - packet_put_cstring(buf); + if ((r = sshpkt_start(ssh, SSH_MSG_DEBUG)) != 0 || + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } - packet_send(); - packet_write_wait(); + ssh_packet_write_wait(ssh); } /* @@ -1699,11 +1857,12 @@ packet_send_debug(const char *fmt,...) */ void -packet_disconnect(const char *fmt,...) +ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...) { char buf[1024]; va_list args; static int disconnecting = 0; + int r; if (disconnecting) /* Guard against recursive invocations. */ fatal("packet_disconnect called recursively."); @@ -1722,37 +1881,38 @@ packet_disconnect(const char *fmt,...) /* Send the disconnect message to the other side, and wait for it to get sent. */ if (compat20) { - packet_start(SSH2_MSG_DISCONNECT); - packet_put_int(SSH2_DISCONNECT_PROTOCOL_ERROR); - packet_put_cstring(buf); - packet_put_cstring(""); + if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } else { - packet_start(SSH_MSG_DISCONNECT); - packet_put_cstring(buf); + if ((r = sshpkt_start(ssh, SSH_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } - packet_send(); - packet_write_wait(); - - /* Stop listening for connections. */ - channel_close_all(); + ssh_packet_write_wait(ssh); /* Close the connection. */ - packet_close(); + ssh_packet_close(ssh); cleanup_exit(255); } /* Checks if there is any buffered output, and tries to write some of the output. */ void -packet_write_poll(void) +ssh_packet_write_poll(struct ssh *ssh) { - int len = buffer_len(&active_state->output); - int cont; + struct session_state *state = ssh->state; + int len = sshbuf_len(state->output); + int cont, r; if (len > 0) { cont = 0; - len = roaming_write(active_state->connection_out, - buffer_ptr(&active_state->output), len, &cont); + len = roaming_write(state->connection_out, + sshbuf_ptr(state->output), len, &cont); if (len == -1) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) @@ -1761,7 +1921,8 @@ packet_write_poll(void) } if (len == 0 && !cont) fatal("Write connection closed"); - buffer_consume(&active_state->output, len); + if ((r = sshbuf_consume(state->output, len)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); } } @@ -1771,36 +1932,39 @@ packet_write_poll(void) */ void -packet_write_wait(void) +ssh_packet_write_wait(struct ssh *ssh) { fd_set *setp; int ret, ms_remain = 0; struct timeval start, timeout, *timeoutp = NULL; + struct session_state *state = ssh->state; - setp = (fd_set *)xcalloc(howmany(active_state->connection_out + 1, + setp = (fd_set *)calloc(howmany(state->connection_out + 1, NFDBITS), sizeof(fd_mask)); - packet_write_poll(); - while (packet_have_data_to_write()) { - memset(setp, 0, howmany(active_state->connection_out + 1, + if (setp == NULL) + fatal("%s: calloc failed", __func__); + ssh_packet_write_poll(ssh); + while (ssh_packet_have_data_to_write(ssh)) { + memset(setp, 0, howmany(state->connection_out + 1, NFDBITS) * sizeof(fd_mask)); - FD_SET(active_state->connection_out, setp); + FD_SET(state->connection_out, setp); - if (active_state->packet_timeout_ms > 0) { - ms_remain = active_state->packet_timeout_ms; + if (state->packet_timeout_ms > 0) { + ms_remain = state->packet_timeout_ms; timeoutp = &timeout; } for (;;) { - if (active_state->packet_timeout_ms != -1) { + if (state->packet_timeout_ms != -1) { ms_to_timeval(&timeout, ms_remain); gettimeofday(&start, NULL); } - if ((ret = select(active_state->connection_out + 1, + if ((ret = select(state->connection_out + 1, NULL, setp, NULL, timeoutp)) >= 0) break; if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) break; - if (active_state->packet_timeout_ms == -1) + if (state->packet_timeout_ms == -1) continue; ms_subtract_diff(&start, &ms_remain); if (ms_remain <= 0) { @@ -1810,10 +1974,10 @@ packet_write_wait(void) } if (ret == 0) { logit("Connection to %.200s timed out while " - "waiting to write", get_remote_ipaddr()); + "waiting to write", ssh_remote_ipaddr(ssh)); cleanup_exit(255); } - packet_write_poll(); + ssh_packet_write_poll(ssh); } free(setp); } @@ -1821,33 +1985,33 @@ packet_write_wait(void) /* Returns true if there is buffered data to write to the connection. */ int -packet_have_data_to_write(void) +ssh_packet_have_data_to_write(struct ssh *ssh) { - return buffer_len(&active_state->output) != 0; + return sshbuf_len(ssh->state->output) != 0; } /* Returns true if there is not too much data to write to the connection. */ int -packet_not_very_much_data_to_write(void) +ssh_packet_not_very_much_data_to_write(struct ssh *ssh) { - if (active_state->interactive_mode) - return buffer_len(&active_state->output) < 16384; + if (ssh->state->interactive_mode) + return sshbuf_len(ssh->state->output) < 16384; else - return buffer_len(&active_state->output) < 128 * 1024; + return sshbuf_len(ssh->state->output) < 128 * 1024; } -static void -packet_set_tos(int tos) +void +ssh_packet_set_tos(struct ssh *ssh, int tos) { #ifndef IP_TOS_IS_BROKEN - if (!packet_connection_is_on_socket()) + if (!ssh_packet_connection_is_on_socket(ssh)) return; - switch (packet_connection_af()) { + switch (ssh_packet_connection_af(ssh)) { # ifdef IP_TOS case AF_INET: debug3("%s: set IP_TOS 0x%02x", __func__, tos); - if (setsockopt(active_state->connection_in, + if (setsockopt(ssh->state->connection_in, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) error("setsockopt IP_TOS %d: %.100s:", tos, strerror(errno)); @@ -1856,7 +2020,7 @@ packet_set_tos(int tos) # ifdef IPV6_TCLASS case AF_INET6: debug3("%s: set IPV6_TCLASS 0x%02x", __func__, tos); - if (setsockopt(active_state->connection_in, + if (setsockopt(ssh->state->connection_in, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) < 0) error("setsockopt IPV6_TCLASS %d: %.100s:", tos, strerror(errno)); @@ -1869,71 +2033,69 @@ packet_set_tos(int tos) /* Informs that the current session is interactive. Sets IP flags for that. */ void -packet_set_interactive(int interactive, int qos_interactive, int qos_bulk) +ssh_packet_set_interactive(struct ssh *ssh, int interactive, int qos_interactive, int qos_bulk) { - if (active_state->set_interactive_called) + struct session_state *state = ssh->state; + + if (state->set_interactive_called) return; - active_state->set_interactive_called = 1; + state->set_interactive_called = 1; /* Record that we are in interactive mode. */ - active_state->interactive_mode = interactive; + state->interactive_mode = interactive; /* Only set socket options if using a socket. */ - if (!packet_connection_is_on_socket()) + if (!ssh_packet_connection_is_on_socket(ssh)) return; - set_nodelay(active_state->connection_in); - packet_set_tos(interactive ? qos_interactive : qos_bulk); + set_nodelay(state->connection_in); + ssh_packet_set_tos(ssh, interactive ? qos_interactive : + qos_bulk); } /* Returns true if the current connection is interactive. */ int -packet_is_interactive(void) +ssh_packet_is_interactive(struct ssh *ssh) { - return active_state->interactive_mode; + return ssh->state->interactive_mode; } int -packet_set_maxsize(u_int s) +ssh_packet_set_maxsize(struct ssh *ssh, u_int s) { - if (active_state->set_maxsize_called) { + struct session_state *state = ssh->state; + + if (state->set_maxsize_called) { logit("packet_set_maxsize: called twice: old %d new %d", - active_state->max_packet_size, s); + state->max_packet_size, s); return -1; } if (s < 4 * 1024 || s > 1024 * 1024) { logit("packet_set_maxsize: bad size %d", s); return -1; } - active_state->set_maxsize_called = 1; + state->set_maxsize_called = 1; debug("packet_set_maxsize: setting to %d", s); - active_state->max_packet_size = s; + state->max_packet_size = s; return s; } int -packet_inc_alive_timeouts(void) +ssh_packet_inc_alive_timeouts(struct ssh *ssh) { - return ++active_state->keep_alive_timeouts; + return ++ssh->state->keep_alive_timeouts; } void -packet_set_alive_timeouts(int ka) +ssh_packet_set_alive_timeouts(struct ssh *ssh, int ka) { - active_state->keep_alive_timeouts = ka; + ssh->state->keep_alive_timeouts = ka; } u_int -packet_get_maxsize(void) -{ - return active_state->max_packet_size; -} - -/* roundup current message to pad bytes */ -void -packet_add_padding(u_char pad) +ssh_packet_get_maxsize(struct ssh *ssh) { - active_state->extra_pad = pad; + return ssh->state->max_packet_size; } /* @@ -1948,155 +2110,702 @@ packet_add_padding(u_char pad) * protection measure against advanced traffic analysis techniques. */ void -packet_send_ignore(int nbytes) +ssh_packet_send_ignore(struct ssh *ssh, int nbytes) { u_int32_t rnd = 0; - int i; + int r, i; - packet_start(compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE); - packet_put_int(nbytes); + if ((r = sshpkt_start(ssh, compat20 ? + SSH2_MSG_IGNORE : SSH_MSG_IGNORE)) != 0 || + (r = sshpkt_put_u32(ssh, nbytes)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); for (i = 0; i < nbytes; i++) { if (i % 4 == 0) rnd = arc4random(); - packet_put_char((u_char)rnd & 0xff); + if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); rnd >>= 8; } } #define MAX_PACKETS (1U<<31) int -packet_need_rekeying(void) +ssh_packet_need_rekeying(struct ssh *ssh) { - if (datafellows & SSH_BUG_NOREKEY) + struct session_state *state = ssh->state; + + if (ssh->compat & SSH_BUG_NOREKEY) return 0; return - (active_state->p_send.packets > MAX_PACKETS) || - (active_state->p_read.packets > MAX_PACKETS) || - (active_state->max_blocks_out && - (active_state->p_send.blocks > active_state->max_blocks_out)) || - (active_state->max_blocks_in && - (active_state->p_read.blocks > active_state->max_blocks_in)) || - (active_state->rekey_interval != 0 && active_state->rekey_time + - active_state->rekey_interval <= monotime()); + (state->p_send.packets > MAX_PACKETS) || + (state->p_read.packets > MAX_PACKETS) || + (state->max_blocks_out && + (state->p_send.blocks > state->max_blocks_out)) || + (state->max_blocks_in && + (state->p_read.blocks > state->max_blocks_in)) || + (state->rekey_interval != 0 && state->rekey_time + + state->rekey_interval <= monotime()); } void -packet_set_rekey_limits(u_int32_t bytes, time_t seconds) +ssh_packet_set_rekey_limits(struct ssh *ssh, u_int32_t bytes, time_t seconds) { debug3("rekey after %lld bytes, %d seconds", (long long)bytes, (int)seconds); - active_state->rekey_limit = bytes; - active_state->rekey_interval = seconds; + ssh->state->rekey_limit = bytes; + ssh->state->rekey_interval = seconds; /* * We set the time here so that in post-auth privsep slave we count * from the completion of the authentication. */ - active_state->rekey_time = monotime(); + ssh->state->rekey_time = monotime(); } time_t -packet_get_rekey_timeout(void) +ssh_packet_get_rekey_timeout(struct ssh *ssh) { time_t seconds; - seconds = active_state->rekey_time + active_state->rekey_interval - + seconds = ssh->state->rekey_time + ssh->state->rekey_interval - monotime(); return (seconds <= 0 ? 1 : seconds); } void -packet_set_server(void) +ssh_packet_set_server(struct ssh *ssh) { - active_state->server_side = 1; + ssh->state->server_side = 1; } void -packet_set_authenticated(void) +ssh_packet_set_authenticated(struct ssh *ssh) { - active_state->after_authentication = 1; + ssh->state->after_authentication = 1; } void * -packet_get_input(void) +ssh_packet_get_input(struct ssh *ssh) { - return (void *)&active_state->input; + return (void *)ssh->state->input; } void * -packet_get_output(void) +ssh_packet_get_output(struct ssh *ssh) { - return (void *)&active_state->output; -} - -void * -packet_get_newkeys(int mode) -{ - return (void *)active_state->newkeys[mode]; + return (void *)ssh->state->output; } +/* XXX TODO update roaming to new API (does not work anyway) */ /* * Save the state for the real connection, and use a separate state when * resuming a suspended connection. */ void -packet_backup_state(void) +ssh_packet_backup_state(struct ssh *ssh, + struct ssh *backup_state) { - struct session_state *tmp; + struct ssh *tmp; - close(active_state->connection_in); - active_state->connection_in = -1; - close(active_state->connection_out); - active_state->connection_out = -1; + close(ssh->state->connection_in); + ssh->state->connection_in = -1; + close(ssh->state->connection_out); + ssh->state->connection_out = -1; if (backup_state) tmp = backup_state; else - tmp = alloc_session_state(); - backup_state = active_state; - active_state = tmp; + tmp = ssh_alloc_session_state(); + backup_state = ssh; + ssh = tmp; } +/* XXX FIXME FIXME FIXME */ /* * Swap in the old state when resuming a connecion. */ void -packet_restore_state(void) +ssh_packet_restore_state(struct ssh *ssh, + struct ssh *backup_state) { - struct session_state *tmp; - void *buf; + struct ssh *tmp; u_int len; + int r; tmp = backup_state; - backup_state = active_state; - active_state = tmp; - active_state->connection_in = backup_state->connection_in; - backup_state->connection_in = -1; - active_state->connection_out = backup_state->connection_out; - backup_state->connection_out = -1; - len = buffer_len(&backup_state->input); + backup_state = ssh; + ssh = tmp; + ssh->state->connection_in = backup_state->state->connection_in; + backup_state->state->connection_in = -1; + ssh->state->connection_out = backup_state->state->connection_out; + backup_state->state->connection_out = -1; + len = sshbuf_len(backup_state->state->input); if (len > 0) { - buf = buffer_ptr(&backup_state->input); - buffer_append(&active_state->input, buf, len); - buffer_clear(&backup_state->input); + if ((r = sshbuf_putb(ssh->state->input, + backup_state->state->input)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + sshbuf_reset(backup_state->state->input); add_recv_bytes(len); } } /* Reset after_authentication and reset compression in post-auth privsep */ -void -packet_set_postauth(void) +static int +ssh_packet_set_postauth(struct ssh *ssh) { - Comp *comp; - int mode; + struct sshcomp *comp; + int r, mode; debug("%s: called", __func__); /* This was set in net child, but is not visible in user child */ - active_state->after_authentication = 1; - active_state->rekeying = 0; + ssh->state->after_authentication = 1; + ssh->state->rekeying = 0; for (mode = 0; mode < MODE_MAX; mode++) { - if (active_state->newkeys[mode] == NULL) + if (ssh->state->newkeys[mode] == NULL) continue; - comp = &active_state->newkeys[mode]->comp; - if (comp && comp->enabled) - packet_init_compression(); + comp = &ssh->state->newkeys[mode]->comp; + if (comp && comp->enabled && + (r = ssh_packet_init_compression(ssh)) != 0) + return r; + } + return 0; +} + +/* Packet state (de-)serialization for privsep */ + +/* turn kex into a blob for packet state serialization */ +static int +kex_to_blob(struct sshbuf *m, struct kex *kex) +{ + int r; + + if ((r = sshbuf_put_string(m, kex->session_id, + kex->session_id_len)) != 0 || + (r = sshbuf_put_u32(m, kex->we_need)) != 0 || + (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 || + (r = sshbuf_put_u32(m, kex->kex_type)) != 0 || + (r = sshbuf_put_stringb(m, kex->my)) != 0 || + (r = sshbuf_put_stringb(m, kex->peer)) != 0 || + (r = sshbuf_put_u32(m, kex->flags)) != 0 || + (r = sshbuf_put_cstring(m, kex->client_version_string)) != 0 || + (r = sshbuf_put_cstring(m, kex->server_version_string)) != 0) + return r; + return 0; +} + +/* turn key exchange results into a blob for packet state serialization */ +static int +newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode) +{ + struct sshbuf *b; + struct sshcipher_ctx *cc; + struct sshcomp *comp; + struct sshenc *enc; + struct sshmac *mac; + struct newkeys *newkey; + int r; + + if ((newkey = ssh->state->newkeys[mode]) == NULL) + return SSH_ERR_INTERNAL_ERROR; + enc = &newkey->enc; + mac = &newkey->mac; + comp = &newkey->comp; + cc = (mode == MODE_OUT) ? &ssh->state->send_context : + &ssh->state->receive_context; + if ((r = cipher_get_keyiv(cc, enc->iv, enc->iv_len)) != 0) + return r; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + /* The cipher struct is constant and shared, you export pointer */ + if ((r = sshbuf_put_cstring(b, enc->name)) != 0 || + (r = sshbuf_put(b, &enc->cipher, sizeof(enc->cipher))) != 0 || + (r = sshbuf_put_u32(b, enc->enabled)) != 0 || + (r = sshbuf_put_u32(b, enc->block_size)) != 0 || + (r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 || + (r = sshbuf_put_string(b, enc->iv, enc->iv_len)) != 0) + goto out; + if (cipher_authlen(enc->cipher) == 0) { + if ((r = sshbuf_put_cstring(b, mac->name)) != 0 || + (r = sshbuf_put_u32(b, mac->enabled)) != 0 || + (r = sshbuf_put_string(b, mac->key, mac->key_len)) != 0) + goto out; + } + if ((r = sshbuf_put_u32(b, comp->type)) != 0 || + (r = sshbuf_put_u32(b, comp->enabled)) != 0 || + (r = sshbuf_put_cstring(b, comp->name)) != 0) + goto out; + r = sshbuf_put_stringb(m, b); + out: + if (b != NULL) + sshbuf_free(b); + return r; +} + +/* serialize packet state into a blob */ +int +ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) +{ + struct session_state *state = ssh->state; + u_char *p; + size_t slen, rlen; + int r, ssh1cipher; + + if (!compat20) { + ssh1cipher = cipher_get_number(state->receive_context.cipher); + slen = cipher_get_keyiv_len(&state->send_context); + rlen = cipher_get_keyiv_len(&state->receive_context); + if ((r = sshbuf_put_u32(m, state->remote_protocol_flags)) != 0 || + (r = sshbuf_put_u32(m, ssh1cipher)) != 0 || + (r = sshbuf_put_string(m, state->ssh1_key, state->ssh1_keylen)) != 0 || + (r = sshbuf_put_u32(m, slen)) != 0 || + (r = sshbuf_reserve(m, slen, &p)) != 0 || + (r = cipher_get_keyiv(&state->send_context, p, slen)) != 0 || + (r = sshbuf_put_u32(m, rlen)) != 0 || + (r = sshbuf_reserve(m, rlen, &p)) != 0 || + (r = cipher_get_keyiv(&state->receive_context, p, rlen)) != 0) + return r; + } else { + if ((r = kex_to_blob(m, ssh->kex)) != 0 || + (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 || + (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 || + (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 || + (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 || + (r = sshbuf_put_u32(m, state->p_send.packets)) != 0 || + (r = sshbuf_put_u64(m, state->p_send.bytes)) != 0 || + (r = sshbuf_put_u32(m, state->p_read.seqnr)) != 0 || + (r = sshbuf_put_u64(m, state->p_read.blocks)) != 0 || + (r = sshbuf_put_u32(m, state->p_read.packets)) != 0 || + (r = sshbuf_put_u64(m, state->p_read.bytes)) != 0) + return r; + } + + slen = cipher_get_keycontext(&state->send_context, NULL); + rlen = cipher_get_keycontext(&state->receive_context, NULL); + if ((r = sshbuf_put_u32(m, slen)) != 0 || + (r = sshbuf_reserve(m, slen, &p)) != 0) + return r; + if (cipher_get_keycontext(&state->send_context, p) != (int)slen) + return SSH_ERR_INTERNAL_ERROR; + if ((r = sshbuf_put_u32(m, rlen)) != 0 || + (r = sshbuf_reserve(m, rlen, &p)) != 0) + return r; + if (cipher_get_keycontext(&state->receive_context, p) != (int)rlen) + return SSH_ERR_INTERNAL_ERROR; + + if ((r = ssh_packet_get_compress_state(m, ssh)) != 0 || + (r = sshbuf_put_stringb(m, state->input)) != 0 || + (r = sshbuf_put_stringb(m, state->output)) != 0) + return r; + + if (compat20) { + if ((r = sshbuf_put_u64(m, get_sent_bytes())) != 0 || + (r = sshbuf_put_u64(m, get_recv_bytes())) != 0) + return r; + } + return 0; +} + +/* restore key exchange results from blob for packet state de-serialization */ +static int +newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) +{ + struct sshbuf *b = NULL; + struct sshcomp *comp; + struct sshenc *enc; + struct sshmac *mac; + struct newkeys *newkey = NULL; + size_t keylen, ivlen, maclen; + int r; + + if ((newkey = calloc(1, sizeof(*newkey))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_froms(m, &b)) != 0) + goto out; +#ifdef DEBUG_PK + sshbuf_dump(b, stderr); +#endif + enc = &newkey->enc; + mac = &newkey->mac; + comp = &newkey->comp; + + if ((r = sshbuf_get_cstring(b, &enc->name, NULL)) != 0 || + (r = sshbuf_get(b, &enc->cipher, sizeof(enc->cipher))) != 0 || + (r = sshbuf_get_u32(b, (u_int *)&enc->enabled)) != 0 || + (r = sshbuf_get_u32(b, &enc->block_size)) != 0 || + (r = sshbuf_get_string(b, &enc->key, &keylen)) != 0 || + (r = sshbuf_get_string(b, &enc->iv, &ivlen)) != 0) + goto out; + if (cipher_authlen(enc->cipher) == 0) { + if ((r = sshbuf_get_cstring(b, &mac->name, NULL)) != 0) + goto out; + if ((r = mac_setup(mac, mac->name)) != 0) + goto out; + if ((r = sshbuf_get_u32(b, (u_int *)&mac->enabled)) != 0 || + (r = sshbuf_get_string(b, &mac->key, &maclen)) != 0) + goto out; + if (maclen > mac->key_len) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + mac->key_len = maclen; + } + if ((r = sshbuf_get_u32(b, &comp->type)) != 0 || + (r = sshbuf_get_u32(b, (u_int *)&comp->enabled)) != 0 || + (r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0) + goto out; + if (enc->name == NULL || + cipher_by_name(enc->name) != enc->cipher) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(b) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + enc->key_len = keylen; + enc->iv_len = ivlen; + ssh->kex->newkeys[mode] = newkey; + newkey = NULL; + r = 0; + out: + if (newkey != NULL) + free(newkey); + if (b != NULL) + sshbuf_free(b); + return r; +} + +/* restore kex from blob for packet state de-serialization */ +static int +kex_from_blob(struct sshbuf *m, struct kex **kexp) +{ + struct kex *kex; + int r; + + if ((kex = calloc(1, sizeof(struct kex))) == NULL || + (kex->my = sshbuf_new()) == NULL || + (kex->peer = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_string(m, &kex->session_id, &kex->session_id_len)) != 0 || + (r = sshbuf_get_u32(m, &kex->we_need)) != 0 || + (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 || + (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 || + (r = sshbuf_get_stringb(m, kex->my)) != 0 || + (r = sshbuf_get_stringb(m, kex->peer)) != 0 || + (r = sshbuf_get_u32(m, &kex->flags)) != 0 || + (r = sshbuf_get_cstring(m, &kex->client_version_string, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &kex->server_version_string, NULL)) != 0) + goto out; + kex->server = 1; + kex->done = 1; + r = 0; + out: + if (r != 0 || kexp == NULL) { + if (kex != NULL) { + if (kex->my != NULL) + sshbuf_free(kex->my); + if (kex->peer != NULL) + sshbuf_free(kex->peer); + free(kex); + } + if (kexp != NULL) + *kexp = NULL; + } else { + *kexp = kex; + } + return r; +} + +/* + * Restore packet state from content of blob 'm' (de-serialization). + * Note that 'm' will be partially consumed on parsing or any other errors. + */ +int +ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) +{ + struct session_state *state = ssh->state; + const u_char *ssh1key, *ivin, *ivout, *keyin, *keyout, *input, *output; + size_t ssh1keylen, rlen, slen, ilen, olen; + int r; + u_int ssh1cipher = 0; + u_int64_t sent_bytes = 0, recv_bytes = 0; + + if (!compat20) { + if ((r = sshbuf_get_u32(m, &state->remote_protocol_flags)) != 0 || + (r = sshbuf_get_u32(m, &ssh1cipher)) != 0 || + (r = sshbuf_get_string_direct(m, &ssh1key, &ssh1keylen)) != 0 || + (r = sshbuf_get_string_direct(m, &ivout, &slen)) != 0 || + (r = sshbuf_get_string_direct(m, &ivin, &rlen)) != 0) + return r; + if (ssh1cipher > INT_MAX) + return SSH_ERR_KEY_UNKNOWN_CIPHER; + ssh_packet_set_encryption_key(ssh, ssh1key, ssh1keylen, + (int)ssh1cipher); + if (cipher_get_keyiv_len(&state->send_context) != (int)slen || + cipher_get_keyiv_len(&state->receive_context) != (int)rlen) + return SSH_ERR_INVALID_FORMAT; + if ((r = cipher_set_keyiv(&state->send_context, ivout)) != 0 || + (r = cipher_set_keyiv(&state->receive_context, ivin)) != 0) + return r; + } else { + if ((r = kex_from_blob(m, &ssh->kex)) != 0 || + (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 || + (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 || + (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 || + (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 || + (r = sshbuf_get_u32(m, &state->p_send.packets)) != 0 || + (r = sshbuf_get_u64(m, &state->p_send.bytes)) != 0 || + (r = sshbuf_get_u32(m, &state->p_read.seqnr)) != 0 || + (r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 || + (r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 || + (r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0) + return r; + /* XXX ssh_set_newkeys overrides p_read.packets? XXX */ + if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 || + (r = ssh_set_newkeys(ssh, MODE_OUT)) != 0) + return r; + } + if ((r = sshbuf_get_string_direct(m, &keyout, &slen)) != 0 || + (r = sshbuf_get_string_direct(m, &keyin, &rlen)) != 0) + return r; + if (cipher_get_keycontext(&state->send_context, NULL) != (int)slen || + cipher_get_keycontext(&state->receive_context, NULL) != (int)rlen) + return SSH_ERR_INVALID_FORMAT; + cipher_set_keycontext(&state->send_context, keyout); + cipher_set_keycontext(&state->receive_context, keyin); + + if ((r = ssh_packet_set_compress_state(ssh, m)) != 0 || + (r = ssh_packet_set_postauth(ssh)) != 0) + return r; + + sshbuf_reset(state->input); + sshbuf_reset(state->output); + if ((r = sshbuf_get_string_direct(m, &input, &ilen)) != 0 || + (r = sshbuf_get_string_direct(m, &output, &olen)) != 0 || + (r = sshbuf_put(state->input, input, ilen)) != 0 || + (r = sshbuf_put(state->output, output, olen)) != 0) + return r; + + if (compat20) { + if ((r = sshbuf_get_u64(m, &sent_bytes)) != 0 || + (r = sshbuf_get_u64(m, &recv_bytes)) != 0) + return r; + roam_set_bytes(sent_bytes, recv_bytes); + } + if (sshbuf_len(m)) + return SSH_ERR_INVALID_FORMAT; + debug3("%s: done", __func__); + return 0; +} + +/* NEW API */ + +/* put data to the outgoing packet */ + +int +sshpkt_put(struct ssh *ssh, const void *v, size_t len) +{ + return sshbuf_put(ssh->state->outgoing_packet, v, len); +} + +int +sshpkt_putb(struct ssh *ssh, const struct sshbuf *b) +{ + return sshbuf_putb(ssh->state->outgoing_packet, b); +} + +int +sshpkt_put_u8(struct ssh *ssh, u_char val) +{ + return sshbuf_put_u8(ssh->state->outgoing_packet, val); +} + +int +sshpkt_put_u32(struct ssh *ssh, u_int32_t val) +{ + return sshbuf_put_u32(ssh->state->outgoing_packet, val); +} + +int +sshpkt_put_u64(struct ssh *ssh, u_int64_t val) +{ + return sshbuf_put_u64(ssh->state->outgoing_packet, val); +} + +int +sshpkt_put_string(struct ssh *ssh, const void *v, size_t len) +{ + return sshbuf_put_string(ssh->state->outgoing_packet, v, len); +} + +int +sshpkt_put_cstring(struct ssh *ssh, const void *v) +{ + return sshbuf_put_cstring(ssh->state->outgoing_packet, v); +} + +int +sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v) +{ + return sshbuf_put_stringb(ssh->state->outgoing_packet, v); +} + +int +sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g) +{ + return sshbuf_put_ec(ssh->state->outgoing_packet, v, g); +} + +int +sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v) +{ + return sshbuf_put_bignum1(ssh->state->outgoing_packet, v); +} + +int +sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v) +{ + return sshbuf_put_bignum2(ssh->state->outgoing_packet, v); +} + +/* fetch data from the incoming packet */ + +int +sshpkt_get(struct ssh *ssh, void *valp, size_t len) +{ + return sshbuf_get(ssh->state->incoming_packet, valp, len); +} + +int +sshpkt_get_u8(struct ssh *ssh, u_char *valp) +{ + return sshbuf_get_u8(ssh->state->incoming_packet, valp); +} + +int +sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp) +{ + return sshbuf_get_u32(ssh->state->incoming_packet, valp); +} + +int +sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp) +{ + return sshbuf_get_u64(ssh->state->incoming_packet, valp); +} + +int +sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp) +{ + return sshbuf_get_string(ssh->state->incoming_packet, valp, lenp); +} + +int +sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) +{ + return sshbuf_get_string_direct(ssh->state->incoming_packet, valp, lenp); +} + +int +sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp) +{ + return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp); +} + +int +sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g) +{ + return sshbuf_get_ec(ssh->state->incoming_packet, v, g); +} + +int +sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v) +{ + return sshbuf_get_bignum1(ssh->state->incoming_packet, v); +} + +int +sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v) +{ + return sshbuf_get_bignum2(ssh->state->incoming_packet, v); +} + +int +sshpkt_get_end(struct ssh *ssh) +{ + if (sshbuf_len(ssh->state->incoming_packet) > 0) + return SSH_ERR_UNEXPECTED_TRAILING_DATA; + return 0; +} + +const u_char * +sshpkt_ptr(struct ssh *ssh, size_t *lenp) +{ + if (lenp != NULL) + *lenp = sshbuf_len(ssh->state->incoming_packet); + return sshbuf_ptr(ssh->state->incoming_packet); +} + +/* start a new packet */ + +int +sshpkt_start(struct ssh *ssh, u_char type) +{ + u_char buf[9]; + int len; + + DBG(debug("packet_start[%d]", type)); + len = compat20 ? 6 : 9; + memset(buf, 0, len - 1); + buf[len - 1] = type; + sshbuf_reset(ssh->state->outgoing_packet); + return sshbuf_put(ssh->state->outgoing_packet, buf, len); +} + +/* send it */ + +int +sshpkt_send(struct ssh *ssh) +{ + if (compat20) + return ssh_packet_send2(ssh); + else + return ssh_packet_send1(ssh); +} + +int +sshpkt_disconnect(struct ssh *ssh, const char *fmt,...) +{ + char buf[1024]; + va_list args; + int r; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (compat20) { + if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + } else { + if ((r = sshpkt_start(ssh, SSH_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; } + return 0; +} + +/* roundup current message to pad bytes */ +int +sshpkt_add_padding(struct ssh *ssh, u_char pad) +{ + ssh->state->extra_pad = pad; + return 0; } diff --git a/packet.h b/packet.h index e7b5fcba9..0d691979c 100644 --- a/packet.h +++ b/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.61 2014/05/03 17:20:34 markus Exp $ */ +/* $OpenBSD: packet.h,v 1.62 2015/01/19 19:52:16 markus Exp $ */ /* * Author: Tatu Ylonen @@ -18,111 +18,147 @@ #include -#include -#ifdef OPENSSL_HAS_ECC -#include +#ifdef WITH_OPENSSL +# include +# ifdef OPENSSL_HAS_ECC +# include +# endif #endif - -void packet_set_connection(int, int); -void packet_set_timeout(int, int); -void packet_set_nonblocking(void); -int packet_get_connection_in(void); -int packet_get_connection_out(void); -void packet_close(void); -void packet_set_encryption_key(const u_char *, u_int, int); -u_int packet_get_encryption_key(u_char *); -void packet_set_protocol_flags(u_int); -u_int packet_get_protocol_flags(void); -void packet_start_compression(int); -void packet_set_interactive(int, int, int); -int packet_is_interactive(void); -void packet_set_server(void); -void packet_set_authenticated(void); - -void packet_start(u_char); -void packet_put_char(int ch); -void packet_put_int(u_int value); -void packet_put_int64(u_int64_t value); -void packet_put_bignum(BIGNUM * value); -void packet_put_bignum2(BIGNUM * value); -#ifdef OPENSSL_HAS_ECC -void packet_put_ecpoint(const EC_GROUP *, const EC_POINT *); -#endif -void packet_put_string(const void *buf, u_int len); -void packet_put_cstring(const char *str); -void packet_put_raw(const void *buf, u_int len); -void packet_send(void); - -int packet_read(void); -void packet_read_expect(int type); -void packet_process_incoming(const char *buf, u_int len); -int packet_read_seqnr(u_int32_t *seqnr_p); -int packet_read_poll_seqnr(u_int32_t *seqnr_p); - -u_int packet_get_char(void); -u_int packet_get_int(void); -u_int64_t packet_get_int64(void); -void packet_get_bignum(BIGNUM * value); -void packet_get_bignum2(BIGNUM * value); -#ifdef OPENSSL_HAS_ECC -void packet_get_ecpoint(const EC_GROUP *, EC_POINT *); -#endif -void *packet_get_raw(u_int *length_ptr); -void *packet_get_string(u_int *length_ptr); -char *packet_get_cstring(u_int *length_ptr); -const void *packet_get_string_ptr(u_int *length_ptr); -void packet_disconnect(const char *fmt,...) __attribute__((noreturn)) __attribute__((format(printf, 1, 2))); -void packet_send_debug(const char *fmt,...) __attribute__((format(printf, 1, 2))); - -void set_newkeys(int mode); -int packet_get_keyiv_len(int); -void packet_get_keyiv(int, u_char *, u_int); -int packet_get_keycontext(int, u_char *); -void packet_set_keycontext(int, u_char *); -void packet_get_state(int, u_int32_t *, u_int64_t *, u_int32_t *, u_int64_t *); -void packet_set_state(int, u_int32_t, u_int64_t, u_int32_t, u_int64_t); -int packet_get_ssh1_cipher(void); -void packet_set_iv(int, u_char *); -void *packet_get_newkeys(int); - -void packet_write_poll(void); -void packet_write_wait(void); -int packet_have_data_to_write(void); -int packet_not_very_much_data_to_write(void); - -int packet_connection_is_on_socket(void); -int packet_remaining(void); -void packet_send_ignore(int); -void packet_add_padding(u_char); +#include +#include + +struct kex; +struct sshkey; +struct sshbuf; +struct session_state; /* private session data */ + +struct ssh { + /* Session state */ + struct session_state *state; + + /* Key exchange */ + struct kex *kex; + + /* cached remote ip address and port*/ + char *remote_ipaddr; + int remote_port; + + /* datafellows */ + int compat; +}; + +struct ssh *ssh_alloc_session_state(void); +struct ssh *ssh_packet_set_connection(struct ssh *, int, int); +void ssh_packet_set_timeout(struct ssh *, int, int); +int ssh_packet_stop_discard(struct ssh *); +int ssh_packet_connection_af(struct ssh *); +void ssh_packet_set_nonblocking(struct ssh *); +int ssh_packet_get_connection_in(struct ssh *); +int ssh_packet_get_connection_out(struct ssh *); +void ssh_packet_close(struct ssh *); +void ssh_packet_set_encryption_key(struct ssh *, const u_char *, u_int, int); +void ssh_packet_set_protocol_flags(struct ssh *, u_int); +u_int ssh_packet_get_protocol_flags(struct ssh *); +int ssh_packet_start_compression(struct ssh *, int); +void ssh_packet_set_tos(struct ssh *, int); +void ssh_packet_set_interactive(struct ssh *, int, int, int); +int ssh_packet_is_interactive(struct ssh *); +void ssh_packet_set_server(struct ssh *); +void ssh_packet_set_authenticated(struct ssh *); + +int ssh_packet_send1(struct ssh *); +int ssh_packet_send2_wrapped(struct ssh *); +int ssh_packet_send2(struct ssh *); + +int ssh_packet_read(struct ssh *); +void ssh_packet_read_expect(struct ssh *, int type); +int ssh_packet_read_poll(struct ssh *); +int ssh_packet_read_poll1(struct ssh *, u_char *); +int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); +void ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len); +int ssh_packet_read_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); +int ssh_packet_read_poll_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); + +const void *ssh_packet_get_string_ptr(struct ssh *, u_int *length_ptr); +void ssh_packet_disconnect(struct ssh *, const char *fmt, ...) + __attribute__((format(printf, 2, 3))) + __attribute__((noreturn)); +void ssh_packet_send_debug(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); + +int ssh_set_newkeys(struct ssh *, int mode); +void ssh_packet_get_bytes(struct ssh *, u_int64_t *, u_int64_t *); + +typedef void *(ssh_packet_comp_alloc_func)(void *, u_int, u_int); +typedef void (ssh_packet_comp_free_func)(void *, void *); +void ssh_packet_set_compress_hooks(struct ssh *, void *, + ssh_packet_comp_alloc_func *, ssh_packet_comp_free_func *); + +void ssh_packet_write_poll(struct ssh *); +void ssh_packet_write_wait(struct ssh *); +int ssh_packet_have_data_to_write(struct ssh *); +int ssh_packet_not_very_much_data_to_write(struct ssh *); + +int ssh_packet_connection_is_on_socket(struct ssh *); +int ssh_packet_remaining(struct ssh *); +void ssh_packet_send_ignore(struct ssh *, int); void tty_make_modes(int, struct termios *); void tty_parse_modes(int, int *); -void packet_set_alive_timeouts(int); -int packet_inc_alive_timeouts(void); -int packet_set_maxsize(u_int); -u_int packet_get_maxsize(void); - -/* don't allow remaining bytes after the end of the message */ -#define packet_check_eom() \ -do { \ - int _len = packet_remaining(); \ - if (_len > 0) { \ - logit("Packet integrity error (%d bytes remaining) at %s:%d", \ - _len ,__FILE__, __LINE__); \ - packet_disconnect("Packet integrity error."); \ - } \ -} while (0) - -int packet_need_rekeying(void); -void packet_set_rekey_limits(u_int32_t, time_t); -time_t packet_get_rekey_timeout(void); - -void packet_backup_state(void); -void packet_restore_state(void); -void packet_set_postauth(void); - -void *packet_get_input(void); -void *packet_get_output(void); +void ssh_packet_set_alive_timeouts(struct ssh *, int); +int ssh_packet_inc_alive_timeouts(struct ssh *); +int ssh_packet_set_maxsize(struct ssh *, u_int); +u_int ssh_packet_get_maxsize(struct ssh *); + +int ssh_packet_get_state(struct ssh *, struct sshbuf *); +int ssh_packet_set_state(struct ssh *, struct sshbuf *); + +const char *ssh_remote_ipaddr(struct ssh *); + +int ssh_packet_need_rekeying(struct ssh *); +void ssh_packet_set_rekey_limits(struct ssh *, u_int32_t, time_t); +time_t ssh_packet_get_rekey_timeout(struct ssh *); + +/* XXX FIXME */ +void ssh_packet_backup_state(struct ssh *, struct ssh *); +void ssh_packet_restore_state(struct ssh *, struct ssh *); + +void *ssh_packet_get_input(struct ssh *); +void *ssh_packet_get_output(struct ssh *); + +/* new API */ +int sshpkt_start(struct ssh *ssh, u_char type); +int sshpkt_send(struct ssh *ssh); +int sshpkt_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); +int sshpkt_add_padding(struct ssh *, u_char); + +int sshpkt_put(struct ssh *ssh, const void *v, size_t len); +int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b); +int sshpkt_put_u8(struct ssh *ssh, u_char val); +int sshpkt_put_u32(struct ssh *ssh, u_int32_t val); +int sshpkt_put_u64(struct ssh *ssh, u_int64_t val); +int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len); +int sshpkt_put_cstring(struct ssh *ssh, const void *v); +int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v); +int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g); +int sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v); +int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v); + +int sshpkt_get(struct ssh *ssh, void *valp, size_t len); +int sshpkt_get_u8(struct ssh *ssh, u_char *valp); +int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp); +int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp); +int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp); +int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); +int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp); +int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g); +int sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v); +int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v); +int sshpkt_get_end(struct ssh *ssh); +const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); + +/* OLD API */ +extern struct ssh *active_state; +#include "opacket.h" #endif /* PACKET_H */ diff --git a/roaming_dummy.c b/roaming_dummy.c index 45c4008e7..837de695d 100644 --- a/roaming_dummy.c +++ b/roaming_dummy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: roaming_dummy.c,v 1.3 2009/06/21 09:04:03 dtucker Exp $ */ +/* $OpenBSD: roaming_dummy.c,v 1.4 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2004-2009 AppGate Network Security AB * @@ -35,6 +35,17 @@ get_recv_bytes(void) return 0; } +u_int64_t +get_sent_bytes(void) +{ + return 0; +} + +void +roam_set_bytes(u_int64_t sent, u_int64_t recvd) +{ +} + ssize_t roaming_write(int fd, const void *buf, size_t count, int *cont) { diff --git a/serverloop.c b/serverloop.c index e92f9e27b..f1fbb0512 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.172 2014/07/15 15:54:14 millert Exp $ */ +/* $OpenBSD: serverloop.c,v 1.173 2015/01/19 19:52:16 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -83,7 +83,6 @@ extern ServerOptions options; /* XXX */ -extern Kex *xxx_kex; extern Authctxt *the_authctxt; extern int use_privsep; @@ -545,7 +544,7 @@ drain_output(void) static void process_buffered_input_packets(void) { - dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL); + dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? active_state->kex : NULL); } /* @@ -851,7 +850,7 @@ server_loop2(Authctxt *authctxt) for (;;) { process_buffered_input_packets(); - rekeying = (xxx_kex != NULL && !xxx_kex->done); + rekeying = (active_state->kex != NULL && !active_state->kex->done); if (!rekeying && packet_not_very_much_data_to_write()) channel_output_poll(); @@ -874,8 +873,8 @@ server_loop2(Authctxt *authctxt) channel_after_select(readset, writeset); if (packet_need_rekeying()) { debug("need rekeying"); - xxx_kex->done = 0; - kex_send_kexinit(xxx_kex); + active_state->kex->done = 0; + kex_send_kexinit(active_state->kex); } } process_input(readset); diff --git a/sshconnect2.c b/sshconnect2.c index aee49a478..ba34762ea 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.216 2015/01/18 13:33:34 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.217 2015/01/19 19:52:16 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -91,8 +91,6 @@ u_int session_id2_len = 0; char *xxx_host; struct sockaddr *xxx_hostaddr; -Kex *xxx_kex = NULL; - static int verify_host_key_callback(Key *hostkey) { @@ -207,6 +205,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) /* start key exchange */ kex = kex_setup(myproposal); + active_state->kex = kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; @@ -219,8 +218,6 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; - xxx_kex = kex; - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); if (options.use_roaming && !kex->roaming) { diff --git a/sshd.c b/sshd.c index a4611e4d3..e45303988 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.433 2015/01/17 18:53:34 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.434 2015/01/19 19:52:16 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -188,9 +188,6 @@ int num_listen_socks = 0; char *client_version_string = NULL; char *server_version_string = NULL; -/* for rekeying XXX fixme */ -Kex *xxx_kex; - /* Daemon's agent connection */ int auth_sock = -1; int have_agent = 0; @@ -663,7 +660,7 @@ privsep_preauth(Authctxt *authctxt) /* Set up unprivileged child process to deal with network data */ pmonitor = monitor_init(); /* Store a pointer to the kex for later rekeying */ - pmonitor->m_pkex = &xxx_kex; + pmonitor->m_pkex = &active_state->kex; if (use_privsep == PRIVSEP_ON) box = ssh_sandbox_init(pmonitor); @@ -2192,8 +2189,7 @@ main(int ac, char **av) do_authenticated(authctxt); /* The connection has been terminated. */ - packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); - packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); + packet_get_bytes(&ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes", (unsigned long long)obytes, (unsigned long long)ibytes); @@ -2505,6 +2501,7 @@ do_ssh2_kex(void) /* start key exchange */ kex = kex_setup(myproposal); + active_state->kex = kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; @@ -2521,8 +2518,6 @@ do_ssh2_kex(void) kex->host_key_index=&get_hostkey_index; kex->sign = sshd_hostkey_sign; - xxx_kex = kex; - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); session_id2 = kex->session_id; -- cgit v1.2.3 From 3fdc88a0def4f86aa88a5846ac079dc964c0546a Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Mon, 19 Jan 2015 20:07:45 +0000 Subject: upstream commit move dispatch to struct ssh; ok djm@ --- auth2-chall.c | 7 ++-- auth2-gss.c | 22 +++++++------ auth2.c | 12 ++++--- channels.c | 63 +++++++++++++++++++++--------------- channels.h | 28 ++++++++-------- clientloop.c | 28 ++++++++++------ dispatch.c | 102 ++++++++++++++++++++++++++++++++++++++++------------------ dispatch.h | 35 ++++++++++++++------ kex.c | 8 +++-- kex.h | 4 +-- packet.h | 9 +++++- serverloop.c | 25 ++++++++------ sshconnect2.c | 54 ++++++++++++++++++------------- 13 files changed, 251 insertions(+), 146 deletions(-) (limited to 'kex.h') diff --git a/auth2-chall.c b/auth2-chall.c index ea4eb6952..ddabe1a90 100644 --- a/auth2-chall.c +++ b/auth2-chall.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-chall.c,v 1.41 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: auth2-chall.c,v 1.42 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Per Allansson. All rights reserved. @@ -49,7 +49,7 @@ extern ServerOptions options; static int auth2_challenge_start(Authctxt *); static int send_userauth_info_request(Authctxt *); -static void input_userauth_info_response(int, u_int32_t, void *); +static int input_userauth_info_response(int, u_int32_t, void *); #ifdef BSD_AUTH extern KbdintDevice bsdauth_device; @@ -279,7 +279,7 @@ send_userauth_info_request(Authctxt *authctxt) return 1; } -static void +static int input_userauth_info_response(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -344,6 +344,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt) } userauth_finish(authctxt, authenticated, "keyboard-interactive", devicename); + return 0; } void diff --git a/auth2-gss.c b/auth2-gss.c index 447f896f2..1ca835773 100644 --- a/auth2-gss.c +++ b/auth2-gss.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-gss.c,v 1.21 2014/02/26 20:28:44 djm Exp $ */ +/* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. @@ -48,10 +48,10 @@ extern ServerOptions options; -static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); -static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); -static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); -static void input_gssapi_errtok(int, u_int32_t, void *); +static int input_gssapi_token(int type, u_int32_t plen, void *ctxt); +static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt); +static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); +static int input_gssapi_errtok(int, u_int32_t, void *); /* * We only support those mechanisms that we know about (ie ones that we know @@ -126,7 +126,7 @@ userauth_gssapi(Authctxt *authctxt) return (0); } -static void +static int input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -178,9 +178,10 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt) } gss_release_buffer(&min_status, &send_tok); + return 0; } -static void +static int input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -212,6 +213,7 @@ input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) /* The client will have already moved on to the next auth */ gss_release_buffer(&maj_status, &send_tok); + return 0; } /* @@ -220,7 +222,7 @@ input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) * which only enables it once the GSSAPI exchange is complete. */ -static void +static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -244,9 +246,10 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL); + return 0; } -static void +static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; @@ -284,6 +287,7 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL); + return 0; } Authmethod method_gssapi = { diff --git a/auth2.c b/auth2.c index 93e355ff6..717796228 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.134 2014/12/22 07:55:51 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.135 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -87,8 +87,8 @@ Authmethod *authmethods[] = { /* protocol */ -static void input_service_request(int, u_int32_t, void *); -static void input_userauth_request(int, u_int32_t, void *); +static int input_service_request(int, u_int32_t, void *); +static int input_userauth_request(int, u_int32_t, void *); /* helper */ static Authmethod *authmethod_lookup(Authctxt *, const char *); @@ -174,7 +174,7 @@ do_authentication2(Authctxt *authctxt) } /*ARGSUSED*/ -static void +static int input_service_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -205,10 +205,11 @@ input_service_request(int type, u_int32_t seq, void *ctxt) packet_disconnect("bad service request %s", service); } free(service); + return 0; } /*ARGSUSED*/ -static void +static int input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -284,6 +285,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) free(service); free(user); free(method); + return 0; } void diff --git a/channels.c b/channels.c index 6db92cbac..29a62f70a 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.338 2014/12/11 08:20:09 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.339 2015/01/19 20:07:45 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2343,7 +2343,7 @@ channel_output_poll(void) /* -- protocol input */ /* ARGSUSED */ -void +int channel_input_data(int type, u_int32_t seq, void *ctxt) { int id; @@ -2360,7 +2360,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) /* Ignore any data for non-open channels (might happen on close) */ if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_X11_OPEN) - return; + return 0; /* Get the data. */ data = packet_get_string_ptr(&data_len); @@ -2380,7 +2380,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) c->local_window -= win_len; c->local_consumed += win_len; } - return; + return 0; } if (compat20) { @@ -2391,7 +2391,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) if (win_len > c->local_window) { logit("channel %d: rcvd too much data %d, win %d", c->self, win_len, c->local_window); - return; + return 0; } c->local_window -= win_len; } @@ -2400,10 +2400,11 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) else buffer_append(&c->output, data, data_len); packet_check_eom(); + return 0; } /* ARGSUSED */ -void +int channel_input_extended_data(int type, u_int32_t seq, void *ctxt) { int id; @@ -2419,7 +2420,7 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) packet_disconnect("Received extended_data for bad channel %d.", id); if (c->type != SSH_CHANNEL_OPEN) { logit("channel %d: ext data for non open", id); - return; + return 0; } if (c->flags & CHAN_EOF_RCVD) { if (datafellows & SSH_BUG_EXTEOF) @@ -2433,7 +2434,7 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { logit("channel %d: bad ext data", c->self); - return; + return 0; } data = packet_get_string(&data_len); packet_check_eom(); @@ -2441,16 +2442,17 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) logit("channel %d: rcvd too much extended_data %d, win %d", c->self, data_len, c->local_window); free(data); - return; + return 0; } debug2("channel %d: rcvd ext data %d", c->self, data_len); c->local_window -= data_len; buffer_append(&c->extended, data, data_len); free(data); + return 0; } /* ARGSUSED */ -void +int channel_input_ieof(int type, u_int32_t seq, void *ctxt) { int id; @@ -2470,11 +2472,11 @@ channel_input_ieof(int type, u_int32_t seq, void *ctxt) if (buffer_len(&c->input) == 0) chan_ibuf_empty(c); } - + return 0; } /* ARGSUSED */ -void +int channel_input_close(int type, u_int32_t seq, void *ctxt) { int id; @@ -2509,11 +2511,12 @@ channel_input_close(int type, u_int32_t seq, void *ctxt) buffer_clear(&c->input); c->type = SSH_CHANNEL_OUTPUT_DRAINING; } + return 0; } /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ /* ARGSUSED */ -void +int channel_input_oclose(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); @@ -2523,10 +2526,11 @@ channel_input_oclose(int type, u_int32_t seq, void *ctxt) if (c == NULL) packet_disconnect("Received oclose for nonexistent channel %d.", id); chan_rcvd_oclose(c); + return 0; } /* ARGSUSED */ -void +int channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); @@ -2540,10 +2544,11 @@ channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) packet_disconnect("Received close confirmation for " "non-closed channel %d (type %d).", id, c->type); channel_free(c); + return 0; } /* ARGSUSED */ -void +int channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) { int id, remote_id; @@ -2572,6 +2577,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) c->remote_window, c->remote_maxpacket); } packet_check_eom(); + return 0; } static char * @@ -2591,7 +2597,7 @@ reason2txt(int reason) } /* ARGSUSED */ -void +int channel_input_open_failure(int type, u_int32_t seq, void *ctxt) { int id, reason; @@ -2623,10 +2629,11 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt) packet_check_eom(); /* Schedule the channel for cleanup/deletion. */ chan_mark_dead(c); + return 0; } /* ARGSUSED */ -void +int channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) { Channel *c; @@ -2634,7 +2641,7 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) u_int adjust; if (!compat20) - return; + return 0; /* Get the channel number and verify it. */ id = packet_get_int(); @@ -2642,16 +2649,17 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) if (c == NULL) { logit("Received window adjust for non-open channel %d.", id); - return; + return 0; } adjust = packet_get_int(); packet_check_eom(); debug2("channel %d: rcvd adjust %u", id, adjust); c->remote_window += adjust; + return 0; } /* ARGSUSED */ -void +int channel_input_port_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -2679,10 +2687,11 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) packet_send(); } else c->remote_id = remote_id; + return 0; } /* ARGSUSED */ -void +int channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) { Channel *c; @@ -2699,15 +2708,15 @@ channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) if ((c = channel_lookup(id)) == NULL) { logit("channel_input_status_confirm: %d: unknown", id); - return; + return 0; } - ; if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) - return; + return 0; cc->cb(type, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); explicit_bzero(cc, sizeof(*cc)); free(cc); + return 0; } /* -- tcp forwarding */ @@ -4095,7 +4104,7 @@ x11_connect_display(void) */ /* ARGSUSED */ -void +int x11_input_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -4135,11 +4144,12 @@ x11_input_open(int type, u_int32_t seq, void *ctxt) packet_put_int(c->self); } packet_send(); + return 0; } /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ /* ARGSUSED */ -void +int deny_input_open(int type, u_int32_t seq, void *ctxt) { int rchan = packet_get_int(); @@ -4159,6 +4169,7 @@ deny_input_open(int type, u_int32_t seq, void *ctxt) packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_send(); + return 0; } /* diff --git a/channels.h b/channels.h index a000c98e5..5a672f22e 100644 --- a/channels.h +++ b/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.115 2014/07/15 15:54:14 millert Exp $ */ +/* $OpenBSD: channels.h,v 1.116 2015/01/19 20:07:45 markus Exp $ */ /* * Author: Tatu Ylonen @@ -230,17 +230,17 @@ void channel_send_window_changes(void); /* protocol handler */ -void channel_input_close(int, u_int32_t, void *); -void channel_input_close_confirmation(int, u_int32_t, void *); -void channel_input_data(int, u_int32_t, void *); -void channel_input_extended_data(int, u_int32_t, void *); -void channel_input_ieof(int, u_int32_t, void *); -void channel_input_oclose(int, u_int32_t, void *); -void channel_input_open_confirmation(int, u_int32_t, void *); -void channel_input_open_failure(int, u_int32_t, void *); -void channel_input_port_open(int, u_int32_t, void *); -void channel_input_window_adjust(int, u_int32_t, void *); -void channel_input_status_confirm(int, u_int32_t, void *); +int channel_input_close(int, u_int32_t, void *); +int channel_input_close_confirmation(int, u_int32_t, void *); +int channel_input_data(int, u_int32_t, void *); +int channel_input_extended_data(int, u_int32_t, void *); +int channel_input_ieof(int, u_int32_t, void *); +int channel_input_oclose(int, u_int32_t, void *); +int channel_input_open_confirmation(int, u_int32_t, void *); +int channel_input_open_failure(int, u_int32_t, void *); +int channel_input_port_open(int, u_int32_t, void *); +int channel_input_window_adjust(int, u_int32_t, void *); +int channel_input_status_confirm(int, u_int32_t, void *); /* file descriptor handling (read/write) */ @@ -286,10 +286,10 @@ int permitopen_port(const char *); int x11_connect_display(void); int x11_create_display_inet(int, int, int, u_int *, int **); -void x11_input_open(int, u_int32_t, void *); +int x11_input_open(int, u_int32_t, void *); void x11_request_forwarding_with_spoofing(int, const char *, const char *, const char *, int); -void deny_input_open(int, u_int32_t, void *); +int deny_input_open(int, u_int32_t, void *); /* agent forwarding */ diff --git a/clientloop.c b/clientloop.c index 3b9700aa4..672d06989 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.263 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: clientloop.c,v 1.264 2015/01/19 20:07:45 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -536,13 +536,13 @@ client_check_window_change(void) } } -static void +static int client_global_request_reply(int type, u_int32_t seq, void *ctxt) { struct global_confirm *gc; if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) - return; + return 0; if (gc->cb != NULL) gc->cb(type, seq, gc->ctx); if (--gc->ref_count <= 0) { @@ -552,6 +552,7 @@ client_global_request_reply(int type, u_int32_t seq, void *ctxt) } packet_set_alive_timeouts(0); + return 0; } static void @@ -1739,7 +1740,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /*********/ -static void +static int client_input_stdout_data(int type, u_int32_t seq, void *ctxt) { u_int data_len; @@ -1748,8 +1749,9 @@ client_input_stdout_data(int type, u_int32_t seq, void *ctxt) buffer_append(&stdout_buffer, data, data_len); explicit_bzero(data, data_len); free(data); + return 0; } -static void +static int client_input_stderr_data(int type, u_int32_t seq, void *ctxt) { u_int data_len; @@ -1758,8 +1760,9 @@ client_input_stderr_data(int type, u_int32_t seq, void *ctxt) buffer_append(&stderr_buffer, data, data_len); explicit_bzero(data, data_len); free(data); + return 0; } -static void +static int client_input_exit_status(int type, u_int32_t seq, void *ctxt) { exit_status = packet_get_int(); @@ -1774,8 +1777,9 @@ client_input_exit_status(int type, u_int32_t seq, void *ctxt) packet_write_wait(); /* Flag that we want to exit. */ quit_pending = 1; + return 0; } -static void +static int client_input_agent_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -1818,6 +1822,7 @@ client_input_agent_open(int type, u_int32_t seq, void *ctxt) packet_put_int(c->self); } packet_send(); + return 0; } static Channel * @@ -1978,7 +1983,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) } /* XXXX move to generic input handler */ -static void +static int client_input_channel_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -2029,8 +2034,9 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) packet_send(); } free(ctype); + return 0; } -static void +static int client_input_channel_req(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -2075,8 +2081,9 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) packet_send(); } free(rtype); + return 0; } -static void +static int client_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; @@ -2094,6 +2101,7 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) packet_write_wait(); } free(rtype); + return 0; } void diff --git a/dispatch.c b/dispatch.c index 64bb80947..70fa84f70 100644 --- a/dispatch.c +++ b/dispatch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dispatch.c,v 1.22 2008/10/31 15:05:34 stevesk Exp $ */ +/* $OpenBSD: dispatch.c,v 1.23 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -36,69 +36,107 @@ #include "dispatch.h" #include "packet.h" #include "compat.h" +#include "ssherr.h" -#define DISPATCH_MAX 255 - -dispatch_fn *dispatch[DISPATCH_MAX]; - -void -dispatch_protocol_error(int type, u_int32_t seq, void *ctxt) +int +dispatch_protocol_error(int type, u_int32_t seq, void *ctx) { + struct ssh *ssh = active_state; /* XXX */ + int r; + logit("dispatch_protocol_error: type %d seq %u", type, seq); if (!compat20) fatal("protocol error"); - packet_start(SSH2_MSG_UNIMPLEMENTED); - packet_put_int(seq); - packet_send(); - packet_write_wait(); + if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || + (r = sshpkt_put_u32(ssh, seq)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + ssh_packet_write_wait(ssh); + return 0; } -void -dispatch_protocol_ignore(int type, u_int32_t seq, void *ctxt) + +int +dispatch_protocol_ignore(int type, u_int32_t seq, void *ssh) { logit("dispatch_protocol_ignore: type %d seq %u", type, seq); + return 0; } + void -dispatch_init(dispatch_fn *dflt) +ssh_dispatch_init(struct ssh *ssh, dispatch_fn *dflt) { u_int i; for (i = 0; i < DISPATCH_MAX; i++) - dispatch[i] = dflt; + ssh->dispatch[i] = dflt; } + void -dispatch_range(u_int from, u_int to, dispatch_fn *fn) +ssh_dispatch_range(struct ssh *ssh, u_int from, u_int to, dispatch_fn *fn) { u_int i; for (i = from; i <= to; i++) { if (i >= DISPATCH_MAX) break; - dispatch[i] = fn; + ssh->dispatch[i] = fn; } } + void -dispatch_set(int type, dispatch_fn *fn) +ssh_dispatch_set(struct ssh *ssh, int type, dispatch_fn *fn) { - dispatch[type] = fn; + ssh->dispatch[type] = fn; } -void -dispatch_run(int mode, volatile sig_atomic_t *done, void *ctxt) + +int +ssh_dispatch_run(struct ssh *ssh, int mode, volatile sig_atomic_t *done, + void *ctxt) { - for (;;) { - int type; - u_int32_t seqnr; + int r; + u_char type; + u_int32_t seqnr; + for (;;) { if (mode == DISPATCH_BLOCK) { - type = packet_read_seqnr(&seqnr); + r = ssh_packet_read_seqnr(ssh, &type, &seqnr); + if (r != 0) + return r; } else { - type = packet_read_poll_seqnr(&seqnr); + r = ssh_packet_read_poll_seqnr(ssh, &type, &seqnr); + if (r != 0) + return r; if (type == SSH_MSG_NONE) - return; + return 0; + } + if (type > 0 && type < DISPATCH_MAX && + ssh->dispatch[type] != NULL) { + if (ssh->dispatch_skip_packets) { + debug2("skipped packet (type %u)", type); + ssh->dispatch_skip_packets--; + continue; + } + /* XXX 'ssh' will replace 'ctxt' later */ + r = (*ssh->dispatch[type])(type, seqnr, ctxt); + if (r != 0) + return r; + } else { + r = sshpkt_disconnect(ssh, + "protocol error: rcvd type %d", type); + if (r != 0) + return r; + return SSH_ERR_DISCONNECTED; } - if (type > 0 && type < DISPATCH_MAX && dispatch[type] != NULL) - (*dispatch[type])(type, seqnr, ctxt); - else - packet_disconnect("protocol error: rcvd type %d", type); if (done != NULL && *done) - return; + return 0; } } + +void +ssh_dispatch_run_fatal(struct ssh *ssh, int mode, volatile sig_atomic_t *done, + void *ctxt) +{ + int r; + + if ((r = ssh_dispatch_run(ssh, mode, done, ctxt)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); +} diff --git a/dispatch.h b/dispatch.h index 3e3d1a1ad..cd51dbc0b 100644 --- a/dispatch.h +++ b/dispatch.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dispatch.h,v 1.11 2006/04/20 09:27:09 djm Exp $ */ +/* $OpenBSD: dispatch.h,v 1.12 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -24,18 +24,35 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#ifndef DISPATCH_H +#define DISPATCH_H + +#define DISPATCH_MAX 255 enum { DISPATCH_BLOCK, DISPATCH_NONBLOCK }; -typedef void dispatch_fn(int, u_int32_t, void *); +struct ssh; + +typedef int dispatch_fn(int, u_int32_t, void *); + +int dispatch_protocol_error(int, u_int32_t, void *); +int dispatch_protocol_ignore(int, u_int32_t, void *); +void ssh_dispatch_init(struct ssh *, dispatch_fn *); +void ssh_dispatch_set(struct ssh *, int, dispatch_fn *); +void ssh_dispatch_range(struct ssh *, u_int, u_int, dispatch_fn *); +int ssh_dispatch_run(struct ssh *, int, volatile sig_atomic_t *, void *); +void ssh_dispatch_run_fatal(struct ssh *, int, volatile sig_atomic_t *, void *); + +#define dispatch_init(dflt) \ + ssh_dispatch_init(active_state, (dflt)) +#define dispatch_range(from, to, fn) \ + ssh_dispatch_range(active_state, (from), (to), (fn)) +#define dispatch_set(type, fn) \ + ssh_dispatch_set(active_state, (type), (fn)) +#define dispatch_run(mode, done, ctxt) \ + ssh_dispatch_run_fatal(active_state, (mode), (done), (ctxt)) -void dispatch_init(dispatch_fn *); -void dispatch_set(int, dispatch_fn *); -void dispatch_range(u_int, u_int, dispatch_fn *); -void dispatch_run(int, volatile sig_atomic_t *, void *); -void dispatch_protocol_error(int, u_int32_t, void *); -void dispatch_protocol_ignore(int, u_int32_t, void *); +#endif diff --git a/kex.c b/kex.c index 7c4dd7a90..92ebaf732 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.100 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kex.c,v 1.101 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -209,10 +209,11 @@ kex_prop_free(char **proposal) } /* ARGSUSED */ -static void +static int kex_protocol_error(int type, u_int32_t seq, void *ctxt) { error("Hm, kex protocol error: type %d seq %u", type, seq); + return 0; } static void @@ -281,7 +282,7 @@ kex_send_kexinit(Kex *kex) } /* ARGSUSED */ -void +int kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { const char *ptr; @@ -316,6 +317,7 @@ kex_input_kexinit(int type, u_int32_t seq, void *ctxt) packet_check_eom(); kex_kexinit_finish(kex); + return 0; } void diff --git a/kex.h b/kex.h index ffceb9fe4..1f7c824b6 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.67 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kex.h,v 1.68 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -143,7 +143,7 @@ void kex_finish(Kex *); void kex_free_newkeys(struct newkeys *); void kex_send_kexinit(Kex *); -void kex_input_kexinit(int, u_int32_t, void *); +int kex_input_kexinit(int, u_int32_t, void *); void kex_derive_keys(Kex *, u_char *, u_int, const u_char *, u_int); void kex_derive_keys_bn(Kex *, u_char *, u_int, const BIGNUM *); diff --git a/packet.h b/packet.h index 0d691979c..d9b56c301 100644 --- a/packet.h +++ b/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.62 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: packet.h,v 1.63 2015/01/19 20:07:45 markus Exp $ */ /* * Author: Tatu Ylonen @@ -32,6 +32,8 @@ struct sshkey; struct sshbuf; struct session_state; /* private session data */ +#include "dispatch.h" /* typedef, DISPATCH_MAX */ + struct ssh { /* Session state */ struct session_state *state; @@ -43,6 +45,11 @@ struct ssh { char *remote_ipaddr; int remote_port; + /* Dispatcher table */ + dispatch_fn *dispatch[DISPATCH_MAX]; + /* number of packets to ignore in the dispatcher */ + int dispatch_skip_packets; + /* datafellows */ int compat; }; diff --git a/serverloop.c b/serverloop.c index f1fbb0512..edf6a813f 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.173 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: serverloop.c,v 1.174 2015/01/19 20:07:45 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -894,7 +894,7 @@ server_loop2(Authctxt *authctxt) session_destroy_all(NULL); } -static void +static int server_input_keep_alive(int type, u_int32_t seq, void *ctxt) { debug("Got %d/%u for keepalive", type, seq); @@ -904,9 +904,10 @@ server_input_keep_alive(int type, u_int32_t seq, void *ctxt) * the bogus CHANNEL_REQUEST we send for keepalives. */ packet_set_alive_timeouts(0); + return 0; } -static void +static int server_input_stdin_data(int type, u_int32_t seq, void *ctxt) { char *data; @@ -915,15 +916,16 @@ server_input_stdin_data(int type, u_int32_t seq, void *ctxt) /* Stdin data from the client. Append it to the buffer. */ /* Ignore any data if the client has closed stdin. */ if (fdin == -1) - return; + return 0; data = packet_get_string(&data_len); packet_check_eom(); buffer_append(&stdin_buffer, data, data_len); explicit_bzero(data, data_len); free(data); + return 0; } -static void +static int server_input_eof(int type, u_int32_t seq, void *ctxt) { /* @@ -934,9 +936,10 @@ server_input_eof(int type, u_int32_t seq, void *ctxt) debug("EOF received for stdin."); packet_check_eom(); stdin_eof = 1; + return 0; } -static void +static int server_input_window_size(int type, u_int32_t seq, void *ctxt) { u_int row = packet_get_int(); @@ -948,6 +951,7 @@ server_input_window_size(int type, u_int32_t seq, void *ctxt) packet_check_eom(); if (fdin != -1) pty_change_window_size(fdin, row, col, xpixel, ypixel); + return 0; } static Channel * @@ -1092,7 +1096,7 @@ server_request_session(void) return c; } -static void +static int server_input_channel_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -1142,9 +1146,10 @@ server_input_channel_open(int type, u_int32_t seq, void *ctxt) packet_send(); } free(ctype); + return 0; } -static void +static int server_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; @@ -1239,9 +1244,10 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) packet_write_wait(); } free(rtype); + return 0; } -static void +static int server_input_channel_req(int type, u_int32_t seq, void *ctxt) { Channel *c; @@ -1271,6 +1277,7 @@ server_input_channel_req(int type, u_int32_t seq, void *ctxt) packet_send(); } free(rtype); + return 0; } static void diff --git a/sshconnect2.c b/sshconnect2.c index ba34762ea..e0d129996 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.217 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.218 2015/01/19 20:07:45 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -285,14 +285,14 @@ struct cauthmethod { int *batch_flag; /* flag in option struct that disables method */ }; -void input_userauth_success(int, u_int32_t, void *); -void input_userauth_success_unexpected(int, u_int32_t, void *); -void input_userauth_failure(int, u_int32_t, void *); -void input_userauth_banner(int, u_int32_t, void *); -void input_userauth_error(int, u_int32_t, void *); -void input_userauth_info_req(int, u_int32_t, void *); -void input_userauth_pk_ok(int, u_int32_t, void *); -void input_userauth_passwd_changereq(int, u_int32_t, void *); +int input_userauth_success(int, u_int32_t, void *); +int input_userauth_success_unexpected(int, u_int32_t, void *); +int input_userauth_failure(int, u_int32_t, void *); +int input_userauth_banner(int, u_int32_t, void *); +int input_userauth_error(int, u_int32_t, void *); +int input_userauth_info_req(int, u_int32_t, void *); +int input_userauth_pk_ok(int, u_int32_t, void *); +int input_userauth_passwd_changereq(int, u_int32_t, void *); int userauth_none(Authctxt *); int userauth_pubkey(Authctxt *); @@ -302,11 +302,11 @@ int userauth_hostbased(Authctxt *); #ifdef GSSAPI int userauth_gssapi(Authctxt *authctxt); -void input_gssapi_response(int type, u_int32_t, void *); -void input_gssapi_token(int type, u_int32_t, void *); -void input_gssapi_hash(int type, u_int32_t, void *); -void input_gssapi_error(int, u_int32_t, void *); -void input_gssapi_errtok(int, u_int32_t, void *); +int input_gssapi_response(int type, u_int32_t, void *); +int input_gssapi_token(int type, u_int32_t, void *); +int input_gssapi_hash(int type, u_int32_t, void *); +int input_gssapi_error(int, u_int32_t, void *); +int input_gssapi_errtok(int, u_int32_t, void *); #endif void userauth(Authctxt *, char *); @@ -455,15 +455,16 @@ userauth(Authctxt *authctxt, char *authlist) } /* ARGSUSED */ -void +int input_userauth_error(int type, u_int32_t seq, void *ctxt) { fatal("input_userauth_error: bad message during authentication: " "type %d", type); + return 0; } /* ARGSUSED */ -void +int input_userauth_banner(int type, u_int32_t seq, void *ctxt) { char *msg, *raw, *lang; @@ -482,10 +483,11 @@ input_userauth_banner(int type, u_int32_t seq, void *ctxt) } free(raw); free(lang); + return 0; } /* ARGSUSED */ -void +int input_userauth_success(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -499,9 +501,10 @@ input_userauth_success(int type, u_int32_t seq, void *ctxt) free(authctxt->methoddata); authctxt->methoddata = NULL; authctxt->success = 1; /* break out */ + return 0; } -void +int input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -511,10 +514,11 @@ input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) fatal("Unexpected authentication success during %s.", authctxt->method->name); + return 0; } /* ARGSUSED */ -void +int input_userauth_failure(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -537,10 +541,11 @@ input_userauth_failure(int type, u_int32_t seq, void *ctxt) debug("Authentications that can continue: %s", authlist); userauth(authctxt, authlist); + return 0; } /* ARGSUSED */ -void +int input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -608,6 +613,7 @@ done: /* try another method if we did not send a packet */ if (sent == 0) userauth(authctxt, NULL); + return 0; } #ifdef GSSAPI @@ -891,7 +897,7 @@ userauth_passwd(Authctxt *authctxt) * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST */ /* ARGSUSED */ -void +int input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) { Authctxt *authctxt = ctxt; @@ -932,7 +938,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) password = read_passphrase(prompt, RP_ALLOW_EOF); if (password == NULL) { /* bail out */ - return; + return 0; } snprintf(prompt, sizeof(prompt), "Retype %.30s@%.128s's new password: ", @@ -955,6 +961,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); + return 0; } static int @@ -1379,7 +1386,7 @@ userauth_kbdint(Authctxt *authctxt) /* * parse INFO_REQUEST, prompt user and send INFO_RESPONSE */ -void +int input_userauth_info_req(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; @@ -1431,6 +1438,7 @@ input_userauth_info_req(int type, u_int32_t seq, void *ctxt) packet_add_padding(64); packet_send(); + return 0; } static int -- cgit v1.2.3 From 57d10cbe861a235dd269c74fb2fe248469ecee9d Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Mon, 19 Jan 2015 20:16:15 +0000 Subject: upstream commit adapt kex to sshbuf and struct ssh; ok djm@ --- auth.h | 13 +- clientloop.c | 11 +- dh.c | 59 +++--- dh.h | 6 +- kex.c | 575 +++++++++++++++++++++++++++++++++------------------------ kex.h | 127 +++++++------ kexc25519.c | 94 +++++----- kexc25519c.c | 159 ++++++++++------ kexc25519s.c | 133 ++++++++----- kexdh.c | 87 ++++----- kexdhc.c | 194 +++++++++++-------- kexdhs.c | 186 ++++++++++++------- kexecdh.c | 81 ++++---- kexecdhc.c | 207 ++++++++++++++------- kexecdhs.c | 190 ++++++++++++------- kexgex.c | 105 +++++------ kexgexc.c | 287 +++++++++++++++++----------- kexgexs.c | 257 ++++++++++++++++---------- monitor.c | 4 +- monitor_wrap.c | 6 +- serverloop.c | 6 +- ssh-keyscan.c | 14 +- sshconnect2.c | 12 +- sshd.c | 47 ++--- 24 files changed, 1704 insertions(+), 1156 deletions(-) (limited to 'kex.h') diff --git a/auth.h b/auth.h index c67e4a4d4..60d1c3355 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.79 2014/12/22 07:51:30 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.80 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -42,6 +42,7 @@ #include #endif +struct ssh; struct sshkey; typedef struct Authctxt Authctxt; @@ -202,12 +203,12 @@ check_key_in_hostfiles(struct passwd *, Key *, const char *, /* hostkey handling */ Key *get_hostkey_by_index(int); -Key *get_hostkey_public_by_index(int); -Key *get_hostkey_public_by_type(int); -Key *get_hostkey_private_by_type(int); -int get_hostkey_index(Key *); +Key *get_hostkey_public_by_index(int, struct ssh *); +Key *get_hostkey_public_by_type(int, struct ssh *); +Key *get_hostkey_private_by_type(int, struct ssh *); +int get_hostkey_index(Key *, struct ssh *); int ssh1_session_key(BIGNUM *); -void sshd_hostkey_sign(Key *, Key *, u_char **, u_int *, u_char *, u_int); +int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, u_char *, size_t, u_int); /* debug messages during authentication */ void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); diff --git a/clientloop.c b/clientloop.c index 672d06989..5a018ac79 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.264 2015/01/19 20:07:45 markus Exp $ */ +/* $OpenBSD: clientloop.c,v 1.265 2015/01/19 20:16:15 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1413,8 +1413,7 @@ client_process_output(fd_set *writeset) static void client_process_buffered_input_packets(void) { - dispatch_run(DISPATCH_NONBLOCK, &quit_pending, - compat20 ? active_state->kex : NULL); + dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state); } /* scan buf[] for '~' before sending data to the peer */ @@ -1468,7 +1467,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) { fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; - int max_fd = 0, max_fd2 = 0, len, rekeying = 0; + int r, max_fd = 0, max_fd2 = 0, len, rekeying = 0; u_int64_t ibytes, obytes; u_int nalloc = 0; char buf[100]; @@ -1598,7 +1597,9 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) if (need_rekeying || packet_need_rekeying()) { debug("need rekeying"); active_state->kex->done = 0; - kex_send_kexinit(active_state->kex); + if ((r = kex_send_kexinit(active_state)) != 0) + fatal("%s: kex_send_kexinit: %s", + __func__, ssh_err(r)); need_rekeying = 0; } } diff --git a/dh.c b/dh.c index 3331cda6c..38ad615c5 100644 --- a/dh.c +++ b/dh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dh.c,v 1.53 2013/11/21 00:45:44 djm Exp $ */ +/* $OpenBSD: dh.c,v 1.54 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * @@ -39,6 +39,7 @@ #include "pathnames.h" #include "log.h" #include "misc.h" +#include "ssherr.h" static int parse_prime(int linenum, char *line, struct dhgroup *dhg) @@ -107,10 +108,11 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) goto fail; } - if ((dhg->g = BN_new()) == NULL) - fatal("parse_prime: BN_new failed"); - if ((dhg->p = BN_new()) == NULL) - fatal("parse_prime: BN_new failed"); + if ((dhg->g = BN_new()) == NULL || + (dhg->p = BN_new()) == NULL) { + error("parse_prime: BN_new failed"); + goto fail; + } if (BN_hex2bn(&dhg->g, gen) == 0) { error("moduli:%d: could not parse generator value", linenum); goto fail; @@ -128,7 +130,6 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) error("moduli:%d: generator is invalid", linenum); goto fail; } - return 1; fail: @@ -137,7 +138,6 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) if (dhg->p != NULL) BN_clear_free(dhg->p); dhg->g = dhg->p = NULL; - error("Bad prime description in line %d", linenum); return 0; } @@ -200,9 +200,11 @@ choose_dh(int min, int wantbits, int max) break; } fclose(f); - if (linenum != which+1) - fatal("WARNING: line %d disappeared in %s, giving up", + if (linenum != which+1) { + logit("WARNING: line %d disappeared in %s, giving up", which, _PATH_DH_PRIMES); + return (dh_new_group14()); + } return (dh_new_group(dhg.g, dhg.p)); } @@ -251,22 +253,22 @@ dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) return 0; } -void +int dh_gen_key(DH *dh, int need) { int pbits; - if (need <= 0) - fatal("%s: need <= 0", __func__); - if (dh->p == NULL) - fatal("%s: dh->p == NULL", __func__); - if ((pbits = BN_num_bits(dh->p)) <= 0) - fatal("%s: bits(p) <= 0", __func__); + if (need < 0 || dh->p == NULL || + (pbits = BN_num_bits(dh->p)) <= 0 || + need > INT_MAX / 2 || 2 * need >= pbits) + return SSH_ERR_INVALID_ARGUMENT; dh->length = MIN(need * 2, pbits - 1); - if (DH_generate_key(dh) == 0) - fatal("%s: key generation failed", __func__); - if (!dh_pub_is_valid(dh, dh->pub_key)) - fatal("%s: generated invalid key", __func__); + if (DH_generate_key(dh) == 0 || + !dh_pub_is_valid(dh, dh->pub_key)) { + BN_clear_free(dh->priv_key); + return SSH_ERR_LIBCRYPTO_ERROR; + } + return 0; } DH * @@ -275,13 +277,12 @@ dh_new_group_asc(const char *gen, const char *modulus) DH *dh; if ((dh = DH_new()) == NULL) - fatal("dh_new_group_asc: DH_new"); - - if (BN_hex2bn(&dh->p, modulus) == 0) - fatal("BN_hex2bn p"); - if (BN_hex2bn(&dh->g, gen) == 0) - fatal("BN_hex2bn g"); - + return NULL; + if (BN_hex2bn(&dh->p, modulus) == 0 || + BN_hex2bn(&dh->g, gen) == 0) { + DH_free(dh); + return NULL; + } return (dh); } @@ -296,7 +297,7 @@ dh_new_group(BIGNUM *gen, BIGNUM *modulus) DH *dh; if ((dh = DH_new()) == NULL) - fatal("dh_new_group: DH_new"); + return NULL; dh->p = modulus; dh->g = gen; @@ -344,7 +345,7 @@ dh_new_group14(void) * from RFC4419 section 3. */ -int +u_int dh_estimate(int bits) { if (bits <= 112) diff --git a/dh.h b/dh.h index 48f7b68ea..63a1b1477 100644 --- a/dh.h +++ b/dh.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dh.h,v 1.11 2013/10/08 11:42:13 dtucker Exp $ */ +/* $OpenBSD: dh.h,v 1.12 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. @@ -38,10 +38,10 @@ DH *dh_new_group(BIGNUM *, BIGNUM *); DH *dh_new_group1(void); DH *dh_new_group14(void); -void dh_gen_key(DH *, int); +int dh_gen_key(DH *, int); int dh_pub_is_valid(DH *, BIGNUM *); -int dh_estimate(int); +u_int dh_estimate(int); /* Min and max values from RFC4419. */ #define DH_GRP_MIN 1024 diff --git a/kex.c b/kex.c index 92ebaf732..9280dd3f4 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.101 2015/01/19 20:07:45 markus Exp $ */ +/* $OpenBSD: kex.c,v 1.102 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -37,20 +37,22 @@ #include #endif -#include "xmalloc.h" #include "ssh2.h" -#include "buffer.h" #include "packet.h" #include "compat.h" #include "cipher.h" -#include "key.h" +#include "sshkey.h" #include "kex.h" #include "log.h" #include "mac.h" #include "match.h" +#include "misc.h" #include "dispatch.h" #include "monitor.h" #include "roaming.h" + +#include "ssherr.h" +#include "sshbuf.h" #include "digest.h" #if OPENSSL_VERSION_NUMBER >= 0x00907000L @@ -62,12 +64,12 @@ extern const EVP_MD *evp_ssh_sha256(void); #endif /* prototype */ -static void kex_kexinit_finish(Kex *); -static void kex_choose_conf(Kex *); +static int kex_choose_conf(struct ssh *); +static int kex_input_newkeys(int, u_int32_t, void *); struct kexalg { char *name; - int type; + u_int type; int ec_nid; int hash_alg; }; @@ -99,7 +101,7 @@ static const struct kexalg kexalgs[] = { char * kex_alg_list(char sep) { - char *ret = NULL; + char *ret = NULL, *tmp; size_t nlen, rlen = 0; const struct kexalg *k; @@ -107,7 +109,11 @@ kex_alg_list(char sep) if (ret != NULL) ret[rlen++] = sep; nlen = strlen(k->name); - ret = xrealloc(ret, 1, rlen + nlen + 2); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; memcpy(ret + rlen, k->name, nlen + 1); rlen += nlen; } @@ -134,7 +140,8 @@ kex_names_valid(const char *names) if (names == NULL || strcmp(names, "") == 0) return 0; - s = cp = xstrdup(names); + if ((s = cp = strdup(names)) == NULL) + return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { if (kex_alg_by_name(p) == NULL) { @@ -149,56 +156,75 @@ kex_names_valid(const char *names) } /* put algorithm proposal into buffer */ -static void -kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) +int +kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) { u_int i; + int r; + + sshbuf_reset(b); - buffer_clear(b); /* * add a dummy cookie, the cookie will be overwritten by * kex_send_kexinit(), each time a kexinit is set */ - for (i = 0; i < KEX_COOKIE_LEN; i++) - buffer_put_char(b, 0); - for (i = 0; i < PROPOSAL_MAX; i++) - buffer_put_cstring(b, proposal[i]); - buffer_put_char(b, 0); /* first_kex_packet_follows */ - buffer_put_int(b, 0); /* uint32 reserved */ + for (i = 0; i < KEX_COOKIE_LEN; i++) { + if ((r = sshbuf_put_u8(b, 0)) != 0) + return r; + } + for (i = 0; i < PROPOSAL_MAX; i++) { + if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) + return r; + } + if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ + (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ + return r; + return 0; } /* parse buffer and return algorithm proposal */ -static char ** -kex_buf2prop(Buffer *raw, int *first_kex_follows) +int +kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) { - Buffer b; + struct sshbuf *b = NULL; + u_char v; u_int i; - char **proposal; - - proposal = xcalloc(PROPOSAL_MAX, sizeof(char *)); - - buffer_init(&b); - buffer_append(&b, buffer_ptr(raw), buffer_len(raw)); - /* skip cookie */ - for (i = 0; i < KEX_COOKIE_LEN; i++) - buffer_get_char(&b); + char **proposal = NULL; + int r; + + *propp = NULL; + if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((b = sshbuf_fromb(raw)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */ + goto out; /* extract kex init proposal strings */ for (i = 0; i < PROPOSAL_MAX; i++) { - proposal[i] = buffer_get_cstring(&b,NULL); + if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) + goto out; debug2("kex_parse_kexinit: %s", proposal[i]); } /* first kex follows / reserved */ - i = buffer_get_char(&b); + if ((r = sshbuf_get_u8(b, &v)) != 0 || + (r = sshbuf_get_u32(b, &i)) != 0) + goto out; if (first_kex_follows != NULL) *first_kex_follows = i; - debug2("kex_parse_kexinit: first_kex_follows %d ", i); - i = buffer_get_int(&b); + debug2("kex_parse_kexinit: first_kex_follows %d ", v); debug2("kex_parse_kexinit: reserved %u ", i); - buffer_free(&b); - return proposal; + r = 0; + *propp = proposal; + out: + if (r != 0 && proposal != NULL) + kex_prop_free(proposal); + sshbuf_free(b); + return r; } -static void +void kex_prop_free(char **proposal) { u_int i; @@ -217,91 +243,103 @@ kex_protocol_error(int type, u_int32_t seq, void *ctxt) } static void -kex_reset_dispatch(void) +kex_reset_dispatch(struct ssh *ssh) { - dispatch_range(SSH2_MSG_TRANSPORT_MIN, + ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); - dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); } -void -kex_finish(Kex *kex) +int +kex_send_newkeys(struct ssh *ssh) { - kex_reset_dispatch(); + int r; - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - /* packet_write_wait(); */ + kex_reset_dispatch(ssh); + if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; debug("SSH2_MSG_NEWKEYS sent"); - debug("expecting SSH2_MSG_NEWKEYS"); - packet_read_expect(SSH2_MSG_NEWKEYS); - packet_check_eom(); - debug("SSH2_MSG_NEWKEYS received"); + ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); + return 0; +} + +static int +kex_input_newkeys(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + int r; + debug("SSH2_MSG_NEWKEYS received"); + ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); + if ((r = sshpkt_get_end(ssh)) != 0) + return r; kex->done = 1; - buffer_clear(kex->peer); - /* buffer_clear(kex->my); */ + sshbuf_reset(kex->peer); + /* sshbuf_reset(kex->my); */ kex->flags &= ~KEX_INIT_SENT; free(kex->name); kex->name = NULL; + return 0; } -void -kex_send_kexinit(Kex *kex) +int +kex_send_kexinit(struct ssh *ssh) { - u_int32_t rnd = 0; u_char *cookie; - u_int i; + struct kex *kex = ssh->kex; + int r; - if (kex == NULL) { - error("kex_send_kexinit: no kex, cannot rekey"); - return; - } - if (kex->flags & KEX_INIT_SENT) { - debug("KEX_INIT_SENT"); - return; - } + if (kex == NULL) + return SSH_ERR_INTERNAL_ERROR; + if (kex->flags & KEX_INIT_SENT) + return 0; kex->done = 0; /* generate a random cookie */ - if (buffer_len(kex->my) < KEX_COOKIE_LEN) - fatal("kex_send_kexinit: kex proposal too short"); - cookie = buffer_ptr(kex->my); - for (i = 0; i < KEX_COOKIE_LEN; i++) { - if (i % 4 == 0) - rnd = arc4random(); - cookie[i] = rnd; - rnd >>= 8; - } - packet_start(SSH2_MSG_KEXINIT); - packet_put_raw(buffer_ptr(kex->my), buffer_len(kex->my)); - packet_send(); + if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) + return SSH_ERR_INVALID_FORMAT; + if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + arc4random_buf(cookie, KEX_COOKIE_LEN); + + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || + (r = sshpkt_putb(ssh, kex->my)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; debug("SSH2_MSG_KEXINIT sent"); kex->flags |= KEX_INIT_SENT; + return 0; } /* ARGSUSED */ int kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { - const char *ptr; + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + const u_char *ptr; u_int i; size_t dlen; - Kex *kex = (Kex *)ctxt; + int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) - fatal("kex_input_kexinit: no kex, cannot rekey"); + return SSH_ERR_INVALID_ARGUMENT; - ptr = packet_get_raw(&dlen); - buffer_append(kex->peer, ptr, dlen); + ptr = sshpkt_ptr(ssh, &dlen); + if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) + return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) - packet_get_char(); + if ((r = sshpkt_get_u8(ssh, NULL)) != 0) + return r; for (i = 0; i < PROPOSAL_MAX; i++) - free(packet_get_string(NULL)); + if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) + return r; /* * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported * KEX method has the server move first, but a server might be using @@ -312,12 +350,47 @@ kex_input_kexinit(int type, u_int32_t seq, void *ctxt) * for cases where the server *doesn't* go first. I guess we should * ignore it when it is set for these cases, which is what we do now. */ - (void) packet_get_char(); /* first_kex_follows */ - (void) packet_get_int(); /* reserved */ - packet_check_eom(); + if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ + (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ + (r = sshpkt_get_end(ssh)) != 0) + return r; - kex_kexinit_finish(kex); - return 0; + if (!(kex->flags & KEX_INIT_SENT)) + if ((r = kex_send_kexinit(ssh)) != 0) + return r; + if ((r = kex_choose_conf(ssh)) != 0) + return r; + + if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) + return (kex->kex[kex->kex_type])(ssh); + + return SSH_ERR_INTERNAL_ERROR; +} + +int +kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp) +{ + struct kex *kex; + int r; + + *kexp = NULL; + if ((kex = calloc(1, sizeof(*kex))) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((kex->peer = sshbuf_new()) == NULL || + (kex->my = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kex_prop2buf(kex->my, proposal)) != 0) + goto out; + kex->done = 0; + kex_reset_dispatch(ssh); + r = 0; + *kexp = kex; + out: + if (r != 0) + kex_free(kex); + return r; } void @@ -351,51 +424,53 @@ kex_free_newkeys(struct newkeys *newkeys) free(newkeys); } -Kex * -kex_setup(char *proposal[PROPOSAL_MAX]) +void +kex_free(struct kex *kex) { - struct kex *kex; + u_int mode; - if ((kex = calloc(1, sizeof(*kex))) == NULL) - fatal("%s: calloc", __func__); - if ((kex->peer = sshbuf_new()) == NULL || - (kex->my = sshbuf_new()) == NULL) { - fatal("%s: sshbuf_new", __func__); +#ifdef WITH_OPENSSL + if (kex->dh) + DH_free(kex->dh); + if (kex->ec_client_key) + EC_KEY_free(kex->ec_client_key); +#endif + for (mode = 0; mode < MODE_MAX; mode++) { + kex_free_newkeys(kex->newkeys[mode]); + kex->newkeys[mode] = NULL; } - kex_prop2buf(kex->my, proposal); - kex->done = 0; - - kex_send_kexinit(kex); /* we start */ - kex_reset_dispatch(); - - return kex; + sshbuf_free(kex->peer); + sshbuf_free(kex->my); + free(kex->session_id); + free(kex->client_version_string); + free(kex->server_version_string); + free(kex); } -static void -kex_kexinit_finish(Kex *kex) +int +kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) { - if (!(kex->flags & KEX_INIT_SENT)) - kex_send_kexinit(kex); - - kex_choose_conf(kex); - - if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX && - kex->kex[kex->kex_type] != NULL) { - (kex->kex[kex->kex_type])(kex); - } else { - fatal("Unsupported key exchange %d", kex->kex_type); + int r; + + if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0) + return r; + if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ + kex_free(ssh->kex); + ssh->kex = NULL; + return r; } + return 0; } -static void -choose_enc(Enc *enc, char *client, char *server) +static int +choose_enc(struct sshenc *enc, char *client, char *server) { char *name = match_list(client, server, NULL); + if (name == NULL) - fatal("no matching cipher found: client %s server %s", - client, server); + return SSH_ERR_NO_CIPHER_ALG_MATCH; if ((enc->cipher = cipher_by_name(name)) == NULL) - fatal("matching cipher is not supported: %s", name); + return SSH_ERR_INTERNAL_ERROR; enc->name = name; enc->enabled = 0; enc->iv = NULL; @@ -403,31 +478,34 @@ choose_enc(Enc *enc, char *client, char *server) enc->key = NULL; enc->key_len = cipher_keylen(enc->cipher); enc->block_size = cipher_blocksize(enc->cipher); + return 0; } -static void -choose_mac(Mac *mac, char *client, char *server) +static int +choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) { char *name = match_list(client, server, NULL); + if (name == NULL) - fatal("no matching mac found: client %s server %s", - client, server); + return SSH_ERR_NO_MAC_ALG_MATCH; if (mac_setup(mac, name) < 0) - fatal("unsupported mac %s", name); + return SSH_ERR_INTERNAL_ERROR; /* truncate the key */ - if (datafellows & SSH_BUG_HMAC) + if (ssh->compat & SSH_BUG_HMAC) mac->key_len = 16; mac->name = name; mac->key = NULL; mac->enabled = 0; + return 0; } -static void -choose_comp(Comp *comp, char *client, char *server) +static int +choose_comp(struct sshcomp *comp, char *client, char *server) { char *name = match_list(client, server, NULL); + if (name == NULL) - fatal("no matching comp found: client %s server %s", client, server); + return SSH_ERR_NO_COMPRESS_ALG_MATCH; if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; } else if (strcmp(name, "zlib") == 0) { @@ -435,36 +513,41 @@ choose_comp(Comp *comp, char *client, char *server) } else if (strcmp(name, "none") == 0) { comp->type = COMP_NONE; } else { - fatal("unsupported comp %s", name); + return SSH_ERR_INTERNAL_ERROR; } comp->name = name; + return 0; } -static void -choose_kex(Kex *k, char *client, char *server) +static int +choose_kex(struct kex *k, char *client, char *server) { const struct kexalg *kexalg; k->name = match_list(client, server, NULL); + if (k->name == NULL) - fatal("Unable to negotiate a key exchange method"); + return SSH_ERR_NO_KEX_ALG_MATCH; if ((kexalg = kex_alg_by_name(k->name)) == NULL) - fatal("unsupported kex alg %s", k->name); + return SSH_ERR_INTERNAL_ERROR; k->kex_type = kexalg->type; k->hash_alg = kexalg->hash_alg; k->ec_nid = kexalg->ec_nid; + return 0; } -static void -choose_hostkeyalg(Kex *k, char *client, char *server) +static int +choose_hostkeyalg(struct kex *k, char *client, char *server) { char *hostkeyalg = match_list(client, server, NULL); + if (hostkeyalg == NULL) - fatal("no hostkey alg"); - k->hostkey_type = key_type_from_name(hostkeyalg); + return SSH_ERR_NO_HOSTKEY_ALG_MATCH; + k->hostkey_type = sshkey_type_from_name(hostkeyalg); if (k->hostkey_type == KEY_UNSPEC) - fatal("bad hostkey alg '%s'", hostkeyalg); + return SSH_ERR_INTERNAL_ERROR; free(hostkeyalg); + return 0; } static int @@ -491,18 +574,20 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) return (1); } -static void -kex_choose_conf(Kex *kex) +static int +kex_choose_conf(struct ssh *ssh) { - Newkeys *newkeys; - char **my, **peer; + struct kex *kex = ssh->kex; + struct newkeys *newkeys; + char **my = NULL, **peer = NULL; char **cprop, **sprop; int nenc, nmac, ncomp; u_int mode, ctos, need, dh_need, authlen; - int first_kex_follows, type; + int r, first_kex_follows; - my = kex_buf2prop(kex->my, NULL); - peer = kex_buf2prop(kex->peer, &first_kex_follows); + if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0 || + (r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) + goto out; if (kex->server) { cprop=peer; @@ -514,8 +599,9 @@ kex_choose_conf(Kex *kex) /* Check whether server offers roaming */ if (!kex->server) { - char *roaming; - roaming = match_list(KEX_RESUME, peer[PROPOSAL_KEX_ALGS], NULL); + char *roaming = match_list(KEX_RESUME, + peer[PROPOSAL_KEX_ALGS], NULL); + if (roaming) { kex->roaming = 1; free(roaming); @@ -524,28 +610,39 @@ kex_choose_conf(Kex *kex) /* Algorithm Negotiation */ for (mode = 0; mode < MODE_MAX; mode++) { - newkeys = xcalloc(1, sizeof(*newkeys)); + if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } kex->newkeys[mode] = newkeys; ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; - choose_enc(&newkeys->enc, cprop[nenc], sprop[nenc]); - /* ignore mac for authenticated encryption */ + if ((r = choose_enc(&newkeys->enc, cprop[nenc], + sprop[nenc])) != 0) + goto out; authlen = cipher_authlen(newkeys->enc.cipher); - if (authlen == 0) - choose_mac(&newkeys->mac, cprop[nmac], sprop[nmac]); - choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]); + /* ignore mac for authenticated encryption */ + if (authlen == 0 && + (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], + sprop[nmac])) != 0) + goto out; + if ((r = choose_comp(&newkeys->comp, cprop[ncomp], + sprop[ncomp])) != 0) + goto out; debug("kex: %s %s %s %s", ctos ? "client->server" : "server->client", newkeys->enc.name, authlen == 0 ? newkeys->mac.name : "", newkeys->comp.name); } - choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); - choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], - sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); + if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], + sprop[PROPOSAL_KEX_ALGS])) != 0 || + (r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) + goto out; need = dh_need = 0; for (mode = 0; mode < MODE_MAX; mode++) { newkeys = kex->newkeys[mode]; @@ -564,45 +661,47 @@ kex_choose_conf(Kex *kex) /* ignore the next message if the proposals do not match */ if (first_kex_follows && !proposals_match(my, peer) && - !(datafellows & SSH_BUG_FIRSTKEX)) { - type = packet_read(); - debug2("skipping next packet (type %u)", type); - } - + !(ssh->compat & SSH_BUG_FIRSTKEX)) + ssh->dispatch_skip_packets = 1; + r = 0; + out: kex_prop_free(my); kex_prop_free(peer); + return r; } -static u_char * -derive_key(Kex *kex, int id, u_int need, u_char *hash, u_int hashlen, - const u_char *shared_secret, u_int slen) +static int +derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, + const struct sshbuf *shared_secret, u_char **keyp) { - Buffer b; - struct ssh_digest_ctx *hashctx; + struct kex *kex = ssh->kex; + struct ssh_digest_ctx *hashctx = NULL; char c = id; u_int have; size_t mdsz; u_char *digest; + int r; if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) - fatal("bad kex md size %zu", mdsz); - digest = xmalloc(roundup(need, mdsz)); - - buffer_init(&b); - buffer_append(&b, shared_secret, slen); + return SSH_ERR_INVALID_ARGUMENT; + if ((digest = calloc(1, roundup(need, mdsz))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } /* K1 = HASH(K || H || "A" || session_id) */ - if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL) - fatal("%s: ssh_digest_start failed", __func__); - if (ssh_digest_update_buffer(hashctx, &b) != 0 || + if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || + ssh_digest_update_buffer(hashctx, shared_secret) != 0 || ssh_digest_update(hashctx, hash, hashlen) != 0 || ssh_digest_update(hashctx, &c, 1) != 0 || ssh_digest_update(hashctx, kex->session_id, - kex->session_id_len) != 0) - fatal("%s: ssh_digest_update failed", __func__); - if (ssh_digest_final(hashctx, digest, mdsz) != 0) - fatal("%s: ssh_digest_final failed", __func__); + kex->session_id_len) != 0 || + ssh_digest_final(hashctx, digest, mdsz) != 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } ssh_digest_free(hashctx); + hashctx = NULL; /* * expand key: @@ -610,38 +709,49 @@ derive_key(Kex *kex, int id, u_int need, u_char *hash, u_int hashlen, * Key = K1 || K2 || ... || Kn */ for (have = mdsz; need > have; have += mdsz) { - if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL) - fatal("%s: ssh_digest_start failed", __func__); - if (ssh_digest_update_buffer(hashctx, &b) != 0 || + if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || + ssh_digest_update_buffer(hashctx, shared_secret) != 0 || ssh_digest_update(hashctx, hash, hashlen) != 0 || - ssh_digest_update(hashctx, digest, have) != 0) - fatal("%s: ssh_digest_update failed", __func__); - if (ssh_digest_final(hashctx, digest + have, mdsz) != 0) - fatal("%s: ssh_digest_final failed", __func__); + ssh_digest_update(hashctx, digest, have) != 0 || + ssh_digest_final(hashctx, digest + have, mdsz) != 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } ssh_digest_free(hashctx); + hashctx = NULL; } - buffer_free(&b); #ifdef DEBUG_KEX fprintf(stderr, "key '%c'== ", c); dump_digest("key", digest, need); #endif - return digest; + *keyp = digest; + digest = NULL; + r = 0; + out: + if (digest) + free(digest); + ssh_digest_free(hashctx); + return r; } #define NKEYS 6 -void -kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen, - const u_char *shared_secret, u_int slen) +int +kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, + const struct sshbuf *shared_secret) { + struct kex *kex = ssh->kex; u_char *keys[NKEYS]; - u_int i, mode, ctos; + u_int i, j, mode, ctos; + int r; for (i = 0; i < NKEYS; i++) { - keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, hashlen, - shared_secret, slen); + if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, + shared_secret, &keys[i])) != 0) { + for (j = 0; j < i; j++) + free(keys[j]); + return r; + } } - - debug2("kex_derive_keys"); for (mode = 0; mode < MODE_MAX; mode++) { ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); @@ -649,54 +759,54 @@ kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen, kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; } + return 0; } #ifdef WITH_OPENSSL -void -kex_derive_keys_bn(Kex *kex, u_char *hash, u_int hashlen, const BIGNUM *secret) +int +kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen, + const BIGNUM *secret) { - Buffer shared_secret; - - buffer_init(&shared_secret); - buffer_put_bignum2(&shared_secret, secret); - kex_derive_keys(kex, hash, hashlen, - buffer_ptr(&shared_secret), buffer_len(&shared_secret)); - buffer_free(&shared_secret); + struct sshbuf *shared_secret; + int r; + + if ((shared_secret = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0) + r = kex_derive_keys(ssh, hash, hashlen, shared_secret); + sshbuf_free(shared_secret); + return r; } #endif #ifdef WITH_SSH1 -void +int derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, u_int8_t cookie[8], u_int8_t id[16]) { u_int8_t nbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH]; - int len; - struct ssh_digest_ctx *hashctx; - - if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) - fatal("%s: ssh_digest_start", __func__); + struct ssh_digest_ctx *hashctx = NULL; + size_t len; + int r; len = BN_num_bytes(host_modulus); if (len < (512 / 8) || (u_int)len > sizeof(nbuf)) - fatal("%s: bad host modulus (len %d)", __func__, len); - BN_bn2bin(host_modulus, nbuf); - if (ssh_digest_update(hashctx, nbuf, len) != 0) - fatal("%s: ssh_digest_update failed", __func__); - - len = BN_num_bytes(server_modulus); - if (len < (512 / 8) || (u_int)len > sizeof(nbuf)) - fatal("%s: bad server modulus (len %d)", __func__, len); - BN_bn2bin(server_modulus, nbuf); - if (ssh_digest_update(hashctx, nbuf, len) != 0 || - ssh_digest_update(hashctx, cookie, 8) != 0) - fatal("%s: ssh_digest_update failed", __func__); - if (ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) - fatal("%s: ssh_digest_final failed", __func__); + return SSH_ERR_KEY_BITS_MISMATCH; + if (BN_bn2bin(host_modulus, nbuf) <= 0 || + (hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || + ssh_digest_update(hashctx, nbuf, len) != 0 || + ssh_digest_update(hashctx, cookie, 8) != 0 || + ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5)); - + r = 0; + out: + ssh_digest_free(hashctx); explicit_bzero(nbuf, sizeof(nbuf)); explicit_bzero(obuf, sizeof(obuf)); + return r; } #endif @@ -704,16 +814,7 @@ derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, void dump_digest(char *msg, u_char *digest, int len) { - int i; - fprintf(stderr, "%s\n", msg); - for (i = 0; i < len; i++) { - fprintf(stderr, "%02x", digest[i]); - if (i%32 == 31) - fprintf(stderr, "\n"); - else if (i%8 == 7) - fprintf(stderr, " "); - } - fprintf(stderr, "\n"); + sshbuf_dump_data(digest, len, stderr); } #endif diff --git a/kex.h b/kex.h index 1f7c824b6..1798eea4d 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.68 2015/01/19 20:07:45 markus Exp $ */ +/* $OpenBSD: kex.h,v 1.69 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -30,8 +30,8 @@ #include "buffer.h" /* XXX for typedef */ #include "key.h" /* XXX for typedef */ -#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) -#include +#ifdef WITH_LEAKMALLOC +#include "leakmalloc.h" #endif #define KEX_COOKIE_LEN 16 @@ -50,6 +50,8 @@ #define COMP_ZLIB 1 #define COMP_DELAYED 2 +#define CURVE25519_SIZE 32 + enum kex_init_proposals { PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, @@ -82,12 +84,6 @@ enum kex_exchange { #define KEX_INIT_SENT 0x0001 -typedef struct kex Kex; -typedef struct sshcomp Comp; -typedef struct sshmac Mac; -typedef struct sshenc Enc; -typedef struct newkeys Newkeys; - struct sshenc { char *name; const struct sshcipher *cipher; @@ -106,8 +102,11 @@ struct sshcomp { struct newkeys { struct sshenc enc; struct sshmac mac; - struct sshcomp comp; + struct sshcomp comp; }; + +struct ssh; + struct kex { u_char *session_id; size_t session_id_len; @@ -117,73 +116,87 @@ struct kex { int server; char *name; int hostkey_type; - int kex_type; + u_int kex_type; int roaming; struct sshbuf *my; struct sshbuf *peer; sig_atomic_t done; - int flags; + u_int flags; int hash_alg; int ec_nid; char *client_version_string; char *server_version_string; - int (*verify_host_key)(Key *); - Key *(*load_host_public_key)(int); - Key *(*load_host_private_key)(int); - int (*host_key_index)(Key *); - void (*sign)(Key *, Key *, u_char **, u_int *, u_char *, u_int); - void (*kex[KEX_MAX])(Kex *); + int (*verify_host_key)(struct sshkey *, struct ssh *); + struct sshkey *(*load_host_public_key)(int, struct ssh *); + struct sshkey *(*load_host_private_key)(int, struct ssh *); + int (*host_key_index)(struct sshkey *, struct ssh *); + int (*sign)(struct sshkey *, struct sshkey *, + u_char **, size_t *, u_char *, size_t, u_int); + int (*kex[KEX_MAX])(struct ssh *); + /* kex specific state */ + DH *dh; /* DH */ + u_int min, max, nbits; /* GEX */ + EC_KEY *ec_client_key; /* ECDH */ + const EC_GROUP *ec_group; /* ECDH */ + u_char c25519_client_key[CURVE25519_SIZE]; /* 25519 */ + u_char c25519_client_pubkey[CURVE25519_SIZE]; /* 25519 */ }; int kex_names_valid(const char *); char *kex_alg_list(char); -Kex *kex_setup(char *[PROPOSAL_MAX]); -void kex_finish(Kex *); -void kex_free_newkeys(struct newkeys *); +int kex_new(struct ssh *, char *[PROPOSAL_MAX], struct kex **); +int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); +void kex_free_newkeys(struct newkeys *); +void kex_free(struct kex *); -void kex_send_kexinit(Kex *); -int kex_input_kexinit(int, u_int32_t, void *); -void kex_derive_keys(Kex *, u_char *, u_int, const u_char *, u_int); -void kex_derive_keys_bn(Kex *, u_char *, u_int, const BIGNUM *); - -void kexdh_client(Kex *); -void kexdh_server(Kex *); -void kexgex_client(Kex *); -void kexgex_server(Kex *); -void kexecdh_client(Kex *); -void kexecdh_server(Kex *); -void kexc25519_client(Kex *); -void kexc25519_server(Kex *); - -void -kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, - BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); -void -kexgex_hash(int, char *, char *, char *, int, char *, - int, u_char *, int, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *, - BIGNUM *, BIGNUM *, u_char **, u_int *); -#ifdef OPENSSL_HAS_ECC -void -kex_ecdh_hash(int, const EC_GROUP *, char *, char *, char *, int, - char *, int, u_char *, int, const EC_POINT *, const EC_POINT *, - const BIGNUM *, u_char **, u_int *); -#endif -void -kex_c25519_hash(int, char *, char *, char *, int, - char *, int, u_char *, int, const u_char *, const u_char *, - const u_char *, u_int, u_char **, u_int *); +int kex_buf2prop(struct sshbuf *, int *, char ***); +int kex_prop2buf(struct sshbuf *, char *proposal[PROPOSAL_MAX]); +void kex_prop_free(char **); -#define CURVE25519_SIZE 32 -void kexc25519_keygen(u_char[CURVE25519_SIZE], u_char[CURVE25519_SIZE]) +int kex_send_kexinit(struct ssh *); +int kex_input_kexinit(int, u_int32_t, void *); +int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *); +int kex_derive_keys_bn(struct ssh *, u_char *, u_int, const BIGNUM *); +int kex_send_newkeys(struct ssh *); + +int kexdh_client(struct ssh *); +int kexdh_server(struct ssh *); +int kexgex_client(struct ssh *); +int kexgex_server(struct ssh *); +int kexecdh_client(struct ssh *); +int kexecdh_server(struct ssh *); +int kexc25519_client(struct ssh *); +int kexc25519_server(struct ssh *); + +int kex_dh_hash(const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); + +int kexgex_hash(int, const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + int, int, int, + const BIGNUM *, const BIGNUM *, const BIGNUM *, + const BIGNUM *, const BIGNUM *, + u_char *, size_t *); + +int kex_ecdh_hash(int, const EC_GROUP *, const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + const EC_POINT *, const EC_POINT *, const BIGNUM *, u_char *, size_t *); + +int kex_c25519_hash(int, const char *, const char *, const char *, size_t, + const char *, size_t, const u_char *, size_t, const u_char *, const u_char *, + const u_char *, size_t, u_char *, size_t *); + +void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); -void kexc25519_shared_key(const u_char key[CURVE25519_SIZE], - const u_char pub[CURVE25519_SIZE], Buffer *out) +int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], + const u_char pub[CURVE25519_SIZE], struct sshbuf *out) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); -void +int derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]); #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) diff --git a/kexc25519.c b/kexc25519.c index e3afa0055..b6e6c4010 100644 --- a/kexc25519.c +++ b/kexc25519.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519.c,v 1.7 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: kexc25519.c,v 1.8 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001, 2013 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -35,13 +35,14 @@ #include #include -#include "buffer.h" +#include "sshbuf.h" #include "ssh2.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #include "kex.h" #include "log.h" #include "digest.h" +#include "ssherr.h" extern int crypto_scalarmult_curve25519(u_char a[CURVE25519_SIZE], const u_char b[CURVE25519_SIZE], const u_char c[CURVE25519_SIZE]) @@ -58,65 +59,70 @@ kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) crypto_scalarmult_curve25519(pub, key, basepoint); } -void +int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], - const u_char pub[CURVE25519_SIZE], Buffer *out) + const u_char pub[CURVE25519_SIZE], struct sshbuf *out) { u_char shared_key[CURVE25519_SIZE]; + int r; crypto_scalarmult_curve25519(shared_key, key, pub); #ifdef DEBUG_KEXECDH dump_digest("shared secret", shared_key, CURVE25519_SIZE); #endif - buffer_clear(out); - buffer_put_bignum2_from_string(out, shared_key, CURVE25519_SIZE); + sshbuf_reset(out); + r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); explicit_bzero(shared_key, CURVE25519_SIZE); + return r; } -void +int kex_c25519_hash( int hash_alg, - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - u_char *serverhostkeyblob, int sbloblen, + const char *client_version_string, + const char *server_version_string, + const char *ckexinit, size_t ckexinitlen, + const char *skexinit, size_t skexinitlen, + const u_char *serverhostkeyblob, size_t sbloblen, const u_char client_dh_pub[CURVE25519_SIZE], const u_char server_dh_pub[CURVE25519_SIZE], - const u_char *shared_secret, u_int secretlen, - u_char **hash, u_int *hashlen) + const u_char *shared_secret, size_t secretlen, + u_char *hash, size_t *hashlen) { - Buffer b; - static u_char digest[SSH_DIGEST_MAX_LENGTH]; - - buffer_init(&b); - buffer_put_cstring(&b, client_version_string); - buffer_put_cstring(&b, server_version_string); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - buffer_put_string(&b, client_dh_pub, CURVE25519_SIZE); - buffer_put_string(&b, server_dh_pub, CURVE25519_SIZE); - buffer_append(&b, shared_secret, secretlen); - + struct sshbuf *b; + int r; + + if (*hashlen < ssh_digest_bytes(hash_alg)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, client_version_string)) < 0 || + (r = sshbuf_put_cstring(b, server_version_string)) < 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, ckexinitlen+1)) < 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) < 0 || + (r = sshbuf_put(b, ckexinit, ckexinitlen)) < 0 || + (r = sshbuf_put_u32(b, skexinitlen+1)) < 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) < 0 || + (r = sshbuf_put(b, skexinit, skexinitlen)) < 0 || + (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) < 0 || + (r = sshbuf_put_string(b, client_dh_pub, CURVE25519_SIZE)) < 0 || + (r = sshbuf_put_string(b, server_dh_pub, CURVE25519_SIZE)) < 0 || + (r = sshbuf_put(b, shared_secret, secretlen)) < 0) { + sshbuf_free(b); + return r; + } #ifdef DEBUG_KEX - buffer_dump(&b); + sshbuf_dump(b, stderr); #endif - if (ssh_digest_buffer(hash_alg, &b, digest, sizeof(digest)) != 0) - fatal("%s: digest_buffer failed", __func__); - - buffer_free(&b); - + if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); + *hashlen = ssh_digest_bytes(hash_alg); #ifdef DEBUG_KEX - dump_digest("hash", digest, ssh_digest_bytes(hash_alg)); + dump_digest("hash", hash, *hashlen); #endif - *hash = digest; - *hashlen = ssh_digest_bytes(hash_alg); + return 0; } diff --git a/kexc25519c.c b/kexc25519c.c index ffb537ef6..833ce0544 100644 --- a/kexc25519c.c +++ b/kexc25519c.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519c.c,v 1.5 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexc25519c.c,v 1.6 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -33,97 +33,136 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #include "kex.h" #include "log.h" #include "packet.h" #include "ssh2.h" +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" -void -kexc25519_client(Kex *kex) -{ - Key *server_host_key; - u_char client_key[CURVE25519_SIZE]; - u_char client_pubkey[CURVE25519_SIZE]; - u_char *server_pubkey = NULL; - u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *hash; - u_int slen, sbloblen, hashlen; - Buffer shared_secret; - - kexc25519_keygen(client_key, client_pubkey); +static int +input_kex_c25519_reply(int type, u_int32_t seq, void *ctxt); - packet_start(SSH2_MSG_KEX_ECDH_INIT); - packet_put_string(client_pubkey, sizeof(client_pubkey)); - packet_send(); - debug("sending SSH2_MSG_KEX_ECDH_INIT"); +int +kexc25519_client(struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + int r; + kexc25519_keygen(kex->c25519_client_key, kex->c25519_client_pubkey); #ifdef DEBUG_KEXECDH - dump_digest("client private key:", client_key, sizeof(client_key)); + dump_digest("client private key:", kex->c25519_client_key, + sizeof(kex->c25519_client_key)); #endif + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_INIT)) != 0 || + (r = sshpkt_put_string(ssh, kex->c25519_client_pubkey, + sizeof(kex->c25519_client_pubkey))) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); - packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_c25519_reply); + return 0; +} + +static int +input_kex_c25519_reply(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + struct sshkey *server_host_key = NULL; + struct sshbuf *shared_secret = NULL; + u_char *server_pubkey = NULL; + u_char *server_host_key_blob = NULL, *signature = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, pklen, sbloblen, hashlen; + int r; + + if (kex->verify_host_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } /* hostkey */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - if (server_host_key->type != kex->hostkey_type) - fatal("type mismatch for decoded server_host_key_blob"); - if (kex->verify_host_key == NULL) - fatal("cannot verify server_host_key"); - if (kex->verify_host_key(server_host_key) == -1) - fatal("server_host_key verification failed"); + if ((r = sshpkt_get_string(ssh, &server_host_key_blob, + &sbloblen)) != 0 || + (r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + goto out; + if (server_host_key->type != kex->hostkey_type) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (kex->verify_host_key(server_host_key, ssh) == -1) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } /* Q_S, server public key */ - server_pubkey = packet_get_string(&slen); - if (slen != CURVE25519_SIZE) - fatal("Incorrect size for server Curve25519 pubkey: %d", slen); + /* signed H */ + if ((r = sshpkt_get_string(ssh, &server_pubkey, &pklen)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + if (pklen != CURVE25519_SIZE) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } #ifdef DEBUG_KEXECDH dump_digest("server public key:", server_pubkey, CURVE25519_SIZE); #endif - /* signed H */ - signature = packet_get_string(&slen); - packet_check_eom(); - - buffer_init(&shared_secret); - kexc25519_shared_key(client_key, server_pubkey, &shared_secret); + if ((shared_secret = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kexc25519_shared_key(kex->c25519_client_key, server_pubkey, + shared_secret)) < 0) + goto out; /* calc and verify H */ - kex_c25519_hash( + hashlen = sizeof(hash); + if ((r = kex_c25519_hash( kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->my), buffer_len(kex->my), - buffer_ptr(kex->peer), buffer_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, - client_pubkey, + kex->c25519_client_pubkey, server_pubkey, - buffer_ptr(&shared_secret), buffer_len(&shared_secret), - &hash, &hashlen - ); - free(server_host_key_blob); - free(server_pubkey); - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - free(signature); + sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), + hash, &hashlen)) < 0) + goto out; + + if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, + ssh->compat)) != 0) + goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } - kex_derive_keys(kex, hash, hashlen, - buffer_ptr(&shared_secret), buffer_len(&shared_secret)); - buffer_free(&shared_secret); - kex_finish(kex); + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); +out: + explicit_bzero(hash, sizeof(hash)); + explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); + free(server_host_key_blob); + free(server_pubkey); + free(signature); + sshkey_free(server_host_key); + sshbuf_free(shared_secret); + return r; } diff --git a/kexc25519s.c b/kexc25519s.c index ba6f546f4..912b0afb1 100644 --- a/kexc25519s.c +++ b/kexc25519s.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519s.c,v 1.5 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexc25519s.c,v 1.6 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -30,97 +30,128 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "ssh2.h" +#include "sshbuf.h" +#include "ssherr.h" -void -kexc25519_server(Kex *kex) +static int input_kex_c25519_init(int, u_int32_t, void *); + +int +kexc25519_server(struct ssh *ssh) +{ + debug("expecting SSH2_MSG_KEX_ECDH_INIT"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &input_kex_c25519_init); + return 0; +} + +static int +input_kex_c25519_init(int type, u_int32_t seq, void *ctxt) { - Key *server_host_private, *server_host_public; + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + struct sshkey *server_host_private, *server_host_public; + struct sshbuf *shared_secret = NULL; u_char *server_host_key_blob = NULL, *signature = NULL; u_char server_key[CURVE25519_SIZE]; u_char *client_pubkey = NULL; u_char server_pubkey[CURVE25519_SIZE]; - u_char *hash; - u_int slen, sbloblen, hashlen; - Buffer shared_secret; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, pklen, sbloblen, hashlen; + int r; /* generate private key */ kexc25519_keygen(server_key, server_pubkey); #ifdef DEBUG_KEXECDH dump_digest("server private key:", server_key, sizeof(server_key)); #endif - if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) - fatal("Cannot load hostkey"); - server_host_public = kex->load_host_public_key(kex->hostkey_type); - if (server_host_public == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - server_host_private = kex->load_host_private_key(kex->hostkey_type); - - debug("expecting SSH2_MSG_KEX_ECDH_INIT"); - packet_read_expect(SSH2_MSG_KEX_ECDH_INIT); - client_pubkey = packet_get_string(&slen); - if (slen != CURVE25519_SIZE) - fatal("Incorrect size for server Curve25519 pubkey: %d", slen); - packet_check_eom(); + kex->load_host_private_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((server_host_public = kex->load_host_public_key(kex->hostkey_type, + ssh)) == NULL || + (server_host_private = kex->load_host_private_key(kex->hostkey_type, + ssh)) == NULL) { + r = SSH_ERR_NO_HOSTKEY_LOADED; + goto out; + } + if ((r = sshpkt_get_string(ssh, &client_pubkey, &pklen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + if (pklen != CURVE25519_SIZE) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } #ifdef DEBUG_KEXECDH dump_digest("client public key:", client_pubkey, CURVE25519_SIZE); #endif - buffer_init(&shared_secret); - kexc25519_shared_key(server_key, client_pubkey, &shared_secret); + if ((shared_secret = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kexc25519_shared_key(server_key, client_pubkey, + shared_secret)) < 0) + goto out; /* calc H */ - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); - kex_c25519_hash( + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + goto out; + hashlen = sizeof(hash); + if ((r = kex_c25519_hash( kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->peer), buffer_len(kex->peer), - buffer_ptr(kex->my), buffer_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, client_pubkey, server_pubkey, - buffer_ptr(&shared_secret), buffer_len(&shared_secret), - &hash, &hashlen - ); + sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), + hash, &hashlen)) < 0) + goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ - kex->sign(server_host_private, server_host_public, &signature, &slen, - hash, hashlen); - - /* destroy_sensitive_data(); */ + if ((r = kex->sign(server_host_private, server_host_public, + &signature, &slen, hash, hashlen, ssh->compat)) < 0) + goto out; /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ - packet_start(SSH2_MSG_KEX_ECDH_REPLY); - packet_put_string(server_host_key_blob, sbloblen); - packet_put_string(server_pubkey, sizeof(server_pubkey)); - packet_put_string(signature, slen); - packet_send(); - - free(signature); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_string(ssh, server_pubkey, sizeof(server_pubkey))) != 0 || + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); +out: + explicit_bzero(hash, sizeof(hash)); + explicit_bzero(server_key, sizeof(server_key)); free(server_host_key_blob); - /* have keys, free server key */ + free(signature); free(client_pubkey); - - kex_derive_keys(kex, hash, hashlen, - buffer_ptr(&shared_secret), buffer_len(&shared_secret)); - buffer_free(&shared_secret); - kex_finish(kex); + sshbuf_free(shared_secret); + return r; } diff --git a/kexdh.c b/kexdh.c index 2c1dfb6f5..feea6697d 100644 --- a/kexdh.c +++ b/kexdh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdh.c,v 1.24 2014/01/09 23:20:00 djm Exp $ */ +/* $OpenBSD: kexdh.c,v 1.25 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -33,58 +33,61 @@ #include -#include "buffer.h" #include "ssh2.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #include "kex.h" +#include "ssherr.h" +#include "sshbuf.h" #include "digest.h" -#include "log.h" -void +int kex_dh_hash( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - u_char *serverhostkeyblob, int sbloblen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret, - u_char **hash, u_int *hashlen) + const char *client_version_string, + const char *server_version_string, + const u_char *ckexinit, size_t ckexinitlen, + const u_char *skexinit, size_t skexinitlen, + const u_char *serverhostkeyblob, size_t sbloblen, + const BIGNUM *client_dh_pub, + const BIGNUM *server_dh_pub, + const BIGNUM *shared_secret, + u_char *hash, size_t *hashlen) { - Buffer b; - static u_char digest[SSH_DIGEST_MAX_LENGTH]; - - buffer_init(&b); - buffer_put_cstring(&b, client_version_string); - buffer_put_cstring(&b, server_version_string); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - buffer_put_bignum2(&b, client_dh_pub); - buffer_put_bignum2(&b, server_dh_pub); - buffer_put_bignum2(&b, shared_secret); + struct sshbuf *b; + int r; + if (*hashlen < ssh_digest_bytes(SSH_DIGEST_SHA1)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || + (r = sshbuf_put_cstring(b, server_version_string)) != 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, ckexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 || + (r = sshbuf_put_u32(b, skexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, skexinit, skexinitlen)) != 0 || + (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 || + (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(b, shared_secret)) != 0) { + sshbuf_free(b); + return r; + } #ifdef DEBUG_KEX - buffer_dump(&b); + sshbuf_dump(b, stderr); #endif - if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, digest, sizeof(digest)) != 0) - fatal("%s: ssh_digest_buffer failed", __func__); - - buffer_free(&b); - + if (ssh_digest_buffer(SSH_DIGEST_SHA1, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); + *hashlen = ssh_digest_bytes(SSH_DIGEST_SHA1); #ifdef DEBUG_KEX - dump_digest("hash", digest, ssh_digest_bytes(SSH_DIGEST_SHA1)); + dump_digest("hash", hash, *hashlen); #endif - *hash = digest; - *hashlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + return 0; } #endif /* WITH_OPENSSL */ diff --git a/kexdhc.c b/kexdhc.c index cd12df33d..52b752295 100644 --- a/kexdhc.c +++ b/kexdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhc.c,v 1.16 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexdhc.c,v 1.17 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -36,129 +36,175 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" +#include "dispatch.h" +#include "compat.h" +#include "ssherr.h" +#include "sshbuf.h" -void -kexdh_client(Kex *kex) +static int input_kex_dh(int, u_int32_t, void *); + +int +kexdh_client(struct ssh *ssh) { - BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; - DH *dh; - Key *server_host_key; - u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *kbuf, *hash; - u_int klen, slen, sbloblen, hashlen; - int kout; + struct kex *kex = ssh->kex; + int r; /* generate and send 'e', client DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: - dh = dh_new_group1(); + kex->dh = dh_new_group1(); break; case KEX_DH_GRP14_SHA1: - dh = dh_new_group14(); + kex->dh = dh_new_group14(); break; default: - fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (kex->dh == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; } - dh_gen_key(dh, kex->we_need * 8); - packet_start(SSH2_MSG_KEXDH_INIT); - packet_put_bignum2(dh->pub_key); - packet_send(); - debug("sending SSH2_MSG_KEXDH_INIT"); + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || + (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, dh); + DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); - BN_print_fp(stderr, dh->pub_key); + BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif - debug("expecting SSH2_MSG_KEXDH_REPLY"); - packet_read_expect(SSH2_MSG_KEXDH_REPLY); + ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh); + r = 0; + out: + return r; +} +static int +input_kex_dh(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + struct sshkey *server_host_key = NULL; + u_char *kbuf = NULL, *server_host_key_blob = NULL, *signature = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t klen = 0, slen, sbloblen, hashlen; + int kout, r; + + if (kex->verify_host_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } /* key, cert */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - if (server_host_key->type != kex->hostkey_type) - fatal("type mismatch for decoded server_host_key_blob"); - if (kex->verify_host_key == NULL) - fatal("cannot verify server_host_key"); - if (kex->verify_host_key(server_host_key) == -1) - fatal("server_host_key verification failed"); - + if ((r = sshpkt_get_string(ssh, &server_host_key_blob, + &sbloblen)) != 0 || + (r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + goto out; + if (server_host_key->type != kex->hostkey_type) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (kex->verify_host_key(server_host_key, ssh) == -1) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } /* DH parameter f, server public DH key */ - if ((dh_server_pub = BN_new()) == NULL) - fatal("dh_server_pub == NULL"); - packet_get_bignum2(dh_server_pub); - + if ((dh_server_pub = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* signed H */ + if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_server_pub= "); BN_print_fp(stderr, dh_server_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #endif + if (!dh_pub_is_valid(kex->dh, dh_server_pub)) { + sshpkt_disconnect(ssh, "bad server public DH value"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } - /* signed H */ - signature = packet_get_string(&slen); - packet_check_eom(); - - if (!dh_pub_is_valid(dh, dh_server_pub)) - packet_disconnect("bad server public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - if ((kout = DH_compute_key(kbuf, dh_server_pub, dh)) < 0) - fatal("DH_compute_key: failed"); + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("kexdh_client: BN_new failed"); - if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) - fatal("kexdh_client: BN_bin2bn failed"); - explicit_bzero(kbuf, klen); - free(kbuf); /* calc and verify H */ - kex_dh_hash( + hashlen = sizeof(hash); + if ((r = kex_dh_hash( kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->my), buffer_len(kex->my), - buffer_ptr(kex->peer), buffer_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, - dh->pub_key, + kex->dh->pub_key, dh_server_pub, shared_secret, - &hash, &hashlen - ); - free(server_host_key_blob); - BN_clear_free(dh_server_pub); - DH_free(dh); + hash, &hashlen)) != 0) + goto out; - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - free(signature); + if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, + ssh->compat)) != 0) + goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - kex_finish(kex); + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + explicit_bzero(hash, sizeof(hash)); + DH_free(kex->dh); + kex->dh = NULL; + if (dh_server_pub) + BN_clear_free(dh_server_pub); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + sshkey_free(server_host_key); + free(server_host_key_blob); + free(signature); + return r; } #endif /* WITH_OPENSSL */ diff --git a/kexdhs.c b/kexdhs.c index 34a215f8c..182657ac9 100644 --- a/kexdhs.c +++ b/kexdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhs.c,v 1.19 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexdhs.c,v 1.20 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -35,55 +35,88 @@ #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" -void -kexdh_server(Kex *kex) +#include "dispatch.h" +#include "compat.h" +#include "ssherr.h" +#include "sshbuf.h" + +static int input_kex_dh_init(int, u_int32_t, void *); + +int +kexdh_server(struct ssh *ssh) { - BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; - DH *dh; - Key *server_host_public, *server_host_private; - u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; - u_int sbloblen, klen, hashlen, slen; - int kout; + struct kex *kex = ssh->kex; + int r; /* generate server DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: - dh = dh_new_group1(); + kex->dh = dh_new_group1(); break; case KEX_DH_GRP14_SHA1: - dh = dh_new_group14(); + kex->dh = dh_new_group14(); break; default: - fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; } - dh_gen_key(dh, kex->we_need * 8); + if (kex->dh == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) + goto out; debug("expecting SSH2_MSG_KEXDH_INIT"); - packet_read_expect(SSH2_MSG_KEXDH_INIT); + ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init); + r = 0; + out: + return r; +} + +int +input_kex_dh_init(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + struct sshkey *server_host_public, *server_host_private; + u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t sbloblen, slen; + size_t klen = 0, hashlen; + int kout, r; if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) - fatal("Cannot load hostkey"); - server_host_public = kex->load_host_public_key(kex->hostkey_type); - if (server_host_public == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - server_host_private = kex->load_host_private_key(kex->hostkey_type); + kex->load_host_private_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((server_host_public = kex->load_host_public_key(kex->hostkey_type, + ssh)) == NULL || + (server_host_private = kex->load_host_private_key(kex->hostkey_type, + ssh)) == NULL) { + r = SSH_ERR_NO_HOSTKEY_LOADED; + goto out; + } /* key, cert */ - if ((dh_client_pub = BN_new()) == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub); - packet_check_eom(); + if ((dh_client_pub = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); @@ -93,71 +126,90 @@ kexdh_server(Kex *kex) #endif #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, dh); + DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); - BN_print_fp(stderr, dh->pub_key); + BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); + if (!dh_pub_is_valid(kex->dh, dh_client_pub)) { + sshpkt_disconnect(ssh, "bad client public DH value"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } - klen = DH_size(dh); - kbuf = xmalloc(klen); - if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) - fatal("DH_compute_key: failed"); + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("kexdh_server: BN_new failed"); - if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) - fatal("kexdh_server: BN_bin2bn failed"); - explicit_bzero(kbuf, klen); - free(kbuf); - - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); - + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + goto out; /* calc H */ - kex_dh_hash( + hashlen = sizeof(hash); + if ((r = kex_dh_hash( kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->peer), buffer_len(kex->peer), - buffer_ptr(kex->my), buffer_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, dh_client_pub, - dh->pub_key, + kex->dh->pub_key, shared_secret, - &hash, &hashlen - ); - BN_clear_free(dh_client_pub); + hash, &hashlen)) != 0) + goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ - kex->sign(server_host_private, server_host_public, &signature, &slen, - hash, hashlen); + if ((r = kex->sign(server_host_private, server_host_public, + &signature, &slen, hash, hashlen, ssh->compat)) < 0) + goto out; /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ - packet_start(SSH2_MSG_KEXDH_REPLY); - packet_put_string(server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string(signature, slen); - packet_send(); - - free(signature); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + explicit_bzero(hash, sizeof(hash)); + DH_free(kex->dh); + kex->dh = NULL; + if (dh_client_pub) + BN_clear_free(dh_client_pub); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); free(server_host_key_blob); - /* have keys, free DH */ - DH_free(dh); - - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - kex_finish(kex); + free(signature); + return r; } #endif /* WITH_OPENSSL */ diff --git a/kexecdh.c b/kexecdh.c index 3115d13d1..2a4fec6b1 100644 --- a/kexecdh.c +++ b/kexecdh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdh.c,v 1.5 2014/01/09 23:20:00 djm Exp $ */ +/* $OpenBSD: kexecdh.c,v 1.6 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -38,60 +38,63 @@ #include #include -#include "buffer.h" #include "ssh2.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #include "kex.h" -#include "log.h" +#include "sshbuf.h" #include "digest.h" +#include "ssherr.h" -void +int kex_ecdh_hash( int hash_alg, const EC_GROUP *ec_group, - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - u_char *serverhostkeyblob, int sbloblen, + const char *client_version_string, + const char *server_version_string, + const u_char *ckexinit, size_t ckexinitlen, + const u_char *skexinit, size_t skexinitlen, + const u_char *serverhostkeyblob, size_t sbloblen, const EC_POINT *client_dh_pub, const EC_POINT *server_dh_pub, const BIGNUM *shared_secret, - u_char **hash, u_int *hashlen) + u_char *hash, size_t *hashlen) { - Buffer b; - static u_char digest[SSH_DIGEST_MAX_LENGTH]; - - buffer_init(&b); - buffer_put_cstring(&b, client_version_string); - buffer_put_cstring(&b, server_version_string); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - buffer_put_ecpoint(&b, ec_group, client_dh_pub); - buffer_put_ecpoint(&b, ec_group, server_dh_pub); - buffer_put_bignum2(&b, shared_secret); + struct sshbuf *b; + int r; + if (*hashlen < ssh_digest_bytes(hash_alg)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || + (r = sshbuf_put_cstring(b, server_version_string)) != 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, ckexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 || + (r = sshbuf_put_u32(b, skexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, skexinit, skexinitlen)) != 0 || + (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 || + (r = sshbuf_put_ec(b, client_dh_pub, ec_group)) != 0 || + (r = sshbuf_put_ec(b, server_dh_pub, ec_group)) != 0 || + (r = sshbuf_put_bignum2(b, shared_secret)) != 0) { + sshbuf_free(b); + return r; + } #ifdef DEBUG_KEX - buffer_dump(&b); + sshbuf_dump(b, stderr); #endif - if (ssh_digest_buffer(hash_alg, &b, digest, sizeof(digest)) != 0) - fatal("%s: ssh_digest_buffer failed", __func__); - - buffer_free(&b); - + if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); + *hashlen = ssh_digest_bytes(hash_alg); #ifdef DEBUG_KEX - dump_digest("hash", digest, ssh_digest_bytes(hash_alg)); + dump_digest("hash", hash, *hashlen); #endif - *hash = digest; - *hashlen = ssh_digest_bytes(hash_alg); + return 0; } #endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ diff --git a/kexecdhc.c b/kexecdhc.c index df811c1c8..3f362c5b1 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhc.c,v 1.8 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexecdhc.c,v 1.9 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -34,126 +34,193 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include + +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" +#include "dispatch.h" +#include "compat.h" +#include "ssherr.h" +#include "sshbuf.h" -#include +static int input_kex_ecdh_reply(int, u_int32_t, void *); -void -kexecdh_client(Kex *kex) +int +kexecdh_client(struct ssh *ssh) { - EC_KEY *client_key; - EC_POINT *server_public; + struct kex *kex = ssh->kex; + EC_KEY *client_key = NULL; const EC_GROUP *group; - BIGNUM *shared_secret; - Key *server_host_key; - u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *kbuf, *hash; - u_int klen, slen, sbloblen, hashlen; + const EC_POINT *public_key; + int r; - if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_generate_key(client_key) != 1) - fatal("%s: EC_KEY_generate_key failed", __func__); + if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(client_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } group = EC_KEY_get0_group(client_key); + public_key = EC_KEY_get0_public_key(client_key); - packet_start(SSH2_MSG_KEX_ECDH_INIT); - packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key)); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_INIT)) != 0 || + (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; debug("sending SSH2_MSG_KEX_ECDH_INIT"); #ifdef DEBUG_KEXECDH fputs("client private key:\n", stderr); - key_dump_ec_key(client_key); + sshkey_dump_ec_key(client_key); #endif + kex->ec_client_key = client_key; + kex->ec_group = group; + client_key = NULL; /* owned by the kex */ debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); - packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_ecdh_reply); + r = 0; + out: + if (client_key) + EC_KEY_free(client_key); + return r; +} + +static int +input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + const EC_GROUP *group; + EC_POINT *server_public = NULL; + EC_KEY *client_key; + BIGNUM *shared_secret = NULL; + struct sshkey *server_host_key = NULL; + u_char *server_host_key_blob = NULL, *signature = NULL; + u_char *kbuf = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, sbloblen; + size_t klen = 0, hashlen; + int r; + + if (kex->verify_host_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + group = kex->ec_group; + client_key = kex->ec_client_key; /* hostkey */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - if (server_host_key->type != kex->hostkey_type) - fatal("type mismatch for decoded server_host_key_blob"); - if (kex->verify_host_key == NULL) - fatal("cannot verify server_host_key"); - if (kex->verify_host_key(server_host_key) == -1) - fatal("server_host_key verification failed"); + if ((r = sshpkt_get_string(ssh, &server_host_key_blob, + &sbloblen)) != 0 || + (r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + goto out; + if (server_host_key->type != kex->hostkey_type) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (kex->verify_host_key(server_host_key, ssh) == -1) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } /* Q_S, server public key */ - if ((server_public = EC_POINT_new(group)) == NULL) - fatal("%s: EC_POINT_new failed", __func__); - packet_get_ecpoint(group, server_public); - - if (key_ec_validate_public(group, server_public) != 0) - fatal("%s: invalid server public key", __func__); + /* signed H */ + if ((server_public = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_ec(ssh, server_public, group)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXECDH fputs("server public key:\n", stderr); - key_dump_ec_point(group, server_public); + sshkey_dump_ec_point(group, server_public); #endif - - /* signed H */ - signature = packet_get_string(&slen); - packet_check_eom(); + if (sshkey_ec_validate_public(group, server_public) != 0) { + sshpkt_disconnect(ssh, "invalid server public key"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } klen = (EC_GROUP_get_degree(group) + 7) / 8; - kbuf = xmalloc(klen); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } if (ECDH_compute_key(kbuf, klen, server_public, - client_key, NULL) != (int)klen) - fatal("%s: ECDH_compute_key failed", __func__); + client_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) - fatal("%s: BN_bin2bn failed", __func__); - explicit_bzero(kbuf, klen); - free(kbuf); - /* calc and verify H */ - kex_ecdh_hash( + hashlen = sizeof(hash); + if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->my), buffer_len(kex->my), - buffer_ptr(kex->peer), buffer_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, EC_KEY_get0_public_key(client_key), server_public, shared_secret, - &hash, &hashlen - ); - free(server_host_key_blob); - EC_POINT_clear_free(server_public); - EC_KEY_free(client_key); + hash, &hashlen)) != 0) + goto out; - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - free(signature); + if ((r = sshkey_verify(server_host_key, signature, slen, hash, + hashlen, ssh->compat)) != 0) + goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - kex_finish(kex); + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + explicit_bzero(hash, sizeof(hash)); + if (kex->ec_client_key) { + EC_KEY_free(kex->ec_client_key); + kex->ec_client_key = NULL; + } + if (server_public) + EC_POINT_clear_free(server_public); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + sshkey_free(server_host_key); + free(server_host_key_blob); + free(signature); + return r; } #endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ + diff --git a/kexecdhs.c b/kexecdhs.c index 6bfad04cf..6b8d95d9c 100644 --- a/kexecdhs.c +++ b/kexecdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhs.c,v 1.11 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexecdhs.c,v 1.12 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -32,124 +32,176 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include + +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "ssh2.h" -#include +#include "dispatch.h" +#include "compat.h" +#include "ssherr.h" +#include "sshbuf.h" + +static int input_kex_ecdh_init(int, u_int32_t, void *); + +int +kexecdh_server(struct ssh *ssh) +{ + debug("expecting SSH2_MSG_KEX_ECDH_INIT"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &input_kex_ecdh_init); + return 0; +} -void -kexecdh_server(Kex *kex) +static int +input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) { + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; EC_POINT *client_public; - EC_KEY *server_key; + EC_KEY *server_key = NULL; const EC_GROUP *group; - BIGNUM *shared_secret; - Key *server_host_private, *server_host_public; + const EC_POINT *public_key; + BIGNUM *shared_secret = NULL; + struct sshkey *server_host_private, *server_host_public; u_char *server_host_key_blob = NULL, *signature = NULL; - u_char *kbuf, *hash; - u_int klen, slen, sbloblen, hashlen; - - if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_generate_key(server_key) != 1) - fatal("%s: EC_KEY_generate_key failed", __func__); + u_char *kbuf = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, sbloblen; + size_t klen = 0, hashlen; + int r; + + if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(server_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } group = EC_KEY_get0_group(server_key); #ifdef DEBUG_KEXECDH fputs("server private key:\n", stderr); - key_dump_ec_key(server_key); + sshkey_dump_ec_key(server_key); #endif if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) - fatal("Cannot load hostkey"); - server_host_public = kex->load_host_public_key(kex->hostkey_type); - if (server_host_public == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - server_host_private = kex->load_host_private_key(kex->hostkey_type); - - debug("expecting SSH2_MSG_KEX_ECDH_INIT"); - packet_read_expect(SSH2_MSG_KEX_ECDH_INIT); - if ((client_public = EC_POINT_new(group)) == NULL) - fatal("%s: EC_POINT_new failed", __func__); - packet_get_ecpoint(group, client_public); - packet_check_eom(); - - if (key_ec_validate_public(group, client_public) != 0) - fatal("%s: invalid client public key", __func__); + kex->load_host_private_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((server_host_public = kex->load_host_public_key(kex->hostkey_type, + ssh)) == NULL || + (server_host_private = kex->load_host_private_key(kex->hostkey_type, + ssh)) == NULL) { + r = SSH_ERR_NO_HOSTKEY_LOADED; + goto out; + } + if ((client_public = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_ec(ssh, client_public, group)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXECDH fputs("client public key:\n", stderr); - key_dump_ec_point(group, client_public); + sshkey_dump_ec_point(group, client_public); #endif + if (sshkey_ec_validate_public(group, client_public) != 0) { + sshpkt_disconnect(ssh, "invalid client public key"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } /* Calculate shared_secret */ klen = (EC_GROUP_get_degree(group) + 7) / 8; - kbuf = xmalloc(klen); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } if (ECDH_compute_key(kbuf, klen, client_public, - server_key, NULL) != (int)klen) - fatal("%s: ECDH_compute_key failed", __func__); + server_key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } -#ifdef DEBUG_KEXDH +#ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) - fatal("%s: BN_bin2bn failed", __func__); - explicit_bzero(kbuf, klen); - free(kbuf); - /* calc H */ - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); - kex_ecdh_hash( + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + goto out; + hashlen = sizeof(hash); + if ((r = kex_ecdh_hash( kex->hash_alg, group, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->peer), buffer_len(kex->peer), - buffer_ptr(kex->my), buffer_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, client_public, EC_KEY_get0_public_key(server_key), shared_secret, - &hash, &hashlen - ); - EC_POINT_clear_free(client_public); + hash, &hashlen)) != 0) + goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ - kex->sign(server_host_private, server_host_public, &signature, &slen, - hash, hashlen); + if ((r = kex->sign(server_host_private, server_host_public, + &signature, &slen, hash, hashlen, ssh->compat)) < 0) + goto out; /* destroy_sensitive_data(); */ + public_key = EC_KEY_get0_public_key(server_key); /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ - packet_start(SSH2_MSG_KEX_ECDH_REPLY); - packet_put_string(server_host_key_blob, sbloblen); - packet_put_ecpoint(group, EC_KEY_get0_public_key(server_key)); - packet_put_string(signature, slen); - packet_send(); - - free(signature); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_ec(ssh, public_key, group)) != 0 || + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + explicit_bzero(hash, sizeof(hash)); + if (kex->ec_client_key) { + EC_KEY_free(kex->ec_client_key); + kex->ec_client_key = NULL; + } + if (server_key) + EC_KEY_free(server_key); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); free(server_host_key_blob); - /* have keys, free server key */ - EC_KEY_free(server_key); - - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - kex_finish(kex); + free(signature); + return r; } #endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ + diff --git a/kexgex.c b/kexgex.c index d1fa1a063..8b0d83332 100644 --- a/kexgex.c +++ b/kexgex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgex.c,v 1.28 2014/01/09 23:20:00 djm Exp $ */ +/* $OpenBSD: kexgex.c,v 1.29 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -33,69 +33,70 @@ #include #include -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #include "kex.h" #include "ssh2.h" +#include "ssherr.h" +#include "sshbuf.h" #include "digest.h" -#include "log.h" -void +int kexgex_hash( int hash_alg, - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - u_char *serverhostkeyblob, int sbloblen, - int min, int wantbits, int max, BIGNUM *prime, BIGNUM *gen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret, - u_char **hash, u_int *hashlen) + const char *client_version_string, + const char *server_version_string, + const u_char *ckexinit, size_t ckexinitlen, + const u_char *skexinit, size_t skexinitlen, + const u_char *serverhostkeyblob, size_t sbloblen, + int min, int wantbits, int max, + const BIGNUM *prime, + const BIGNUM *gen, + const BIGNUM *client_dh_pub, + const BIGNUM *server_dh_pub, + const BIGNUM *shared_secret, + u_char *hash, size_t *hashlen) { - Buffer b; - static u_char digest[SSH_DIGEST_MAX_LENGTH]; + struct sshbuf *b; + int r; - buffer_init(&b); - buffer_put_cstring(&b, client_version_string); - buffer_put_cstring(&b, server_version_string); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - if (min == -1 || max == -1) - buffer_put_int(&b, wantbits); - else { - buffer_put_int(&b, min); - buffer_put_int(&b, wantbits); - buffer_put_int(&b, max); + if (*hashlen < ssh_digest_bytes(SSH_DIGEST_SHA1)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || + (r = sshbuf_put_cstring(b, server_version_string)) != 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, ckexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 || + (r = sshbuf_put_u32(b, skexinitlen+1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_put(b, skexinit, skexinitlen)) != 0 || + (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 || + (min != -1 && (r = sshbuf_put_u32(b, min)) != 0) || + (r = sshbuf_put_u32(b, wantbits)) != 0 || + (max != -1 && (r = sshbuf_put_u32(b, max)) != 0) || + (r = sshbuf_put_bignum2(b, prime)) != 0 || + (r = sshbuf_put_bignum2(b, gen)) != 0 || + (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(b, shared_secret)) != 0) { + sshbuf_free(b); + return r; } - buffer_put_bignum2(&b, prime); - buffer_put_bignum2(&b, gen); - buffer_put_bignum2(&b, client_dh_pub); - buffer_put_bignum2(&b, server_dh_pub); - buffer_put_bignum2(&b, shared_secret); - #ifdef DEBUG_KEXDH - buffer_dump(&b); + sshbuf_dump(b, stderr); #endif - if (ssh_digest_buffer(hash_alg, &b, digest, sizeof(digest)) != 0) - fatal("%s: ssh_digest_buffer failed", __func__); - - buffer_free(&b); - -#ifdef DEBUG_KEX - dump_digest("hash", digest, ssh_digest_bytes(hash_alg)); -#endif - *hash = digest; + if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); *hashlen = ssh_digest_bytes(hash_alg); +#ifdef DEBUG_KEXDH + dump_digest("hash", hash, *hashlen); +#endif + return 0; } #endif /* WITH_OPENSSL */ diff --git a/kexgexc.c b/kexgexc.c index 18d09cfb0..0898824f3 100644 --- a/kexgexc.c +++ b/kexgexc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexc.c,v 1.18 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexgexc.c,v 1.19 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -37,174 +37,237 @@ #include #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" #include "compat.h" +#include "dispatch.h" +#include "ssherr.h" +#include "sshbuf.h" -void -kexgex_client(Kex *kex) +static int input_kex_dh_gex_group(int, u_int32_t, void *); +static int input_kex_dh_gex_reply(int, u_int32_t, void *); + +int +kexgex_client(struct ssh *ssh) { - BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; - BIGNUM *p = NULL, *g = NULL; - Key *server_host_key; - u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; - u_int klen, slen, sbloblen, hashlen; - int kout; - int min, max, nbits; - DH *dh; + struct kex *kex = ssh->kex; + int r; + u_int nbits; nbits = dh_estimate(kex->dh_need * 8); - if (datafellows & SSH_OLD_DHGEX) { + kex->min = DH_GRP_MIN; + kex->max = DH_GRP_MAX; + kex->nbits = nbits; + if (ssh->compat & SSH_OLD_DHGEX) { /* Old GEX request */ - packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); - packet_put_int(nbits); - min = DH_GRP_MIN; - max = DH_GRP_MAX; - - debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD(%u) sent", nbits); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD)) + != 0 || + (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD(%u) sent", kex->nbits); } else { /* New GEX request */ - min = DH_GRP_MIN; - max = DH_GRP_MAX; - packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); - packet_put_int(min); - packet_put_int(nbits); - packet_put_int(max); - + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, kex->min)) != 0 || + (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || + (r = sshpkt_put_u32(ssh, kex->max)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent", - min, nbits, max); + kex->min, kex->nbits, kex->max); } #ifdef DEBUG_KEXDH fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", - min, nbits, max); + kex->min, kex->nbits, kex->max); #endif - packet_send(); - - debug("expecting SSH2_MSG_KEX_DH_GEX_GROUP"); - packet_read_expect(SSH2_MSG_KEX_DH_GEX_GROUP); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, + &input_kex_dh_gex_group); + r = 0; + out: + return r; +} - if ((p = BN_new()) == NULL) - fatal("BN_new"); - packet_get_bignum2(p); - if ((g = BN_new()) == NULL) - fatal("BN_new"); - packet_get_bignum2(g); - packet_check_eom(); +static int +input_kex_dh_gex_group(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + BIGNUM *p = NULL, *g = NULL; + int r, bits; - if (BN_num_bits(p) < min || BN_num_bits(p) > max) - fatal("DH_GEX group out of range: %d !< %d !< %d", - min, BN_num_bits(p), max); + debug("got SSH2_MSG_KEX_DH_GEX_GROUP"); - dh = dh_new_group(g, p); - dh_gen_key(dh, kex->we_need * 8); + if ((p = BN_new()) == NULL || + (g = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_bignum2(ssh, p)) != 0 || + (r = sshpkt_get_bignum2(ssh, g)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + if ((bits = BN_num_bits(p)) < 0 || + (u_int)bits < kex->min || (u_int)bits > kex->max) { + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } + if ((kex->dh = dh_new_group(g, p)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + p = g = NULL; /* belong to kex->dh now */ + /* generate and send 'e', client DH public key */ + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || + (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, dh); + DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); - BN_print_fp(stderr, dh->pub_key); + BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); + r = 0; +out: + if (p) + BN_clear_free(p); + if (g) + BN_clear_free(g); + return r; +} - debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); - /* generate and send 'e', client DH public key */ - packet_start(SSH2_MSG_KEX_DH_GEX_INIT); - packet_put_bignum2(dh->pub_key); - packet_send(); - - debug("expecting SSH2_MSG_KEX_DH_GEX_REPLY"); - packet_read_expect(SSH2_MSG_KEX_DH_GEX_REPLY); +static int +input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + struct sshkey *server_host_key = NULL; + u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t klen = 0, slen, sbloblen, hashlen; + int kout, r; + debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); + if (kex->verify_host_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } /* key, cert */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - if (server_host_key->type != kex->hostkey_type) - fatal("type mismatch for decoded server_host_key_blob"); - if (kex->verify_host_key == NULL) - fatal("cannot verify server_host_key"); - if (kex->verify_host_key(server_host_key) == -1) - fatal("server_host_key verification failed"); - + if ((r = sshpkt_get_string(ssh, &server_host_key_blob, + &sbloblen)) != 0 || + (r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + goto out; + if (server_host_key->type != kex->hostkey_type) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (kex->verify_host_key(server_host_key, ssh) == -1) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } /* DH parameter f, server public DH key */ - if ((dh_server_pub = BN_new()) == NULL) - fatal("dh_server_pub == NULL"); - packet_get_bignum2(dh_server_pub); - + if ((dh_server_pub = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* signed H */ + if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_server_pub= "); BN_print_fp(stderr, dh_server_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #endif + if (!dh_pub_is_valid(kex->dh, dh_server_pub)) { + sshpkt_disconnect(ssh, "bad server public DH value"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } - /* signed H */ - signature = packet_get_string(&slen); - packet_check_eom(); - - if (!dh_pub_is_valid(dh, dh_server_pub)) - packet_disconnect("bad server public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - if ((kout = DH_compute_key(kbuf, dh_server_pub, dh)) < 0) - fatal("DH_compute_key: failed"); + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("kexgex_client: BN_new failed"); - if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) - fatal("kexgex_client: BN_bin2bn failed"); - explicit_bzero(kbuf, klen); - free(kbuf); - - if (datafellows & SSH_OLD_DHGEX) - min = max = -1; + if (ssh->compat & SSH_OLD_DHGEX) + kex->min = kex->max = -1; /* calc and verify H */ - kexgex_hash( + hashlen = sizeof(hash); + if ((r = kexgex_hash( kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->my), buffer_len(kex->my), - buffer_ptr(kex->peer), buffer_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), server_host_key_blob, sbloblen, - min, nbits, max, - dh->p, dh->g, - dh->pub_key, + kex->min, kex->nbits, kex->max, + kex->dh->p, kex->dh->g, + kex->dh->pub_key, dh_server_pub, shared_secret, - &hash, &hashlen - ); - - /* have keys, free DH */ - DH_free(dh); - free(server_host_key_blob); - BN_clear_free(dh_server_pub); + hash, &hashlen)) != 0) + goto out; - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - free(signature); + if ((r = sshkey_verify(server_host_key, signature, slen, hash, + hashlen, ssh->compat)) != 0) + goto out; /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - kex_finish(kex); + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + explicit_bzero(hash, sizeof(hash)); + DH_free(kex->dh); + kex->dh = NULL; + if (dh_server_pub) + BN_clear_free(dh_server_pub); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); + sshkey_free(server_host_key); + free(server_host_key_blob); + free(signature); + return r; } #endif /* WITH_OPENSSL */ diff --git a/kexgexs.c b/kexgexs.c index 1021e0bf6..6e2b009b5 100644 --- a/kexgexs.c +++ b/kexgexs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexs.c,v 1.20 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: kexgexs.c,v 1.21 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -37,10 +37,9 @@ #include -#include "xmalloc.h" -#include "buffer.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" +#include "digest.h" #include "kex.h" #include "log.h" #include "packet.h" @@ -51,33 +50,43 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" +#include "dispatch.h" +#include "ssherr.h" +#include "sshbuf.h" -void -kexgex_server(Kex *kex) +static int input_kex_dh_gex_request(int, u_int32_t, void *); +static int input_kex_dh_gex_init(int, u_int32_t, void *); + +int +kexgex_server(struct ssh *ssh) { - BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; - Key *server_host_public, *server_host_private; - DH *dh; - u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; - u_int sbloblen, klen, slen, hashlen; - int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; - int type, kout; + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, + &input_kex_dh_gex_request); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, + &input_kex_dh_gex_request); + debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST"); + return 0; +} + +static int +input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + int r; + u_int min = 0, max = 0, nbits = 0; - if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) - fatal("Cannot load hostkey"); - server_host_public = kex->load_host_public_key(kex->hostkey_type); - if (server_host_public == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - server_host_private = kex->load_host_private_key(kex->hostkey_type); - - type = packet_read(); switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); - omin = min = packet_get_int(); - onbits = nbits = packet_get_int(); - omax = max = packet_get_int(); + if ((r = sshpkt_get_u32(ssh, &min)) != 0 || + (r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_u32(ssh, &max)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; + kex->min = min; + kex->max = max; min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); @@ -85,45 +94,88 @@ kexgex_server(Kex *kex) break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); - onbits = nbits = packet_get_int(); + if ((r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; /* unused for old GEX */ - omin = min = DH_GRP_MIN; - omax = max = DH_GRP_MAX; + kex->min = min = DH_GRP_MIN; + kex->max = max = DH_GRP_MAX; break; default: - fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; } - packet_check_eom(); - if (omax < omin || onbits < omin || omax < onbits) - fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", - omin, onbits, omax); + if (kex->max < kex->min || kex->nbits < kex->min || + kex->max < kex->nbits) { + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } /* Contact privileged parent */ - dh = PRIVSEP(choose_dh(min, nbits, max)); - if (dh == NULL) - packet_disconnect("Protocol error: no matching DH grp found"); - + kex->dh = PRIVSEP(choose_dh(min, nbits, max)); + if (kex->dh == NULL) { + sshpkt_disconnect(ssh, "no matching DH grp found"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); - packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); - packet_put_bignum2(dh->p); - packet_put_bignum2(dh->g); - packet_send(); - - /* flush */ - packet_write_wait(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; /* Compute our exchange value in parallel with the client */ - dh_gen_key(dh, kex->we_need * 8); + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) + goto out; + + /* old KEX does not use min/max in kexgex_hash() */ + if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) + kex->min = kex->max = -1; debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); - packet_read_expect(SSH2_MSG_KEX_DH_GEX_INIT); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); + r = 0; + out: + return r; +} + +static int +input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) +{ + struct ssh *ssh = ctxt; + struct kex *kex = ssh->kex; + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + struct sshkey *server_host_public, *server_host_private; + u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t sbloblen, slen; + size_t klen = 0, hashlen; + int kout, r; + + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((server_host_public = kex->load_host_public_key(kex->hostkey_type, + ssh)) == NULL || + (server_host_private = kex->load_host_private_key(kex->hostkey_type, + ssh)) == NULL) { + r = SSH_ERR_NO_HOSTKEY_LOADED; + goto out; + } /* key, cert */ - if ((dh_client_pub = BN_new()) == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub); - packet_check_eom(); + if ((dh_client_pub = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); @@ -133,79 +185,92 @@ kexgex_server(Kex *kex) #endif #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, dh); + DHparams_print_fp(stderr, kex->dh); fprintf(stderr, "pub= "); - BN_print_fp(stderr, dh->pub_key); + BN_print_fp(stderr, kex->dh->pub_key); fprintf(stderr, "\n"); #endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); + if (!dh_pub_is_valid(kex->dh, dh_client_pub)) { + sshpkt_disconnect(ssh, "bad client public DH value"); + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } - klen = DH_size(dh); - kbuf = xmalloc(klen); - if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) - fatal("DH_compute_key: failed"); + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif - if ((shared_secret = BN_new()) == NULL) - fatal("kexgex_server: BN_new failed"); - if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) - fatal("kexgex_server: BN_bin2bn failed"); - explicit_bzero(kbuf, klen); - free(kbuf); - - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); - - if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) - omin = min = omax = max = -1; - + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + goto out; /* calc H */ - kexgex_hash( + hashlen = sizeof(hash); + if ((r = kexgex_hash( kex->hash_alg, kex->client_version_string, kex->server_version_string, - buffer_ptr(kex->peer), buffer_len(kex->peer), - buffer_ptr(kex->my), buffer_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + sshbuf_ptr(kex->my), sshbuf_len(kex->my), server_host_key_blob, sbloblen, - omin, onbits, omax, - dh->p, dh->g, + kex->min, kex->nbits, kex->max, + kex->dh->p, kex->dh->g, dh_client_pub, - dh->pub_key, + kex->dh->pub_key, shared_secret, - &hash, &hashlen - ); - BN_clear_free(dh_client_pub); + hash, &hashlen)) != 0) + goto out; /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; - kex->session_id = xmalloc(kex->session_id_len); + kex->session_id = malloc(kex->session_id_len); + if (kex->session_id == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ - kex->sign(server_host_private, server_host_public, &signature, &slen, - hash, hashlen); + if ((r = kex->sign(server_host_private, server_host_public, + &signature, &slen, hash, hashlen, ssh->compat)) < 0) + goto out; /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ - debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); - packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); - packet_put_string(server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string(signature, slen); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || + (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; - free(signature); + if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + out: + DH_free(kex->dh); + kex->dh = NULL; + if (dh_client_pub) + BN_clear_free(dh_client_pub); + if (kbuf) { + explicit_bzero(kbuf, klen); + free(kbuf); + } + if (shared_secret) + BN_clear_free(shared_secret); free(server_host_key_blob); - /* have keys, free DH */ - DH_free(dh); - - kex_derive_keys_bn(kex, hash, hashlen, shared_secret); - BN_clear_free(shared_secret); - - kex_finish(kex); + free(signature); + return r; } #endif /* WITH_OPENSSL */ diff --git a/monitor.c b/monitor.c index 6ee291128..40fff097d 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.139 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: monitor.c,v 1.140 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -717,7 +717,7 @@ mm_answer_sign(int sock, Buffer *m) datafellows)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); - } else if ((key = get_hostkey_public_by_index(keyid)) != NULL && + } else if ((key = get_hostkey_public_by_index(keyid, active_state)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, datafellows)) != 0) { diff --git a/monitor_wrap.c b/monitor_wrap.c index 84df481d8..c0935dc69 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.82 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.83 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -221,13 +221,13 @@ mm_choose_dh(int min, int nbits, int max) int mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { - Kex *kex = *pmonitor->m_pkex; + struct kex *kex = *pmonitor->m_pkex; Buffer m; debug3("%s entering", __func__); buffer_init(&m); - buffer_put_int(&m, kex->host_key_index(key)); + buffer_put_int(&m, kex->host_key_index(key, active_state)); buffer_put_string(&m, data, datalen); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SIGN, &m); diff --git a/serverloop.c b/serverloop.c index edf6a813f..83a1e010d 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.174 2015/01/19 20:07:45 markus Exp $ */ +/* $OpenBSD: serverloop.c,v 1.175 2015/01/19 20:16:15 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -544,7 +544,7 @@ drain_output(void) static void process_buffered_input_packets(void) { - dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? active_state->kex : NULL); + dispatch_run(DISPATCH_NONBLOCK, NULL, active_state); } /* @@ -874,7 +874,7 @@ server_loop2(Authctxt *authctxt) if (packet_need_rekeying()) { debug("need rekeying"); active_state->kex->done = 0; - kex_send_kexinit(active_state->kex); + kex_send_kexinit(active_state); } } process_input(readset); diff --git a/ssh-keyscan.c b/ssh-keyscan.c index 84301b6ff..223ac58f1 100644 --- a/ssh-keyscan.c +++ b/ssh-keyscan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.93 2014/12/11 08:20:09 djm Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.94 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright 1995, 1996 by David Mazieres . * @@ -100,7 +100,7 @@ typedef struct Connection { char *c_namelist; /* Pointer to other possible addresses */ char *c_output_name; /* Hostname of connection for output */ char *c_data; /* Data read from this fd */ - Kex *c_kex; /* The key-exchange struct for ssh2 */ + struct kex *c_kex; /* The key-exchange struct for ssh2 */ struct timeval c_tv; /* Time at which connection gets aborted */ TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ } con; @@ -221,7 +221,7 @@ keygrab_ssh1(con *c) #endif static int -hostjump(Key *hostkey) +hostjump(Key *hostkey, struct ssh *ssh) { kexjmp_key = hostkey; longjmp(kexjmp, 1); @@ -247,7 +247,7 @@ static Key * keygrab_ssh2(con *c) { char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; - int j; + int r, j; packet_set_connection(c->c_fd, c->c_fd); enable_compat20(); @@ -256,7 +256,9 @@ keygrab_ssh2(con *c) (c->c_keytype == KT_RSA ? "ssh-rsa" : (c->c_keytype == KT_ED25519 ? "ssh-ed25519" : "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521")); - c->c_kex = kex_setup(myproposal); + if ((r = kex_setup(active_state, myproposal)) < 0) + fatal("%s: kex_setup: %s", __func__, ssh_err(r)); + c->c_kex = active_state->kex; #ifdef WITH_OPENSSL c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; @@ -269,7 +271,7 @@ keygrab_ssh2(con *c) if (!(j = setjmp(kexjmp))) { nonfatal_fatal = 1; - dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); + dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, active_state); fprintf(stderr, "Impossible! dispatch_run() returned!\n"); exit(1); } diff --git a/sshconnect2.c b/sshconnect2.c index e0d129996..e5802abcc 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.218 2015/01/19 20:07:45 markus Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.219 2015/01/19 20:16:15 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -92,7 +92,7 @@ char *xxx_host; struct sockaddr *xxx_hostaddr; static int -verify_host_key_callback(Key *hostkey) +verify_host_key_callback(Key *hostkey, struct ssh *ssh) { if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1) fatal("Host key verification failed."); @@ -157,7 +157,7 @@ void ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) { char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; - Kex *kex; + struct kex *kex; xxx_host = host; xxx_hostaddr = hostaddr; @@ -204,8 +204,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) (time_t)options.rekey_interval); /* start key exchange */ - kex = kex_setup(myproposal); - active_state->kex = kex; + kex_setup(active_state, myproposal); + kex = active_state->kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; @@ -218,7 +218,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); if (options.use_roaming && !kex->roaming) { debug("Roaming not allowed by server"); diff --git a/sshd.c b/sshd.c index e45303988..21d8dd699 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.434 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: sshd.c,v 1.435 2015/01/19 20:16:15 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -836,7 +836,7 @@ list_hostkey_types(void) } static Key * -get_hostkey_by_type(int type, int need_private) +get_hostkey_by_type(int type, int need_private, struct ssh *ssh) { int i; Key *key; @@ -865,15 +865,15 @@ get_hostkey_by_type(int type, int need_private) } Key * -get_hostkey_public_by_type(int type) +get_hostkey_public_by_type(int type, struct ssh *ssh) { - return get_hostkey_by_type(type, 0); + return get_hostkey_by_type(type, 0, ssh); } Key * -get_hostkey_private_by_type(int type) +get_hostkey_private_by_type(int type, struct ssh *ssh) { - return get_hostkey_by_type(type, 1); + return get_hostkey_by_type(type, 1, ssh); } Key * @@ -885,7 +885,7 @@ get_hostkey_by_index(int ind) } Key * -get_hostkey_public_by_index(int ind) +get_hostkey_public_by_index(int ind, struct ssh *ssh) { if (ind < 0 || ind >= options.num_host_key_files) return (NULL); @@ -893,7 +893,7 @@ get_hostkey_public_by_index(int ind) } int -get_hostkey_index(Key *key) +get_hostkey_index(Key *key, struct ssh *ssh) { int i; @@ -2432,29 +2432,30 @@ do_ssh1_kex(void) } #endif -void -sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, u_int *slen, - u_char *data, u_int dlen) +int +sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, size_t *slen, + u_char *data, size_t dlen, u_int flag) { int r; + u_int xxx_slen, xxx_dlen = dlen; if (privkey) { - if (PRIVSEP(key_sign(privkey, signature, slen, data, dlen) < 0)) + if (PRIVSEP(key_sign(privkey, signature, &xxx_slen, data, xxx_dlen) < 0)) fatal("%s: key_sign failed", __func__); + if (slen) + *slen = xxx_slen; } else if (use_privsep) { - if (mm_key_sign(pubkey, signature, slen, data, dlen) < 0) + if (mm_key_sign(pubkey, signature, &xxx_slen, data, xxx_dlen) < 0) fatal("%s: pubkey_sign failed", __func__); + if (slen) + *slen = xxx_slen; } else { - size_t xxx_slen; - - if ((r = ssh_agent_sign(auth_sock, pubkey, signature, &xxx_slen, + if ((r = ssh_agent_sign(auth_sock, pubkey, signature, slen, data, dlen, datafellows)) != 0) fatal("%s: ssh_agent_sign failed: %s", __func__, ssh_err(r)); - /* XXX: Old API is u_int; new size_t */ - if (slen != NULL) - *slen = xxx_slen; } + return 0; } /* @@ -2464,7 +2465,7 @@ static void do_ssh2_kex(void) { char *myproposal[PROPOSAL_MAX] = { KEX_SERVER }; - Kex *kex; + struct kex *kex; if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = @@ -2500,8 +2501,8 @@ do_ssh2_kex(void) list_hostkey_types()); /* start key exchange */ - kex = kex_setup(myproposal); - active_state->kex = kex; + kex_setup(active_state, myproposal); + kex = active_state->kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; @@ -2518,7 +2519,7 @@ do_ssh2_kex(void) kex->host_key_index=&get_hostkey_index; kex->sign = sshd_hostkey_sign; - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); session_id2 = kex->session_id; session_id2_len = kex->session_id_len; -- cgit v1.2.3 From 5104db7cbd6cdd9c5971f4358e74414862fc1022 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 26 Jan 2015 06:10:03 +0000 Subject: upstream commit correctly match ECDSA subtype (== curve) for offered/recevied host keys. Fixes connection-killing host key mismatches when a server offers multiple ECDSA keys with different curve type (an extremely unlikely configuration). ok markus, "looks mechanical" deraadt@ --- auth.h | 6 +++--- kex.c | 3 ++- kex.h | 7 ++++--- kexc25519c.c | 6 ++++-- kexc25519s.c | 8 +++++--- kexdhc.c | 6 ++++-- kexdhs.c | 8 +++++--- kexecdhc.c | 6 ++++-- kexecdhs.c | 8 +++++--- kexgexc.c | 8 +++++++- kexgexs.c | 8 +++++--- ssh_api.c | 16 +++++++++------- sshconnect.c | 5 +++-- sshd.c | 15 ++++++++------- 14 files changed, 68 insertions(+), 42 deletions(-) (limited to 'kex.h') diff --git a/auth.h b/auth.h index 60d1c3355..d28261929 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.80 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: auth.h,v 1.81 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -204,8 +204,8 @@ check_key_in_hostfiles(struct passwd *, Key *, const char *, /* hostkey handling */ Key *get_hostkey_by_index(int); Key *get_hostkey_public_by_index(int, struct ssh *); -Key *get_hostkey_public_by_type(int, struct ssh *); -Key *get_hostkey_private_by_type(int, struct ssh *); +Key *get_hostkey_public_by_type(int, int, struct ssh *); +Key *get_hostkey_private_by_type(int, int, struct ssh *); int get_hostkey_index(Key *, struct ssh *); int ssh1_session_key(BIGNUM *); int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, u_char *, size_t, u_int); diff --git a/kex.c b/kex.c index 5b7b1e86a..7eb3185e6 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.103 2015/01/20 23:14:00 deraadt Exp $ */ +/* $OpenBSD: kex.c,v 1.104 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -546,6 +546,7 @@ choose_hostkeyalg(struct kex *k, char *client, char *server) k->hostkey_type = sshkey_type_from_name(hostkeyalg); if (k->hostkey_type == KEY_UNSPEC) return SSH_ERR_INTERNAL_ERROR; + k->hostkey_nid = sshkey_ecdsa_nid_from_name(hostkeyalg); free(hostkeyalg); return 0; } diff --git a/kex.h b/kex.h index 1798eea4d..45d35773c 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.69 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: kex.h,v 1.70 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -116,6 +116,7 @@ struct kex { int server; char *name; int hostkey_type; + int hostkey_nid; u_int kex_type; int roaming; struct sshbuf *my; @@ -127,8 +128,8 @@ struct kex { char *client_version_string; char *server_version_string; int (*verify_host_key)(struct sshkey *, struct ssh *); - struct sshkey *(*load_host_public_key)(int, struct ssh *); - struct sshkey *(*load_host_private_key)(int, struct ssh *); + struct sshkey *(*load_host_public_key)(int, int, struct ssh *); + struct sshkey *(*load_host_private_key)(int, int, struct ssh *); int (*host_key_index)(struct sshkey *, struct ssh *); int (*sign)(struct sshkey *, struct sshkey *, u_char **, size_t *, u_char *, size_t, u_int); diff --git a/kexc25519c.c b/kexc25519c.c index 833ce0544..b7ef65dc3 100644 --- a/kexc25519c.c +++ b/kexc25519c.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519c.c,v 1.6 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: kexc25519c.c,v 1.7 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -92,7 +92,9 @@ input_kex_c25519_reply(int type, u_int32_t seq, void *ctxt) (r = sshkey_from_blob(server_host_key_blob, sbloblen, &server_host_key)) != 0) goto out; - if (server_host_key->type != kex->hostkey_type) { + if (server_host_key->type != kex->hostkey_type || + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } diff --git a/kexc25519s.c b/kexc25519s.c index d840856d2..b2d2c858f 100644 --- a/kexc25519s.c +++ b/kexc25519s.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexc25519s.c,v 1.7 2015/01/20 07:55:33 djm Exp $ */ +/* $OpenBSD: kexc25519s.c,v 1.8 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -75,8 +75,10 @@ input_kex_c25519_init(int type, u_int32_t seq, void *ctxt) r = SSH_ERR_INVALID_ARGUMENT; goto out; } - server_host_public = kex->load_host_public_key(kex->hostkey_type, ssh); - server_host_private = kex->load_host_private_key(kex->hostkey_type, ssh); + server_host_public = kex->load_host_public_key(kex->hostkey_type, + kex->hostkey_nid, ssh); + server_host_private = kex->load_host_private_key(kex->hostkey_type, + kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; diff --git a/kexdhc.c b/kexdhc.c index 52b752295..af259f16a 100644 --- a/kexdhc.c +++ b/kexdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhc.c,v 1.17 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: kexdhc.c,v 1.18 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -114,7 +114,9 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt) (r = sshkey_from_blob(server_host_key_blob, sbloblen, &server_host_key)) != 0) goto out; - if (server_host_key->type != kex->hostkey_type) { + if (server_host_key->type != kex->hostkey_type || + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } diff --git a/kexdhs.c b/kexdhs.c index 0bfa08b07..de7c05b17 100644 --- a/kexdhs.c +++ b/kexdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhs.c,v 1.21 2015/01/20 07:55:33 djm Exp $ */ +/* $OpenBSD: kexdhs.c,v 1.22 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -101,8 +101,10 @@ input_kex_dh_init(int type, u_int32_t seq, void *ctxt) r = SSH_ERR_INVALID_ARGUMENT; goto out; } - server_host_public = kex->load_host_public_key(kex->hostkey_type, ssh); - server_host_private = kex->load_host_private_key(kex->hostkey_type, ssh); + server_host_public = kex->load_host_public_key(kex->hostkey_type, + kex->hostkey_nid, ssh); + server_host_private = kex->load_host_private_key(kex->hostkey_type, + kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; diff --git a/kexecdhc.c b/kexecdhc.c index 3f362c5b1..90220ce82 100644 --- a/kexecdhc.c +++ b/kexecdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhc.c,v 1.9 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: kexecdhc.c,v 1.10 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -124,7 +124,9 @@ input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt) (r = sshkey_from_blob(server_host_key_blob, sbloblen, &server_host_key)) != 0) goto out; - if (server_host_key->type != kex->hostkey_type) { + if (server_host_key->type != kex->hostkey_type || + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } diff --git a/kexecdhs.c b/kexecdhs.c index f47a7b207..0adb80e6a 100644 --- a/kexecdhs.c +++ b/kexecdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexecdhs.c,v 1.13 2015/01/20 07:55:33 djm Exp $ */ +/* $OpenBSD: kexecdhs.c,v 1.14 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -95,8 +95,10 @@ input_kex_ecdh_init(int type, u_int32_t seq, void *ctxt) r = SSH_ERR_INVALID_ARGUMENT; goto out; } - server_host_public = kex->load_host_public_key(kex->hostkey_type, ssh); - server_host_private = kex->load_host_private_key(kex->hostkey_type, ssh); + server_host_public = kex->load_host_public_key(kex->hostkey_type, + kex->hostkey_nid, ssh); + server_host_private = kex->load_host_private_key(kex->hostkey_type, + kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; diff --git a/kexgexc.c b/kexgexc.c index 0898824f3..e8e059a88 100644 --- a/kexgexc.c +++ b/kexgexc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexc.c,v 1.19 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: kexgexc.c,v 1.20 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -176,6 +176,12 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } + if (server_host_key->type != kex->hostkey_type || + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } if (kex->verify_host_key(server_host_key, ssh) == -1) { r = SSH_ERR_SIGNATURE_INVALID; goto out; diff --git a/kexgexs.c b/kexgexs.c index ca5ee13e9..9c281d288 100644 --- a/kexgexs.c +++ b/kexgexs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexs.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */ +/* $OpenBSD: kexgexs.c,v 1.24 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -160,8 +160,10 @@ input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) r = SSH_ERR_INVALID_ARGUMENT; goto out; } - server_host_public = kex->load_host_public_key(kex->hostkey_type, ssh); - server_host_private = kex->load_host_private_key(kex->hostkey_type, ssh); + server_host_public = kex->load_host_public_key(kex->hostkey_type, + kex->hostkey_nid, ssh); + server_host_private = kex->load_host_private_key(kex->hostkey_type, + kex->hostkey_nid, ssh); if (server_host_public == NULL) { r = SSH_ERR_NO_HOSTKEY_LOADED; goto out; diff --git a/ssh_api.c b/ssh_api.c index 1df995c94..9794e0e57 100644 --- a/ssh_api.c +++ b/ssh_api.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh_api.c,v 1.1 2015/01/19 20:30:23 markus Exp $ */ +/* $OpenBSD: ssh_api.c,v 1.2 2015/01/26 06:10:03 djm Exp $ */ /* * Copyright (c) 2012 Markus Friedl. All rights reserved. * @@ -38,8 +38,8 @@ int _ssh_send_banner(struct ssh *, char **); int _ssh_read_banner(struct ssh *, char **); int _ssh_order_hostkeyalgs(struct ssh *); int _ssh_verify_host_key(struct sshkey *, struct ssh *); -struct sshkey *_ssh_host_public_key(int, struct ssh *); -struct sshkey *_ssh_host_private_key(int, struct ssh *); +struct sshkey *_ssh_host_public_key(int, int, struct ssh *); +struct sshkey *_ssh_host_private_key(int, int, struct ssh *); int _ssh_host_key_sign(struct sshkey *, struct sshkey *, u_char **, size_t *, u_char *, size_t, u_int); @@ -425,28 +425,30 @@ _ssh_exchange_banner(struct ssh *ssh) } struct sshkey * -_ssh_host_public_key(int type, struct ssh *ssh) +_ssh_host_public_key(int type, int nid, struct ssh *ssh) { struct key_entry *k; debug3("%s: need %d", __func__, type); TAILQ_FOREACH(k, &ssh->public_keys, next) { debug3("%s: check %s", __func__, sshkey_type(k->key)); - if (k->key->type == type) + if (k->key->type == type && + (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) return (k->key); } return (NULL); } struct sshkey * -_ssh_host_private_key(int type, struct ssh *ssh) +_ssh_host_private_key(int type, int nid, struct ssh *ssh) { struct key_entry *k; debug3("%s: need %d", __func__, type); TAILQ_FOREACH(k, &ssh->private_keys, next) { debug3("%s: check %s", __func__, sshkey_type(k->key)); - if (k->key->type == type) + if (k->key->type == type && + (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) return (k->key); } return (NULL); diff --git a/sshconnect.c b/sshconnect.c index ae3b642cb..df921bec1 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.257 2015/01/26 03:04:46 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.258 2015/01/26 06:10:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1243,7 +1243,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) goto out; } - debug("Server host key: %s %s", sshkey_type(host_key), fp); + debug("Server host key: %s %s", + compat20 ? sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); if (sshkey_equal(previous_host_key, host_key)) { debug2("%s: server host key %s %s matches cached key", diff --git a/sshd.c b/sshd.c index f2ee10d2c..004ddd4a5 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.439 2015/01/26 03:04:46 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.440 2015/01/26 06:10:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -836,7 +836,7 @@ list_hostkey_types(void) } static Key * -get_hostkey_by_type(int type, int need_private, struct ssh *ssh) +get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) { int i; Key *key; @@ -857,7 +857,8 @@ get_hostkey_by_type(int type, int need_private, struct ssh *ssh) key = sensitive_data.host_pubkeys[i]; break; } - if (key != NULL && key->type == type) + if (key != NULL && key->type == type && + (key->type != KEY_ECDSA || key->ecdsa_nid == nid)) return need_private ? sensitive_data.host_keys[i] : key; } @@ -865,15 +866,15 @@ get_hostkey_by_type(int type, int need_private, struct ssh *ssh) } Key * -get_hostkey_public_by_type(int type, struct ssh *ssh) +get_hostkey_public_by_type(int type, int nid, struct ssh *ssh) { - return get_hostkey_by_type(type, 0, ssh); + return get_hostkey_by_type(type, nid, 0, ssh); } Key * -get_hostkey_private_by_type(int type, struct ssh *ssh) +get_hostkey_private_by_type(int type, int nid, struct ssh *ssh) { - return get_hostkey_by_type(type, 1, ssh); + return get_hostkey_by_type(type, nid, 1, ssh); } Key * -- cgit v1.2.3 From 523463a3a2a9bfc6cfc5afa01bae9147f76a37cc Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 16 Feb 2015 22:13:32 +0000 Subject: upstream commit Revise hostkeys@openssh.com hostkey learning extension. The client will not ask the server to prove ownership of the private halves of any hitherto-unseen hostkeys it offers to the client. Allow UpdateHostKeys option to take an 'ask' argument to let the user manually review keys offered. ok markus@ --- PROTOCOL | 53 ++++++--- auth.h | 7 +- clientloop.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++++++------- kex.h | 6 +- monitor.c | 45 +++++++- monitor_wrap.c | 7 +- monitor_wrap.h | 4 +- readconf.c | 6 +- readconf.h | 8 +- serverloop.c | 88 +++++++++++++- ssh_api.c | 7 +- ssh_config.5 | 15 ++- sshd.c | 35 ++++-- ssherr.c | 4 +- 14 files changed, 537 insertions(+), 101 deletions(-) (limited to 'kex.h') diff --git a/PROTOCOL b/PROTOCOL index 8150c577b..f9560839e 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -40,8 +40,8 @@ http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt "ecdsa-sha2-nistp521-cert-v01@openssh.com" OpenSSH introduces new public key algorithms to support certificate -authentication for users and hostkeys. These methods are documented in -the file PROTOCOL.certkeys +authentication for users and host keys. These methods are documented +in the file PROTOCOL.certkeys 1.4. transport: Elliptic Curve cryptography @@ -283,26 +283,51 @@ by the client cancel the forwarding of a Unix domain socket. string socket path 2.5. connection: hostkey update and rotation "hostkeys@openssh.com" +and "hostkeys-prove@openssh.com" OpenSSH supports a protocol extension allowing a server to inform -a client of all its protocol v.2 hostkeys after user-authentication +a client of all its protocol v.2 host keys 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. +Upon receiving this message, a client should check which of the +supplied host keys are present in known_hosts. For keys that are +not present, it should send a "hostkeys-prove@openssh.com" message +to request the server prove ownership of the private half of the +key. -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. + byte SSH_MSG_GLOBAL_REQUEST + string "hostkeys-prove@openssh.com" + char 1 /* want-reply */ + string[] hostkeys + +When a server receives this message, it should generate a signature +using each requested key over the following: + + string session identifier + string "hostkeys-prove@openssh.com" + string hostkey + +These signatures should be included in the reply, in the order matching +the hostkeys in the request: + + byte SSH_MSG_REQUEST_SUCCESS + string[] signatures + +When the client receives this reply (and not a failure), it should +validate the signatures and 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. + +These extensions let a client 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 @@ -428,4 +453,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.25 2015/01/26 03:04:45 djm Exp $ +$OpenBSD: PROTOCOL,v 1.26 2015/02/16 22:13:32 djm Exp $ diff --git a/auth.h b/auth.h index d28261929..db8603760 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.81 2015/01/26 06:10:03 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.82 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -206,9 +206,10 @@ Key *get_hostkey_by_index(int); Key *get_hostkey_public_by_index(int, struct ssh *); Key *get_hostkey_public_by_type(int, int, struct ssh *); Key *get_hostkey_private_by_type(int, int, struct ssh *); -int get_hostkey_index(Key *, struct ssh *); +int get_hostkey_index(Key *, int, struct ssh *); int ssh1_session_key(BIGNUM *); -int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, u_char *, size_t, u_int); +int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, + const u_char *, size_t, u_int); /* debug messages during authentication */ void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); diff --git a/clientloop.c b/clientloop.c index c6f8e9dc1..a19d9d06f 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.268 2015/02/16 22:08:57 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.269 2015/02/16 22:13:32 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2089,6 +2089,216 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) return 0; } +struct hostkeys_update_ctx { + /* The hostname and (optionally) IP address string for the server */ + char *host_str, *ip_str; + + /* + * Keys received from the server and a flag for each indicating + * whether they already exist in known_hosts. + * keys_seen is filled in by hostkeys_find() and later (for new + * keys) by client_global_hostkeys_private_confirm(). + */ + struct sshkey **keys; + int *keys_seen; + size_t nkeys; + + size_t nnew; + + /* + * Keys that are in known_hosts, but were not present in the update + * from the server (i.e. scheduled to be deleted). + * Filled in by hostkeys_find(). + */ + struct sshkey **old_keys; + size_t nold; +}; + +static void +hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) +{ + size_t i; + + if (ctx == NULL) + return; + for (i = 0; i < ctx->nkeys; i++) + sshkey_free(ctx->keys[i]); + free(ctx->keys); + free(ctx->keys_seen); + for (i = 0; i < ctx->nold; i++) + sshkey_free(ctx->old_keys[i]); + free(ctx->old_keys); + free(ctx->host_str); + free(ctx->ip_str); + free(ctx); +} + +static int +hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) +{ + struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; + size_t i; + struct sshkey **tmp; + + if (l->status != HKF_STATUS_MATCHED || l->key == NULL || + l->key->type == KEY_RSA1) + return 0; + + /* Mark off keys we've already seen for this host */ + for (i = 0; i < ctx->nkeys; i++) { + if (sshkey_equal(l->key, ctx->keys[i])) { + debug3("%s: found %s key at %s:%ld", __func__, + sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); + ctx->keys_seen[i] = 1; + return 0; + } + } + /* This line contained a key that not offered by the server */ + debug3("%s: deprecated %s key at %s:%ld", __func__, + sshkey_ssh_name(l->key), l->path, l->linenum); + if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1, + sizeof(*ctx->old_keys))) == NULL) + fatal("%s: reallocarray failed nold = %zu", + __func__, ctx->nold); + ctx->old_keys = tmp; + ctx->old_keys[ctx->nold++] = l->key; + l->key = NULL; + + return 0; +} + +static void +update_known_hosts(struct hostkeys_update_ctx *ctx) +{ + int r, loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? + SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; + char *fp, *response; + size_t i; + + for (i = 0; i < ctx->nkeys; i++) { + if (ctx->keys_seen[i] != 2) + continue; + if ((fp = sshkey_fingerprint(ctx->keys[i], + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal("%s: sshkey_fingerprint failed", __func__); + do_log2(loglevel, "Learned new hostkey: %s %s", + sshkey_type(ctx->keys[i]), fp); + free(fp); + } + for (i = 0; i < ctx->nold; i++) { + if ((fp = sshkey_fingerprint(ctx->old_keys[i], + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal("%s: sshkey_fingerprint failed", __func__); + do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", + sshkey_type(ctx->old_keys[i]), fp); + free(fp); + } + if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + response = NULL; + for (i = 0; !quit_pending && i < 3; i++) { + free(response); + response = read_passphrase("Accept updated hostkeys? " + "(yes/no): ", RP_ECHO); + if (strcasecmp(response, "yes") == 0) + break; + else if (quit_pending || response == NULL || + strcasecmp(response, "no") == 0) { + options.update_hostkeys = 0; + break; + } else { + do_log2(loglevel, "Please enter " + "\"yes\" or \"no\""); + } + } + if (quit_pending || i >= 3 || response == NULL) + options.update_hostkeys = 0; + free(response); + enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + } + + /* + * Now that all the keys are verified, we can go ahead and replace + * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't + * cancel the operation). + */ + if (options.update_hostkeys != 0 && + (r = hostfile_replace_entries(options.user_hostfiles[0], + ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, + options.hash_known_hosts, 0, + options.fingerprint_hash)) != 0) + error("%s: hostfile_replace_entries failed: %s", + __func__, ssh_err(r)); +} + +static void +client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) +{ + struct ssh *ssh = active_state; /* XXX */ + struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; + size_t i, ndone; + struct sshbuf *signdata; + int r; + const u_char *sig; + size_t siglen; + + if (ctx->nnew == 0) + fatal("%s: ctx->nnew == 0", __func__); /* sanity */ + if (type != SSH2_MSG_REQUEST_SUCCESS) { + error("Server failed to confirm ownership of " + "private host keys"); + hostkeys_update_ctx_free(ctx); + return; + } + if ((signdata = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + /* Don't want to accidentally accept an unbound signature */ + if (ssh->kex->session_id_len == 0) + fatal("%s: ssh->kex->session_id_len == 0", __func__); + /* + * Expect a signature for each of the ctx->nnew private keys we + * haven't seen before. They will be in the same order as the + * ctx->keys where the corresponding ctx->keys_seen[i] == 0. + */ + for (ndone = i = 0; i < ctx->nkeys; i++) { + if (ctx->keys_seen[i]) + continue; + /* Prepare data to be signed: session ID, unique string, key */ + sshbuf_reset(signdata); + if ((r = sshbuf_put_string(signdata, ssh->kex->session_id, + ssh->kex->session_id_len)) != 0 || + (r = sshbuf_put_cstring(signdata, + "hostkeys-prove@openssh.com")) != 0 || + (r = sshkey_puts(ctx->keys[i], signdata)) != 0) + fatal("%s: failed to prepare signature: %s", + __func__, ssh_err(r)); + /* Extract and verify signature */ + if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { + error("%s: couldn't parse message: %s", + __func__, ssh_err(r)); + goto out; + } + if ((r = sshkey_verify(ctx->keys[i], sig, siglen, + sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { + error("%s: server gave bad signature for %s key %zu", + __func__, sshkey_type(ctx->keys[i]), i); + goto out; + } + /* Key is good. Mark it as 'seen' */ + ctx->keys_seen[i] = 2; + ndone++; + } + if (ndone != ctx->nnew) + fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, + ndone, ctx->nnew); /* Shouldn't happen */ + ssh_packet_check_eom(ssh); + + /* Make the edits to known_hosts */ + update_known_hosts(ctx); + out: + hostkeys_update_ctx_free(ctx); +} + /* * Handle hostkeys@openssh.com global request to inform the client of all * the server's hostkeys. The keys are checked against the user's @@ -2097,34 +2307,35 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) static int client_input_hostkeys(void) { + struct ssh *ssh = active_state; /* XXX */ const u_char *blob = NULL; - u_int i, len = 0, nkeys = 0; + size_t i, len = 0; struct sshbuf *buf = NULL; - struct sshkey *key = NULL, **tmp, **keys = NULL; - int r, success = 1; - char *fp, *host_str = NULL, *ip_str = NULL; + struct sshkey *key = NULL, **tmp; + int r; + char *fp; static int hostkeys_seen = 0; /* XXX use struct ssh */ extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ + struct hostkeys_update_ctx *ctx; - /* - * 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(); + ctx = xcalloc(1, sizeof(*ctx)); if (hostkeys_seen) fatal("%s: server already sent hostkeys", __func__); + if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && + options.batch_mode) + return 1; /* won't ask in batchmode, so don't even try */ 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) { + while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; - if ((r = sshkey_froms(buf, &key)) != 0) + if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { + error("%s: couldn't parse message: %s", + __func__, ssh_err(r)); + goto out; + } + if ((r = sshkey_from_blob(blob, len, &key)) != 0) fatal("%s: parse key: %s", __func__, ssh_err(r)); fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); @@ -2140,47 +2351,107 @@ client_input_hostkeys(void) __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; + /* Skip certs */ + if (sshkey_is_cert(key)) { + debug3("%s: %s key is a certificate; skipping", + __func__, sshkey_ssh_name(key)); + continue; + } + /* Ensure keys are unique */ + for (i = 0; i < ctx->nkeys; i++) { + if (sshkey_equal(key, ctx->keys[i])) { + error("%s: received duplicated %s host key", + __func__, sshkey_ssh_name(key)); + goto out; + } + } + /* Key is good, record it */ + if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1, + sizeof(*ctx->keys))) == NULL) + fatal("%s: reallocarray failed nkeys = %zu", + __func__, ctx->nkeys); + ctx->keys = tmp; + ctx->keys[ctx->nkeys++] = key; key = NULL; } - if (nkeys == 0) { + if (ctx->nkeys == 0) { error("%s: server sent no hostkeys", __func__); goto out; } + if ((ctx->keys_seen = calloc(ctx->nkeys, + sizeof(*ctx->keys_seen))) == NULL) + fatal("%s: calloc failed", __func__); get_hostfile_hostname_ipaddr(host, options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, - options.port, &host_str, options.check_host_ip ? &ip_str : NULL); + options.port, &ctx->host_str, + options.check_host_ip ? &ctx->ip_str : NULL); + + /* Find which keys we already know about. */ + if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, + ctx, ctx->host_str, ctx->ip_str, + HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { + error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); + goto out; + } + + /* Figure out if we have any new keys to add */ + ctx->nnew = 0; + for (i = 0; i < ctx->nkeys; i++) { + if (!ctx->keys_seen[i]) + ctx->nnew++; + } - 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); + debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", + __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); - 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; + if (ctx->nnew == 0 && ctx->nold != 0) { + /* We have some keys to remove. Just do it. */ + update_known_hosts(ctx); + } else if (ctx->nnew != 0) { + /* + * We have received hitherto-unseen keys from the server. + * Ask the server to confirm ownership of the private halves. + */ + debug3("%s: asking server to prove ownership for %zu keys", + __func__, ctx->nnew); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "hostkeys-prove@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ + fatal("%s: cannot prepare packet: %s", + __func__, ssh_err(r)); + if ((buf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + for (i = 0; i < ctx->nkeys; i++) { + if (ctx->keys_seen[i]) + continue; + sshbuf_reset(buf); + if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) + fatal("%s: sshkey_putb: %s", + __func__, ssh_err(r)); + if ((r = sshpkt_put_stringb(ssh, buf)) != 0) + fatal("%s: sshpkt_put_string: %s", + __func__, ssh_err(r)); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); + client_register_global_confirm( + client_global_hostkeys_private_confirm, ctx); + ctx = NULL; /* will be freed in callback */ } /* Success */ out: - free(host_str); - free(ip_str); + hostkeys_update_ctx_free(ctx); sshkey_free(key); - for (i = 0; i < nkeys; i++) - sshkey_free(keys[i]); sshbuf_free(buf); - return success; + /* + * NB. Return success for all cases. The server doesn't need to know + * what the client does with its hosts file. + */ + return 1; } static int diff --git a/kex.h b/kex.h index 45d35773c..99a7d55bf 100644 --- a/kex.h +++ b/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.70 2015/01/26 06:10:03 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.71 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -130,9 +130,9 @@ struct kex { int (*verify_host_key)(struct sshkey *, struct ssh *); struct sshkey *(*load_host_public_key)(int, int, struct ssh *); struct sshkey *(*load_host_private_key)(int, int, struct ssh *); - int (*host_key_index)(struct sshkey *, struct ssh *); + int (*host_key_index)(struct sshkey *, int, struct ssh *); int (*sign)(struct sshkey *, struct sshkey *, - u_char **, size_t *, u_char *, size_t, u_int); + u_char **, size_t *, const u_char *, size_t, u_int); int (*kex[KEX_MAX])(struct ssh *); /* kex specific state */ DH *dh; /* DH */ diff --git a/monitor.c b/monitor.c index e97b20ef0..6e97def1c 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.143 2015/02/13 18:57:00 markus Exp $ */ +/* $OpenBSD: monitor.c,v 1.144 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -685,12 +685,15 @@ mm_answer_moduli(int sock, Buffer *m) int mm_answer_sign(int sock, Buffer *m) { + struct ssh *ssh = active_state; /* XXX */ extern int auth_sock; /* XXX move to state struct? */ struct sshkey *key; + struct sshbuf *sigbuf; u_char *p; u_char *signature; size_t datlen, siglen; - int r, keyid; + int r, keyid, is_proof = 0; + const char proof_req[] = "hostkeys-prove@openssh.com"; debug3("%s", __func__); @@ -701,9 +704,38 @@ mm_answer_sign(int sock, Buffer *m) /* * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), * SHA384 (48 bytes) and SHA512 (64 bytes). + * + * Otherwise, verify the signature request is for a hostkey + * proof. + * + * XXX perform similar check for KEX signature requests too? + * it's not trivial, since what is signed is the hash, rather + * than the full kex structure... */ - if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) - fatal("%s: data length incorrect: %zu", __func__, datlen); + if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) { + /* + * Construct expected hostkey proof and compare it to what + * the client sent us. + */ + if (session_id2_len == 0) /* hostkeys is never first */ + fatal("%s: bad data length: %zu", __func__, datlen); + if ((key = get_hostkey_public_by_index(keyid, ssh)) == NULL) + fatal("%s: no hostkey for index %d", __func__, keyid); + if ((sigbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + if ((r = sshbuf_put_string(sigbuf, session_id2, + session_id2_len) != 0) || + (r = sshbuf_put_cstring(sigbuf, proof_req)) != 0 || + (r = sshkey_puts(key, sigbuf)) != 0) + fatal("%s: couldn't prepare private key " + "proof buffer: %s", __func__, ssh_err(r)); + if (datlen != sshbuf_len(sigbuf) || + memcmp(p, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf)) != 0) + fatal("%s: bad data length: %zu, hostkey proof len %zu", + __func__, datlen, sshbuf_len(sigbuf)); + sshbuf_free(sigbuf); + is_proof = 1; + } /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { @@ -717,7 +749,7 @@ mm_answer_sign(int sock, Buffer *m) datafellows)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); - } else if ((key = get_hostkey_public_by_index(keyid, active_state)) != NULL && + } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, datafellows)) != 0) { @@ -727,7 +759,8 @@ mm_answer_sign(int sock, Buffer *m) } else fatal("%s: no hostkey from index %d", __func__, keyid); - debug3("%s: signature %p(%zu)", __func__, signature, siglen); + debug3("%s: %s signature %p(%zu)", __func__, + is_proof ? "KEX" : "hostkey proof", signature, siglen); sshbuf_reset(m); if ((r = sshbuf_put_string(m, signature, siglen)) != 0) diff --git a/monitor_wrap.c b/monitor_wrap.c index c0935dc69..b379f0555 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.83 2015/01/19 20:16:15 markus Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.84 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -219,7 +219,8 @@ mm_choose_dh(int min, int nbits, int max) #endif int -mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) +mm_key_sign(Key *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen) { struct kex *kex = *pmonitor->m_pkex; Buffer m; @@ -227,7 +228,7 @@ mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) debug3("%s entering", __func__); buffer_init(&m); - buffer_put_int(&m, kex->host_key_index(key, active_state)); + buffer_put_int(&m, kex->host_key_index(key, 0, active_state)); buffer_put_string(&m, data, datalen); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SIGN, &m); diff --git a/monitor_wrap.h b/monitor_wrap.h index d97e8db1e..e18784ac4 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.h,v 1.25 2015/01/19 19:52:16 markus Exp $ */ +/* $OpenBSD: monitor_wrap.h,v 1.26 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright 2002 Niels Provos @@ -40,7 +40,7 @@ struct Authctxt; void mm_log_handler(LogLevel, const char *, void *); int mm_is_monitor(void); DH *mm_choose_dh(int, int, int); -int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int); +int mm_key_sign(Key *, u_char **, u_int *, const u_char *, u_int); void mm_inform_authserv(char *, char *); struct passwd *mm_getpwnamallow(const char *); char *mm_auth2_read_banner(void); diff --git a/readconf.c b/readconf.c index a5bb4a25e..42a2961fa 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.231 2015/02/02 07:41:40 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.232 2015/02/16 22:13:32 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1480,7 +1480,8 @@ parse_int: case oUpdateHostkeys: intptr = &options->update_hostkeys; - goto parse_flag; + multistate_ptr = multistate_yesnoask; + goto parse_multistate; case oHostbasedKeyTypes: charptr = &options->hostbased_key_types; @@ -2107,6 +2108,7 @@ fmt_intarg(OpCodes code, int val) return fmt_multistate_int(val, multistate_addressfamily); case oVerifyHostKeyDNS: case oStrictHostKeyChecking: + case oUpdateHostkeys: return fmt_multistate_int(val, multistate_yesnoask); case oControlMaster: return fmt_multistate_int(val, multistate_controlmaster); diff --git a/readconf.h b/readconf.h index 701b9c696..576b9e352 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.108 2015/01/30 11:43:14 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.109 2015/02/16 22:13:32 djm Exp $ */ /* * Author: Tatu Ylonen @@ -148,7 +148,7 @@ typedef struct { int fingerprint_hash; - int update_hostkeys; + int update_hostkeys; /* one of SSH_UPDATE_HOSTKEYS_* */ char *hostbased_key_types; @@ -174,6 +174,10 @@ typedef struct { #define SSHCONF_USERCONF 2 /* user provided config file not system */ #define SSHCONF_POSTCANON 4 /* After hostname canonicalisation */ +#define SSH_UPDATE_HOSTKEYS_NO 0 +#define SSH_UPDATE_HOSTKEYS_YES 1 +#define SSH_UPDATE_HOSTKEYS_ASK 2 + void initialize_options(Options *); void fill_default_options(Options *); void fill_default_options_for_canonicalization(Options *); diff --git a/serverloop.c b/serverloop.c index 48bb3f631..5633ceb41 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.176 2015/01/20 23:14:00 deraadt Exp $ */ +/* $OpenBSD: serverloop.c,v 1.177 2015/02/16 22:13:32 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -79,6 +79,7 @@ #include "auth-options.h" #include "serverloop.h" #include "roaming.h" +#include "ssherr.h" extern ServerOptions options; @@ -1149,12 +1150,83 @@ server_input_channel_open(int type, u_int32_t seq, void *ctxt) return 0; } +static int +server_input_hostkeys_prove(struct sshbuf **respp) +{ + struct ssh *ssh = active_state; /* XXX */ + struct sshbuf *resp = NULL; + struct sshbuf *sigbuf = NULL; + struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; + int r, ndx, success = 0; + const u_char *blob; + u_char *sig = 0; + size_t blen, slen; + + if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + + while (ssh_packet_remaining(ssh) > 0) { + sshkey_free(key); + key = NULL; + if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 || + (r = sshkey_from_blob(blob, blen, &key)) != 0) { + error("%s: couldn't parse key: %s", + __func__, ssh_err(r)); + goto out; + } + /* + * Better check that this is actually one of our hostkeys + * before attempting to sign anything with it. + */ + if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) { + error("%s: unknown host %s key", + __func__, sshkey_type(key)); + goto out; + } + /* + * XXX refactor: make kex->sign just use an index rather + * than passing in public and private keys + */ + if ((key_prv = get_hostkey_by_index(ndx)) == NULL && + (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) { + error("%s: can't retrieve hostkey %d", __func__, ndx); + goto out; + } + sshbuf_reset(sigbuf); + free(sig); + sig = NULL; + if ((r = sshbuf_put_string(sigbuf, + ssh->kex->session_id, ssh->kex->session_id_len)) != 0 || + (r = sshbuf_put_cstring(sigbuf, + "hostkeys-prove@openssh.com")) != 0 || + (r = sshkey_puts(key, sigbuf)) != 0 || + (r = ssh->kex->sign(key_prv, key_pub, &sig, &slen, + sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), 0)) != 0 || + (r = sshbuf_put_string(resp, sig, slen)) != 0) { + error("%s: couldn't prepare signature: %s", + __func__, ssh_err(r)); + goto out; + } + } + /* Success */ + *respp = resp; + resp = NULL; /* don't free it */ + success = 1; + out: + free(sig); + sshbuf_free(resp); + sshbuf_free(sigbuf); + sshkey_free(key); + return success; +} + static int server_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; - int success = 0, allocated_listen_port = 0; + int r, success = 0, allocated_listen_port = 0; + struct sshbuf *resp = NULL; rtype = packet_get_string(NULL); want_reply = packet_get_char(); @@ -1191,6 +1263,10 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) &allocated_listen_port, &options.fwd_opts); } free(fwd.listen_host); + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + if ((r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) + fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r)); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { struct Forward fwd; @@ -1234,16 +1310,20 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { no_more_sessions = 1; success = 1; + } else if (strcmp(rtype, "hostkeys-prove@openssh.com") == 0) { + success = server_input_hostkeys_prove(&resp); } if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); - if (success && allocated_listen_port > 0) - packet_put_int(allocated_listen_port); + if (success && resp != NULL) + ssh_packet_put_raw(active_state, sshbuf_ptr(resp), + sshbuf_len(resp)); packet_send(); packet_write_wait(); } free(rtype); + sshbuf_free(resp); return 0; } diff --git a/ssh_api.c b/ssh_api.c index 7097c063c..265a3e639 100644 --- a/ssh_api.c +++ b/ssh_api.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh_api.c,v 1.3 2015/01/30 01:13:33 djm Exp $ */ +/* $OpenBSD: ssh_api.c,v 1.4 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright (c) 2012 Markus Friedl. All rights reserved. * @@ -41,7 +41,7 @@ int _ssh_verify_host_key(struct sshkey *, struct ssh *); struct sshkey *_ssh_host_public_key(int, int, struct ssh *); struct sshkey *_ssh_host_private_key(int, int, struct ssh *); int _ssh_host_key_sign(struct sshkey *, struct sshkey *, u_char **, - size_t *, u_char *, size_t, u_int); + size_t *, const u_char *, size_t, u_int); /* * stubs for the server side implementation of kex. @@ -524,7 +524,8 @@ _ssh_order_hostkeyalgs(struct ssh *ssh) int _ssh_host_key_sign(struct sshkey *privkey, struct sshkey *pubkey, - u_char **signature, size_t *slen, u_char *data, size_t dlen, u_int compat) + u_char **signature, size_t *slen, + const u_char *data, size_t dlen, u_int compat) { return sshkey_sign(privkey, signature, slen, data, dlen, compat); } diff --git a/ssh_config.5 b/ssh_config.5 index ce79fe03f..fa59c518e 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.203 2015/02/02 07:41:40 djm Exp $ -.Dd $Mdocdate: February 2 2015 $ +.\" $OpenBSD: ssh_config.5,v 1.204 2015/02/16 22:13:32 djm Exp $ +.Dd $Mdocdate: February 16 2015 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -1510,15 +1510,20 @@ 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 -or +.Dq yes , .Dq no -(the default). +(the default) or +.Dq ask . Enabling this option allows learning alternate hostkeys for a server and supports graceful key rotation by allowing a server to send replacement public 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. +If +.Cm UpdateHostKeys +is set to +.Dq ask , +then the user is asked to confirm the modifications to the known_hosts file. .Pp Presently, only .Xr sshd 8 diff --git a/sshd.c b/sshd.c index 4282bdc1b..aaa63d497 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.441 2015/01/31 20:30:05 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.442 2015/02/16 22:13:32 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -894,18 +894,25 @@ get_hostkey_public_by_index(int ind, struct ssh *ssh) } int -get_hostkey_index(Key *key, struct ssh *ssh) +get_hostkey_index(Key *key, int compare, struct ssh *ssh) { int i; for (i = 0; i < options.num_host_key_files; i++) { if (key_is_cert(key)) { - if (key == sensitive_data.host_certificates[i]) + if (key == sensitive_data.host_certificates[i] || + (compare && sensitive_data.host_certificates[i] && + sshkey_equal(key, + sensitive_data.host_certificates[i]))) return (i); } else { - if (key == sensitive_data.host_keys[i]) + if (key == sensitive_data.host_keys[i] || + (compare && sensitive_data.host_keys[i] && + sshkey_equal(key, sensitive_data.host_keys[i]))) return (i); - if (key == sensitive_data.host_pubkeys[i]) + if (key == sensitive_data.host_pubkeys[i] || + (compare && sensitive_data.host_pubkeys[i] && + sshkey_equal(key, sensitive_data.host_pubkeys[i]))) return (i); } } @@ -933,19 +940,23 @@ notify_hostkeys(struct ssh *ssh) debug3("%s: key %d: %s %s", __func__, i, sshkey_ssh_name(key), fp); free(fp); - if ((r = sshkey_puts(key, buf)) != 0) + if (nkeys == 0) { + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("hostkeys@openssh.com"); + packet_put_char(0); /* want-reply */ + } + sshbuf_reset(buf); + if ((r = sshkey_putb(key, buf)) != 0) fatal("%s: couldn't put hostkey %d: %s", __func__, i, ssh_err(r)); + packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf)); nkeys++; } + debug3("%s: sent %d hostkeys", __func__, 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(); + sshbuf_free(buf); } /* @@ -2484,7 +2495,7 @@ do_ssh1_kex(void) int sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, size_t *slen, - u_char *data, size_t dlen, u_int flag) + const u_char *data, size_t dlen, u_int flag) { int r; u_int xxx_slen, xxx_dlen = dlen; diff --git a/ssherr.c b/ssherr.c index 5c29c467c..4ca793992 100644 --- a/ssherr.c +++ b/ssherr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssherr.c,v 1.3 2015/01/30 01:13:33 djm Exp $ */ +/* $OpenBSD: ssherr.c,v 1.4 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -121,6 +121,8 @@ ssh_err(int n) return "agent not present"; case SSH_ERR_AGENT_NO_IDENTITIES: return "agent contains no identities"; + case SSH_ERR_BUFFER_READ_ONLY: + return "internal error: buffer is read-only"; case SSH_ERR_KRL_BAD_MAGIC: return "KRL file has invalid magic number"; case SSH_ERR_KEY_REVOKED: -- cgit v1.2.3 From 773dda25e828c4c9a52f7bdce6e1e5924157beab Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 30 Jan 2015 23:10:17 +1100 Subject: repair --without-openssl; broken in refactor --- kex.h | 20 ++++++++++++++++++++ monitor.c | 2 ++ packet.c | 16 ++++++++++++++-- packet.h | 25 +++++++++++++++++++++++-- ssh-keygen.c | 5 +++-- ssh_api.c | 2 ++ 6 files changed, 64 insertions(+), 6 deletions(-) (limited to 'kex.h') diff --git a/kex.h b/kex.h index 99a7d55bf..f70b81fc1 100644 --- a/kex.h +++ b/kex.h @@ -34,6 +34,20 @@ #include "leakmalloc.h" #endif +#ifdef WITH_OPENSSL +# ifdef OPENSSL_HAS_ECC +# include +# else /* OPENSSL_HAS_ECC */ +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +# endif /* OPENSSL_HAS_ECC */ +#else /* WITH_OPENSSL */ +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +#endif /* WITH_OPENSSL */ + #define KEX_COOKIE_LEN 16 #define KEX_DH1 "diffie-hellman-group1-sha1" @@ -204,4 +218,10 @@ derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]); void dump_digest(char *, u_char *, int); #endif +#if !defined(WITH_OPENSSL) || !defined(OPENSSL_HAS_ECC) +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#endif + #endif diff --git a/monitor.c b/monitor.c index 6e97def1c..bc4f039c5 100644 --- a/monitor.c +++ b/monitor.c @@ -1848,11 +1848,13 @@ monitor_apply_keystate(struct monitor *pmonitor) if ((kex = ssh->kex) != 0) { /* XXX set callbacks */ +#ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; kex->kex[KEX_ECDH_SHA2] = kexecdh_server; +#endif /* WITH_OPENSSL */ kex->kex[KEX_C25519_SHA256] = kexc25519_server; kex->load_host_public_key=&get_hostkey_public_by_type; kex->load_host_private_key=&get_hostkey_private_by_type; diff --git a/packet.c b/packet.c index b29d875c0..b1219c85b 100644 --- a/packet.c +++ b/packet.c @@ -788,10 +788,10 @@ ssh_packet_set_compress_hooks(struct ssh *ssh, void *ctx, * encrypted independently of each other. */ -#ifdef WITH_OPENSSL void ssh_packet_set_encryption_key(struct ssh *ssh, const u_char *key, u_int keylen, int number) { +#ifdef WITH_SSH1 struct session_state *state = ssh->state; const struct sshcipher *cipher = cipher_by_number(number); int r; @@ -816,8 +816,8 @@ ssh_packet_set_encryption_key(struct ssh *ssh, const u_char *key, u_int keylen, error("Warning: %s", wmsg); state->cipher_warning_done = 1; } +#endif /* WITH_SSH1 */ } -#endif /* * Finalizes and sends the packet. If the encryption key has been set, @@ -2727,23 +2727,29 @@ sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v) return sshbuf_put_stringb(ssh->state->outgoing_packet, v); } +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g) { return sshbuf_put_ec(ssh->state->outgoing_packet, v, g); } +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ +#ifdef WITH_SSH1 int sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v) { return sshbuf_put_bignum1(ssh->state->outgoing_packet, v); } +#endif /* WITH_SSH1 */ +#ifdef WITH_OPENSSL int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v) { return sshbuf_put_bignum2(ssh->state->outgoing_packet, v); } +#endif /* WITH_OPENSSL */ /* fetch data from the incoming packet */ @@ -2789,23 +2795,29 @@ sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp) return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp); } +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g) { return sshbuf_get_ec(ssh->state->incoming_packet, v, g); } +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ +#ifdef WITH_SSH1 int sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v) { return sshbuf_get_bignum1(ssh->state->incoming_packet, v); } +#endif /* WITH_SSH1 */ +#ifdef WITH_OPENSSL int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v) { return sshbuf_get_bignum2(ssh->state->incoming_packet, v); } +#endif /* WITH_OPENSSL */ int sshpkt_get_end(struct ssh *ssh) diff --git a/packet.h b/packet.h index 01df9f413..cb194567a 100644 --- a/packet.h +++ b/packet.h @@ -22,8 +22,18 @@ # include # ifdef OPENSSL_HAS_ECC # include -# endif -#endif +# else /* OPENSSL_HAS_ECC */ +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +# endif /* OPENSSL_HAS_ECC */ +#else /* WITH_OPENSSL */ +# define BIGNUM void +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +#endif /* WITH_OPENSSL */ + #include #include @@ -182,4 +192,15 @@ const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); extern struct ssh *active_state; #include "opacket.h" +#if !defined(WITH_OPENSSL) +# undef BIGNUM +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#elif !defined(OPENSSL_HAS_ECC) +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#endif + #endif /* PACKET_H */ diff --git a/ssh-keygen.c b/ssh-keygen.c index 9b2068254..923874825 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -192,6 +192,7 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp) exit(1); } if (*bitsp == 0) { +#ifdef WITH_OPENSSL if (type == KEY_DSA) *bitsp = DEFAULT_BITS_DSA; else if (type == KEY_ECDSA) { @@ -200,8 +201,8 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp) *bitsp = sshkey_curve_nid_to_bits(nid); if (*bitsp == 0) *bitsp = DEFAULT_BITS_ECDSA; - } - else + } else +#endif *bitsp = DEFAULT_BITS; } #ifdef WITH_OPENSSL diff --git a/ssh_api.c b/ssh_api.c index 265a3e639..ca4789b54 100644 --- a/ssh_api.c +++ b/ssh_api.c @@ -81,7 +81,9 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params) int r; if (!called) { +#ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); +#endif /* WITH_OPENSSL */ called = 1; } -- cgit v1.2.3