From 7cadd489698be117c47efcadd742651594429e6d Mon Sep 17 00:00:00 2001
From: Stuart Caie <kyzer@cabextract.org.uk>
Date: Sat, 20 Oct 2018 19:06:32 +0100
Subject: [PATCH] add anti "../" and leading slash protection to chmextract

---
 libmspack/ChangeLog        |  10 +++
 libmspack/src/chmextract.c | 140 +++++--------------------------------
 2 files changed, 27 insertions(+), 123 deletions(-)

Index: libmspack-0.4alpha/ChangeLog
===================================================================
--- libmspack-0.4alpha.orig/ChangeLog
+++ libmspack-0.4alpha/ChangeLog
@@ -1,3 +1,13 @@
+2018-10-20  Stuart Caie <kyzer@cabextract.org.uk>
+
+	* test/chmx.c: add anti "../" and leading slash protection to
+	chmextract. I'm not pleased about this. All the sample code provided
+	with libmspack is meant to be simple examples of library use, not
+	"productised" binaries. Making the "useful" code samples install
+	as binaries was a mistake. They were never intended to protect you
+	from unpacking archive files with relative/absolute paths, and I
+	would prefer that they never will be.
+
 2013-05-27  Stuart Caie <kyzer@4u.net>
 
 	* test/oabx.c: added new example command for unpacking OAB files.
@@ -401,7 +411,7 @@
 2005-03-22:  Stuart Caie   <kyzer@4u.net>
 
 	* system.h: now undefs "read", as the latest glibc defines read()
-	as a macro which messes everything up. Thanks to Ville Skytt� for
+	as a macro which messes everything up. Thanks to Ville Skytt� for
 	the update.
 
 2005-03-14:  Stuart Caie   <kyzer@4u.net>
Index: libmspack-0.4alpha/test/chmx.c
===================================================================
--- libmspack-0.4alpha.orig/test/chmx.c
+++ libmspack-0.4alpha/test/chmx.c
@@ -13,8 +13,6 @@
 
 mode_t user_umask;
 
-#define FILENAME ".test.chmx"
-
 /**
  * Ensures that all directory components in a filepath exist. New directory
  * components are created, if necessary.
@@ -39,126 +37,22 @@ static int ensure_filepath(char *path) {
   return 1;
 }
 
-/**
- * Creates a UNIX filename from the internal CAB filename and the given
- * parameters.
- *
- * @param fname  the internal CAB filename.
- * @param dir    a directory path to prepend to the output filename.
- * @param lower  if non-zero, filename should be made lower-case.
- * @param isunix if zero, MS-DOS path seperators are used in the internal
- *               CAB filename. If non-zero, UNIX path seperators are used.
- * @param utf8   if non-zero, the internal CAB filename is encoded in UTF8.
- * @return a freshly allocated and created filename, or NULL if there was
- *         not enough memory.
- * @see unix_path_seperators()
- */
-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 int x;
-
-  sep   = (isunix) ? '/'  : '\\'; /* the path-seperator */
-  slash = (isunix) ? '\\' : '/';  /* the other slash */
-
-  /* length of filename */
-  x = strlen((char *) fname);
-  /* UTF8 worst case scenario: tolower() expands all chars from 1 to 3 bytes */
-  if (utf8) x *= 3;
-  /* length of output directory */
-  if (dir) x += strlen((char *) dir);
-
-  if (!(name = (unsigned char *) malloc(x + 2))) {
-    fprintf(stderr, "out of memory!\n");
-    return NULL;
-  }
-  
-  /* start with blank name */
-  *name = '\0';
-
-  /* add output directory if needed */
-  if (dir) {
-    strcpy((char *) name, (char *) dir);
-    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)];
-  fe = &fname[strlen((char *)fname)];
-
-  if (utf8) {
-    /* UTF8 translates two-byte unicode characters into 1, 2 or 3 bytes.
-     * %000000000xxxxxxx -> %0xxxxxxx
-     * %00000xxxxxyyyyyy -> %110xxxxx %10yyyyyy
-     * %xxxxyyyyyyzzzzzz -> %1110xxxx %10yyyyyy %10zzzzzz
-     *
-     * Therefore, the inverse is as follows:
-     * First char:
-     *  0x00 - 0x7F = one byte char
-     *  0x80 - 0xBF = invalid
-     *  0xC0 - 0xDF = 2 byte char (next char only 0x80-0xBF is valid)
-     *  0xE0 - 0xEF = 3 byte char (next 2 chars only 0x80-0xBF is valid)
-     *  0xF0 - 0xFF = invalid
-     */
-    do {
-      if (fname >= fe) {
-	free(name);
-	return NULL;
-      }
-
-      /* get next UTF8 char */
-      if ((c = *fname++) < 0x80) x = c;
-      else {
-	if ((c >= 0xC0) && (c < 0xE0)) {
-	  x = (c & 0x1F) << 6;
-	  x |= *fname++ & 0x3F;
-	}
-	else if ((c >= 0xE0) && (c < 0xF0)) {
-	  x = (c & 0xF) << 12;
-	  x |= (*fname++ & 0x3F) << 6;
-	  x |= *fname++ & 0x3F;
-	}
-	else x = '?';
-      }
-
-      /* whatever is the path seperator -> '/'
-       * whatever is the other slash    -> '\\'
-       * otherwise, if lower is set, the lowercase version */
-      if      (x == sep)   x = '/';
-      else if (x == slash) x = '\\';
-      else if (lower)      x = (unsigned int) tolower((int) x);
-
-      /* integer back to UTF8 */
-      if (x < 0x80) {
-	*p++ = (unsigned char) x;
-      }
-      else if (x < 0x800) {
-	*p++ = 0xC0 | (x >> 6);   
-	*p++ = 0x80 | (x & 0x3F);
-      }
-      else {
-	*p++ = 0xE0 | (x >> 12);
-	*p++ = 0x80 | ((x >> 6) & 0x3F);
-	*p++ = 0x80 | (x & 0x3F);
-      }
-    } while (x);
-  }
-  else {
-    /* regular non-utf8 version */
-    do {
-      c = *fname++;
-      if      (c == sep)   c = '/';
-      else if (c == slash) c = '\\';
-      else if (lower)      c = (unsigned char) tolower((int) c);
-    } while ((*p++ = c));
-  }
-  return (char *) name;
+char *create_output_name(char *fname) {
+    char *out, *p;
+    if ((out = malloc(strlen(fname) + 1))) {
+        /* remove leading slashes */
+        while (*fname == '/' || *fname == '\\') fname++;
+        /* if that removes all characters, just call it "x" */
+        strcpy(out, (*fname) ? fname : "x");
+
+        /* change "../" to "xx/" */
+        for (p = out; *p; p++) {
+            if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\\')) {
+               p[0] = p[1] = 'x';
+            }
+        }
+    }
+    return out;
 }
 
 static int sortfunc(const void *a, const void *b) {
@@ -193,7 +87,7 @@ int main(int argc, char *argv[]) {
 	  qsort(f, numf, sizeof(struct mschmd_file *), &sortfunc);
 
 	  for (i = 0; i < numf; i++) {
-	    char *outname = create_output_name((unsigned char *)f[i]->filename,NULL,0,1,0);
+	    char *outname = create_output_name(f[i]->filename);
 	    printf("Extracting %s\n", outname);
 	    ensure_filepath(outname);
 	    if (chmd->extract(chmd, f[i], outname)) {
