$OpenBSD: patch-src_cabextract_c,v 1.3 2015/02/18 13:04:09 sthen Exp $

Prevent leading slashes check being evaded with malformed UTF-8
http://sourceforge.net/p/libmspack/code/217/

--- src/cabextract.c.orig	Sat Jan 10 18:13:11 2015
+++ src/cabextract.c	Wed Feb 18 13:01:06 2015
@@ -738,7 +738,7 @@ static int unix_path_seperators(struct mscabd_file *fi
 static char *create_output_name(unsigned char *fname, unsigned char *dir,
                          int lower, int isunix, int utf8)
 {
-  unsigned char *p, *name, c, *fe, sep, slash;
+  unsigned char *p, *name, c, *o, *fe, sep, slash;
   int x;
 
   sep   = (isunix) ? '/'  : '\\'; /* the path-seperator */
@@ -750,7 +750,7 @@ static char *create_output_name(unsigned char *fname, 
   if (utf8) x *= 4;
   /* length of output directory */
   if (dir) x += strlen((char *) dir);
-  x += 2;
+  x += 3;
 
   if (!(name = malloc(x))) {
     fprintf(stderr, "Can't allocate output filename (%u bytes)\n", x);
@@ -766,16 +766,14 @@ static char *create_output_name(unsigned char *fname, 
     strcat((char *) name, "/");
   }
 
-  /* remove leading slashes */
-  while (*fname == sep) fname++;
-
   /* copy from fi->filename to new name, converting MS-DOS slashes to UNIX
    * slashes as we go. Also lowercases characters if needed.
    */
-  p = &name[strlen((char *)name)];    /* p  = start of output filename */
+  p = o = &name[strlen((char *)name)]; /* p = o = start of output filename */
   fe = &fname[strlen((char *)fname)]; /* fe = end of input filename */
 
   if (utf8) {
+    int first = 1;
     /* handle UTF-8 encoded filenames (see RFC 3629). This doesn't reject bad
      * UTF-8 with overlong encodings, but does re-encode it as valid UTF-8.
      */
@@ -815,6 +813,10 @@ static char *create_output_name(unsigned char *fname, 
         x = 0xFFFD; /* invalid code point or cheeky null byte */
       }
 
+      /* remove leading slashes */
+      if (first && x == sep) continue;
+      first = 0;
+
       /* whatever is the path seperator -> '/'
        * whatever is the other slash    -> '\\'
        * otherwise, if lower is set, the lowercase version */
@@ -851,6 +853,7 @@ static char *create_output_name(unsigned char *fname, 
   }
   else {
     /* regular non-utf8 version */
+    while (*fname == sep) fname++;
     do {
       c = *fname++;
       if      (c == sep)   c = '/';
@@ -861,11 +864,16 @@ static char *create_output_name(unsigned char *fname, 
 
   /* search for "../" in cab filename part and change to "xx/".  This
    * prevents any unintended directory traversal. */
-  for (p = &name[dir ? strlen((char *) dir)+1 : 0]; *p; p++) {
+  for (p = o; *p; p++) {
     if ((p[0] == '.') && (p[1] == '.') && (p[2] == '/')) {
       p[0] = p[1] = 'x';
       p += 2;
     }
+  }
+
+  /* change filename composed entirely of leading slashes to "x" */
+  if (strlen(o) == 0) {
+      strcat(o, "x");
   }
 
   return (char *) name;
