New buffer API; the first installment of the conversion/replacement
authordjm <djm@openbsd.org>
Wed, 30 Apr 2014 05:29:56 +0000 (05:29 +0000)
committerdjm <djm@openbsd.org>
Wed, 30 Apr 2014 05:29:56 +0000 (05:29 +0000)
of OpenSSH's internals to make them usable as a standalone library.

This includes a set of wrappers to make it compatible with the
existing buffer API so replacement can occur incrementally.

With and ok markus@

Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew
Dempsky and Ron Bowes for a detailed review.

13 files changed:
usr.bin/ssh/bufaux.c
usr.bin/ssh/bufbn.c
usr.bin/ssh/bufec.c
usr.bin/ssh/buffer.c
usr.bin/ssh/buffer.h
usr.bin/ssh/lib/Makefile
usr.bin/ssh/sshbuf-getput-basic.c [new file with mode: 0644]
usr.bin/ssh/sshbuf-getput-crypto.c [new file with mode: 0644]
usr.bin/ssh/sshbuf-misc.c [new file with mode: 0644]
usr.bin/ssh/sshbuf.c [new file with mode: 0644]
usr.bin/ssh/sshbuf.h [new file with mode: 0644]
usr.bin/ssh/ssherr.c [new file with mode: 0644]
usr.bin/ssh/ssherr.h [new file with mode: 0644]

index 488cbde..aa9b892 100644 (file)
@@ -1,66 +1,38 @@
-/* $OpenBSD: bufaux.c,v 1.59 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: bufaux.c,v 1.60 2014/04/30 05:29:56 djm Exp $ */
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Auxiliary functions for storing and retrieving various data types to/from
- * Buffers.
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * 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".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- *
- * SSH2 packet format added by Markus Friedl
- * Copyright (c) 2000 Markus Friedl.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/types.h>
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
+#include <sys/types.h>
 
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-#include "misc.h"
-
-/*
- * Returns integers from the buffer (msb first).
- */
+#include "ssherr.h"
 
 int
-buffer_get_short_ret(u_short *ret, Buffer *buffer)
+buffer_get_short_ret(u_short *v, Buffer *buffer)
 {
-       u_char buf[2];
+       int ret;
 
-       if (buffer_get_ret(buffer, (char *) buf, 2) == -1)
-               return (-1);
-       *ret = get_u16(buf);
-       return (0);
+       if ((ret = sshbuf_get_u16(buffer, v)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
+       }
+       return 0;
 }
 
 u_short
@@ -69,21 +41,21 @@ buffer_get_short(Buffer *buffer)
        u_short ret;
 
        if (buffer_get_short_ret(&ret, buffer) == -1)
-               fatal("buffer_get_short: buffer error");
+               fatal("%s: buffer error", __func__);
 
        return (ret);
 }
 
 int
-buffer_get_int_ret(u_int *ret, Buffer *buffer)
+buffer_get_int_ret(u_int *v, Buffer *buffer)
 {
-       u_char buf[4];
+       int ret;
 
-       if (buffer_get_ret(buffer, (char *) buf, 4) == -1)
-               return (-1);
-       if (ret != NULL)
-               *ret = get_u32(buf);
-       return (0);
+       if ((ret = sshbuf_get_u32(buffer, v)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
+       }
+       return 0;
 }
 
 u_int
@@ -92,21 +64,21 @@ buffer_get_int(Buffer *buffer)
        u_int ret;
 
        if (buffer_get_int_ret(&ret, buffer) == -1)
-               fatal("buffer_get_int: buffer error");
+               fatal("%s: buffer error", __func__);
 
        return (ret);
 }
 
 int
-buffer_get_int64_ret(u_int64_t *ret, Buffer *buffer)
+buffer_get_int64_ret(u_int64_t *v, Buffer *buffer)
 {
-       u_char buf[8];
+       int ret;
 
-       if (buffer_get_ret(buffer, (char *) buf, 8) == -1)
-               return (-1);
-       if (ret != NULL)
-               *ret = get_u64(buf);
-       return (0);
+       if ((ret = sshbuf_get_u64(buffer, v)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
+       }
+       return 0;
 }
 
 u_int64_t
@@ -115,78 +87,52 @@ buffer_get_int64(Buffer *buffer)
        u_int64_t ret;
 
        if (buffer_get_int64_ret(&ret, buffer) == -1)
-               fatal("buffer_get_int: buffer error");
+               fatal("%s: buffer error", __func__);
 
        return (ret);
 }
 
-/*
- * Stores integers in the buffer, msb first.
- */
 void
 buffer_put_short(Buffer *buffer, u_short value)
 {
-       char buf[2];
+       int ret;
 
-       put_u16(buf, value);
-       buffer_append(buffer, buf, 2);
+       if ((ret = sshbuf_put_u16(buffer, value)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_put_int(Buffer *buffer, u_int value)
 {
-       char buf[4];
+       int ret;
 
-       put_u32(buf, value);
-       buffer_append(buffer, buf, 4);
+       if ((ret = sshbuf_put_u32(buffer, value)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_put_int64(Buffer *buffer, u_int64_t value)
 {
-       char buf[8];
+       int ret;
 
-       put_u64(buf, value);
-       buffer_append(buffer, buf, 8);
+       if ((ret = sshbuf_put_u64(buffer, value)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/*
- * Returns an arbitrary binary string from the buffer.  The string cannot
- * be longer than 256k.  The returned value points to memory allocated
- * with xmalloc; it is the responsibility of the calling function to free
- * the data.  If length_ptr is non-NULL, the length of the returned data
- * will be stored there.  A null character will be automatically appended
- * to the returned string, and is not counted in length.
- */
 void *
 buffer_get_string_ret(Buffer *buffer, u_int *length_ptr)
 {
+       size_t len;
+       int ret;
        u_char *value;
-       u_int len;
 
-       /* Get the length. */
-       if (buffer_get_int_ret(&len, buffer) != 0) {
-               error("buffer_get_string_ret: cannot extract length");
-               return (NULL);
-       }
-       if (len > 256 * 1024) {
-               error("buffer_get_string_ret: bad string length %u", len);
-               return (NULL);
-       }
-       /* Allocate space for the string.  Add one byte for a null character. */
-       value = xmalloc(len + 1);
-       /* Get the string. */
-       if (buffer_get_ret(buffer, value, len) == -1) {
-               error("buffer_get_string_ret: buffer_get failed");
-               free(value);
-               return (NULL);
+       if ((ret = sshbuf_get_string(buffer, &value, &len)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return NULL;
        }
-       /* Append a null character to make processing easier. */
-       value[len] = '\0';
-       /* Optionally return the length of the string. */
-       if (length_ptr)
-               *length_ptr = len;
-       return (value);
+       if (length_ptr != NULL)
+               *length_ptr = len;  /* Safe: sshbuf never stores len > 2^31 */
+       return value;
 }
 
 void *
@@ -195,31 +141,24 @@ buffer_get_string(Buffer *buffer, u_int *length_ptr)
        void *ret;
 
        if ((ret = buffer_get_string_ret(buffer, length_ptr)) == NULL)
-               fatal("buffer_get_string: buffer error");
+               fatal("%s: buffer error", __func__);
        return (ret);
 }
 
 char *
 buffer_get_cstring_ret(Buffer *buffer, u_int *length_ptr)
 {
-       u_int length;
-       char *cp, *ret = buffer_get_string_ret(buffer, &length);
+       size_t len;
+       int ret;
+       char *value;
 
-       if (ret == NULL)
+       if ((ret = sshbuf_get_cstring(buffer, &value, &len)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
                return NULL;
-       if ((cp = memchr(ret, '\0', length)) != NULL) {
-               /* XXX allow \0 at end-of-string for a while, remove later */
-               if (cp == ret + length - 1)
-                       error("buffer_get_cstring_ret: string contains \\0");
-               else {
-                       explicit_bzero(ret, length);
-                       free(ret);
-                       return NULL;
-               }
        }
        if (length_ptr != NULL)
-               *length_ptr = length;
-       return ret;
+               *length_ptr = len;  /* Safe: sshbuf never stores len > 2^31 */
+       return value;
 }
 
 char *
@@ -228,27 +167,24 @@ buffer_get_cstring(Buffer *buffer, u_int *length_ptr)
        char *ret;
 
        if ((ret = buffer_get_cstring_ret(buffer, length_ptr)) == NULL)
-               fatal("buffer_get_cstring: buffer error");
+               fatal("%s: buffer error", __func__);
        return ret;
 }
 
 const void *
 buffer_get_string_ptr_ret(Buffer *buffer, u_int *length_ptr)
 {
-       void *ptr;
-       u_int len;
+       size_t len;
+       int ret;
+       const u_char *value;
 
-       if (buffer_get_int_ret(&len, buffer) != 0)
-               return NULL;
-       if (len > 256 * 1024) {
-               error("buffer_get_string_ptr: bad string length %u", len);
+       if ((ret = sshbuf_get_string_direct(buffer, &value, &len)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
                return NULL;
        }
-       ptr = buffer_ptr(buffer);
-       buffer_consume(buffer, len);
-       if (length_ptr)
-               *length_ptr = len;
-       return (ptr);
+       if (length_ptr != NULL)
+               *length_ptr = len;  /* Safe: sshbuf never stores len > 2^31 */
+       return value;
 }
 
 const void *
@@ -257,133 +193,65 @@ buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr)
        const void *ret;
 
        if ((ret = buffer_get_string_ptr_ret(buffer, length_ptr)) == NULL)
-               fatal("buffer_get_string_ptr: buffer error");
+               fatal("%s: buffer error", __func__);
        return (ret);
 }
 
-/*
- * Stores and arbitrary binary string in the buffer.
- */
 void
 buffer_put_string(Buffer *buffer, const void *buf, u_int len)
 {
-       buffer_put_int(buffer, len);
-       buffer_append(buffer, buf, len);
+       int ret;
+
+       if ((ret = sshbuf_put_string(buffer, buf, len)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
+
 void
 buffer_put_cstring(Buffer *buffer, const char *s)
 {
-       if (s == NULL)
-               fatal("buffer_put_cstring: s == NULL");
-       buffer_put_string(buffer, s, strlen(s));
+       int ret;
+
+       if ((ret = sshbuf_put_cstring(buffer, s)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/*
- * Returns a character from the buffer (0 - 255).
- */
 int
-buffer_get_char_ret(u_char *ret, Buffer *buffer)
+buffer_get_char_ret(char *v, Buffer *buffer)
 {
-       if (buffer_get_ret(buffer, ret, 1) == -1) {
-               error("buffer_get_char_ret: buffer_get_ret failed");
-               return (-1);
+       int ret;
+
+       if ((ret = sshbuf_get_u8(buffer, (u_char *)v)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
        }
-       return (0);
+       return 0;
 }
 
 int
 buffer_get_char(Buffer *buffer)
 {
-       u_char ch;
+       char ch;
 
        if (buffer_get_char_ret(&ch, buffer) == -1)
-               fatal("buffer_get_char: buffer error");
-       return ch;
+               fatal("%s: buffer error", __func__);
+       return (u_char) ch;
 }
 
-/*
- * Stores a character in the buffer.
- */
 void
 buffer_put_char(Buffer *buffer, int value)
 {
-       char ch = value;
+       int ret;
 
-       buffer_append(buffer, &ch, 1);
+       if ((ret = sshbuf_put_u8(buffer, value)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/* Pseudo bignum functions */
-
-void *
-buffer_get_bignum2_as_string_ret(Buffer *buffer, u_int *length_ptr)
-{
-       u_int len;
-       u_char *bin, *p, *ret;
-
-       if ((p = bin = buffer_get_string_ret(buffer, &len)) == NULL) {
-               error("%s: invalid bignum", __func__);
-               return NULL;
-       }
-
-       if (len > 0 && (bin[0] & 0x80)) {
-               error("%s: negative numbers not supported", __func__);
-               free(bin);
-               return NULL;
-       }
-       if (len > 8 * 1024) {
-               error("%s: cannot handle BN of size %d", __func__, len);
-               free(bin);
-               return NULL;
-       }
-       /* Skip zero prefix on numbers with the MSB set */
-       if (len > 1 && bin[0] == 0x00 && (bin[1] & 0x80) != 0) {
-               p++;
-               len--;
-       }
-       ret = xmalloc(len);
-       memcpy(ret, p, len);
-       explicit_bzero(p, len);
-       free(bin);
-       return ret;
-}
-
-void *
-buffer_get_bignum2_as_string(Buffer *buffer, u_int *l)
-{
-       void *ret = buffer_get_bignum2_as_string_ret(buffer, l);
-
-       if (ret == NULL)
-               fatal("%s: buffer error", __func__);
-       return ret;
-}
-
-/*
- * Stores a string using the bignum encoding rules (\0 pad if MSB set).
- */
 void
 buffer_put_bignum2_from_string(Buffer *buffer, const u_char *s, u_int l)
 {
-       u_char *buf, *p;
-       int pad = 0;
-
-       if (l > 8 * 1024)
-               fatal("%s: length %u too long", __func__, l);
-       /* Skip leading zero bytes */
-       for (; l > 0 && *s == 0; l--, s++)
-               ;
-       p = buf = xmalloc(l + 1);
-       /*
-        * If most significant bit is set then prepend a zero byte to
-        * avoid interpretation as a negative number.
-        */
-       if (l > 0 && (s[0] & 0x80) != 0) {
-               *p++ = '\0';
-               pad = 1;
-       }
-       memcpy(p, s, l);
-       buffer_put_string(buffer, buf, l + pad);
-       explicit_bzero(buf, l + pad);
-       free(buf);
-}
+       int ret;
 
+       if ((ret = sshbuf_put_bignum2_bytes(buffer, s, l)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
+}
 
index 301922a..0a519ed 100644 (file)
-/* $OpenBSD: bufbn.c,v 1.11 2014/02/27 08:25:09 djm Exp $*/
+/* $OpenBSD: bufbn.c,v 1.12 2014/04/30 05:29:56 djm Exp $ */
+
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Auxiliary functions for storing and retrieving various data types to/from
- * Buffers.
- *
- * 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".
- *
- *
- * SSH2 packet format added by Markus Friedl
- * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/types.h>
-
-#include <openssl/bn.h>
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
+#include <sys/types.h>
 
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-#include "misc.h"
+#include "ssherr.h"
 
-/*
- * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed
- * by (bits+7)/8 bytes of binary data, msb first.
- */
 int
 buffer_put_bignum_ret(Buffer *buffer, const BIGNUM *value)
 {
-       int bits = BN_num_bits(value);
-       int bin_size = (bits + 7) / 8;
-       u_char *buf = xmalloc(bin_size);
-       int oi;
-       char msg[2];
-
-       /* Get the value of in binary */
-       oi = BN_bn2bin(value, buf);
-       if (oi != bin_size) {
-               error("buffer_put_bignum_ret: BN_bn2bin() failed: oi %d != bin_size %d",
-                   oi, bin_size);
-               free(buf);
-               return (-1);
-       }
-
-       /* Store the number of bits in the buffer in two bytes, msb first. */
-       put_u16(msg, bits);
-       buffer_append(buffer, msg, 2);
-       /* Store the binary data. */
-       buffer_append(buffer, buf, oi);
-
-       explicit_bzero(buf, bin_size);
-       free(buf);
+       int ret;
 
-       return (0);
+       if ((ret = sshbuf_put_bignum1(buffer, value)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
+       }
+       return 0;
 }
 
 void
 buffer_put_bignum(Buffer *buffer, const BIGNUM *value)
 {
        if (buffer_put_bignum_ret(buffer, value) == -1)
-               fatal("buffer_put_bignum: buffer error");
+               fatal("%s: buffer error", __func__);
 }
 
-/*
- * Retrieves a BIGNUM from the buffer.
- */
 int
 buffer_get_bignum_ret(Buffer *buffer, BIGNUM *value)
 {
-       u_int bits, bytes;
-       u_char buf[2], *bin;
+       int ret;
 
-       /* Get the number of bits. */
-       if (buffer_get_ret(buffer, (char *) buf, 2) == -1) {
-               error("buffer_get_bignum_ret: invalid length");
-               return (-1);
+       if ((ret = sshbuf_get_bignum1(buffer, value)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
        }
-       bits = get_u16(buf);
-       if (bits > 65535-7) {
-               error("buffer_get_bignum_ret: cannot handle BN of size %d",
-                   bits);
-               return (-1);
-       }
-       /* Compute the number of binary bytes that follow. */
-       bytes = (bits + 7) / 8;
-       if (bytes > 8 * 1024) {
-               error("buffer_get_bignum_ret: cannot handle BN of size %d", bytes);
-               return (-1);
-       }
-       if (buffer_len(buffer) < bytes) {
-               error("buffer_get_bignum_ret: input buffer too small");
-               return (-1);
-       }
-       bin = buffer_ptr(buffer);
-       if (BN_bin2bn(bin, bytes, value) == NULL) {
-               error("buffer_get_bignum_ret: BN_bin2bn failed");
-               return (-1);
-       }
-       if (buffer_consume_ret(buffer, bytes) == -1) {
-               error("buffer_get_bignum_ret: buffer_consume failed");
-               return (-1);
-       }
-       return (0);
+       return 0;
 }
 
 void
 buffer_get_bignum(Buffer *buffer, BIGNUM *value)
 {
        if (buffer_get_bignum_ret(buffer, value) == -1)
-               fatal("buffer_get_bignum: buffer error");
+               fatal("%s: buffer error", __func__);
 }
 
-/*
- * Stores a BIGNUM in the buffer in SSH2 format.
- */
 int
 buffer_put_bignum2_ret(Buffer *buffer, const BIGNUM *value)
 {
-       u_int bytes;
-       u_char *buf;
-       int oi;
-       u_int hasnohigh = 0;
-
-       if (BN_is_zero(value)) {
-               buffer_put_int(buffer, 0);
-               return 0;
-       }
-       if (value->neg) {
-               error("buffer_put_bignum2_ret: negative numbers not supported");
-               return (-1);
-       }
-       bytes = BN_num_bytes(value) + 1; /* extra padding byte */
-       if (bytes < 2) {
-               error("buffer_put_bignum2_ret: BN too small");
-               return (-1);
-       }
-       buf = xmalloc(bytes);
-       buf[0] = 0x00;
-       /* Get the value of in binary */
-       oi = BN_bn2bin(value, buf+1);
-       if (oi < 0 || (u_int)oi != bytes - 1) {
-               error("buffer_put_bignum2_ret: BN_bn2bin() failed: "
-                   "oi %d != bin_size %d", oi, bytes);
-               free(buf);
-               return (-1);
+       int ret;
+
+       if ((ret = sshbuf_put_bignum2(buffer, value)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
        }
-       hasnohigh = (buf[1] & 0x80) ? 0 : 1;
-       buffer_put_string(buffer, buf+hasnohigh, bytes-hasnohigh);
-       explicit_bzero(buf, bytes);
-       free(buf);
-       return (0);
+       return 0;
 }
 
 void
 buffer_put_bignum2(Buffer *buffer, const BIGNUM *value)
 {
        if (buffer_put_bignum2_ret(buffer, value) == -1)
-               fatal("buffer_put_bignum2: buffer error");
+               fatal("%s: buffer error", __func__);
 }
 
 int
 buffer_get_bignum2_ret(Buffer *buffer, BIGNUM *value)
 {
-       u_int len;
-       u_char *bin;
+       int ret;
 
-       if ((bin = buffer_get_string_ret(buffer, &len)) == NULL) {
-               error("buffer_get_bignum2_ret: invalid bignum");
-               return (-1);
-       }
-
-       if (len > 0 && (bin[0] & 0x80)) {
-               error("buffer_get_bignum2_ret: negative numbers not supported");
-               free(bin);
-               return (-1);
-       }
-       if (len > 8 * 1024) {
-               error("buffer_get_bignum2_ret: cannot handle BN of size %d",
-                   len);
-               free(bin);
-               return (-1);
-       }
-       if (BN_bin2bn(bin, len, value) == NULL) {
-               error("buffer_get_bignum2_ret: BN_bin2bn failed");
-               free(bin);
-               return (-1);
+       if ((ret = sshbuf_get_bignum2(buffer, value)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
        }
-       free(bin);
-       return (0);
+       return 0;
 }
 
 void
 buffer_get_bignum2(Buffer *buffer, BIGNUM *value)
 {
        if (buffer_get_bignum2_ret(buffer, value) == -1)
-               fatal("buffer_get_bignum2: buffer error");
+               fatal("%s: buffer error", __func__);
 }
index f326b4e..b33ede3 100644 (file)
@@ -1,6 +1,7 @@
-/* $OpenBSD: bufec.c,v 1.3 2014/01/31 16:39:19 tedu Exp $ */
+/* $OpenBSD: bufec.c,v 1.4 2014/04/30 05:29:56 djm Exp $ */
+
 /*
- * Copyright (c) 2010 Damien Miller <djm@mindrot.org>
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#include <sys/types.h>
 
-#include <openssl/bn.h>
-#include <openssl/ec.h>
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
-#include <string.h>
-#include <stdarg.h>
+#include <sys/types.h>
 
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-#include "misc.h"
-
-/*
- * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed
- * encoding represents this as two bitstring points that should each
- * be no longer than the field length, SEC1 specifies a 1 byte
- * point type header.
- * Being paranoid here may insulate us to parsing problems in
- * EC_POINT_oct2point.
- */
-#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1)
+#include "ssherr.h"
 
-/*
- * Append an EC_POINT to the buffer as a string containing a SEC1 encoded
- * uncompressed point. Fortunately OpenSSL handles the gory details for us.
- */
 int
 buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
     const EC_POINT *point)
 {
-       u_char *buf = NULL;
-       size_t len;
-       BN_CTX *bnctx;
-       int ret = -1;
+       int ret;
 
-       /* Determine length */
-       if ((bnctx = BN_CTX_new()) == NULL)
-               fatal("%s: BN_CTX_new failed", __func__);
-       len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
-           NULL, 0, bnctx);
-       if (len > BUFFER_MAX_ECPOINT_LEN) {
-               error("%s: giant EC point: len = %lu (max %u)",
-                   __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN);
-               goto out;
-       }
-       /* Convert */
-       buf = xmalloc(len);
-       if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
-           buf, len, bnctx) != len) {
-               error("%s: EC_POINT_point2oct length mismatch", __func__);
-               goto out;
-       }
-       /* Append */
-       buffer_put_string(buffer, buf, len);
-       ret = 0;
- out:
-       if (buf != NULL) {
-               explicit_bzero(buf, len);
-               free(buf);
+       if ((ret = sshbuf_put_ec(buffer, point, curve)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
        }
-       BN_CTX_free(bnctx);
-       return ret;
+       return 0;
 }
 
 void
@@ -91,43 +49,13 @@ int
 buffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
     EC_POINT *point)
 {
-       u_char *buf;
-       u_int len;
-       BN_CTX *bnctx;
-       int ret = -1;
+       int ret;
 
-       if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) {
-               error("%s: invalid point", __func__);
+       if ((ret = sshbuf_get_ec(buffer, point, curve)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
                return -1;
        }
-       if ((bnctx = BN_CTX_new()) == NULL)
-               fatal("%s: BN_CTX_new failed", __func__);
-       if (len > BUFFER_MAX_ECPOINT_LEN) {
-               error("%s: EC_POINT too long: %u > max %u", __func__,
-                   len, BUFFER_MAX_ECPOINT_LEN);
-               goto out;
-       }
-       if (len == 0) {
-               error("%s: EC_POINT buffer is empty", __func__);
-               goto out;
-       }
-       if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) {
-               error("%s: EC_POINT is in an incorrect form: "
-                   "0x%02x (want 0x%02x)", __func__, buf[0],
-                   POINT_CONVERSION_UNCOMPRESSED);
-               goto out;
-       }
-       if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) {
-               error("buffer_get_bignum2_ret: BN_bin2bn failed");
-               goto out;
-       }
-       /* EC_POINT_oct2point verifies that the point is on the curve for us */
-       ret = 0;
- out:
-       BN_CTX_free(bnctx);
-       explicit_bzero(buf, len);
-       free(buf);
-       return ret;
+       return 0;
 }
 
 void
@@ -138,3 +66,4 @@ buffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve,
                fatal("%s: buffer error", __func__);
 }
 
+
index eb765f1..07bc186 100644 (file)
-/* $OpenBSD: buffer.c,v 1.35 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: buffer.c,v 1.36 2014/04/30 05:29:56 djm Exp $ */
+
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Functions for manipulating fifo buffers (that can grow if needed).
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * 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".
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/param.h>
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
+#include <sys/types.h>
 
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-
-#define        BUFFER_MAX_CHUNK        0x100000
-#define        BUFFER_MAX_LEN          0xa00000
-#define        BUFFER_ALLOCSZ          0x008000
-
-/* Initializes the buffer structure. */
-
-void
-buffer_init(Buffer *buffer)
-{
-       const u_int len = 4096;
-
-       buffer->alloc = 0;
-       buffer->buf = xmalloc(len);
-       buffer->alloc = len;
-       buffer->offset = 0;
-       buffer->end = 0;
-}
-
-/* Frees any memory used for the buffer. */
-
-void
-buffer_free(Buffer *buffer)
-{
-       if (buffer->alloc > 0) {
-               explicit_bzero(buffer->buf, buffer->alloc);
-               buffer->alloc = 0;
-               free(buffer->buf);
-       }
-}
-
-/*
- * Clears any data from the buffer, making it empty.  This does not actually
- * zero the memory.
- */
-
-void
-buffer_clear(Buffer *buffer)
-{
-       buffer->offset = 0;
-       buffer->end = 0;
-}
-
-/* Appends data to the buffer, expanding it if necessary. */
+#include "ssherr.h"
 
 void
 buffer_append(Buffer *buffer, const void *data, u_int len)
 {
-       void *p;
-       p = buffer_append_space(buffer, len);
-       memcpy(p, data, len);
-}
+       int ret;
 
-static int
-buffer_compact(Buffer *buffer)
-{
-       /*
-        * If the buffer is quite empty, but all data is at the end, move the
-        * data to the beginning.
-        */
-       if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) {
-               memmove(buffer->buf, buffer->buf + buffer->offset,
-                       buffer->end - buffer->offset);
-               buffer->end -= buffer->offset;
-               buffer->offset = 0;
-               return (1);
-       }
-       return (0);
+       if ((ret = sshbuf_put(buffer, data, len)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/*
- * Appends space to the buffer, expanding the buffer if necessary. This does
- * not actually copy the data into the buffer, but instead returns a pointer
- * to the allocated region.
- */
-
 void *
 buffer_append_space(Buffer *buffer, u_int len)
 {
-       u_int newlen;
-       void *p;
-
-       if (len > BUFFER_MAX_CHUNK)
-               fatal("buffer_append_space: len %u not supported", len);
+       int ret;
+       u_char *p;
 
-       /* If the buffer is empty, start using it from the beginning. */
-       if (buffer->offset == buffer->end) {
-               buffer->offset = 0;
-               buffer->end = 0;
-       }
-restart:
-       /* If there is enough space to store all data, store it now. */
-       if (buffer->end + len < buffer->alloc) {
-               p = buffer->buf + buffer->end;
-               buffer->end += len;
-               return p;
-       }
-
-       /* Compact data back to the start of the buffer if necessary */
-       if (buffer_compact(buffer))
-               goto restart;
-
-       /* Increase the size of the buffer and retry. */
-       newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ);
-       if (newlen > BUFFER_MAX_LEN)
-               fatal("buffer_append_space: alloc %u not supported",
-                   newlen);
-       buffer->buf = xrealloc(buffer->buf, 1, newlen);
-       buffer->alloc = newlen;
-       goto restart;
-       /* NOTREACHED */
+       if ((ret = sshbuf_reserve(buffer, len, &p)) != 0)
+               fatal("%s: %s", __func__, ssh_err(ret));
+       return p;
 }
 
-/*
- * Check whether an allocation of 'len' will fit in the buffer
- * This must follow the same math as buffer_append_space
- */
 int
 buffer_check_alloc(Buffer *buffer, u_int len)
 {
-       if (buffer->offset == buffer->end) {
-               buffer->offset = 0;
-               buffer->end = 0;
-       }
- restart:
-       if (buffer->end + len < buffer->alloc)
-               return (1);
-       if (buffer_compact(buffer))
-               goto restart;
-       if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN)
-               return (1);
-       return (0);
-}
+       int ret = sshbuf_check_reserve(buffer, len);
 
-/* Returns the number of bytes of data in the buffer. */
-
-u_int
-buffer_len(const Buffer *buffer)
-{
-       return buffer->end - buffer->offset;
+       if (ret == 0)
+               return 1;
+       if (ret == SSH_ERR_NO_BUFFER_SPACE)
+               return 0;
+       fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/* Gets data from the beginning of the buffer. */
-
 int
 buffer_get_ret(Buffer *buffer, void *buf, u_int len)
 {
-       if (len > buffer->end - buffer->offset) {
-               error("buffer_get_ret: trying to get more bytes %d than in buffer %d",
-                   len, buffer->end - buffer->offset);
-               return (-1);
+       int ret;
+
+       if ((ret = sshbuf_get(buffer, buf, len)) != 0) {
+               error("%s: %s", __func__, ssh_err(ret));
+               return -1;
        }
-       memcpy(buf, buffer->buf + buffer->offset, len);
-       buffer->offset += len;
-       return (0);
+       return 0;
 }
 
 void
 buffer_get(Buffer *buffer, void *buf, u_int len)
 {
        if (buffer_get_ret(buffer, buf, len) == -1)
-               fatal("buffer_get: buffer error");
+               fatal("%s: buffer error", __func__);
 }
 
-/* Consumes the given number of bytes from the beginning of the buffer. */
-
 int
 buffer_consume_ret(Buffer *buffer, u_int bytes)
 {
-       if (bytes > buffer->end - buffer->offset) {
-               error("buffer_consume_ret: trying to get more bytes than in buffer");
-               return (-1);
-       }
-       buffer->offset += bytes;
-       return (0);
+       int ret = sshbuf_consume(buffer, bytes);
+
+       if (ret == 0)
+               return 0;
+       if (ret == SSH_ERR_MESSAGE_INCOMPLETE)
+               return -1;
+       fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_consume(Buffer *buffer, u_int bytes)
 {
        if (buffer_consume_ret(buffer, bytes) == -1)
-               fatal("buffer_consume: buffer error");
+               fatal("%s: buffer error", __func__);
 }
 
-/* Consumes the given number of bytes from the end of the buffer. */
-
 int
 buffer_consume_end_ret(Buffer *buffer, u_int bytes)
 {
-       if (bytes > buffer->end - buffer->offset)
-               return (-1);
-       buffer->end -= bytes;
-       return (0);
+       int ret = sshbuf_consume_end(buffer, bytes);
+
+       if (ret == 0)
+               return 0;
+       if (ret == SSH_ERR_MESSAGE_INCOMPLETE)
+               return -1;
+       fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_consume_end(Buffer *buffer, u_int bytes)
 {
        if (buffer_consume_end_ret(buffer, bytes) == -1)
-               fatal("buffer_consume_end: trying to get more bytes than in buffer");
+               fatal("%s: buffer error", __func__);
 }
 
-/* Returns a pointer to the first used byte in the buffer. */
 
-void *
-buffer_ptr(const Buffer *buffer)
-{
-       return buffer->buf + buffer->offset;
-}
-
-/* Dumps the contents of the buffer to stderr. */
-
-void
-buffer_dump(const Buffer *buffer)
-{
-       u_int i;
-       u_char *ucp = buffer->buf;
-
-       for (i = buffer->offset; i < buffer->end; i++) {
-               fprintf(stderr, "%02x", ucp[i]);
-               if ((i-buffer->offset)%16==15)
-                       fprintf(stderr, "\r\n");
-               else if ((i-buffer->offset)%2==1)
-                       fprintf(stderr, " ");
-       }
-       fprintf(stderr, "\r\n");
-}
index aca360a..072e23e 100644 (file)
@@ -1,57 +1,57 @@
-/* $OpenBSD: buffer.h,v 1.24 2014/04/28 03:09:18 djm Exp $ */
+/* $OpenBSD: buffer.h,v 1.25 2014/04/30 05:29:56 djm Exp $ */
 
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Code for manipulating FIFO buffers.
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * 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".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
+
 #ifndef BUFFER_H
 #define BUFFER_H
 
-typedef struct {
-       u_char  *buf;           /* Buffer for data. */
-       u_int    alloc;         /* Number of bytes allocated for data. */
-       u_int    offset;        /* Offset of first byte containing data. */
-       u_int    end;           /* Offset of last byte containing data. */
-}       Buffer;
+#include "sshbuf.h"
+
+typedef struct sshbuf Buffer;
 
-void    buffer_init(Buffer *);
-void    buffer_clear(Buffer *);
-void    buffer_free(Buffer *);
+#define buffer_init(b)         sshbuf_init(b)
+#define buffer_clear(b)                sshbuf_reset(b)
+#define buffer_free(b)         sshbuf_free(b)
+#define buffer_dump(b)         sshbuf_dump(b, stderr)
 
-u_int   buffer_len(const Buffer *);
-void   *buffer_ptr(const Buffer *);
+/* XXX cast is safe: sshbuf never stores more than len 2^31 */
+#define buffer_len(b)          ((u_int) sshbuf_len(b))
+#define        buffer_ptr(b)           sshbuf_mutable_ptr(b)
 
 void    buffer_append(Buffer *, const void *, u_int);
 void   *buffer_append_space(Buffer *, u_int);
-
 int     buffer_check_alloc(Buffer *, u_int);
-
 void    buffer_get(Buffer *, void *, u_int);
 
 void    buffer_consume(Buffer *, u_int);
 void    buffer_consume_end(Buffer *, u_int);
 
-void     buffer_dump(const Buffer *);
 
 int     buffer_get_ret(Buffer *, void *, u_int);
 int     buffer_consume_ret(Buffer *, u_int);
 int     buffer_consume_end_ret(Buffer *, u_int);
 
-#include <openssl/bn.h>
-
 void    buffer_put_bignum(Buffer *, const BIGNUM *);
 void    buffer_put_bignum2(Buffer *, const BIGNUM *);
 void   buffer_get_bignum(Buffer *, BIGNUM *);
 void   buffer_get_bignum2(Buffer *, BIGNUM *);
+void   buffer_put_bignum2_from_string(Buffer *, const u_char *, u_int);
 
 u_short        buffer_get_short(Buffer *);
 void   buffer_put_short(Buffer *, u_short);
@@ -71,8 +71,7 @@ void    buffer_put_string(Buffer *, const void *, u_int);
 char   *buffer_get_cstring(Buffer *, u_int *);
 void   buffer_put_cstring(Buffer *, const char *);
 
-#define buffer_skip_string(b) \
-    do { u_int l = buffer_get_int(b); buffer_consume(b, l); } while (0)
+#define buffer_skip_string(b) (void)buffer_get_string_ptr(b, NULL);
 
 int    buffer_put_bignum_ret(Buffer *, const BIGNUM *);
 int    buffer_get_bignum_ret(Buffer *, BIGNUM *);
@@ -84,17 +83,12 @@ int buffer_get_int64_ret(u_int64_t *, Buffer *);
 void   *buffer_get_string_ret(Buffer *, u_int *);
 char   *buffer_get_cstring_ret(Buffer *, u_int *);
 const void *buffer_get_string_ptr_ret(Buffer *, u_int *);
-int    buffer_get_char_ret(u_char *, Buffer *);
-
-void *buffer_get_bignum2_as_string_ret(Buffer *, u_int *);
-void *buffer_get_bignum2_as_string(Buffer *, u_int *);
-void  buffer_put_bignum2_from_string(Buffer *, const u_char *, u_int);
-
-#include <openssl/ec.h>
+int    buffer_get_char_ret(char *, Buffer *);
 
 int    buffer_put_ecpoint_ret(Buffer *, const EC_GROUP *, const EC_POINT *);
 void   buffer_put_ecpoint(Buffer *, const EC_GROUP *, const EC_POINT *);
 int    buffer_get_ecpoint_ret(Buffer *, const EC_GROUP *, EC_POINT *);
 void   buffer_get_ecpoint(Buffer *, const EC_GROUP *, EC_POINT *);
 
-#endif                         /* BUFFER_H */
+#endif /* BUFFER_H */
+
index 506a652..8df065e 100644 (file)
@@ -1,10 +1,23 @@
-#      $OpenBSD: Makefile,v 1.76 2014/04/29 18:01:49 markus Exp $
+#      $OpenBSD: Makefile,v 1.77 2014/04/30 05:29:56 djm Exp $
 
 .PATH:         ${.CURDIR}/..
 .include "${.CURDIR}/../Makefile.inc"
 
 LIB=   ssh
-SRCS=  authfd.c authfile.c bufaux.c buffer.c canohost.c \
+
+# These are files intended for a standalone libopenssh in the future.
+LIB_SRCS= \
+       ssherr.c \
+       sshbuf.c \
+       sshbuf-getput-basic.c \
+       sshbuf-misc.c
+
+.if (${OPENSSL:L} == "yes")
+LIB_SRCS+=     sshbuf-getput-crypto.c
+.endif
+
+SRCS=  ${LIB_SRCS} \
+       authfd.c authfile.c bufaux.c buffer.c canohost.c \
        channels.c cipher.c \
        cleanup.c compat.c compress.c crc32.c deattack.c fatal.c \
        hostfile.c log.c match.c nchan.c packet.c readpass.c \
diff --git a/usr.bin/ssh/sshbuf-getput-basic.c b/usr.bin/ssh/sshbuf-getput-basic.c
new file mode 100644 (file)
index 0000000..50a1bbb
--- /dev/null
@@ -0,0 +1,419 @@
+/*     $OpenBSD: sshbuf-getput-basic.c,v 1.1 2014/04/30 05:29:56 djm Exp $     */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+int
+sshbuf_get(struct sshbuf *buf, void *v, size_t len)
+{
+       const u_char *p = sshbuf_ptr(buf);
+       int r;
+
+       if ((r = sshbuf_consume(buf, len)) < 0)
+               return r;
+       if (v != NULL)
+               memcpy(v, p, len);
+       return 0;
+}
+
+int
+sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp)
+{
+       const u_char *p = sshbuf_ptr(buf);
+       int r;
+
+       if ((r = sshbuf_consume(buf, 8)) < 0)
+               return r;
+       if (valp != NULL)
+               *valp = PEEK_U64(p);
+       return 0;
+}
+
+int
+sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp)
+{
+       const u_char *p = sshbuf_ptr(buf);
+       int r;
+
+       if ((r = sshbuf_consume(buf, 4)) < 0)
+               return r;
+       if (valp != NULL)
+               *valp = PEEK_U32(p);
+       return 0;
+}
+
+int
+sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp)
+{
+       const u_char *p = sshbuf_ptr(buf);
+       int r;
+
+       if ((r = sshbuf_consume(buf, 2)) < 0)
+               return r;
+       if (valp != NULL)
+               *valp = PEEK_U16(p);
+       return 0;
+}
+
+int
+sshbuf_get_u8(struct sshbuf *buf, u_char *valp)
+{
+       const u_char *p = sshbuf_ptr(buf);
+       int r;
+
+       if ((r = sshbuf_consume(buf, 1)) < 0)
+               return r;
+       if (valp != NULL)
+               *valp = (u_int8_t)*p;
+       return 0;
+}
+
+int
+sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp)
+{
+       const u_char *val;
+       size_t len;
+       int r;
+
+       if (valp != NULL)
+               *valp = NULL;
+       if (lenp != NULL)
+               *lenp = 0;
+       if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0)
+               return r;
+       if (valp != NULL) {
+               if ((*valp = malloc(len + 1)) == NULL) {
+                       SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
+                       return SSH_ERR_ALLOC_FAIL;
+               }
+               memcpy(*valp, val, len);
+               (*valp)[len] = '\0';
+       }
+       if (lenp != NULL)
+               *lenp = len;
+       return 0;
+}
+
+int
+sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp)
+{
+       size_t len;
+       const u_char *p;
+       int r;
+
+       if (valp != NULL)
+               *valp = NULL;
+       if (lenp != NULL)
+               *lenp = 0;
+       if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0)
+               return r;
+       if (valp != 0)
+               *valp = p;
+       if (lenp != NULL)
+               *lenp = len;
+       if (sshbuf_consume(buf, len + 4) != 0) {
+               /* Shouldn't happen */
+               SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+               SSHBUF_ABORT();
+               return SSH_ERR_INTERNAL_ERROR;
+       }
+       return 0;
+}
+
+int
+sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp,
+    size_t *lenp)
+{
+       u_int32_t len;
+       const u_char *p = sshbuf_ptr(buf);
+
+       if (valp != NULL)
+               *valp = NULL;
+       if (lenp != NULL)
+               *lenp = 0;
+       if (sshbuf_len(buf) < 4) {
+               SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
+               return SSH_ERR_MESSAGE_INCOMPLETE;
+       }
+       len = PEEK_U32(p);
+       if (len > SSHBUF_SIZE_MAX - 4) {
+               SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE"));
+               return SSH_ERR_STRING_TOO_LARGE;
+       }
+       if (sshbuf_len(buf) - 4 < len) {
+               SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
+               return SSH_ERR_MESSAGE_INCOMPLETE;
+       }
+       if (valp != 0)
+               *valp = p + 4;
+       if (lenp != NULL)
+               *lenp = len;
+       return 0;
+}
+
+int
+sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp)
+{
+       size_t len;
+       const u_char *p, *z;
+       int r;
+
+       if (valp != NULL)
+               *valp = NULL;
+       if (lenp != NULL)
+               *lenp = 0;
+       if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
+               return r;
+       /* Allow a \0 only at the end of the string */
+       if (len > 0 &&
+           (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) {
+               SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT"));
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       if ((r = sshbuf_skip_string(buf)) != 0)
+               return -1;
+       if (valp != NULL) {
+               if ((*valp = malloc(len + 1)) == NULL) {
+                       SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
+                       return SSH_ERR_ALLOC_FAIL;
+               }
+               memcpy(*valp, p, len);
+               (*valp)[len] = '\0';
+       }
+       if (lenp != NULL)
+               *lenp = (size_t)len;
+       return 0;
+}
+
+int
+sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v)
+{
+       u_int32_t len;
+       u_char *p;
+       int r;
+
+       /*
+        * Use sshbuf_peek_string_direct() to figure out if there is
+        * a complete string in 'buf' and copy the string directly
+        * into 'v'.
+        */
+       if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 ||
+           (r = sshbuf_get_u32(buf, &len)) != 0 ||
+           (r = sshbuf_reserve(v, len, &p)) != 0 ||
+           (r = sshbuf_get(buf, p, len)) != 0)
+               return r;
+       return 0;
+}
+
+int
+sshbuf_put(struct sshbuf *buf, const void *v, size_t len)
+{
+       u_char *p;
+       int r;
+
+       if ((r = sshbuf_reserve(buf, len, &p)) < 0)
+               return r;
+       memcpy(p, v, len);
+       return 0;
+}
+
+int
+sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v)
+{
+       return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v));
+}
+
+int
+sshbuf_putf(struct sshbuf *buf, const char *fmt, ...)
+{
+       va_list ap;
+       int r;
+
+       va_start(ap, fmt);
+       r = sshbuf_putfv(buf, fmt, ap);
+       va_end(ap);
+       return r;
+}
+
+int
+sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap)
+{
+       va_list ap2;
+       int r, len;
+       u_char *p;
+
+       va_copy(ap2, ap);
+       if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) {
+               r = SSH_ERR_INVALID_ARGUMENT;
+               goto out;
+       }
+       if (len == 0) {
+               r = 0;
+               goto out; /* Nothing to do */
+       }
+       va_end(ap2);
+       va_copy(ap2, ap);
+       if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0)
+               goto out;
+       if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) {
+               r = SSH_ERR_INTERNAL_ERROR;
+               goto out; /* Shouldn't happen */
+       }
+       /* Consume terminating \0 */
+       if ((r = sshbuf_consume_end(buf, 1)) != 0)
+               goto out;
+       r = 0;
+ out:
+       va_end(ap2);
+       return r;
+}
+
+int
+sshbuf_put_u64(struct sshbuf *buf, u_int64_t val)
+{
+       u_char *p;
+       int r;
+
+       if ((r = sshbuf_reserve(buf, 8, &p)) < 0)
+               return r;
+       POKE_U64(p, val);
+       return 0;
+}
+
+int
+sshbuf_put_u32(struct sshbuf *buf, u_int32_t val)
+{
+       u_char *p;
+       int r;
+
+       if ((r = sshbuf_reserve(buf, 4, &p)) < 0)
+               return r;
+       POKE_U32(p, val);
+       return 0;
+}
+
+int
+sshbuf_put_u16(struct sshbuf *buf, u_int16_t val)
+{
+       u_char *p;
+       int r;
+
+       if ((r = sshbuf_reserve(buf, 2, &p)) < 0)
+               return r;
+       POKE_U16(p, val);
+       return 0;
+}
+
+int
+sshbuf_put_u8(struct sshbuf *buf, u_char val)
+{
+       u_char *p;
+       int r;
+
+       if ((r = sshbuf_reserve(buf, 1, &p)) < 0)
+               return r;
+       p[0] = val;
+       return 0;
+}
+
+int
+sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len)
+{
+       u_char *d;
+       int r;
+
+       if (len > SSHBUF_SIZE_MAX - 4) {
+               SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
+               return SSH_ERR_NO_BUFFER_SPACE;
+       }
+       if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0)
+               return r;
+       POKE_U32(d, len);
+       memcpy(d + 4, v, len);
+       return 0;
+}
+
+int
+sshbuf_put_cstring(struct sshbuf *buf, const char *v)
+{
+       return sshbuf_put_string(buf, (u_char *)v, strlen(v));
+}
+
+int
+sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v)
+{
+       return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v));
+}
+
+int
+sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp)
+{
+       const u_char *p;
+       size_t len;
+       struct sshbuf *ret;
+       int r;
+
+       if (buf == NULL || bufp == NULL)
+               return SSH_ERR_INVALID_ARGUMENT;
+       *bufp = NULL;
+       if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
+               return r;
+       if ((ret = sshbuf_from(p, len)) == NULL)
+               return SSH_ERR_ALLOC_FAIL;
+       if ((r = sshbuf_consume(buf, len + 4)) != 0 ||  /* Shouldn't happen */
+           (r = sshbuf_set_parent(ret, buf)) != 0) {
+               sshbuf_free(ret);
+               return r;
+       }
+       *bufp = ret;
+       return 0;
+}
+
+int
+sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len)
+{
+       u_char *d;
+       const u_char *s = (const u_char *)v;
+       int r, prepend;
+
+       if (len > SSHBUF_SIZE_MAX - 5) {
+               SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
+               return SSH_ERR_NO_BUFFER_SPACE;
+       }
+       /* Skip leading zero bytes */
+       for (; len > 0 && *s == 0; len--, s++)
+               ;
+       /*
+        * If most significant bit is set then prepend a zero byte to
+        * avoid interpretation as a negative number.
+        */
+       prepend = len > 0 && (s[0] & 0x80) != 0;
+       if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0)
+               return r;
+       POKE_U32(d, len + prepend);
+       if (prepend)
+               d[4] = 0;
+       memcpy(d + 4 + prepend, s, len);
+       return 0;
+}
diff --git a/usr.bin/ssh/sshbuf-getput-crypto.c b/usr.bin/ssh/sshbuf-getput-crypto.c
new file mode 100644 (file)
index 0000000..6cfb25a
--- /dev/null
@@ -0,0 +1,227 @@
+/*     $OpenBSD: sshbuf-getput-crypto.c,v 1.1 2014/04/30 05:29:56 djm Exp $    */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+int
+sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v)
+{
+       const u_char *d;
+       size_t len;
+       int r;
+
+       if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0)
+               return r;
+       /* Refuse negative (MSB set) and overlong bignums */
+       if ((len != 0 && (*d & 0x80) != 0))
+               return SSH_ERR_BIGNUM_IS_NEGATIVE;
+       if (len > SSHBUF_MAX_BIGNUM)
+               return SSH_ERR_BIGNUM_TOO_LARGE;
+       if (v != NULL && BN_bin2bn(d, len, v) == NULL)
+               return SSH_ERR_ALLOC_FAIL;
+       /* Consume the string */
+       if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) {
+               /* Shouldn't happen */
+               SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+               SSHBUF_ABORT();
+               return SSH_ERR_INTERNAL_ERROR;
+       }
+       return 0;
+}
+
+int
+sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v)
+{
+       const u_char *d = sshbuf_ptr(buf);
+       u_int16_t len_bits;
+       size_t len_bytes;
+
+       /* Length in bits */
+       if (sshbuf_len(buf) < 2)
+               return SSH_ERR_MESSAGE_INCOMPLETE;
+       len_bits = PEEK_U16(d);
+       len_bytes = (len_bits + 7) >> 3;
+       if (len_bytes > SSHBUF_MAX_BIGNUM + 1)
+               return SSH_ERR_BIGNUM_TOO_LARGE;
+       if (sshbuf_len(buf) < 2 + len_bytes)
+               return SSH_ERR_MESSAGE_INCOMPLETE;
+       if (v != NULL && BN_bin2bn(d + 2, len_bytes, v) == NULL)
+               return SSH_ERR_ALLOC_FAIL;
+       if (sshbuf_consume(buf, 2 + len_bytes) != 0) {
+               SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+               SSHBUF_ABORT();
+               return SSH_ERR_INTERNAL_ERROR;
+       }
+       return 0;
+}
+
+static int
+get_ec(const u_char *d, size_t len, EC_POINT *v, const EC_GROUP *g)
+{
+       /* Refuse overlong bignums */
+       if (len == 0 || len > SSHBUF_MAX_ECPOINT)
+               return SSH_ERR_ECPOINT_TOO_LARGE;
+       /* Only handle uncompressed points */
+       if (*d != POINT_CONVERSION_UNCOMPRESSED)
+               return SSH_ERR_INVALID_FORMAT;
+       if (v != NULL && EC_POINT_oct2point(g, v, d, len, NULL) != 1)
+               return SSH_ERR_INVALID_FORMAT; /* XXX assumption */
+       return 0;
+}
+
+int
+sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g)
+{
+       const u_char *d;
+       size_t len;
+       int r;
+
+       if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0)
+               return r;
+       if ((r = get_ec(d, len, v, g)) != 0)
+               return r;
+       /* Skip string */
+       if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) {
+               /* Shouldn't happen */
+               SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+               SSHBUF_ABORT();
+               return SSH_ERR_INTERNAL_ERROR;
+       }
+       return 0;
+}
+
+int
+sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v)
+{
+       EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v));
+       int r;
+       const u_char *d;
+       size_t len;
+
+       if (pt == NULL) {
+               SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
+               return SSH_ERR_ALLOC_FAIL;
+       }
+       if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) {
+               EC_POINT_free(pt);
+               return r;
+       }
+       if ((r = get_ec(d, len, pt, EC_KEY_get0_group(v))) != 0) {
+               EC_POINT_free(pt);
+               return r;
+       }
+       if (EC_KEY_set_public_key(v, pt) != 1) {
+               EC_POINT_free(pt);
+               return SSH_ERR_ALLOC_FAIL; /* XXX assumption */
+       }
+       EC_POINT_free(pt);
+       /* Skip string */
+       if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) {
+               /* Shouldn't happen */
+               SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+               SSHBUF_ABORT();
+               return SSH_ERR_INTERNAL_ERROR;
+       }
+       return 0;       
+}
+
+int
+sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v)
+{
+       u_char d[SSHBUF_MAX_BIGNUM + 1];
+       int len = BN_num_bytes(v), prepend = 0, r;
+
+       if (len < 0 || len > SSHBUF_MAX_BIGNUM)
+               return SSH_ERR_INVALID_ARGUMENT;
+       *d = '\0';
+       if (BN_bn2bin(v, d + 1) != len)
+               return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */
+       /* If MSB is set, prepend a \0 */
+       if (len > 0 && (d[1] & 0x80) != 0)
+               prepend = 1;
+       if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) {
+               bzero(d, sizeof(d));
+               return r;
+       }
+       bzero(d, sizeof(d));
+       return 0;
+}
+
+int
+sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v)
+{
+       int r, len_bits = BN_num_bits(v);
+       size_t len_bytes = (len_bits + 7) / 8;
+       u_char d[SSHBUF_MAX_BIGNUM], *dp;
+
+       if (len_bits < 0 || len_bytes > SSHBUF_MAX_BIGNUM)
+               return SSH_ERR_INVALID_ARGUMENT;
+       if (BN_bn2bin(v, d) != (int)len_bytes)
+               return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */
+       if ((r = sshbuf_reserve(buf, len_bytes + 2, &dp)) < 0) {
+               bzero(d, sizeof(d));
+               return r;
+       }
+       POKE_U16(dp, len_bits);
+       memcpy(dp + 2, d, len_bytes);
+       bzero(d, sizeof(d));
+       return 0;
+}
+
+int
+sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g)
+{
+       u_char d[SSHBUF_MAX_ECPOINT];
+       BN_CTX *bn_ctx;
+       size_t len;
+       int ret;
+
+       if ((bn_ctx = BN_CTX_new()) == NULL)
+               return SSH_ERR_ALLOC_FAIL;
+       if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED,
+           NULL, 0, bn_ctx)) > SSHBUF_MAX_ECPOINT) {
+               BN_CTX_free(bn_ctx);
+               return SSH_ERR_INVALID_ARGUMENT;
+       }
+       if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED,
+           d, len, bn_ctx) != len) {
+               BN_CTX_free(bn_ctx);
+               return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */
+       }
+       BN_CTX_free(bn_ctx);
+       ret = sshbuf_put_string(buf, d, len);
+       bzero(d, len);
+       return ret;
+}
+
+int
+sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v)
+{
+       return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v),
+           EC_KEY_get0_group(v));
+}
+
diff --git a/usr.bin/ssh/sshbuf-misc.c b/usr.bin/ssh/sshbuf-misc.c
new file mode 100644 (file)
index 0000000..6ee3253
--- /dev/null
@@ -0,0 +1,127 @@
+/*     $OpenBSD: sshbuf-misc.c,v 1.1 2014/04/30 05:29:56 djm Exp $     */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <resolv.h>
+#include <ctype.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+void
+sshbuf_dump(struct sshbuf *buf, FILE *f)
+{
+       const u_char *p = sshbuf_ptr(buf);
+       size_t i, j, len = sshbuf_len(buf);
+
+       fprintf(f, "buffer %p len = %zu\n", buf, len);
+       for (i = 0; i < len; i += 16) {
+               fprintf(f, "%.4zd: ", i);
+               for (j = i; j < i + 16; j++) {
+                       if (j < len)
+                               fprintf(f, "%02x ", p[j]);
+                       else
+                               fprintf(f, "   ");
+               }
+               fprintf(f, " ");
+               for (j = i; j < i + 16; j++) {
+                       if (j < len) {
+                               if  (isascii(p[j]) && isprint(p[j]))
+                                       fprintf(f, "%c", p[j]);
+                               else
+                                       fprintf(f, ".");
+                       }
+               }
+               fprintf(f, "\n");
+       }
+}
+
+char *
+sshbuf_dtob16(struct sshbuf *buf)
+{
+       size_t i, j, len = sshbuf_len(buf);
+       const u_char *p = sshbuf_ptr(buf);
+       char *ret;
+       const char hex[] = "0123456789abcdef";
+
+       if (len == 0)
+               return strdup("");
+       if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL)
+               return NULL;
+       for (i = j = 0; i < len; i++) {
+               ret[j++] = hex[(p[i] >> 4) & 0xf];
+               ret[j++] = hex[p[i] & 0xf];
+       }
+       ret[j] = '\0';
+       return ret;
+}
+
+char *
+sshbuf_dtob64(struct sshbuf *buf)
+{
+       size_t len = sshbuf_len(buf), plen;
+       const u_char *p = sshbuf_ptr(buf);
+       char *ret;
+       int r;
+
+       if (len == 0)
+               return strdup("");
+       plen = ((len + 2) / 3) * 4 + 1;
+       if (SIZE_MAX / 2 <= len || (ret = malloc(plen)) == NULL)
+               return NULL;
+       if ((r = b64_ntop(p, len, ret, plen)) == -1) {
+               bzero(ret, plen);
+               free(ret);
+               return NULL;
+       }
+       return ret;
+}
+
+int
+sshbuf_b64tod(struct sshbuf *buf, const char *b64)
+{
+       size_t plen = strlen(b64);
+       int nlen, r;
+       u_char *p;
+
+       if (plen == 0)
+               return 0;
+       if ((p = malloc(plen)) == NULL)
+               return SSH_ERR_ALLOC_FAIL;
+       if ((nlen = b64_pton(b64, p, plen)) < 0) {
+               bzero(p, plen);
+               free(p);
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       if ((r = sshbuf_put(buf, p, nlen)) < 0) {
+               bzero(p, plen);
+               free(p);
+               return r;
+       }
+       bzero(p, plen);
+       free(p);
+       return 0;
+}
+
diff --git a/usr.bin/ssh/sshbuf.c b/usr.bin/ssh/sshbuf.c
new file mode 100644 (file)
index 0000000..bb1583b
--- /dev/null
@@ -0,0 +1,403 @@
+/*     $OpenBSD: sshbuf.c,v 1.1 2014/04/30 05:29:56 djm Exp $  */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+static inline int
+sshbuf_check_sanity(const struct sshbuf *buf)
+{
+       SSHBUF_TELL("sanity");
+       if (__predict_false(buf == NULL ||
+           (!buf->readonly && buf->d != buf->cd) ||
+           buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
+           buf->cd == NULL ||
+           (buf->dont_free && (buf->readonly || buf->parent != NULL)) ||
+           buf->max_size > SSHBUF_SIZE_MAX ||
+           buf->alloc > buf->max_size ||
+           buf->size > buf->alloc ||
+           buf->off > buf->size)) {
+               /* Do not try to recover from corrupted buffer internals */
+               SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+               raise(SIGSEGV);
+               return SSH_ERR_INTERNAL_ERROR;
+       }
+       return 0;
+}
+
+static void
+sshbuf_maybe_pack(struct sshbuf *buf, int force)
+{
+       SSHBUF_DBG(("force %d", force));
+       SSHBUF_TELL("pre-pack");
+       if (buf->off == 0 || buf->readonly || buf->refcount > 1)
+               return;
+       if (force ||
+           (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
+               memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
+               buf->size -= buf->off;
+               buf->off = 0;
+               SSHBUF_TELL("packed");
+       }
+}
+
+struct sshbuf *
+sshbuf_new(void)
+{
+       struct sshbuf *ret;
+
+       if ((ret = calloc(sizeof(*ret), 1)) == NULL)
+               return NULL;
+       ret->alloc = SSHBUF_SIZE_INIT;
+       ret->max_size = SSHBUF_SIZE_MAX;
+       ret->readonly = 0;
+       ret->refcount = 1;
+       ret->parent = NULL;
+       if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
+               free(ret);
+               return NULL;
+       }
+       return ret;
+}
+
+struct sshbuf *
+sshbuf_from(const void *blob, size_t len)
+{
+       struct sshbuf *ret;
+
+       if (blob == NULL || len > SSHBUF_SIZE_MAX ||
+           (ret = calloc(sizeof(*ret), 1)) == NULL)
+               return NULL;
+       ret->alloc = ret->size = ret->max_size = len;
+       ret->readonly = 1;
+       ret->refcount = 1;
+       ret->parent = NULL;
+       ret->cd = blob;
+       ret->d = NULL;
+       return ret;
+}
+
+int
+sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
+{
+       int r;
+
+       if ((r = sshbuf_check_sanity(child)) != 0 ||
+           (r = sshbuf_check_sanity(parent)) != 0)
+               return r;
+       child->parent = parent;
+       child->parent->refcount++;
+       return 0;
+}
+
+struct sshbuf *
+sshbuf_fromb(struct sshbuf *buf)
+{
+       struct sshbuf *ret;
+
+       if (sshbuf_check_sanity(buf) != 0)
+               return NULL;
+       if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
+               return NULL;
+       if (sshbuf_set_parent(ret, buf) != 0) {
+               sshbuf_free(ret);
+               return NULL;
+       }
+       return ret;
+}
+
+void
+sshbuf_init(struct sshbuf *ret)
+{
+       bzero(ret, sizeof(*ret));
+       ret->alloc = SSHBUF_SIZE_INIT;
+       ret->max_size = SSHBUF_SIZE_MAX;
+       ret->readonly = 0;
+       ret->dont_free = 1;
+       ret->refcount = 1;
+       if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL)
+               ret->alloc = 0;
+}
+
+void
+sshbuf_free(struct sshbuf *buf)
+{
+       int dont_free = 0;
+
+       if (buf == NULL)
+               return;
+       /*
+        * The following will leak on insane buffers, but this is the safest
+        * course of action - an invalid pointer or already-freed pointer may
+        * have been passed to us and continuing to scribble over memory would
+        * be bad.
+        */
+       if (sshbuf_check_sanity(buf) != 0)
+               return;
+       /*
+        * If we are a child, the free our parent to decrement its reference
+        * count and possibly free it.
+        */
+       if (buf->parent != NULL) {
+               sshbuf_free(buf->parent);
+               buf->parent = NULL;
+       }
+       /*
+        * If we are a parent with still-extant children, then don't free just
+        * yet. The last child's call to sshbuf_free should decrement our
+        * refcount to 0 and trigger the actual free.
+        */
+       buf->refcount--;
+       if (buf->refcount > 0)
+               return;
+       dont_free = buf->dont_free;
+       if (!buf->readonly) {
+               bzero(buf->d, buf->alloc);
+               free(buf->d);
+       }
+       bzero(buf, sizeof(*buf));
+       if (!dont_free)
+               free(buf);
+}
+
+void
+sshbuf_reset(struct sshbuf *buf)
+{
+       u_char *d;
+
+       if (buf->readonly || buf->refcount > 1) {
+               /* Nonsensical. Just make buffer appear empty */
+               buf->off = buf->size;
+               return;
+       }
+       if (sshbuf_check_sanity(buf) == 0)
+               bzero(buf->d, buf->alloc);
+       buf->off = buf->size = 0;
+       if (buf->alloc != SSHBUF_SIZE_INIT) {
+               if ((d = realloc(buf->d, SSHBUF_SIZE_INIT)) != NULL) {
+                       buf->cd = buf->d = d;
+                       buf->alloc = SSHBUF_SIZE_INIT;
+               }
+       }
+}
+
+size_t
+sshbuf_max_size(const struct sshbuf *buf)
+{
+       return buf->max_size;
+}
+
+size_t
+sshbuf_alloc(const struct sshbuf *buf)
+{
+       return buf->alloc;
+}
+
+const struct sshbuf *
+sshbuf_parent(const struct sshbuf *buf)
+{
+       return buf->parent;
+}
+
+u_int
+sshbuf_refcount(const struct sshbuf *buf)
+{
+       return buf->refcount;
+}
+
+int
+sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
+{
+       size_t rlen;
+       u_char *dp;
+       int r;
+
+       SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
+       if ((r = sshbuf_check_sanity(buf)) != 0)
+               return r;
+       if (max_size == buf->max_size)
+               return 0;
+       if (buf->readonly || buf->refcount > 1)
+               return SSH_ERR_BUFFER_READ_ONLY;
+       if (max_size > SSHBUF_SIZE_MAX)
+               return SSH_ERR_NO_BUFFER_SPACE;
+       /* pack and realloc if necessary */
+       sshbuf_maybe_pack(buf, max_size < buf->size);
+       if (max_size < buf->alloc && max_size > buf->size) {
+               if (buf->size < SSHBUF_SIZE_INIT)
+                       rlen = SSHBUF_SIZE_INIT;
+               else
+                       rlen = roundup(buf->size, SSHBUF_SIZE_INC);
+               if (rlen > max_size)
+                       rlen = max_size;
+               bzero(buf->d + buf->size, buf->alloc - buf->size);
+               SSHBUF_DBG(("new alloc = %zu", rlen));
+               if ((dp = realloc(buf->d, rlen)) == NULL)
+                       return SSH_ERR_ALLOC_FAIL;
+               buf->cd = buf->d = dp;
+               buf->alloc = rlen;
+       }
+       SSHBUF_TELL("new-max");
+       if (max_size < buf->alloc)
+               return SSH_ERR_NO_BUFFER_SPACE;
+       buf->max_size = max_size;
+       return 0;
+}
+
+size_t
+sshbuf_len(const struct sshbuf *buf)
+{
+       if (sshbuf_check_sanity(buf) != 0)
+               return 0;
+       return buf->size - buf->off;
+}
+
+size_t
+sshbuf_avail(const struct sshbuf *buf)
+{
+       if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
+               return 0;
+       return buf->max_size - (buf->size - buf->off);
+}
+
+const u_char *
+sshbuf_ptr(const struct sshbuf *buf)
+{
+       if (sshbuf_check_sanity(buf) != 0)
+               return NULL;
+       return buf->cd + buf->off;
+}
+
+u_char *
+sshbuf_mutable_ptr(const struct sshbuf *buf)
+{
+       if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
+               return NULL;
+       return buf->d + buf->off;
+}
+
+int
+sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
+{
+       int r;
+
+       if ((r = sshbuf_check_sanity(buf)) != 0)
+               return r;
+       if (buf->readonly || buf->refcount > 1)
+               return SSH_ERR_BUFFER_READ_ONLY;
+       SSHBUF_TELL("check");
+       /* Check that len is reasonable and that max_size + available < len */
+       if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
+               return SSH_ERR_NO_BUFFER_SPACE;
+       return 0;
+}
+
+int
+sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
+{
+       size_t rlen, need;
+       u_char *dp;
+       int r;
+
+       if (dpp != NULL)
+               *dpp = NULL;
+
+       SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
+       if ((r = sshbuf_check_reserve(buf, len)) != 0)
+               return r;
+       /*
+        * If the requested allocation appended would push us past max_size
+        * then pack the buffer, zeroing buf->off.
+        */
+       sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
+       SSHBUF_TELL("reserve");
+       if (len + buf->size > buf->alloc) {
+               /*
+                * Prefer to alloc in SSHBUF_SIZE_INC units, but
+                * allocate less if doing so would overflow max_size.
+                */
+               need = len + buf->size - buf->alloc;
+               rlen = roundup(buf->alloc + need, SSHBUF_SIZE_INC);
+               SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
+               if (rlen > buf->max_size)
+                       rlen = buf->alloc + need;
+               SSHBUF_DBG(("adjusted rlen %zu", rlen));
+               if ((dp = realloc(buf->d, rlen)) == NULL) {
+                       SSHBUF_DBG(("realloc fail"));
+                       if (dpp != NULL)
+                               *dpp = NULL;
+                       return SSH_ERR_ALLOC_FAIL;
+               }
+               buf->alloc = rlen;
+               buf->cd = buf->d = dp;
+               if ((r = sshbuf_check_reserve(buf, len)) < 0) {
+                       /* shouldn't fail */
+                       if (dpp != NULL)
+                               *dpp = NULL;
+                       return r;
+               }
+       }
+       dp = buf->d + buf->size;
+       buf->size += len;
+       SSHBUF_TELL("done");
+       if (dpp != NULL)
+               *dpp = dp;
+       return 0;
+}
+
+int
+sshbuf_consume(struct sshbuf *buf, size_t len)
+{
+       int r;
+
+       SSHBUF_DBG(("len = %zu", len));
+       if ((r = sshbuf_check_sanity(buf)) != 0)
+               return r;
+       if (len == 0)
+               return 0;
+       if (len > sshbuf_len(buf))
+               return SSH_ERR_MESSAGE_INCOMPLETE;
+       buf->off += len;
+       SSHBUF_TELL("done");
+       return 0;
+}
+
+int
+sshbuf_consume_end(struct sshbuf *buf, size_t len)
+{
+       int r;
+
+       SSHBUF_DBG(("len = %zu", len));
+       if ((r = sshbuf_check_sanity(buf)) != 0)
+               return r;
+       if (len == 0)
+               return 0;
+       if (len > sshbuf_len(buf))
+               return SSH_ERR_MESSAGE_INCOMPLETE;
+       buf->size -= len;
+       SSHBUF_TELL("done");
+       return 0;
+}
+
diff --git a/usr.bin/ssh/sshbuf.h b/usr.bin/ssh/sshbuf.h
new file mode 100644 (file)
index 0000000..1d7da31
--- /dev/null
@@ -0,0 +1,325 @@
+/*     $OpenBSD: sshbuf.h,v 1.1 2014/04/30 05:29:56 djm Exp $  */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SSHBUF_H
+#define _SSHBUF_H
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+#define SSHBUF_SIZE_MAX                0x8000000       /* Hard maximum size */
+#define SSHBUF_REFS_MAX                0x100000        /* Max child buffers */
+#define SSHBUF_MAX_BIGNUM      (16384 / 8)     /* Max bignum *bytes* */
+#define SSHBUF_MAX_ECPOINT     ((528 * 2 / 8) + 1) /* Max EC point *bytes* */
+
+/*
+ * NB. do not depend on the internals of this. It will be made opaque
+ * one day.
+ */
+struct sshbuf {
+       u_char *d;              /* Data */
+       const u_char *cd;       /* Const data */
+       size_t off;             /* First available byte is buf->d + buf->off */
+       size_t size;            /* Last byte is buf->d + buf->size - 1 */
+       size_t max_size;        /* Maximum size of buffer */
+       size_t alloc;           /* Total bytes allocated to buf->d */
+       int readonly;           /* Refers to external, const data */
+       int dont_free;          /* Kludge to support sshbuf_init */
+       u_int refcount;         /* Tracks self and number of child buffers */
+       struct sshbuf *parent;  /* If child, pointer to parent */
+};
+
+#ifndef SSHBUF_NO_DEPREACTED
+/*
+ * NB. Please do not use sshbuf_init() in new code. Please use sshbuf_new()
+ * instead. sshbuf_init() is deprectated and will go away soon (it is
+ * only included to allow compat with buffer_* in OpenSSH)
+ */
+void sshbuf_init(struct sshbuf *buf);
+#endif
+
+/*
+ * Create a new sshbuf buffer.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+struct sshbuf *sshbuf_new(void);
+
+/*
+ * Create a new, read-only sshbuf buffer from existing data.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+struct sshbuf *sshbuf_from(const void *blob, size_t len);
+
+/*
+ * Create a new, read-only sshbuf buffer from the contents of an existing
+ * buffer. The contents of "buf" must not change in the lifetime of the
+ * resultant buffer.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+struct sshbuf *sshbuf_fromb(struct sshbuf *buf);
+
+/*
+ * Create a new, read-only sshbuf buffer from the contents of a string in
+ * an existing buffer (the string is consumed in the process).
+ * The contents of "buf" must not change in the lifetime of the resultant
+ * buffer.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+int    sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp);
+
+/*
+ * Clear and free buf
+ */
+void   sshbuf_free(struct sshbuf *buf);
+
+/*
+ * Reset buf, clearing its contents. NB. max_size is preserved.
+ */
+void   sshbuf_reset(struct sshbuf *buf);
+
+/*
+ * Return the maximum size of buf
+ */
+size_t sshbuf_max_size(const struct sshbuf *buf);
+
+/*
+ * Set the maximum size of buf
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int    sshbuf_set_max_size(struct sshbuf *buf, size_t max_size);
+
+/*
+ * Returns the length of data in buf
+ */
+size_t sshbuf_len(const struct sshbuf *buf);
+
+/*
+ * Returns number of bytes left in buffer before hitting max_size.
+ */
+size_t sshbuf_avail(const struct sshbuf *buf);
+
+/*
+ * Returns a read-only pointer to the start of the the data in buf
+ */
+const u_char *sshbuf_ptr(const struct sshbuf *buf);
+
+/*
+ * Returns a mutable pointer to the start of the the data in buf, or
+ * NULL if the buffer is read-only.
+ */
+u_char *sshbuf_mutable_ptr(const struct sshbuf *buf);
+
+/*
+ * Check whether a reservation of size len will succeed in buf
+ * Safer to use than direct comparisons again sshbuf_avail as it copes
+ * with unsigned overflows correctly.
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int    sshbuf_check_reserve(const struct sshbuf *buf, size_t len);
+
+/*
+ * Reserve len bytes in buf.
+ * Returns 0 on success and a pointer to the first reserved byte via the
+ * optional dpp parameter or a negative * SSH_ERR_* error code on failure.
+ */
+int    sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp);
+
+/*
+ * Consume len bytes from the start of buf
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int    sshbuf_consume(struct sshbuf *buf, size_t len);
+
+/*
+ * Consume len bytes from the end of buf
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int    sshbuf_consume_end(struct sshbuf *buf, size_t len);
+
+/* Extract or deposit some bytes */
+int    sshbuf_get(struct sshbuf *buf, void *v, size_t len);
+int    sshbuf_put(struct sshbuf *buf, const void *v, size_t len);
+int    sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v);
+
+/* Append using a printf(3) format */
+int    sshbuf_putf(struct sshbuf *buf, const char *fmt, ...)
+           __attribute__((format(printf, 2, 3)));
+int    sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap);
+
+/* Functions to extract or store big-endian words of various sizes */
+int    sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp);
+int    sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp);
+int    sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp);
+int    sshbuf_get_u8(struct sshbuf *buf, u_char *valp);
+int    sshbuf_put_u64(struct sshbuf *buf, u_int64_t val);
+int    sshbuf_put_u32(struct sshbuf *buf, u_int32_t val);
+int    sshbuf_put_u16(struct sshbuf *buf, u_int16_t val);
+int    sshbuf_put_u8(struct sshbuf *buf, u_char val);
+
+/*
+ * Functions to extract or store SSH wire encoded strings (u32 len || data)
+ * The "cstring" variants admit no \0 characters in the string contents.
+ * Caller must free *valp.
+ */
+int    sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp);
+int    sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp);
+int    sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v);
+int    sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len);
+int    sshbuf_put_cstring(struct sshbuf *buf, const char *v);
+int    sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v);
+
+/*
+ * "Direct" variant of sshbuf_get_string, returns pointer into the sshbuf to
+ * avoid an malloc+memcpy. The pointer is guaranteed to be valid until the
+ * next sshbuf-modifying function call. Caller does not free.
+ */
+int    sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp,
+           size_t *lenp);
+
+/* Skip past a string */
+#define sshbuf_skip_string(buf) sshbuf_get_string_direct(buf, NULL, NULL)
+
+/* Another variant: "peeks" into the buffer without modifying it */
+int    sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp,
+           size_t *lenp);
+
+/*
+ * Functions to extract or store SSH wire encoded bignums and elliptic
+ * curve points.
+ */
+int    sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v);
+int    sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v);
+int    sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g);
+int    sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v);
+int    sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v);
+int    sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v);
+int    sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g);
+int    sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v);
+int    sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len);
+
+/* Dump the contents of the buffer to stderr in a human-readable format */
+void   sshbuf_dump(struct sshbuf *buf, FILE *f);
+
+/* Return the hexadecimal representation of the contents of the buffer */
+char   *sshbuf_dtob16(struct sshbuf *buf);
+
+/* Encode the contents of the buffer as base64 */
+char   *sshbuf_dtob64(struct sshbuf *buf);
+
+/* Decode base64 data and append it to the buffer */
+int    sshbuf_b64tod(struct sshbuf *buf, const char *b64);
+
+/* Macros for decoding/encoding integers */
+#define PEEK_U64(p) \
+       (((u_int64_t)(((u_char *)(p))[0]) << 56) | \
+        ((u_int64_t)(((u_char *)(p))[1]) << 48) | \
+        ((u_int64_t)(((u_char *)(p))[2]) << 40) | \
+        ((u_int64_t)(((u_char *)(p))[3]) << 32) | \
+        ((u_int64_t)(((u_char *)(p))[4]) << 24) | \
+        ((u_int64_t)(((u_char *)(p))[5]) << 16) | \
+        ((u_int64_t)(((u_char *)(p))[6]) << 8) | \
+         (u_int64_t)(((u_char *)(p))[7]))
+#define PEEK_U32(p) \
+       (((u_int32_t)(((u_char *)(p))[0]) << 24) | \
+        ((u_int32_t)(((u_char *)(p))[1]) << 16) | \
+        ((u_int32_t)(((u_char *)(p))[2]) << 8) | \
+         (u_int32_t)(((u_char *)(p))[3]))
+#define PEEK_U16(p) \
+       (((u_int16_t)(((u_char *)(p))[0]) << 8) | \
+         (u_int16_t)(((u_char *)(p))[1]))
+
+#define POKE_U64(p, v) \
+       do { \
+               ((u_char *)(p))[0] = (((u_int64_t)(v)) >> 56) & 0xff; \
+               ((u_char *)(p))[1] = (((u_int64_t)(v)) >> 48) & 0xff; \
+               ((u_char *)(p))[2] = (((u_int64_t)(v)) >> 40) & 0xff; \
+               ((u_char *)(p))[3] = (((u_int64_t)(v)) >> 32) & 0xff; \
+               ((u_char *)(p))[4] = (((u_int64_t)(v)) >> 24) & 0xff; \
+               ((u_char *)(p))[5] = (((u_int64_t)(v)) >> 16) & 0xff; \
+               ((u_char *)(p))[6] = (((u_int64_t)(v)) >> 8) & 0xff; \
+               ((u_char *)(p))[7] = ((u_int64_t)(v)) & 0xff; \
+       } while (0)
+#define POKE_U32(p, v) \
+       do { \
+               ((u_char *)(p))[0] = (((u_int64_t)(v)) >> 24) & 0xff; \
+               ((u_char *)(p))[1] = (((u_int64_t)(v)) >> 16) & 0xff; \
+               ((u_char *)(p))[2] = (((u_int64_t)(v)) >> 8) & 0xff; \
+               ((u_char *)(p))[3] = ((u_int64_t)(v)) & 0xff; \
+       } while (0)
+#define POKE_U16(p, v) \
+       do { \
+               ((u_char *)(p))[0] = (((u_int64_t)(v)) >> 8) & 0xff; \
+               ((u_char *)(p))[1] = ((u_int64_t)(v)) & 0xff; \
+       } while (0)
+
+/* Internal definitions follow. Exposed for regress tests */
+#ifdef SSHBUF_INTERNAL
+
+/*
+ * Return the allocation size of buf
+ */
+size_t sshbuf_alloc(const struct sshbuf *buf);
+
+/*
+ * Increment the reference count of buf.
+ */
+int    sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent);
+
+/*
+ * Return the parent buffer of buf, or NULL if it has no parent.
+ */
+const struct sshbuf *sshbuf_parent(const struct sshbuf *buf);
+
+/*
+ * Return the reference count of buf
+ */
+u_int  sshbuf_refcount(const struct sshbuf *buf);
+
+# define SSHBUF_SIZE_INIT      256             /* Initial allocation */
+# define SSHBUF_SIZE_INC       256             /* Preferred increment length */
+# define SSHBUF_PACK_MIN       8192            /* Minimim packable offset */
+
+/* # define SSHBUF_ABORT abort */
+/* # define SSHBUF_DEBUG */
+
+# ifndef SSHBUF_ABORT
+#  define SSHBUF_ABORT()
+# endif
+
+# ifdef SSHBUF_DEBUG
+#  define SSHBUF_TELL(what) do { \
+               printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \
+                   __FILE__, __LINE__, __func__, what, \
+                   buf->size, buf->alloc, buf->off, buf->max_size); \
+               fflush(stdout); \
+       } while (0)
+#  define SSHBUF_DBG(x) do { \
+               printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
+               printf x; \
+               printf("\n"); \
+               fflush(stdout); \
+       } while (0)
+# else
+#  define SSHBUF_TELL(what)
+#  define SSHBUF_DBG(x)
+# endif
+#endif /* SSHBUF_INTERNAL */
+
+#endif /* _SSHBUF_H */
diff --git a/usr.bin/ssh/ssherr.c b/usr.bin/ssh/ssherr.c
new file mode 100644 (file)
index 0000000..49fbb71
--- /dev/null
@@ -0,0 +1,131 @@
+/*     $OpenBSD: ssherr.c,v 1.1 2014/04/30 05:29:56 djm Exp $  */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include "ssherr.h"
+
+const char *
+ssh_err(int n)
+{
+       switch (n) {
+       case SSH_ERR_SUCCESS:
+               return "success";
+       case SSH_ERR_INTERNAL_ERROR:
+               return "unexpected internal error";
+       case SSH_ERR_ALLOC_FAIL:
+               return "memory allocation failed";
+       case SSH_ERR_MESSAGE_INCOMPLETE:
+               return "incomplete message";
+       case SSH_ERR_INVALID_FORMAT:
+               return "invalid format";
+       case SSH_ERR_BIGNUM_IS_NEGATIVE:
+               return "bignum is negative";
+       case SSH_ERR_STRING_TOO_LARGE:
+               return "string is too large";
+       case SSH_ERR_BIGNUM_TOO_LARGE:
+               return "bignum is too large";
+       case SSH_ERR_ECPOINT_TOO_LARGE:
+               return "elliptic curve point is too large";
+       case SSH_ERR_NO_BUFFER_SPACE:
+               return "insufficient buffer space";
+       case SSH_ERR_INVALID_ARGUMENT:
+               return "invalid argument";
+       case SSH_ERR_KEY_BITS_MISMATCH:
+               return "key bits do not match";
+       case SSH_ERR_EC_CURVE_INVALID:
+               return "invalid elliptic curve";
+       case SSH_ERR_KEY_TYPE_MISMATCH:
+               return "key type does not match";
+       case SSH_ERR_KEY_TYPE_UNKNOWN:
+               return "unknown or unsupported key type";
+       case SSH_ERR_EC_CURVE_MISMATCH:
+               return "elliptic curve does not match";
+       case SSH_ERR_EXPECTED_CERT:
+               return "plain key provided where certificate required";
+       case SSH_ERR_KEY_LACKS_CERTBLOB:
+               return "key lacks certificate data";
+       case SSH_ERR_KEY_CERT_UNKNOWN_TYPE:
+               return "unknown/unsupported certificate type";
+       case SSH_ERR_KEY_CERT_INVALID_SIGN_KEY:
+               return "invalid certificate signing key";
+       case SSH_ERR_KEY_INVALID_EC_VALUE:
+               return "invalid elliptic curve value";
+       case SSH_ERR_SIGNATURE_INVALID:
+               return "incorrect signature";
+       case SSH_ERR_LIBCRYPTO_ERROR:
+               return "error in libcrypto";  /* XXX fetch and return */
+       case SSH_ERR_UNEXPECTED_TRAILING_DATA:
+               return "unexpected bytes remain after decoding";
+       case SSH_ERR_SYSTEM_ERROR:
+               return strerror(errno);
+       case SSH_ERR_KEY_CERT_INVALID:
+               return "invalid certificate";
+       case SSH_ERR_AGENT_COMMUNICATION:
+               return "communication with agent failed";
+       case SSH_ERR_AGENT_FAILURE:
+               return "agent refused operation";
+       case SSH_ERR_DH_GEX_OUT_OF_RANGE:
+               return "DH GEX group out of range";
+       case SSH_ERR_DISCONNECTED:
+               return "disconnected";
+       case SSH_ERR_MAC_INVALID:
+               return "message authentication code incorrect";
+       case SSH_ERR_NO_CIPHER_ALG_MATCH:
+               return "no matching cipher found";
+       case SSH_ERR_NO_MAC_ALG_MATCH:
+               return "no matching MAC found";
+       case SSH_ERR_NO_COMPRESS_ALG_MATCH:
+               return "no matching compression method found";
+       case SSH_ERR_NO_KEX_ALG_MATCH:
+               return "no matching key exchange method found";
+       case SSH_ERR_NO_HOSTKEY_ALG_MATCH:
+               return "no matching host key type found";
+       case SSH_ERR_PROTOCOL_MISMATCH:
+               return "protocol version mismatch";
+       case SSH_ERR_NO_PROTOCOL_VERSION:
+               return "could not read protocol version";
+       case SSH_ERR_NO_HOSTKEY_LOADED:
+               return "could not load host key";
+       case SSH_ERR_NEED_REKEY:
+               return "rekeying not supported by peer";
+       case SSH_ERR_PASSPHRASE_TOO_SHORT:
+               return "passphrase is too short (minimum four characters)";
+       case SSH_ERR_FILE_CHANGED:
+               return "file changed while reading";
+       case SSH_ERR_KEY_UNKNOWN_CIPHER:
+               return "key encrypted using unsupported cipher";
+       case SSH_ERR_KEY_WRONG_PASSPHRASE:
+               return "incorrect passphrase supplied to decrypt private key";
+       case SSH_ERR_KEY_BAD_PERMISSIONS:
+               return "bad permissions";
+       case SSH_ERR_KEY_CERT_MISMATCH:
+               return "certificate does not match key";
+       case SSH_ERR_KEY_NOT_FOUND:
+               return "key not found";
+       case SSH_ERR_AGENT_NOT_PRESENT:
+               return "agent not present";
+       case SSH_ERR_AGENT_NO_IDENTITIES:
+               return "agent contains no identities";
+       case SSH_ERR_KRL_BAD_MAGIC:
+               return "KRL file has invalid magic number";
+       case SSH_ERR_KEY_REVOKED:
+               return "Key is revoked";
+       default:
+               return "unknown error";
+       }
+}
diff --git a/usr.bin/ssh/ssherr.h b/usr.bin/ssh/ssherr.h
new file mode 100644 (file)
index 0000000..106f786
--- /dev/null
@@ -0,0 +1,80 @@
+/*     $OpenBSD: ssherr.h,v 1.1 2014/04/30 05:29:56 djm Exp $  */
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SSHERR_H
+#define _SSHERR_H
+
+/* XXX are these too granular? not granular enough? I can't decide - djm */
+
+/* Error codes */
+#define SSH_ERR_SUCCESS                                0
+#define SSH_ERR_INTERNAL_ERROR                 -1
+#define SSH_ERR_ALLOC_FAIL                     -2
+#define SSH_ERR_MESSAGE_INCOMPLETE             -3
+#define SSH_ERR_INVALID_FORMAT                 -4
+#define SSH_ERR_BIGNUM_IS_NEGATIVE             -5
+#define SSH_ERR_STRING_TOO_LARGE               -6
+#define SSH_ERR_BIGNUM_TOO_LARGE               -7
+#define SSH_ERR_ECPOINT_TOO_LARGE              -8
+#define SSH_ERR_NO_BUFFER_SPACE                        -9
+#define SSH_ERR_INVALID_ARGUMENT               -10
+#define SSH_ERR_KEY_BITS_MISMATCH              -11
+#define SSH_ERR_EC_CURVE_INVALID               -12
+#define SSH_ERR_KEY_TYPE_MISMATCH              -13
+#define SSH_ERR_KEY_TYPE_UNKNOWN               -14 /* XXX UNSUPPORTED? */
+#define SSH_ERR_EC_CURVE_MISMATCH              -15
+#define SSH_ERR_EXPECTED_CERT                  -16
+#define SSH_ERR_KEY_LACKS_CERTBLOB             -17
+#define SSH_ERR_KEY_CERT_UNKNOWN_TYPE          -18
+#define SSH_ERR_KEY_CERT_INVALID_SIGN_KEY      -19
+#define SSH_ERR_KEY_INVALID_EC_VALUE           -20
+#define SSH_ERR_SIGNATURE_INVALID              -21
+#define SSH_ERR_LIBCRYPTO_ERROR                        -22
+#define SSH_ERR_UNEXPECTED_TRAILING_DATA       -23
+#define SSH_ERR_SYSTEM_ERROR                   -24
+#define SSH_ERR_KEY_CERT_INVALID               -25
+#define SSH_ERR_AGENT_COMMUNICATION            -26
+#define SSH_ERR_AGENT_FAILURE                  -27
+#define SSH_ERR_DH_GEX_OUT_OF_RANGE            -28
+#define SSH_ERR_DISCONNECTED                   -29
+#define SSH_ERR_MAC_INVALID                    -30
+#define SSH_ERR_NO_CIPHER_ALG_MATCH            -31
+#define SSH_ERR_NO_MAC_ALG_MATCH               -32
+#define SSH_ERR_NO_COMPRESS_ALG_MATCH          -33
+#define SSH_ERR_NO_KEX_ALG_MATCH               -34
+#define SSH_ERR_NO_HOSTKEY_ALG_MATCH           -35
+#define SSH_ERR_NO_HOSTKEY_LOADED              -36
+#define SSH_ERR_PROTOCOL_MISMATCH              -37
+#define SSH_ERR_NO_PROTOCOL_VERSION            -38
+#define SSH_ERR_NEED_REKEY                     -39
+#define SSH_ERR_PASSPHRASE_TOO_SHORT           -40
+#define SSH_ERR_FILE_CHANGED                   -41
+#define SSH_ERR_KEY_UNKNOWN_CIPHER             -42
+#define SSH_ERR_KEY_WRONG_PASSPHRASE           -43
+#define SSH_ERR_KEY_BAD_PERMISSIONS            -44
+#define SSH_ERR_KEY_CERT_MISMATCH              -45
+#define SSH_ERR_KEY_NOT_FOUND                  -46
+#define SSH_ERR_AGENT_NOT_PRESENT              -47
+#define SSH_ERR_AGENT_NO_IDENTITIES            -48
+#define SSH_ERR_BUFFER_READ_ONLY               -49
+#define SSH_ERR_KRL_BAD_MAGIC                  -50
+#define SSH_ERR_KEY_REVOKED                    -51
+
+/* Translate a numeric error code to a human-readable error string */
+const char *ssh_err(int n);
+
+#endif /* _SSHERR_H */