[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Wed May 15 23:09:02 UTC 2019


The branch, master has been updated
       via  f0ea0800982 s3: net: Test of fuzzer problems with net rpc registry import.
       via  11c35c8f783 s3: net: Rewrite of reg_parse_fd() to harden against buffer overwrites.
       via  70025b4a703 s3: net: Harden srprs_str() against memcmp overread.
       via  b3bfad39d64 s3: net: Harden act_val_hex() act_val_sz() against errors.
       via  226544f6f56 s3: net: Harden guess_charset() against overflow errors.
      from  0daa0ff921b s4 dsdb/repl_meta_data: fix use after free in dsdb_audit_add_ldb_value

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit f0ea08009821805b1abfe2ff3b2a3d5ee96de396
Author: Jeremy Allison <jra at samba.org>
Date:   Thu May 9 14:34:37 2019 -0700

    s3: net: Test of fuzzer problems with net rpc registry import.
    
    Found by Michael Hanselmann using fuzzing tools
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13842
    
    Signed-off-by: Jeremy Allison <jra at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Wed May 15 23:08:58 UTC 2019 on sn-devel-184

commit 11c35c8f783d359fcc3ff6f22d19aae5836a16d2
Author: Jeremy Allison <jra at samba.org>
Date:   Tue May 7 10:42:55 2019 -0700

    s3: net: Rewrite of reg_parse_fd() to harden against buffer overwrites.
    
    Remove unused handle_iconv_errno(). Fix leaks of iconv handles.
    
    Found by Michael Hanselmann using fuzzing tools
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13842
    
    Signed-off-by: Jeremy Allison <jra at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 70025b4a70364a68d08e2880675449e4e4729420
Author: Jeremy Allison <jra at samba.org>
Date:   Mon May 13 13:45:10 2019 -0700

    s3: net: Harden srprs_str() against memcmp overread.
    
    Found by Michael Hanselmann using fuzzing tools
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13842
    
    Signed-off-by: Jeremy Allison <jra at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b3bfad39d64eab0e3a9218182b34385c2a397ff5
Author: Jeremy Allison <jra at samba.org>
Date:   Mon Mar 25 11:13:24 2019 -0700

    s3: net: Harden act_val_hex() act_val_sz() against errors.
    
    Found by Michael Hanselmann using fuzzing tools
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13842
    
    Signed-off-by: Jeremy Allison <jra at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 226544f6f5699891bbd933361c65750a26cfaccf
Author: Jeremy Allison <jra at samba.org>
Date:   Mon Mar 25 10:32:08 2019 -0700

    s3: net: Harden guess_charset() against overflow errors.
    
    Found by Michael Hanselmann using fuzzing tools
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13842
    
    Signed-off-by: Jeremy Allison <jra at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 source3/lib/srprs.c                              |   8 +
 source3/registry/reg_parse.c                     | 314 +++++++++++++++--------
 source3/script/tests/test_net_registry_import.sh | 192 ++++++++++++++
 source3/selftest/tests.py                        |   1 +
 4 files changed, 415 insertions(+), 100 deletions(-)
 create mode 100755 source3/script/tests/test_net_registry_import.sh


Changeset truncated at 500 lines:

diff --git a/source3/lib/srprs.c b/source3/lib/srprs.c
index 02f4c80e27b..67ada3796f0 100644
--- a/source3/lib/srprs.c
+++ b/source3/lib/srprs.c
@@ -46,9 +46,17 @@ bool srprs_char(const char** ptr, char c) {
 
 bool srprs_str(const char** ptr, const char* str, ssize_t len)
 {
+	/* By definition *ptr must be null terminated. */
+	size_t ptr_len = strlen(*ptr);
+
 	if (len == -1)
 		len = strlen(str);
 
+	/* Don't memcmp read past end of buffer. */
+	if (len > ptr_len) {
+		return false;
+	}
+
 	if (memcmp(*ptr, str, len) == 0) {
 		*ptr += len;
 		return true;
diff --git a/source3/registry/reg_parse.c b/source3/registry/reg_parse.c
index 81815a4fd98..c64cf66a5ab 100644
--- a/source3/registry/reg_parse.c
+++ b/source3/registry/reg_parse.c
@@ -117,6 +117,7 @@ static bool act_val_hex(struct reg_parse* p, cbuf* value, bool cont)
 					cbuf_swapptr(p->valblob, &dst, dlen);
 				} else {
 					DEBUG(0, ("iconvert_talloc failed\n"));
+					return false;
 				}
 				talloc_free(dst);
 			}
@@ -166,6 +167,7 @@ static bool act_val_sz(struct reg_parse* p, cbuf* value, bool cont)
 		} else {
 			DEBUG(0, ("convert_string_talloc failed: >%s<\n"
 				  "use it as is\t", src));
+			return false;
 		}
 		talloc_free(dst);
 
@@ -263,6 +265,7 @@ struct reg_parse* reg_parse_new(const void* ctx,
 	assert(&s->reg_format_callback == (struct reg_format_callback*)s);
 	return s;
 fail:
+	set_iconv(&s->str2UTF16, NULL, NULL);
 	talloc_free(s);
 	return NULL;
 }
@@ -688,7 +691,15 @@ static bool guess_charset(const char** ptr,
 	}
 
 	if (srprs_bom(&pos, &charset, NULL)) {
-		*len -= (pos - *ptr);
+		size_t declen;
+		if (pos < *ptr) {
+			return false;
+		}
+		declen = (pos - *ptr);
+		if (*len < declen) {
+			return false;
+		}
+		*len -= declen;
 		*ptr = pos;
 		if (*file_enc == NULL) {
 			*file_enc = charset;
@@ -777,59 +788,13 @@ done:
 	return ret;
 }
 
-
-static void
-handle_iconv_errno(int err, const char* obuf, size_t linenum,
-		   smb_iconv_t cd, const char** iptr, size_t* ilen,
-		   char** optr, size_t *olen)
+static void display_iconv_error_bytes(const char *inbuf, size_t len)
 {
-	const char *pos = obuf;
-	const char *ptr = obuf;
-	switch(err) {
-	case EINVAL:
-		/* DEBUG(0, ("Incomplete multibyte sequence\n")); */
-	case E2BIG:
-		return;
-	case EILSEQ:
-		break;
-	default:
-		assert(false);
-	}
-
-	**optr = '\0';
-	while (srprs_line(&ptr, NULL) && srprs_nl(&ptr, NULL)) {
-		pos = ptr;
-		linenum++;
-	}
-	if (pos == *optr) {
-		pos = MAX(obuf, *optr-60);
-	}
-	DEBUG(0, ("Illegal multibyte sequence at line %lu: %s",
-		  (long unsigned)(linenum+1), pos));
-
-	assert((*ilen) > 0);
-	do {
-		size_t il = 1;
-		DEBUGADD(0, ("<%02x>", (unsigned char)**iptr));
-
-		if ((*olen) > 0) {
-			*(*optr)++ = '\?';
-			(*iptr)++;
-			/* Todo: parametrize, e.g. skip: *optr++ = *iptr++; */
-			(*ilen)--;
-		}
-
-		if (smb_iconv(cd, iptr, &il, optr, olen) != (size_t)-1 || (errno != EILSEQ)) {
-			if(il == 0)
-				(*ilen)-- ;
-			break;
-		}
-
+	size_t i;
+	for (i = 0; i < 4 && i < len; i++) {
+		DEBUGADD(0, ("<%02x>", (unsigned char)inbuf[i]));
 	}
-	while ((*ilen > 0) && (*olen > 0));
-
 	DEBUGADD(0, ("\n"));
-
 }
 
 int reg_parse_fd(int fd, const struct reg_parse_callback* cb, const char* opts)
@@ -838,106 +803,255 @@ int reg_parse_fd(int fd, const struct reg_parse_callback* cb, const char* opts)
 	cbuf* line               = cbuf_new(mem_ctx);
 	smb_iconv_t cd           = (smb_iconv_t)-1;
 	struct reg_parse* parser = NULL;
-	char buf_raw[1024];
-	char buf_unix[1025];
-
+	char buf_in[1024];
+	char buf_out[1025] = { 0 };
 	ssize_t nread;
-	size_t  nconv;
-	const char* pos;
 	const char* iptr;
 	char* optr;
         size_t ilen;
 	size_t olen;
+	size_t avail_osize = sizeof(buf_out)-1;
+	size_t space_to_read = sizeof(buf_in);
 	int ret = -1;
 	bool eof = false;
-	size_t linenum = 0;
+	size_t linecount = 0;
 
 	struct reg_parse_fd_opt opt = reg_parse_fd_opt(mem_ctx, opts);
 
 	if (cb == NULL) {
-		DEBUG(0,("reg_parse_fd: NULL callback\n"));
+		DBG_ERR("NULL callback\n");
+		ret = -1;
 		goto done;
 	}
 
-	nread = read(fd, buf_raw, sizeof(buf_raw));
+	nread = read(fd, buf_in, space_to_read);
 	if (nread < 0) {
-		DEBUG(0, ("reg_parse_fd: read failed: %s\n", strerror(errno)));
-		ret = nread;
+		DBG_ERR("read failed: %s\n", strerror(errno));
+		ret = -1;
+		goto done;
+	}
+	if (nread == 0) {
+		/* Empty file. */
+		eof = true;
 		goto done;
 	}
 
-	iptr = &buf_raw[0];
+	iptr = buf_in;
 	ilen = nread;
 
 	if (!guess_charset(&iptr, &ilen,
 			   &opt.file_enc, &opt.str_enc))
 	{
-		DEBUG(0, ("reg_parse_fd: failed to guess encoding\n"));
+		DBG_ERR("reg_parse_fd: failed to guess encoding\n");
+		ret = -1;
 		goto done;
 	}
 
-	DEBUG(10, ("reg_parse_fd: encoding file: %s str: %s\n",
-		  opt.file_enc, opt.str_enc));
+	if (ilen == 0) {
+		/* File only contained charset info. */
+		eof = true;
+		ret = -1;
+		goto done;
+	}
+
+	DBG_DEBUG("reg_parse_fd: encoding file: %s str: %s\n",
+		  opt.file_enc, opt.str_enc);
 
 
 	if (!set_iconv(&cd, "unix", opt.file_enc)) {
-		DEBUG(0, ("reg_parse_fd: failed to set file encoding %s\n",
-			  opt.file_enc));
+		DBG_ERR("reg_parse_fd: failed to set file encoding %s\n",
+			  opt.file_enc);
+		ret = -1;
 		goto done;
 	}
 
 	parser = reg_parse_new(mem_ctx, *cb, opt.str_enc, opt.flags);
+	if (parser == NULL) {
+		ret = -1;
+		goto done;
+	}
+
+	/* Move input data to start of buf_in. */
+	if (iptr > buf_in) {
+		memmove(buf_in, iptr, ilen);
+		iptr = buf_in;
+	}
+
+	optr = buf_out;
+	/* Leave last byte for null termination. */
+	olen = avail_osize;
+
+	/*
+	 * We read from buf_in (iptr), iconv converting into
+	 * buf_out (optr).
+	 */
 
-	optr = &buf_unix[0];
 	while (!eof) {
-		olen = sizeof(buf_unix) - (optr - buf_unix) - 1 ;
-		while ( olen > 0 ) {
-			memmove(buf_raw, iptr, ilen);
-
-			nread = read(fd, buf_raw + ilen, sizeof(buf_raw) - ilen);
-			if (nread < 0) {
-				DEBUG(0, ("reg_parse_fd: read failed: %s\n", strerror(errno)));
-				ret = nread;
+		const char *pos;
+		size_t nconv;
+
+		if (olen == 0) {
+			/* We're out of possible room. */
+			DBG_ERR("no room in output buffer\n");
+			ret = -1;
+			goto done;
+		}
+		nconv = smb_iconv(cd, &iptr, &ilen, &optr, &olen);
+		if (nconv == (size_t)-1) {
+			bool valid_err = false;
+			if (errno == EINVAL) {
+				valid_err = true;
+			}
+			if (errno == E2BIG) {
+				valid_err = true;
+			}
+			if (!valid_err) {
+				DBG_ERR("smb_iconv error in file at line %zu: ",
+					  linecount);
+				display_iconv_error_bytes(iptr, ilen);
+				ret = -1;
 				goto done;
 			}
+			/*
+			 * For valid errors process the
+			 * existing buffer then continue.
+			 */
+		}
 
-			iptr =  buf_raw;
-			ilen += nread;
-
-			if (ilen == 0) {
-				smb_iconv(cd, NULL, NULL, &optr, &olen);
-				eof = true;
-				break;
-			}
+		/*
+		 * We know this is safe as we have an extra
+		 * enforced zero byte at the end of buf_out.
+		 */
+		*optr = '\0';
+		pos = buf_out;
 
-			nconv = smb_iconv(cd, &iptr, &ilen, &optr, &olen);
+		while (srprs_line(&pos, line) && srprs_nl_no_eos(&pos, line, eof)) {
+			int retval;
 
-			if (nconv == (size_t)-1) {
-				handle_iconv_errno(errno, buf_unix, linenum,
-						   cd, &iptr, &ilen,
-						   &optr, &olen);
-				break;
+			/* Process all lines we got. */
+			retval = reg_parse_line(parser, cbuf_gets(line, 0));
+			if (retval < opt.fail_level) {
+				DBG_ERR("reg_parse_line %zu fail %d\n",
+					linecount,
+					retval);
+				ret = -1;
+				goto done;
 			}
+			cbuf_clear(line);
+			linecount++;
 		}
-	/* process_lines: */
-		*optr = '\0';
-		pos = &buf_unix[0];
+		if (pos > buf_out) {
+			/*
+			 * The output data we have
+			 * processed starts at buf_out
+			 * and ends at pos.
+			 * The unprocessed output
+			 * data starts at pos and
+			 * ends at optr.
+			 *
+			 *  <------ sizeof(buf_out) - 1------------->|0|
+			 *  <--------- avail_osize------------------>|0|
+			 *  +----------------------+-------+-----------+
+			 *  |                      |       |         |0|
+			 *  +----------------------+-------+-----------+
+			 *  ^                      ^       ^
+			 *  |                      |       |
+			 * buf_out               pos      optr
+			 */
+			size_t unprocessed_len;
+
+			/* Paranoia checks. */
+			if (optr < pos) {
+				ret = -1;
+				goto done;
+			}
+			unprocessed_len = optr - pos;
 
-		while ( srprs_line(&pos, line) && srprs_nl_no_eos(&pos, line, eof)) {
-			linenum ++;
-			ret = reg_parse_line(parser, cbuf_gets(line, 0));
-			if (ret < opt.fail_level) {
+			/* Paranoia checks. */
+			if (avail_osize < unprocessed_len) {
+				ret = -1;
 				goto done;
 			}
-			cbuf_clear(line);
+			/* Move down any unprocessed data. */
+			memmove(buf_out, pos, unprocessed_len);
+
+			/*
+			 * After the move, reset the output length.
+			 *
+			 *  <------ sizeof(buf_out) - 1------------->|0|
+			 *  <--------- avail_osize------------------>|0|
+			 *  +----------------------+-------+-----------+
+			 *  |       |                                |0|
+			 *  +----------------------+-------+-----------+
+			 *  ^       ^
+			 *  |       optr
+			 * buf_out
+			 */
+			optr = buf_out + unprocessed_len;
+			/*
+			 * Calculate the new output space available
+			 * for iconv.
+			 * We already did the paranoia check for this
+			 * arithmetic above.
+			 */
+			olen = avail_osize - unprocessed_len;
 		}
-		memmove(buf_unix, pos, optr - pos);
-		optr -= (pos - buf_unix);
+
+		/*
+		 * Move any unprocessed data to the start of
+		 * the input buffer (buf_in).
+		 */
+		if (ilen > 0 && iptr > buf_in) {
+			memmove(buf_in, iptr, ilen);
+		}
+
+		/* Is there any space to read more input ? */
+		if (ilen >= sizeof(buf_in)) {
+			/* No space. Nothing was converted. Error. */
+			DBG_ERR("no space in input buffer\n");
+			ret = -1;
+			goto done;
+		}
+
+		space_to_read = sizeof(buf_in) - ilen;
+
+		/* Read the next chunk from the file. */
+		nread = read(fd, buf_in, space_to_read);
+		if (nread < 0) {
+			DBG_ERR("read failed: %s\n", strerror(errno));
+			ret = -1;
+			goto done;
+		}
+		if (nread == 0) {
+			/* Empty file. */
+			eof = true;
+			continue;
+		}
+
+		/* Paranoia check. */
+		if (nread + ilen < ilen) {
+			ret = -1;
+			goto done;
+		}
+
+		/* Paranoia check. */
+		if (nread + ilen > sizeof(buf_in)) {
+			ret = -1;
+			goto done;
+		}
+
+		iptr = buf_in;
+		ilen = nread + ilen;
 	}
 
 	ret = 0;
+
 done:
+
 	set_iconv(&cd, NULL, NULL);
+	if (parser) {
+		set_iconv(&parser->str2UTF16, NULL, NULL);
+	}
 	talloc_free(mem_ctx);
 	return ret;
 }
diff --git a/source3/script/tests/test_net_registry_import.sh b/source3/script/tests/test_net_registry_import.sh
new file mode 100755
index 00000000000..40e68fad875
--- /dev/null
+++ b/source3/script/tests/test_net_registry_import.sh
@@ -0,0 +1,192 @@
+#!/bin/sh
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_net_registry_import.sh SERVER LOCAL_PATH USERNAME PASSWORD
+EOF
+exit 1;
+fi
+
+SERVER="$1"
+LOCAL_PATH="$2"
+USERNAME="$3"
+PASSWORD="$4"
+shift 4
+ADDARGS="$@"
+
+failed=0
+
+samba_net="$BINDIR/net"
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+
+test_net_registry_import() {
+
+#
+# Expect:
+# Found Byte Order Mark for : UTF-16LE
+#
+	cmd='$VALGRIND $samba_net rpc registry import $LOCAL_PATH/case3b45ccc3b.dat -S$SERVER -U$USERNAME%$PASSWORD $ADDARGS'
+
+	eval echo "$cmd"
+	out=`eval $cmd 2>&1`
+	ret=$?
+
+	if [ $ret != 0 ] ; then
+		echo "$out"
+		echo "command failed with output $ret"
+		false
+		return
+	fi
+
+	echo "$out" | grep 'Found Byte Order Mark for : UTF-16LE'
+	ret=$?
+
+	if [ $ret -ne 0 ] ; then
+		echo "$out"
+		echo "$samba_net rpc registry import $LOCAL_PATH/case3b45ccc3b.dat failed - should get 'Found Byte Order Mark for : UTF-16LE'"
+		false
+		return
+	fi
+
+#
+# Expect:
+# reg_parse_fd: smb_iconv error in file at line 0: <bf><77><d4><41>
+#
+	cmd='$VALGRIND $samba_net rpc registry import $LOCAL_PATH/casecbe8c2427.dat -S$SERVER -U$USERNAME%$PASSWORD $ADDARGS'


-- 
Samba Shared Repository



More information about the samba-cvs mailing list