summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm <djm>2014-05-15 04:33:43 +0000
committerdjm <djm>2014-05-15 04:33:43 +0000
commit4d51d8b609a972c6be4007e0d1c1202802db3395 (patch)
treefc9b4ce7048ceec1162cc76a0bbcc30b0cb11163
parent873252b1d0a3688b523a7593dbfb8af48decdbbe (diff)
downloadopenssh-4d51d8b609a972c6be4007e0d1c1202802db3395.tar.gz
- djm@cvs.openbsd.org 2014/04/30 05:29:56
[bufaux.c bufbn.c bufec.c buffer.c buffer.h sshbuf-getput-basic.c] [sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c sshbuf.h ssherr.c] [ssherr.h] New buffer API; the first installment of the conversion/replacement 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.
-rw-r--r--ChangeLog14
-rw-r--r--bufaux.c332
-rw-r--r--bufbn.c210
-rw-r--r--bufec.c107
-rw-r--r--buffer.c245
-rw-r--r--buffer.h64
-rw-r--r--sshbuf-getput-basic.c421
-rw-r--r--sshbuf-getput-crypto.c233
-rw-r--r--sshbuf-misc.c129
-rw-r--r--sshbuf.c405
-rw-r--r--sshbuf.h325
-rw-r--r--ssherr.c131
-rw-r--r--ssherr.h80
13 files changed, 1977 insertions, 719 deletions
diff --git a/ChangeLog b/ChangeLog
index 50c83e64..5ffe464e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -52,6 +52,20 @@
Don't attempt to append a nul quote char to the filename. Should prevent
fatal'ing with "el_insertstr failed" when there's a single quote char
somewhere in the string. bz#2238, ok markus@
+ - djm@cvs.openbsd.org 2014/04/30 05:29:56
+ [bufaux.c bufbn.c bufec.c buffer.c buffer.h sshbuf-getput-basic.c]
+ [sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c sshbuf.h ssherr.c]
+ [ssherr.h]
+ New buffer API; the first installment of the conversion/replacement
+ 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.
20140430
- (dtucker) [defines.h] Define __GNUC_PREREQ__ macro if we don't already
diff --git a/bufaux.c b/bufaux.c
index 320bc2cb..aa9b892d 100644
--- a/bufaux.c
+++ b/bufaux.c
@@ -1,68 +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 "includes.h"
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
#include <sys/types.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.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
@@ -71,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
@@ -94,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
@@ -117,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 *
@@ -197,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 *
@@ -230,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 *
@@ -259,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;
-
- buffer_append(buffer, &ch, 1);
-}
-
-/* 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);
+ int ret;
- if (ret == NULL)
- fatal("%s: buffer error", __func__);
- return ret;
+ if ((ret = sshbuf_put_u8(buffer, value)) != 0)
+ fatal("%s: %s", __func__, ssh_err(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));
+}
diff --git a/bufbn.c b/bufbn.c
index 1d2e0126..0a519ed2 100644
--- a/bufbn.c
+++ b/bufbn.c
@@ -1,229 +1,101 @@
-/* $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 "includes.h"
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
#include <sys/types.h>
-#include <openssl/bn.h>
-
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.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);
+ int ret;
- explicit_bzero(buf, bin_size);
- free(buf);
-
- 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);
- }
- bits = get_u16(buf);
- if (bits > 65535-7) {
- error("buffer_get_bignum_ret: cannot handle BN of size %d",
- bits);
- return (-1);
+ if ((ret = sshbuf_get_bignum1(buffer, value)) != 0) {
+ error("%s: %s", __func__, ssh_err(ret));
+ 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__);
}
diff --git a/bufec.c b/bufec.c
index 89482b90..b33ede38 100644
--- a/bufec.c
+++ b/bufec.c
@@ -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
@@ -15,73 +16,25 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "includes.h"
-
-#ifdef OPENSSL_HAS_ECC
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
#include <sys/types.h>
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-
-#include <string.h>
-#include <stdarg.h>
-
-#include "xmalloc.h"
#include "buffer.h"
#include "log.h"
-#include "misc.h"
+#include "ssherr.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)
-
-/*
- * 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
@@ -96,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
@@ -143,4 +66,4 @@ buffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve,
fatal("%s: buffer error", __func__);
}
-#endif /* OPENSSL_HAS_ECC */
+
diff --git a/buffer.c b/buffer.c
index d240f675..07bc186d 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1,253 +1,116 @@
-/* $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 "includes.h"
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
-#include <sys/param.h>
+#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.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;
+ int ret;
+ u_char *p;
- if (len > BUFFER_MAX_CHUNK)
- fatal("buffer_append_space: len %u not supported", len);
-
- /* 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);
-}
-
-/* Returns the number of bytes of data in the buffer. */
+ int ret = sshbuf_check_reserve(buffer, len);
-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");
-}
-
-/* Returns a pointer to the first used byte in the buffer. */
-
-void *
-buffer_ptr(const Buffer *buffer)
-{
- return buffer->buf + buffer->offset;
+ fatal("%s: buffer error", __func__);
}
-/* 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");
-}
diff --git a/buffer.h b/buffer.h
index 74a7b814..9d853edf 100644
--- a/buffer.h
+++ b/buffer.h
@@ -1,57 +1,58 @@
-/* $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 +72,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,19 +84,15 @@ 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);
+int buffer_get_char_ret(char *, Buffer *);
#ifdef OPENSSL_HAS_ECC
#include <openssl/ec.h>
-
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
-#endif /* BUFFER_H */
+#endif /* BUFFER_H */
+
diff --git a/sshbuf-getput-basic.c b/sshbuf-getput-basic.c
new file mode 100644
index 00000000..6b16b214
--- /dev/null
+++ b/sshbuf-getput-basic.c
@@ -0,0 +1,421 @@
+/* $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 "includes.h"
+
+#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/sshbuf-getput-crypto.c b/sshbuf-getput-crypto.c
new file mode 100644
index 00000000..9c801a45
--- /dev/null
+++ b/sshbuf-getput-crypto.c
@@ -0,0 +1,233 @@
+/* $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 "includes.h"
+
+#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;
+}
+
+#ifdef OPENSSL_HAS_ECC
+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;
+}
+#endif /* OPENSSL_HAS_ECC */
+
+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;
+}
+
+#ifdef OPENSSL_HAS_ECC
+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));
+}
+#endif /* OPENSSL_HAS_ECC */
+
diff --git a/sshbuf-misc.c b/sshbuf-misc.c
new file mode 100644
index 00000000..22dbfd5a
--- /dev/null
+++ b/sshbuf-misc.c
@@ -0,0 +1,129 @@
+/* $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 "includes.h"
+
+#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/sshbuf.c b/sshbuf.c
new file mode 100644
index 00000000..11d8d41d
--- /dev/null
+++ b/sshbuf.c
@@ -0,0 +1,405 @@
+/* $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 "includes.h"
+
+#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/sshbuf.h b/sshbuf.h
new file mode 100644
index 00000000..1d7da31c
--- /dev/null
+++ b/sshbuf.h
@@ -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/ssherr.c b/ssherr.c
new file mode 100644
index 00000000..49fbb71d
--- /dev/null
+++ b/ssherr.c
@@ -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/ssherr.h b/ssherr.h
new file mode 100644
index 00000000..106f786e
--- /dev/null
+++ b/ssherr.h
@@ -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 */