File openssh-Add-more-sshbuf-functions-sshbuf_dup_string-sshbuf_c.patch of Package openssh.29886

From e785bb50f5aaed6c090fc59e8313bef7111dc566 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <msuchanek@suse.de>
Date: Fri, 11 Nov 2022 08:14:19 +0100
Subject: [PATCH] Add more sshbuf functions sshbuf_dup_string, sshbuf_cmp,
 sshbuf_find, sshbuf_dtob64_string

Patch-mainline: V_7_3_P1
Git-commit: 1a31d02b2411c4718de58ce796dbb7b5e14db93e (partial - only add sshbuf_dup_string)
Upstream-ID: 71f926d9bb3f1efed51319a6daf37e93d57c8820

fix signed/unsigned errors reported by clang-3.7; add
 sshbuf_dup_string() to replace a common idiom of strdup(sshbuf_ptr()) with
 better safety checking; feedback and ok markus@

Patch-mainline: V_7_3_P1
Git-commit: 01cabf10adc7676cba5f40536a34d3b246edb73f
Upstream-Regress-ID: 7521ff150dc7f20511d1c2c48fd3318e5850a96d

unit tests for sshbuf_dup_string()

Patch-mainline: V_8_1_P1
Git-commit: e18a27eedccb024acb3cd9820b650a5dff323f01
OpenBSD-Commit-ID: fd071ec2485c7198074a168ff363a0d6052a706a

upstream: two more bounds-checking sshbuf counterparts to common
string operations: sshbuf_cmp() (bcmp-like) and sshbuf_find() (memmem like)
feedback and ok markus@

Patch-mainline: V_8_1_P1
Git-commit: 477e2a3be8b10df76e8d76f0427b043280d73d68
upstream: unit tests for sshbuf_cmp() and sshbuf_find(); ok markus
OpenBSD-Regress-ID: b52d36bc3ab6dc158c1e59a9a4735f821cf9e1fd

Patch-mainline: V_8_1_P1
Git-commit: 16dd8b2c78a0de106c7429e2a294d203f6bda3c7
OpenBSD-Commit-ID: 4dba6735d88c57232f6fccec8a08bdcfea44ac4c

upstream: remove mostly vestigal uuencode.[ch]; moving the only unique
functionality there (wrapping of base64-encoded data) to sshbuf functions;
feedback and ok markus@

Patch-mainline: V_8_1_P1
Git-commit: f8829fe57fb0479d6103cfe1190095da3c032c6d
OpenBSD-Regress-ID: 82374a83edf0955fd1477169eee3f5d6467405a6

upstream: adapt to sshbuf_dtob64() change
---
 Makefile.in                                 |   2 +-
 monitor_wrap.c                              |   1 -
 regress/unittests/sshbuf/test_sshbuf_misc.c |  98 +++++++++++++++--
 ssh-keygen.c                                |  51 ++++-----
 sshbuf-misc.c                               | 116 ++++++++++++++++++--
 sshbuf.h                                    |  37 ++++++-
 sshkey.c                                    |  25 +----
 uuencode.c                                  |  95 ----------------
 uuencode.h                                  |  29 -----
 9 files changed, 261 insertions(+), 193 deletions(-)
 delete mode 100644 uuencode.c
 delete mode 100644 uuencode.h

diff --git a/Makefile.in b/Makefile.in
index 552b8d62..1b4be188 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -91,7 +91,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
 	compat.o crc32.o deattack.o fatal.o hostfile.o \
 	log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \
 	readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
-	atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o utf8.o \
+	atomicio.o key.o dispatch.o mac.o uidswap.o misc.o utf8.o \
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
 	ssh-pkcs11.o smult_curve25519_ref.o \
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 819362c4..2b7cf5cf 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -75,7 +75,6 @@
 #include "atomicio.h"
 #include "monitor_fdpass.h"
 #include "misc.h"
-#include "uuencode.h"
 
 #include "channels.h"
 #include "session.h"
diff --git a/regress/unittests/sshbuf/test_sshbuf_misc.c b/regress/unittests/sshbuf/test_sshbuf_misc.c
index f155491a..0dc39d03 100644
--- a/regress/unittests/sshbuf/test_sshbuf_misc.c
+++ b/regress/unittests/sshbuf/test_sshbuf_misc.c
@@ -19,6 +19,7 @@
 #include "../test_helper/test_helper.h"
 
 #include "sshbuf.h"
+#include "ssherr.h"
 
 void sshbuf_misc_tests(void);
 
@@ -26,7 +27,7 @@ void
 sshbuf_misc_tests(void)
 {
 	struct sshbuf *p1;
-	char tmp[512], *p;
+	char tmp[512], msg[] = "imploring ping silence ping over", *p;
 	FILE *out;
 	size_t sz;
 
@@ -60,48 +61,48 @@ sshbuf_misc_tests(void)
 	sshbuf_free(p1);
 	TEST_DONE();
 
-	TEST_START("sshbuf_dtob64 len 1");
+	TEST_START("sshbuf_dtob64_string len 1");
 	p1 = sshbuf_new();
 	ASSERT_PTR_NE(p1, NULL);
 	ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0);
-	p = sshbuf_dtob64(p1);
+	p = sshbuf_dtob64_string(p1, 0);
 	ASSERT_PTR_NE(p, NULL);
 	ASSERT_STRING_EQ(p, "EQ==");
 	free(p);
 	sshbuf_free(p1);
 	TEST_DONE();
 
-	TEST_START("sshbuf_dtob64 len 2");
+	TEST_START("sshbuf_dtob64_string len 2");
 	p1 = sshbuf_new();
 	ASSERT_PTR_NE(p1, NULL);
 	ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0);
 	ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x22), 0);
-	p = sshbuf_dtob64(p1);
+	p = sshbuf_dtob64_string(p1, 0);
 	ASSERT_PTR_NE(p, NULL);
 	ASSERT_STRING_EQ(p, "ESI=");
 	free(p);
 	sshbuf_free(p1);
 	TEST_DONE();
 
-	TEST_START("sshbuf_dtob64 len 3");
+	TEST_START("sshbuf_dtob64_string len 3");
 	p1 = sshbuf_new();
 	ASSERT_PTR_NE(p1, NULL);
 	ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0);
 	ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x22), 0);
 	ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x33), 0);
-	p = sshbuf_dtob64(p1);
+	p = sshbuf_dtob64_string(p1, 0);
 	ASSERT_PTR_NE(p, NULL);
 	ASSERT_STRING_EQ(p, "ESIz");
 	free(p);
 	sshbuf_free(p1);
 	TEST_DONE();
 
-	TEST_START("sshbuf_dtob64 len 8191");
+	TEST_START("sshbuf_dtob64_string len 8191");
 	p1 = sshbuf_new();
 	ASSERT_PTR_NE(p1, NULL);
 	ASSERT_INT_EQ(sshbuf_reserve(p1, 8192, NULL), 0);
 	bzero(sshbuf_mutable_ptr(p1), 8192);
-	p = sshbuf_dtob64(p1);
+	p = sshbuf_dtob64_string(p1, 0);
 	ASSERT_PTR_NE(p, NULL);
 	ASSERT_SIZE_T_EQ(strlen(p), ((8191 + 2) / 3) * 4);
 	free(p);
@@ -134,5 +135,84 @@ sshbuf_misc_tests(void)
 	ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), 0xd00fd00f);
 	sshbuf_free(p1);
 	TEST_DONE();
+
+	TEST_START("sshbuf_dup_string");
+	p1 = sshbuf_new();
+	ASSERT_PTR_NE(p1, NULL);
+	/* Check empty buffer */
+	p = sshbuf_dup_string(p1);
+	ASSERT_PTR_NE(p, NULL);
+	ASSERT_SIZE_T_EQ(strlen(p), 0);
+	free(p);
+	/* Check buffer with string */
+	ASSERT_INT_EQ(sshbuf_put(p1, "quad1", strlen("quad1")), 0);
+	p = sshbuf_dup_string(p1);
+	ASSERT_PTR_NE(p, NULL);
+	ASSERT_SIZE_T_EQ(strlen(p), strlen("quad1"));
+	ASSERT_STRING_EQ(p, "quad1");
+	free(p);
+	/* Check buffer with terminating nul */
+	ASSERT_INT_EQ(sshbuf_put(p1, "\0", 1), 0);
+	p = sshbuf_dup_string(p1);
+	ASSERT_PTR_NE(p, NULL);
+	ASSERT_SIZE_T_EQ(strlen(p), strlen("quad1"));
+	ASSERT_STRING_EQ(p, "quad1");
+	free(p);
+	/* Check buffer with data after nul (expect failure) */
+	ASSERT_INT_EQ(sshbuf_put(p1, "quad2", strlen("quad2")), 0);
+	p = sshbuf_dup_string(p1);
+	ASSERT_PTR_EQ(p, NULL);
+	sshbuf_free(p1);
+	TEST_DONE();
+
+	TEST_START("sshbuf_cmp");
+	p1 = sshbuf_from(msg, sizeof(msg) - 1);
+	ASSERT_PTR_NE(p1, NULL);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "i", 1), 0);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "j", 1), SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "imploring", 9), 0);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "implored", 9), SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 10, "ping", 4), 0);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 10, "ring", 4), SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 28, "over", 4), 0);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 28, "rove", 4), SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 28, "overt", 5),
+	    SSH_ERR_MESSAGE_INCOMPLETE);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 32, "ping", 4),
+	    SSH_ERR_MESSAGE_INCOMPLETE);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 1000, "silence", 7),
+	    SSH_ERR_MESSAGE_INCOMPLETE);
+	ASSERT_INT_EQ(sshbuf_cmp(p1, 0, msg, sizeof(msg) - 1), 0);
+	TEST_DONE();
+
+	TEST_START("sshbuf_find");
+	p1 = sshbuf_from(msg, sizeof(msg) - 1);
+	ASSERT_PTR_NE(p1, NULL);
+	ASSERT_INT_EQ(sshbuf_find(p1, 0, "i", 1, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 0);
+	ASSERT_INT_EQ(sshbuf_find(p1, 0, "j", 1, &sz), SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_find(p1, 0, "imploring", 9, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 0);
+	ASSERT_INT_EQ(sshbuf_find(p1, 0, "implored", 9, &sz),
+	    SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_find(p1, 3, "ping", 4, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 10);
+	ASSERT_INT_EQ(sshbuf_find(p1, 11, "ping", 4, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 23);
+	ASSERT_INT_EQ(sshbuf_find(p1, 20, "over", 4, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 28);
+	ASSERT_INT_EQ(sshbuf_find(p1, 28, "over", 4, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 28);
+	ASSERT_INT_EQ(sshbuf_find(p1, 28, "rove", 4, &sz),
+	    SSH_ERR_INVALID_FORMAT);
+	ASSERT_INT_EQ(sshbuf_find(p1, 28, "overt", 5, &sz),
+	    SSH_ERR_MESSAGE_INCOMPLETE);
+	ASSERT_INT_EQ(sshbuf_find(p1, 32, "ping", 4, &sz),
+	    SSH_ERR_MESSAGE_INCOMPLETE);
+	ASSERT_INT_EQ(sshbuf_find(p1, 1000, "silence", 7, &sz),
+	    SSH_ERR_MESSAGE_INCOMPLETE);
+	ASSERT_INT_EQ(sshbuf_find(p1, 0, msg + 1, sizeof(msg) - 2, &sz), 0);
+	ASSERT_SIZE_T_EQ(sz, 1);
+	TEST_DONE();
 }
 
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 53186982..3df6c340 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -42,7 +42,6 @@
 #include "sshkey.h"
 #include "rsa.h"
 #include "authfile.h"
-#include "uuencode.h"
 #include "sshbuf.h"
 #include "pathnames.h"
 #include "log.h"
@@ -306,27 +305,32 @@ load_identity(char *filename)
 static void
 do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
 {
-	size_t len;
-	u_char *blob;
-	char comment[61];
+	struct sshbuf *b;
+	char comment[61], *b64;
 	int r;
 
 	if (k->type == KEY_RSA1)
 		fatal("version 1 keys are not supported");
-	if ((r = sshkey_to_blob(k, &blob, &len)) != 0)
+	if ((b = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshkey_putb(k, b)) != 0)
 		fatal("key_to_blob failed: %s", ssh_err(r));
+	if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL)
+		fatal("%s: sshbuf_dtob64_string failed", __func__);
+
 	/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
 	snprintf(comment, sizeof(comment),
 	    "%u-bit %s, converted by %s@%s from OpenSSH",
 	    sshkey_size(k), sshkey_type(k),
 	    pw->pw_name, hostname);
 
+	sshkey_free(k);
+	sshbuf_free(b);
+
 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
-	fprintf(stdout, "Comment: \"%s\"\n", comment);
-	dump_base64(stdout, blob, len);
+	fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64);
 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
-	sshkey_free(k);
-	free(blob);
+	free(b64);
 	exit(0);
 }
 
@@ -429,9 +433,8 @@ buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
 }
 
 static struct sshkey *
-do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
+do_convert_private_ssh2(struct sshbuf *b)
 {
-	struct sshbuf *b;
 	struct sshkey *key = NULL;
 	char *type, *cipher;
 	u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
@@ -440,15 +443,13 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
 	size_t slen;
 	u_long e;
 
-	if ((b = sshbuf_from(blob, blen)) == NULL)
-		fatal("%s: sshbuf_from failed", __func__);
+
 	if ((r = sshbuf_get_u32(b, &magic)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 
 	if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
 		error("bad magic 0x%x != 0x%x", magic,
 		    SSH_COM_PRIVATE_KEY_MAGIC);
-		sshbuf_free(b);
 		return NULL;
 	}
 	if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
@@ -462,7 +463,6 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
 	if (strcmp(cipher, "none") != 0) {
 		error("unsupported cipher %s", cipher);
 		free(cipher);
-		sshbuf_free(b);
 		free(type);
 		return NULL;
 	}
@@ -473,7 +473,6 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
 	} else if (strstr(type, "rsa")) {
 		ktype = KEY_RSA;
 	} else {
-		sshbuf_free(b);
 		free(type);
 		return NULL;
 	}
@@ -505,7 +504,6 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
 			debug("e %lx", e);
 		}
 		if (!BN_set_word(key->rsa->e, e)) {
-			sshbuf_free(b);
 			sshkey_free(key);
 			return NULL;
 		}
@@ -520,9 +518,7 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
 	}
 	rlen = sshbuf_len(b);
 	if (rlen != 0)
-		error("do_convert_private_ssh2_from_blob: "
-		    "remaining bytes in key blob %d", rlen);
-	sshbuf_free(b);
+		error("%s: remaining bytes in key blob %d", __func__, rlen);
 
 	/* try the key */
 	if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 ||
@@ -567,10 +563,12 @@ do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
 	int r, blen, escaped = 0;
 	u_int len;
 	char line[1024];
-	u_char blob[8096];
+	struct sshbuf *buf;
 	char encoded[8096];
 	FILE *fp;
 
+	if ((buf = sshbuf_new()) == NULL)
+		fatal("sshbuf_new failed");
 	if ((fp = fopen(identity_file, "r")) == NULL)
 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
 	encoded[0] = '\0';
@@ -600,12 +598,11 @@ do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
 	    (encoded[len-2] == '=') &&
 	    (encoded[len-3] == '='))
 		encoded[len-3] = '\0';
-	blen = uudecode(encoded, blob, sizeof(blob));
-	if (blen < 0)
-		fatal("uudecode failed.");
+	if ((r = sshbuf_b64tod(buf, encoded)) != 0)
+		fatal("%s: base64 decoding failed: %s", __func__, ssh_err(r));
 	if (*private)
-		*k = do_convert_private_ssh2_from_blob(blob, blen);
-	else if ((r = sshkey_from_blob(blob, blen, k)) != 0)
+		*k = do_convert_private_ssh2(buf);
+	else if ((r = sshkey_fromb(buf, k)) != 0)
 		fatal("decode blob failed: %s", ssh_err(r));
 	fclose(fp);
 }
@@ -1644,7 +1641,7 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
 			}
 			free(otmp);
 		}
-	
+
 		tmp = tilde_expand_filename(argv[i], pw->pw_uid);
 		if ((r = sshkey_load_public(tmp, &public, &comment)) != 0)
 			fatal("%s: unable to open \"%s\": %s",
diff --git a/sshbuf-misc.c b/sshbuf-misc.c
index 3da4b80e..5cacc587 100644
--- a/sshbuf-misc.c
+++ b/sshbuf-misc.c
@@ -89,24 +89,58 @@ sshbuf_dtob16(struct sshbuf *buf)
 	return ret;
 }
 
+int
+sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap)
+{
+	size_t i, slen = 0;
+	char *s = NULL;
+	int r;
+
+	if (d == NULL || b64 == NULL || sshbuf_len(d) >= SIZE_MAX / 2)
+		return SSH_ERR_INVALID_ARGUMENT;
+	if (sshbuf_len(d) == 0)
+		return 0;
+	slen = ((sshbuf_len(d) + 2) / 3) * 4 + 1;
+	if ((s = malloc(slen)) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	if (b64_ntop(sshbuf_ptr(d), sshbuf_len(d), s, slen) == -1) {
+		r = SSH_ERR_INTERNAL_ERROR;
+		goto fail;
+	}
+	if (wrap) {
+		for (i = 0; s[i] != '\0'; i++) {
+			if ((r = sshbuf_put_u8(b64, s[i])) != 0)
+				goto fail;
+			if (i % 70 == 69 && (r = sshbuf_put_u8(b64, '\n')) != 0)
+				goto fail;
+		}
+		if (i % 70 != 69 && (r = sshbuf_put_u8(b64, '\n')) != 0)
+			goto fail;
+	} else {
+		if ((r = sshbuf_put(b64, s, strlen(s))) != 0)
+			goto fail;
+	}
+	/* Success */
+	r = 0;
+ fail:
+	freezero(s, slen);
+	return r;
+}
+
 char *
-sshbuf_dtob64(struct sshbuf *buf)
+sshbuf_dtob64_string(const struct sshbuf *buf, int wrap)
 {
-	size_t len = sshbuf_len(buf), plen;
-	const u_char *p = sshbuf_ptr(buf);
+	struct sshbuf *tmp;
 	char *ret;
-	int r;
 
-	if (len == 0)
-		return strdup("");
-	plen = ((len + 2) / 3) * 4 + 1;
-	if (SIZE_MAX / 2 <= len || (ret = malloc(plen)) == NULL)
+	if ((tmp = sshbuf_new()) == NULL)
 		return NULL;
-	if ((r = b64_ntop(p, len, ret, plen)) == -1) {
-		explicit_bzero(ret, plen);
-		free(ret);
+	if (sshbuf_dtob64(buf, tmp, wrap) != 0) {
+		sshbuf_free(tmp);
 		return NULL;
 	}
+	ret = sshbuf_dup_string(tmp);
+	sshbuf_free(tmp);
 	return ret;
 }
 
@@ -136,3 +170,63 @@ sshbuf_b64tod(struct sshbuf *buf, const char *b64)
 	return 0;
 }
 
+char *
+sshbuf_dup_string(struct sshbuf *buf)
+{
+	const u_char *p = NULL, *s = sshbuf_ptr(buf);
+	size_t l = sshbuf_len(buf);
+	char *r;
+
+	if (s == NULL || l > SIZE_MAX)
+		return NULL;
+	/* accept a nul only as the last character in the buffer */
+	if (l > 0 && (p = memchr(s, '\0', l)) != NULL) {
+		if (p != s + l - 1)
+			return NULL;
+		l--; /* the nul is put back below */
+	}
+	if ((r = malloc(l + 1)) == NULL)
+		return NULL;
+	if (l > 0)
+		memcpy(r, s, l);
+	r[l] = '\0';
+	return r;
+}
+
+int
+sshbuf_cmp(const struct sshbuf *b, size_t offset,
+    const u_char *s, size_t len)
+{
+	if (sshbuf_ptr(b) == NULL)
+		return SSH_ERR_INTERNAL_ERROR;
+	if (offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0)
+		return SSH_ERR_INVALID_ARGUMENT;
+	if (offset + len > sshbuf_len(b))
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	if (timingsafe_bcmp(sshbuf_ptr(b) + offset, s, len) != 0)
+		return SSH_ERR_INVALID_FORMAT;
+	return 0;
+}
+
+int
+sshbuf_find(const struct sshbuf *b, size_t start_offset,
+    const u_char *s, size_t len, size_t *offsetp)
+{
+	void *p;
+
+	if (offsetp != NULL)
+		*offsetp = 0;
+
+	if (sshbuf_ptr(b) == NULL)
+		return SSH_ERR_INTERNAL_ERROR;
+	if (start_offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0)
+		return SSH_ERR_INVALID_ARGUMENT;
+	if (start_offset > sshbuf_len(b) || start_offset + len > sshbuf_len(b))
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	if ((p = memmem(sshbuf_ptr(b) + start_offset,
+	    sshbuf_len(b) - start_offset, s, len)) == NULL)
+		return SSH_ERR_INVALID_FORMAT;
+	if (offsetp != NULL)
+		*offsetp = (const u_char *)p - sshbuf_ptr(b);
+	return 0;
+}
diff --git a/sshbuf.h b/sshbuf.h
index 1cae68eb..2158af10 100644
--- a/sshbuf.h
+++ b/sshbuf.h
@@ -242,11 +242,46 @@ void	sshbuf_dump_data(const void *s, size_t len, FILE *f);
 char	*sshbuf_dtob16(struct sshbuf *buf);
 
 /* Encode the contents of the buffer as base64 */
-char	*sshbuf_dtob64(struct sshbuf *buf);
+char	*sshbuf_dtob64_string(const struct sshbuf *buf, int wrap);
+int	sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap);
 
 /* Decode base64 data and append it to the buffer */
 int	sshbuf_b64tod(struct sshbuf *buf, const char *b64);
 
+/*
+ * Tests whether the buffer contains the specified byte sequence at the
+ * specified offset. Returns 0 on successful match, or a ssherr.h code
+ * otherwise. SSH_ERR_INVALID_FORMAT indicates sufficient bytes were
+ * present but the buffer contents did not match those supplied. Zero-
+ * length comparisons are not allowed.
+ *
+ * If sufficient data is present to make a comparison, then it is
+ * performed with timing independent of the value of the data. If
+ * insufficient data is present then the comparison is not attempted at
+ * all.
+ */
+int	sshbuf_cmp(const struct sshbuf *b, size_t offset,
+    const u_char *s, size_t len);
+
+/*
+ * Searches the buffer for the specified string. Returns 0 on success
+ * and updates *offsetp with the offset of the first match, relative to
+ * the start of the buffer. Otherwise sshbuf_find will return a ssherr.h
+ * error code. SSH_ERR_INVALID_FORMAT indicates sufficient bytes were
+ * present in the buffer for a match to be possible but none was found.
+ * Searches for zero-length data are not allowed.
+ */
+int
+sshbuf_find(const struct sshbuf *b, size_t start_offset,
+    const u_char *s, size_t len, size_t *offsetp);
+
+/*
+ * Duplicate the contents of a buffer to a string (caller to free).
+ * Returns NULL on buffer error, or if the buffer contains a premature
+ * nul character.
+ */
+char *sshbuf_dup_string(struct sshbuf *buf);
+
 /* Macros for decoding/encoding integers */
 #define PEEK_U64(p) \
 	(((u_int64_t)(((const u_char *)(p))[0]) << 56) | \
diff --git a/sshkey.c b/sshkey.c
index f7905a2e..22d45d53 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -1423,7 +1423,7 @@ sshkey_to_base64(const struct sshkey *key, char **b64p)
 		return SSH_ERR_ALLOC_FAIL;
 	if ((r = sshkey_putb(key, b)) != 0)
 		goto out;
-	if ((uu = sshbuf_dtob64(b)) == NULL) {
+	if ((uu = sshbuf_dtob64_string(b, 0)) == NULL) {
 		r = SSH_ERR_ALLOC_FAIL;
 		goto out;
 	}
@@ -3152,25 +3152,12 @@ sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,
 	    sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0)
 		goto out;
 
-	/* uuencode */
-	if ((b64 = sshbuf_dtob64(encoded)) == NULL) {
-		r = SSH_ERR_ALLOC_FAIL;
-		goto out;
-	}
-
 	sshbuf_reset(blob);
-	if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0)
-		goto out;
-	for (i = 0; i < strlen(b64); i++) {
-		if ((r = sshbuf_put_u8(blob, b64[i])) != 0)
-			goto out;
-		/* insert line breaks */
-		if (i % 70 == 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
-			goto out;
-	}
-	if (i % 70 != 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
-		goto out;
-	if ((r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0)
+
+	/* assemble uuencoded key */
+	if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0 ||
+	    (r = sshbuf_dtob64(encoded, blob, 1)) != 0 ||
+	    (r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0)
 		goto out;
 
 	/* success */
diff --git a/uuencode.c b/uuencode.c
deleted file mode 100644
index 7fc867a1..00000000
--- a/uuencode.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* $OpenBSD: uuencode.c,v 1.28 2015/04/24 01:36:24 deraadt Exp $ */
-/*
- * 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.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <resolv.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "xmalloc.h"
-#include "uuencode.h"
-
-/*
- * Encode binary 'src' of length 'srclength', writing base64-encoded text
- * to 'target' of size 'targsize'. Will always nul-terminate 'target'.
- * Returns the number of bytes stored in 'target' or -1 on error (inc.
- * 'targsize' too small).
- */
-int
-uuencode(const u_char *src, u_int srclength,
-    char *target, size_t targsize)
-{
-	return __b64_ntop(src, srclength, target, targsize);
-}
-
-/*
- * Decode base64-encoded 'src' into buffer 'target' of 'targsize' bytes.
- * Will skip leading and trailing whitespace. Returns the number of bytes
- * stored in 'target' or -1 on error (inc. targsize too small).
- */
-int
-uudecode(const char *src, u_char *target, size_t targsize)
-{
-	int len;
-	char *encoded, *p;
-
-	/* copy the 'readonly' source */
-	encoded = xstrdup(src);
-	/* skip whitespace and data */
-	for (p = encoded; *p == ' ' || *p == '\t'; p++)
-		;
-	for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
-		;
-	/* and remove trailing whitespace because __b64_pton needs this */
-	*p = '\0';
-	len = __b64_pton(encoded, target, targsize);
-	free(encoded);
-	return len;
-}
-
-void
-dump_base64(FILE *fp, const u_char *data, u_int len)
-{
-	char *buf;
-	int i, n;
-
-	if (len > 65536) {
-		fprintf(fp, "dump_base64: len > 65536\n");
-		return;
-	}
-	buf = xreallocarray(NULL, 2, len);
-	n = uuencode(data, len, buf, 2*len);
-	for (i = 0; i < n; i++) {
-		fprintf(fp, "%c", buf[i]);
-		if (i % 70 == 69)
-			fprintf(fp, "\n");
-	}
-	if (i % 70 != 69)
-		fprintf(fp, "\n");
-	free(buf);
-}
diff --git a/uuencode.h b/uuencode.h
deleted file mode 100644
index 4d988812..00000000
--- a/uuencode.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* $OpenBSD: uuencode.h,v 1.14 2010/08/31 11:54:45 djm Exp $ */
-
-/*
- * 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.
- */
-
-int	 uuencode(const u_char *, u_int, char *, size_t);
-int	 uudecode(const char *, u_char *, size_t);
-void	 dump_base64(FILE *, const u_char *, u_int);
-- 
2.38.0

openSUSE Build Service is sponsored by