The following patches fix two security bugs: 1. buffer overflows (CAN-2004-0234) 2. directory traversal problems (CAN-2004-0235) 3. symlink vulnerability --- src/header.c.orig 2000-10-05 19:36:03.000000000 +0200 +++ src/header.c 2004-04-21 14:30:52.000000000 +0200 @@ -538,6 +538,10 @@ /* * filename */ + if (header_size >= 256) { + fprintf(stderr, "Possible buffer overflow hack attack, type #1\n"); + exit(109); + } for (i = 0; i < header_size - 3; i++) hdr->name[i] = (char) get_byte(); hdr->name[header_size - 3] = '\0'; @@ -547,6 +551,10 @@ /* * directory */ + if (header_size >= FILENAME_LENGTH) { + fprintf(stderr, "Possible buffer overflow hack attack, type #2\n"); + exit(110); + } for (i = 0; i < header_size - 3; i++) dirname[i] = (char) get_byte(); dirname[header_size - 3] = '\0'; --- src/lhext.c.orig 2000-10-04 16:57:38.000000000 +0200 +++ src/lhext.c 2004-04-21 14:30:52.000000000 +0200 @@ -190,8 +190,13 @@ q = (char *) rindex(hdr->name, '/') + 1; } else { + if (is_directory_traversal(q)) { + fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q); + exit(111); + } + if (*q == '/') { - q++; + while (*q == '/') { q++; } /* * if OSK then strip device name */ @@ -419,6 +424,33 @@ return; } +int +is_directory_traversal(char *string) +{ + unsigned int type = 0; /* 0 = new, 1 = only dots, 2 = other chars than dots */ + char *temp; + + temp = string; + + while (*temp != 0) { + if (temp[0] == '/') { + if (type == 1) { return 1; } + type = 0; + temp++; + continue; + } + + if ((temp[0] == '.') && (type < 2)) + type = 1; + if (temp[0] != '.') + type = 2; + + temp++; + } /* while */ + + return (type == 1); +} + /* Local Variables: */ /* mode:c */ /* tab-width:4 */ --- src/lhext.c.symlink 2000-10-04 10:57:38.000000000 -0400 +++ src/lhext.c 2003-05-19 22:55:57.000000000 -0400 @@ -351,6 +351,7 @@ extract_one(afp, hdr) } unlink(bb1); + make_parent_path(bb1); l_code = symlink(bb2, bb1); if (l_code < 0) { if (quiet != TRUE) ################################################################ The following patches are fixing the following Security bugs: Fixed multiple vulnerabilities; bug #62618 from GenToo Gentoo Linux Security Advisory GLSA 200409-13 CAN-2004-0694, CAN-2004-0745, CAN-2004-0769, CAN-2004-0771 ################################################################ Index: src/lha.h --- src/lha.h.orig 2000-10-05 19:35:38 +0200 +++ src/lha.h 2004-09-20 14:59:52 +0200 @@ -16,6 +16,7 @@ #include #include #include +#include #include Index: src/lha_macro.h --- src/lha_macro.h.orig 2000-10-04 16:57:38 +0200 +++ src/lha_macro.h 2004-09-20 14:59:52 +0200 @@ -53,7 +53,7 @@ #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 -#endif /* SEEK_SET +#endif /* SEEK_SET */ /* non-integral functions */ Index: src/lharc.c --- src/lharc.c.orig 2000-10-05 19:33:34 +0200 +++ src/lharc.c 2004-09-20 14:59:52 +0200 @@ -830,9 +830,10 @@ DIRENTRY *dp; struct stat tmp_stbuf, arc_stbuf, fil_stbuf; - strcpy(newname, name); + strncpy(newname, name, sizeof(newname)); + newname[sizeof(newname)-1] = 0; len = strlen(name); - if (len > 0 && newname[len - 1] != '/') + if (len > 0 && newname[len - 1] != '/' && len < (sizeof(newname)-1)) newname[len++] = '/'; dirp = opendir(name); @@ -846,6 +847,11 @@ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { n = NAMLEN(dp); + if (len >= (sizeof(newname)-1) || + (len+n) >= (sizeof(newname)-1) || + n <= 0 || + (len+n) <= 0) + break; strncpy(newname + len, dp->d_name, n); newname[len + n] = '\0'; if (GETSTAT(newname, &fil_stbuf) < 0) @@ -903,7 +909,8 @@ strcpy(temporary_name, TMP_FILENAME_TEMPLATE); } else { - sprintf(temporary_name, "%s/lhXXXXXX", extract_directory); + snprintf(temporary_name, sizeof(temporary_name), + "%s/lhXXXXXX", extract_directory); } #ifdef MKSTEMP mkstemp(temporary_name); @@ -913,10 +920,16 @@ #else char *p, *s; - strcpy(temporary_name, archive_name); + strncpy(temporary_name, archive_name, sizeof(temporary_name)); + temporary_name[sizeof(temporary_name)-1] = 0; for (p = temporary_name, s = (char *) 0; *p; p++) if (*p == '/') s = p; + + if( sizeof(temporary_name) - ((size_t) (s-temporary_name)) - 1 + <= strlen("lhXXXXXX")) + exit(-1); + strcpy((s ? s + 1 : temporary_name), "lhXXXXXX"); #ifdef MKSTEMP mkstemp(temporary_name); @@ -1052,7 +1065,8 @@ if (open_old_archive_1(archive_name, &fp)) return fp; - sprintf(expanded_archive_name, "%s.lzh", archive_name); + snprintf(expanded_archive_name, sizeof(expanded_archive_name), + "%s.lzh", archive_name); if (open_old_archive_1(expanded_archive_name, &fp)) { archive_name = expanded_archive_name; return fp; @@ -1061,7 +1075,8 @@ * if ( (errno&0xffff)!=E_PNNF ) { archive_name = * expanded_archive_name; return NULL; } */ - sprintf(expanded_archive_name, "%s.lzs", archive_name); + snprintf(expanded_archive_name, sizeof(expanded_archive_name), + "%s.lzs", archive_name); if (open_old_archive_1(expanded_archive_name, &fp)) { archive_name = expanded_archive_name; return fp; Index: src/lhext.c --- src/lhext.c.orig 2004-09-20 14:59:52 +0200 +++ src/lhext.c 2004-09-20 14:59:52 +0200 @@ -82,7 +82,8 @@ register char *p; /* make parent directory name into PATH for recursive call */ - strcpy(path, name); + memset(path, 0, sizeof(path)); + strncpy(path, name, sizeof(path)-1); for (p = path + strlen(path); p > path; p--) if (p[-1] == '/') { *--p = '\0'; @@ -212,9 +213,11 @@ } if (extract_directory) - sprintf(name, "%s/%s", extract_directory, q); - else - strcpy(name, q); + snprintf(name, sizeof(name), "%s/%s", extract_directory, q); + else { + strncpy(name, q, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } /* LZHDIRS_METHODを持つヘッダをチェックする */ @@ -335,7 +338,8 @@ if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) { char buf[256], *bb1, *bb2; int l_code; - strcpy(buf, name); + strncpy(buf, name, sizeof(buf)); + buf[sizeof(buf)-1] = 0; bb1 = strtok(buf, "|"); bb2 = strtok(NULL, "|"); @@ -365,9 +369,10 @@ if (quiet != TRUE) { printf("Symbolic Link %s -> %s\n", bb1, bb2); } - strcpy(name, bb1); /* Symbolic's name set */ + strncpy(name, bb1, 255); /* Symbolic's name set */ + name[255] = 0; #else - sprintf(buf, "%s -> %s", bb1, bb2); + sprintf(buf, sizeof(buf), "%s -> %s", bb1, bb2); warning("Can't make Symbolic Link", buf); return; #endif Index: src/lhlist.c --- src/lhlist.c.orig 2000-10-04 16:57:38 +0200 +++ src/lhlist.c 2004-09-20 14:59:52 +0200 @@ -250,7 +250,8 @@ printf(" %s", hdr->name); else { char buf[256], *b1, *b2; - strcpy(buf, hdr->name); + strncpy(buf, hdr->name, sizeof(buf)); + buf[sizeof(buf)-1] = 0; b1 = strtok(buf, "|"); b2 = strtok(NULL, "|"); printf(" %s -> %s", b1, b2); Index: src/util.c --- src/util.c.orig 2000-10-04 16:57:38 +0200 +++ src/util.c 2004-09-20 14:59:52 +0200 @@ -276,21 +276,27 @@ char *path; { int stat, rtn = 0; - char *cmdname; - if ((cmdname = (char *) malloc(strlen(RMDIRPATH) + 1 + strlen(path) + 1)) - == 0) + pid_t child; + + + /* XXX thomas: shell meta chars in path could exec commands */ + /* therefore we should avoid using system() */ + if ((child = fork()) < 0) + return (-1); /* fork error */ + else if (child) { /* parent process */ + while (child != wait(&stat)) /* ignore signals */ + continue; + } + else { /* child process */ + execl(RMDIRPATH, "rmdir", path, (char *) 0); + /* never come here except execl is error */ return (-1); - strcpy(cmdname, RMDIRPATH); - *(cmdname + strlen(RMDIRPATH)) = ' '; - strcpy(cmdname + strlen(RMDIRPATH) + 1, path); - if ((stat = system(cmdname)) < 0) - rtn = -1; /* fork or exec error */ - else if (stat) { /* RMDIR command error */ - errno = EIO; - rtn = -1; } - free(cmdname); - return (rtn); + if (stat != 0) { + errno = EIO; /* cannot get error num. */ + return (-1); + } + return (0); } /* ------------------------------------------------------------------------ */ Index: src/header.c --- src/header.c.orig 2004-09-20 14:59:52 +0200 +++ src/header.c 2004-09-20 14:59:52 +0200 @@ -656,8 +656,17 @@ } if (dir_length) { + if ((dir_length + name_length) > sizeof(dirname)) { + fprintf(stderr, "Insufficient buffer size\n"); + exit(112); + } strcat(dirname, hdr->name); - strcpy(hdr->name, dirname); + + if ((dir_length + name_length) > sizeof(hdr->name)) { + fprintf(stderr, "Insufficient buffer size\n"); + exit(112); + } + strncpy(hdr->name, dirname, sizeof(hdr->name)); name_length += dir_length; }