samba-3.0.24-CVE-2007-2447.patch   [plain text]


Only in source-orig/: configure
diff -u -r source-orig/lib/charcnv.c source/lib/charcnv.c
--- source-orig/lib/charcnv.c	2006-04-19 19:29:23.000000000 -0700
+++ source/lib/charcnv.c	2007-05-10 09:59:49.023262000 -0700
@@ -1398,5 +1398,5 @@
 	/* We're hosed - we don't know how big this is... */
 	DEBUG(10,("next_mb_char_size: unknown size at string %s\n", s));
 	conv_silent = False;
-	return 1;
+	return (size_t)-1;
 }
diff -u -r source-orig/lib/smbrun.c source/lib/smbrun.c
--- source-orig/lib/smbrun.c	2006-04-19 19:29:23.000000000 -0700
+++ source/lib/smbrun.c	2007-05-10 09:57:03.305061000 -0700
@@ -55,7 +55,7 @@
 outfd (or discard it if outfd is NULL).
 ****************************************************************************/
 
-int smbrun(const char *cmd, int *outfd)
+static int smbrun_internal(const char *cmd, int *outfd, BOOL sanitize)
 {
 	pid_t pid;
 	uid_t uid = current_user.ut.uid;
@@ -173,13 +173,36 @@
 	}
 #endif
 
-	execl("/bin/sh","sh","-c",cmd,NULL);  
+	{
+		const char *newcmd = sanitize ? escape_shell_string(cmd) : cmd;
+		if (!newcmd) {
+			exit(82);
+		}
+		execl("/bin/sh","sh","-c",newcmd,NULL);  
+	}
 	
 	/* not reached */
-	exit(82);
+	exit(83);
 	return 1;
 }
 
+/****************************************************************************
+ Use only in known safe shell calls (printing).
+****************************************************************************/
+
+int smbrun_no_sanitize(const char *cmd, int *outfd)
+{
+	return smbrun_internal(cmd, outfd, False);
+}
+
+/****************************************************************************
+ By default this now sanitizes shell expansion.
+****************************************************************************/
+
+int smbrun(const char *cmd, int *outfd)
+{
+	return smbrun_internal(cmd, outfd, True);
+}
 
 /****************************************************************************
 run a command being careful about uid/gid handling and putting the output in
@@ -302,7 +325,7 @@
 #endif
 
 	execl("/bin/sh", "sh", "-c", cmd, NULL);  
-	
+
 	/* not reached */
 	exit(82);
 	return 1;
diff -u -r source-orig/lib/util_str.c source/lib/util_str.c
--- source-orig/lib/util_str.c	2007-02-04 10:59:17.000000000 -0800
+++ source/lib/util_str.c	2007-05-10 09:59:36.718762000 -0700
@@ -2426,3 +2426,165 @@
 	return True;
 }
 
+
+/*******************************************************************
+ Add a shell escape character '\' to any character not in a known list
+ of characters. UNIX charset format.
+*******************************************************************/
+
+#define INCLUDE_LIST "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabdefghijklmnopqrstuvwxyz_/ \t.,"
+#define INSIDE_DQUOTE_LIST "$`\n\"\\"
+
+char *escape_shell_string(const char *src)
+{
+	size_t srclen = strlen(src);
+	char *ret = SMB_MALLOC((srclen * 2) + 1);
+	char *dest = ret;
+	BOOL in_s_quote = False;
+	BOOL in_d_quote = False;
+	BOOL next_escaped = False;
+
+	if (!ret) {
+		return NULL;
+	}
+
+	while (*src) {
+		size_t c_size = next_mb_char_size(src);
+
+		if (c_size == (size_t)-1) {
+			SAFE_FREE(ret);
+			return NULL;
+		}
+
+		if (c_size > 1) {
+			memcpy(dest, src, c_size);
+			src += c_size;
+			dest += c_size;
+			next_escaped = False;
+			continue;
+		}
+
+		/*
+		 * Deal with backslash escaped state.
+		 * This only lasts for one character.
+		 */
+
+		if (next_escaped) {
+			*dest++ = *src++;
+			next_escaped = False;
+			continue;
+		}
+
+		/*
+		 * Deal with single quote state. The
+		 * only thing we care about is exiting
+		 * this state.
+		 */
+
+		if (in_s_quote) {
+			if (*src == '\'') {
+				in_s_quote = False;
+			}
+			*dest++ = *src++;
+			continue;
+		}
+
+		/* 
+		 * Deal with double quote state. The most
+		 * complex state. We must cope with \, meaning
+		 * possibly escape next char (depending what it
+		 * is), ", meaning exit this state, and possibly
+		 * add an \ escape to any unprotected character
+		 * (listed in INSIDE_DQUOTE_LIST).
+		 */
+
+		if (in_d_quote) {
+			if (*src == '\\') {
+				/* 
+				 * Next character might be escaped.
+				 * We have to peek. Inside double
+				 * quotes only INSIDE_DQUOTE_LIST
+				 * characters are escaped by a \.
+				 */
+
+				char nextchar;
+
+				c_size = next_mb_char_size(&src[1]);
+				if (c_size == (size_t)-1) {
+					SAFE_FREE(ret);
+					return NULL;
+				}
+				if (c_size > 1) {
+					/*
+					 * Don't escape the next char.
+					 * Just copy the \.
+					 */
+					*dest++ = *src++;
+					continue;
+				}
+
+				nextchar = src[1];
+
+				if (nextchar && strchr(INSIDE_DQUOTE_LIST, (int)nextchar)) {
+					next_escaped = True;
+				}
+				*dest++ = *src++;
+				continue;
+			}
+
+			if (*src == '\"') {
+				/* Exit double quote state. */
+				in_d_quote = False;
+				*dest++ = *src++;
+				continue;
+			}
+
+			/*
+			 * We know the character isn't \ or ",
+			 * so escape it if it's any of the other
+			 * possible unprotected characters.
+			 */
+
+	       		if (strchr(INSIDE_DQUOTE_LIST, (int)*src)) {
+				*dest++ = '\\';
+			}
+			*dest++ = *src++;
+			continue;
+		}
+
+		/* 
+		 * From here to the end of the loop we're
+		 * not in the single or double quote state.
+		 */
+
+		if (*src == '\\') {
+			/* Next character must be escaped. */
+			next_escaped = True;
+			*dest++ = *src++;
+			continue;
+		}
+
+		if (*src == '\'') {
+			/* Go into single quote state. */
+			in_s_quote = True;
+			*dest++ = *src++;
+			continue;
+		}
+
+		if (*src == '\"') {
+			/* Go into double quote state. */
+			in_d_quote = True;
+			*dest++ = *src++;
+			continue;
+		}
+
+		/* Check if we need to escape the character. */
+
+	       	if (!strchr(INCLUDE_LIST, (int)*src)) {
+			*dest++ = '\\';
+		}
+		*dest++ = *src++;
+	}
+	*dest++ = '\0';
+	return ret;
+}
diff -u -r source-orig/printing/print_generic.c source/printing/print_generic.c
--- source-orig/printing/print_generic.c	2007-02-04 10:59:13.000000000 -0800
+++ source/printing/print_generic.c	2007-05-10 09:57:03.292061000 -0700
@@ -58,7 +58,7 @@
 	if ( do_sub && snum != -1 )
 		standard_sub_snum(snum,syscmd,sizeof(syscmd));
 		
-	ret = smbrun(syscmd,outfd);
+	ret = smbrun_no_sanitize(syscmd,outfd);
 
 	DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));