Hi

I have installed the cfengine 2.1.20 package from Fedora Core 5 Extras, and when I run it with my previous configuration, cfagent fails with the message:

        *** stack smashing detected ***: cfagent terminated
        Aborted

Apparently a problem found by the "Buffer Overflow detection ..." mentioned at http://fedora.redhat.com/docs/release-notes/fc5/#id2934140

I installed debuginfo and gdb, and run this with gdb cfagent, run --no-splay -v, and after the abort, see the stack trace below. Note the "out of bounds" at #5, this was actually the first arg to HandleIPRange in #6. Looking closer, I see that arg 2 to FuzzySetMatch is that of an IPv6 interface being matched against a definition in my cfagent.conf:

        ikr = ( IPRange(129.132.166.67-98) ) #HostRange(ikr,1-32)

This causes trouble at lines 833 and 845 of item.c, which sscanf the entire arg 2 of FuzzySetMatch ("fe80::240:63ff:fee2:1ecc") into a buffer of length 8, killing the canary on the stack.

I see similar behaviour at line 840 of item.c by crafting a special arg in the configuration file (IPRange(123456789....), more than 7 chars before the dot) - this writes beyond buffer1.

I don't have an easy patch at hand. First, I think that the address family of both args should be checked (false if ipv4 and ipv6 are mixed). Second I was thinking of using stuff like "if (2==sscanf(sp1, "%d-%d", &min, &max)) ... else if (1==sscanf(sp1, "%d", &min)) { max=min; } ...", but it is not satisfactory either.

I also attach a small test prog isolating the problem.

Kind regards, Martin


(gdb) where
#0  0x0050a402 in __kernel_vsyscall ()
#1  0x00550159 in raise () from /lib/libc.so.6
#2  0x005516e3 in abort () from /lib/libc.so.6
#3  0x00584a1b in __libc_message () from /lib/libc.so.6
#4  0x00604e45 in __stack_chk_fail () from /lib/libc.so.6
#5  0x08069753 in FuzzySetMatch (s1=0x3 <Address 0x3 out of bounds>,
    s2=0x8cf3528 "fe80::240:63ff:fee2:1ecc") at item.c:968
#6  0x0807a1b1 in HandleIPRange (args=0xbfc6fce8 "129.132.166.67-98",
    value=0xbfc72128 "XX_CF_opposite_any_XX") at functions.c:422
#7 0x0807a7d7 in EvaluateFunction (f=0x8d18dcd "IPRange(129.132.166.67-98)",
    value=0xbfc72128 "XX_CF_opposite_any_XX") at functions.c:147
#8  0x08074e44 in HandleFunctionObject (
    fn=0x8d18dcd "IPRange(129.132.166.67-98)") at parse.c:648
#9  0x080a6b0e in yylex () at cflex.l:431
#10 0x0809f4ce in yyparse () at y.tab.c:1189
#11 0x08073cbc in ParseFile (
    filename=0xbfc73738 "/var/cfengine/inputs/cfagent.conf",
    env=0xbfc73738 "/var/cfengine/inputs/cfagent.conf") at parse.c:1051
#12 0x080749e9 in ParseInputFile (file=0x8136200 "cfagent.conf") at parse.c:82
#13 0x0804f466 in main (argc=3, argv=0xbfc74804) at cfagent.c:140
#14 0x0053d7e4 in __libc_start_main () from /lib/libc.so.6
#15 0x0804b231 in _start ()
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>

#define CF_ADDRSIZE 128
#define false 0
#define CF_BUFSIZE 4096
#define true  1
#define Debug   if (DEBUG || D1 || D2) printf

char OUTPUT[CF_BUFSIZE*2];

enum cfoutputlevel
   {
   cfsilent,
   cfinform,
   cfverbose,
   cfeditverbose,
   cferror,
   cflogonly,
   cfloginform
   };

void CfLog (enum cfoutputlevel level, char *string, char *errstr)
{
}

int DEBUG=0, D1=0, D2=0;

void FatalError(s)

char *s;

{
//fprintf (stderr,"%s:%s:%s\n",VPREFIX,VCURRENTFILE,s);
//SILENT = true;
//ReleaseCurrentLock();
//closelog();
exit(1);
}

char *sockaddr_ntop(struct sockaddr *sa) {
        static char addrbuf[20];
        struct in_addr addr;
        switch (sa->sa_family)
   {
   case AF_INET:
       Debug("IPV4 address\n");
       snprintf(addrbuf,20,"%.19s",inet_ntoa(((struct sockaddr_in 
*)sa)->sin_addr));
       break;

#ifdef AF_LOCAL
   case AF_LOCAL:
       Debug("Local socket\n") ;
       strcpy(addrbuf, "127.0.0.1") ;
       break;
#endif
   default:
       Debug("Address family was %d\n",sa->sa_family);
       FatalError("Software failure in sockaddr_ntop\n");
   }

        Debug("sockaddr_ntop(%s)\n",addrbuf);
        return addrbuf;
}

void *sockaddr_pton(int af,void *src) { int err;
  static struct sockaddr_in adr;

switch (af)
   {
   case AF_INET:
       memset(&adr,0,sizeof(adr));
       adr.sin_family = AF_INET;
       adr.sin_addr.s_addr = inet_addr(src);
       Debug("Coded ipv4 %s\n",sockaddr_ntop((struct sockaddr *)&adr));
       return (void *)&adr;
   default:
       Debug("Address family was %d\n",af);
       FatalError("Software failure in sockaddr_pton\n");
   }

 return NULL;
}

int FuzzySetMatch(char *s1,char *s2)

/* Match two IP strings - with : or . in hex or decimal
   s1 is the test string, and s2 is the reference e.g.
   FuzzySetMatch("128.39.74.10/23","128.39.75.56") == 0 */

{ short isCIDR = false, isrange = false, isv6 = false, isv4 = false;
  char address[CF_ADDRSIZE];
  int mask;
  unsigned long a1,a2;

if (strstr(s1,"/") != 0)
   {
   isCIDR = true;
   }

if (strstr(s1,"-") != 0)
   {
   isrange = true;
   }

if (strstr(s1,".") != 0)
   {
   isv4 = true;
   }

if (strstr(s1,":") != 0)
   {
   isv6 = true;
   }

if (isv4 && isv6)
   {
   snprintf(OUTPUT,CF_BUFSIZE,"Mixture of IPv6 and IPv4 addresses: %s",s1);
   CfLog(cferror,OUTPUT,"");
   return -1;
   }

if (isCIDR && isrange)
   {
   snprintf(OUTPUT,CF_BUFSIZE,"Cannot mix CIDR notation with xxx-yyy range 
notation: %s",s1);
   CfLog(cferror,OUTPUT,"");
   return -1;
   }

if (!(isv6 || isv4))
   {
   snprintf(OUTPUT,CF_BUFSIZE,"Not a valid address range - or not a fully 
qualified name: %s",s1);
   CfLog(cferror,OUTPUT,"");
   return -1;
   }

if (!(isrange||isCIDR)) 
   {
   return strncmp(s1,s2,strlen(s1)); /* do partial string match */
   }

 
if (isv4)
   {
   struct sockaddr_in addr1,addr2;
   int shift;

   memset(&addr1,0,sizeof(struct sockaddr_in));
   memset(&addr2,0,sizeof(struct sockaddr_in));
   
   if (isCIDR)
      {
      address[0] = '\0';
      mask = 0;
      sscanf(s1,"%16[^/]/%d",address,&mask);
      shift = 32 - mask;
      
      memcpy(&addr1,(struct sockaddr_in *) 
sockaddr_pton(AF_INET,address),sizeof(struct sockaddr_in));
      memcpy(&addr2,(struct sockaddr_in *) 
sockaddr_pton(AF_INET,s2),sizeof(struct sockaddr_in));

      a1 = htonl(addr1.sin_addr.s_addr);
      a2 = htonl(addr2.sin_addr.s_addr);
      
      a1 = a1 >> shift;
      a2 = a2 >> shift;
      
      if (a1 == a2)
         {
         return 0;
         }
      else
         {
         return -1;
         }
      }
   else
      {
      long i, from = -1, to = -1, cmp = -1;
      char *sp1,*sp2,buffer1[8],buffer2[8];
      
      sp1 = s1;
      sp2 = s2;
      
      for (i = 0; i < 4; i++)
         {
         if (sscanf(sp1,"%[^.]",buffer1) <= 0)
            {
            break;
            }
         sp1 += strlen(buffer1)+1;
         sscanf(sp2,"%[^.]",buffer2);
         sp2 += strlen(buffer2)+1;
         
         if (strstr(buffer1,"-"))
            {
            sscanf(buffer1,"%ld-%ld",&from,&to);
            sscanf(buffer2,"%ld",&cmp);
            
            if (from < 0 || to < 0)
               {
               Debug("Couldn't read range\n");
               return -1;
               }
            
            if ((from > cmp) || (cmp > to))
               {
               Debug("Out of range %d > %d > %d (range 
%s)\n",from,cmp,to,buffer2);
               return -1;
               }
            }
         else
            {
            sscanf(buffer1,"%ld",&from);
            sscanf(buffer2,"%ld",&cmp);
            
            if (from != cmp)
               {
               Debug("Unequal\n");
               return -1;
               }
            }
         
         Debug("Matched octet %s with %s\n",buffer1,buffer2);
         }
      
      Debug("Matched IP range\n");
      return 0;
      }
   }

#if defined(HAVE_GETADDRINFO) && !defined(DARWIN)
if (isv6)
   {
   struct sockaddr_in6 addr1,addr2;
   int blocks, i;

   memset(&addr1,0,sizeof(struct sockaddr_in6));
   memset(&addr2,0,sizeof(struct sockaddr_in6));
   
   if (isCIDR)
      {
      address[0] = '\0';
      mask = 0;
      sscanf(s1,"%40[^/]/%d",address,&mask);
      blocks = mask/8;

      if (mask % 8 != 0)
         {
         CfLog(cferror,"Cannot handle ipv6 masks which are not 8 bit multiples 
(fix me)","");
         return -1;
         }
      
      memcpy(&addr1,(struct sockaddr_in6 *) 
sockaddr_pton(AF_INET6,address),sizeof(struct sockaddr_in6));
      memcpy(&addr2,(struct sockaddr_in6 *) 
sockaddr_pton(AF_INET6,s2),sizeof(struct sockaddr_in6));
      
      for (i = 0; i < blocks; i++) /* blocks < 16 */
         {
         if (addr1.sin6_addr.s6_addr[i] != addr2.sin6_addr.s6_addr[i])
            {
            return -1;
            }
         }
      return 0;
      }
   else
      {
      long i, from = -1, to = -1, cmp = -1;
      char *sp1,*sp2,buffer1[16],buffer2[16];

      sp1 = s1;
      sp2 = s2;
      
      for (i = 0; i < 8; i++)
         {
         sscanf(sp1,"%[^:]",buffer1);
         sp1 += strlen(buffer1)+1;
         sscanf(sp2,"%[^:]",buffer2);
         sp2 += strlen(buffer2)+1;
         
         if (strstr(buffer1,"-"))
            {
            sscanf(buffer1,"%lx-%lx",&from,&to);
            sscanf(buffer2,"%lx",&cmp);
            
            if (from < 0 || to < 0)
               {
               return -1;
               }
            
            if ((from >= cmp) || (cmp > to))
               {
               printf("%x < %x < %x\n",from,cmp,to);
               return -1;
               }
            }
         else
            {
            sscanf(buffer1,"%ld",&from);
            sscanf(buffer2,"%ld",&cmp);
            
            if (from != cmp)
               {
               return -1;
               }
            }
         }
      
      return 0;
      }
   }
#endif 

return -1; 
}

int main(int argc, char *argv[]) {
        FuzzySetMatch("129.132.166.67-98", "fe80::240:63ff:fee2:1ecc");
}
_______________________________________________
Bug-cfengine mailing list
[email protected]
http://cfengine.org/mailman/listinfo/bug-cfengine

Reply via email to