r22852: merge fixes for CVE-2007-2446 and CVE-2007-2447 to all branches
authorGerald Carter <jerry@samba.org>
Mon, 14 May 2007 14:23:51 +0000 (14:23 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:22:02 +0000 (12:22 -0500)
source/include/smb_macros.h
source/lib/smbrun.c
source/lib/util_str.c
source/printing/print_generic.c
source/rpc_parse/parse_lsa.c
source/rpc_parse/parse_prs.c
source/rpc_parse/parse_sec.c
source/rpc_parse/parse_spoolss.c

index afe7c1a4776f4eaafc54a28bbed114a66fa76a52..bfed27c16748480c8ccc810bbf27c834ec6bf99b 100644 (file)
@@ -295,7 +295,6 @@ copy an IP address from one buffer to another
 #if defined(PARANOID_MALLOC_CHECKER)
 
 #define PRS_ALLOC_MEM(ps, type, count) (type *)prs_alloc_mem_((ps),sizeof(type),(count))
-#define PRS_ALLOC_MEM_VOID(ps, size) prs_alloc_mem_((ps),(size),1)
 
 /* Get medieval on our ass about malloc.... */
 
@@ -334,7 +333,6 @@ copy an IP address from one buffer to another
 #else
 
 #define PRS_ALLOC_MEM(ps, type, count) (type *)prs_alloc_mem((ps),sizeof(type),(count))
-#define PRS_ALLOC_MEM_VOID(ps, size) prs_alloc_mem((ps),(size),1)
 
 /* Regular malloc code. */
 
index 4400aeb4433e20219f96eeb1173a7d25ed2e6379..e81224b5af376ff98bd79b2cf40b85cf191ee277 100644 (file)
@@ -55,7 +55,7 @@ run a command being careful about uid/gid handling and putting the output in
 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 @@ int smbrun(const char *cmd, int *outfd)
        }
 #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 @@ int smbrunsecret(const char *cmd, const char *secret)
 #endif
 
        execl("/bin/sh", "sh", "-c", cmd, NULL);  
-       
+
        /* not reached */
        exit(82);
        return 1;
index 457232c2b21741ed57d64ec2bfa38fbf7a60e487..1439ac6fcd39314ab137154a91aa6f93becdb252 100644 (file)
@@ -2616,3 +2616,166 @@ size_t utf16_len_n(const void *src, size_t n)
 
        return len;
 }
+
+/*******************************************************************
+ 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;
+               codepoint_t c = next_codepoint(src, &c_size);
+
+               if (c == INVALID_CODEPOINT) {
+                       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 = next_codepoint(&src[1], &c_size);
+                               if (c == INVALID_CODEPOINT) {
+                                       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;
+}
index 1e55f712c5395fb57738d992f340586d7f0bfabd..669658097006abc5f02ade66979e9774910d1c9d 100644 (file)
@@ -64,7 +64,7 @@ static int print_run_command(int snum, const char* printername, BOOL do_sub,
                                      current_user_info.domain,
                                      syscmd, sizeof(syscmd));
                
-       ret = smbrun(syscmd,outfd);
+       ret = smbrun_no_sanitize(syscmd,outfd);
 
        DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
 
index ea249dc5600ad4c18abffe915d4a177f3ed4c2f6..06ccec4ab34f3d740f5356c40c29983a881314e4 100644 (file)
@@ -1356,12 +1356,17 @@ static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn,
                               &trn->num_entries2))
                        return False;
 
+               if (trn->num_entries2 != trn->num_entries) {
+                       /* RPC fault */
+                       return False;
+               }
+
                if (UNMARSHALLING(ps)) {
-                       if ((trn->name = PRS_ALLOC_MEM(ps, LSA_TRANS_NAME, trn->num_entries)) == NULL) {
+                       if ((trn->name = PRS_ALLOC_MEM(ps, LSA_TRANS_NAME, trn->num_entries2)) == NULL) {
                                return False;
                        }
 
-                       if ((trn->uni_name = PRS_ALLOC_MEM(ps, UNISTR2, trn->num_entries)) == NULL) {
+                       if ((trn->uni_name = PRS_ALLOC_MEM(ps, UNISTR2, trn->num_entries2)) == NULL) {
                                return False;
                        }
                }
@@ -1413,12 +1418,17 @@ static BOOL lsa_io_trans_names2(const char *desc, LSA_TRANS_NAME_ENUM2 *trn,
                               &trn->num_entries2))
                        return False;
 
+               if (trn->num_entries2 != trn->num_entries) {
+                       /* RPC fault */
+                       return False;
+               }
+
                if (UNMARSHALLING(ps)) {
-                       if ((trn->name = PRS_ALLOC_MEM(ps, LSA_TRANS_NAME2, trn->num_entries)) == NULL) {
+                       if ((trn->name = PRS_ALLOC_MEM(ps, LSA_TRANS_NAME2, trn->num_entries2)) == NULL) {
                                return False;
                        }
 
-                       if ((trn->uni_name = PRS_ALLOC_MEM(ps, UNISTR2, trn->num_entries)) == NULL) {
+                       if ((trn->uni_name = PRS_ALLOC_MEM(ps, UNISTR2, trn->num_entries2)) == NULL) {
                                return False;
                        }
                }
@@ -2771,7 +2781,7 @@ static BOOL lsa_io_luid_attr(const char *desc, LUID_ATTR *out, prs_struct *ps, i
 
 static BOOL lsa_io_privilege_set(const char *desc, PRIVILEGE_SET *out, prs_struct *ps, int depth)
 {
-       uint32 i;
+       uint32 i, dummy;
 
        prs_debug(ps, depth, desc, "lsa_io_privilege_set");
        depth++;
@@ -2779,7 +2789,7 @@ static BOOL lsa_io_privilege_set(const char *desc, PRIVILEGE_SET *out, prs_struc
        if(!prs_align(ps))
                return False;
  
-       if(!prs_uint32("count", ps, depth, &out->count))
+       if(!prs_uint32("count", ps, depth, &dummy))
                return False;
        if(!prs_uint32("control", ps, depth, &out->control))
                return False;
index 56fffcc26ee0556bb03936afd8d0605c8ba8d6df..84f64a2a6635d7937e3e9a8039725fdf7d1f9e0a 100644 (file)
@@ -644,7 +644,7 @@ BOOL prs_pointer( const char *name, prs_struct *ps, int depth,
                return True;
 
        if (UNMARSHALLING(ps)) {
-               if ( !(*data = PRS_ALLOC_MEM_VOID(ps, data_size)) )
+               if ( !(*data = PRS_ALLOC_MEM(ps, char, data_size)) )
                        return False;
        }
 
index bc33062264884dba79106575b03cbbec8449815b..72accad1b6cd0de39f5f7f106b1e1927460bb60b 100644 (file)
@@ -183,13 +183,12 @@ BOOL sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
                return False;
 
        if (UNMARSHALLING(ps)) {
-               /*
-                * Even if the num_aces is zero, allocate memory as there's a difference
-                * between a non-present DACL (allow all access) and a DACL with no ACE's
-                * (allow no access).
-                */
-               if((psa->aces = PRS_ALLOC_MEM(ps, SEC_ACE, psa->num_aces+1)) == NULL)
-                       return False;
+               if (psa->num_aces) {
+                       if((psa->aces = PRS_ALLOC_MEM(ps, SEC_ACE, psa->num_aces)) == NULL)
+                               return False;
+               } else {
+                       psa->aces = NULL;
+               }
        }
 
        for (i = 0; i < psa->num_aces; i++) {
index 98280ee8444930644fb6c2a942a192bb514680d1..936587fdf5a1478635fd5c7bd10bd6d64879960b 100644 (file)
@@ -230,6 +230,10 @@ static BOOL smb_io_notify_option_type_data(const char *desc, SPOOL_NOTIFY_OPTION
        if (type->count2 != type->count)
                DEBUG(4,("What a mess, count was %x now is %x !\n", type->count, type->count2));
 
+       if (type->count2 > MAX_NOTIFY_TYPE_FOR_NOW) {
+               return False;
+       }
+
        /* parse the option type data */
        for(i=0;i<type->count2;i++)
                if(!prs_uint16("fields",ps,depth,&type->fields[i]))