diff options
author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2013-02-19 13:27:20 +0000 |
---|---|---|
committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2013-02-19 13:27:20 +0000 |
commit | 1c7fe7463d0204f08806b9da6d5d50fb7f75c5ad (patch) | |
tree | 227fb75a2a5f50439b8fc8344fcd41525e4fcd8f | |
parent | 97a8436b12801792594457d69cbb5a6e131755d9 (diff) |
Fix Denial of Service vulnerability in named(8) with DNS64. [13:01]releng/7.4
Fix Denial of Service vulnerability in libc's glob(3) functionality.
[13:02]
Security: CVE-2012-5688
Security: FreeBSD-SA-13:01.bind
Security: CVE-2010-2632
Security: FreeBSD-SA-13:02.libc
Approved by: so (simon, bz)
Notes
Notes:
svn path=/releng/7.4/; revision=246989
-rw-r--r-- | UPDATING | 3 | ||||
-rw-r--r-- | lib/libc/gen/glob.c | 101 | ||||
-rw-r--r-- | sys/conf/newvers.sh | 2 |
3 files changed, 83 insertions, 23 deletions
@@ -8,6 +8,9 @@ Items affecting the ports and packages system can be found in /usr/ports/UPDATING. Please read that file before running portupgrade. +20130218: p12 FreeBSD-SA-13:02.libc + Fix Denial of Service vulnerability in libc's glob(3) functionality. + 20121122: p11 FreeBSD-SA-12:06.bind FreeBSD-SA-12:08.linux Fix multiple Denial of Service vulnerabilities with named(8). diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c index fa14394a50c1..b39d7ad6d005 100644 --- a/lib/libc/gen/glob.c +++ b/lib/libc/gen/glob.c @@ -89,6 +89,25 @@ __FBSDID("$FreeBSD$"); #include "collate.h" +/* + * glob(3) expansion limits. Stop the expansion if any of these limits + * is reached. This caps the runtime in the face of DoS attacks. See + * also CVE-2010-2632 + */ +#define GLOB_LIMIT_BRACE 128 /* number of brace calls */ +#define GLOB_LIMIT_PATH 65536 /* number of path elements */ +#define GLOB_LIMIT_READDIR 16384 /* number of readdirs */ +#define GLOB_LIMIT_STAT 1024 /* number of stat system calls */ +#define GLOB_LIMIT_STRING ARG_MAX /* maximum total size for paths */ + +struct glob_limit { + size_t l_brace_cnt; + size_t l_path_lim; + size_t l_readdir_cnt; + size_t l_stat_cnt; + size_t l_string_cnt; +}; + #define DOLLAR '$' #define DOT '.' #define EOS '\0' @@ -148,15 +167,18 @@ static Char *g_strchr(Char *, wchar_t); static Char *g_strcat(Char *, const Char *); #endif static int g_stat(Char *, struct stat *, glob_t *); -static int glob0(const Char *, glob_t *, size_t *); -static int glob1(Char *, glob_t *, size_t *); -static int glob2(Char *, Char *, Char *, Char *, glob_t *, size_t *); -static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *); -static int globextend(const Char *, glob_t *, size_t *); -static const Char * +static int glob0(const Char *, glob_t *, struct glob_limit *); +static int glob1(Char *, glob_t *, struct glob_limit *); +static int glob2(Char *, Char *, Char *, Char *, glob_t *, + struct glob_limit *); +static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, + struct glob_limit *); +static int globextend(const Char *, glob_t *, struct glob_limit *); +static const Char * globtilde(const Char *, Char *, size_t, glob_t *); -static int globexp1(const Char *, glob_t *, size_t *); -static int globexp2(const Char *, const Char *, glob_t *, int *, size_t *); +static int globexp1(const Char *, glob_t *, struct glob_limit *); +static int globexp2(const Char *, const Char *, glob_t *, int *, + struct glob_limit *); static int match(Char *, Char *, Char *); #ifdef DEBUG static void qprintf(const char *, Char *); @@ -165,8 +187,8 @@ static void qprintf(const char *, Char *); int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) { + struct glob_limit limit = { 0, 0, 0, 0, 0 }; const char *patnext; - size_t limit; Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot; mbstate_t mbs; wchar_t wc; @@ -180,11 +202,10 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t * pglob->gl_offs = 0; } if (flags & GLOB_LIMIT) { - limit = pglob->gl_matchc; - if (limit == 0) - limit = ARG_MAX; - } else - limit = 0; + limit.l_path_lim = pglob->gl_matchc; + if (limit.l_path_lim == 0) + limit.l_path_lim = GLOB_LIMIT_PATH; + } pglob->gl_flags = flags & ~GLOB_MAGCHAR; pglob->gl_errfunc = errfunc; pglob->gl_matchc = 0; @@ -237,11 +258,17 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t * * characters */ static int -globexp1(const Char *pattern, glob_t *pglob, size_t *limit) +globexp1(const Char *pattern, glob_t *pglob, struct glob_limit *limit) { const Char* ptr = pattern; int rv; + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { + errno = 0; + return (GLOB_NOSPACE); + } + /* Protect a single {}, for find(1), like csh */ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) return glob0(pattern, pglob, limit); @@ -260,7 +287,8 @@ globexp1(const Char *pattern, glob_t *pglob, size_t *limit) * If it fails then it tries to glob the rest of the pattern and returns. */ static int -globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv, size_t *limit) +globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv, + struct glob_limit *limit) { int i; Char *lm, *ls; @@ -430,7 +458,7 @@ globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) * if things went well, nonzero if errors occurred. */ static int -glob0(const Char *pattern, glob_t *pglob, size_t *limit) +glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit) { const Char *qpatnext; int err; @@ -523,7 +551,7 @@ compare(const void *p, const void *q) } static int -glob1(Char *pattern, glob_t *pglob, size_t *limit) +glob1(Char *pattern, glob_t *pglob, struct glob_limit *limit) { Char pathbuf[MAXPATHLEN]; @@ -541,7 +569,7 @@ glob1(Char *pattern, glob_t *pglob, size_t *limit) */ static int glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, - glob_t *pglob, size_t *limit) + glob_t *pglob, struct glob_limit *limit) { struct stat sb; Char *p, *q; @@ -557,6 +585,15 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, if (g_lstat(pathbuf, &sb, pglob)) return(0); + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_stat_cnt++ >= GLOB_LIMIT_STAT) { + errno = 0; + if (pathend + 1 > pathend_last) + return (GLOB_ABORTED); + *pathend++ = SEP; + *pathend = EOS; + return (GLOB_NOSPACE); + } if (((pglob->gl_flags & GLOB_MARK) && pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) || (S_ISLNK(sb.st_mode) && @@ -600,7 +637,7 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, static int glob3(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, Char *restpattern, - glob_t *pglob, size_t *limit) + glob_t *pglob, struct glob_limit *limit) { struct dirent *dp; DIR *dirp; @@ -646,6 +683,19 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last, size_t clen; mbstate_t mbs; + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) { + errno = 0; + if (pathend + 1 > pathend_last) + err = GLOB_ABORTED; + else { + *pathend++ = SEP; + *pathend = EOS; + err = GLOB_NOSPACE; + } + break; + } + /* Initial DOT must be matched literally. */ if (dp->d_name[0] == DOT && *pattern != DOT) continue; @@ -696,14 +746,15 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last, * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int -globextend(const Char *path, glob_t *pglob, size_t *limit) +globextend(const Char *path, glob_t *pglob, struct glob_limit *limit) { char **pathv; size_t i, newsize, len; char *copy; const Char *p; - if (*limit && pglob->gl_pathc > *limit) { + if ((pglob->gl_flags & GLOB_LIMIT) && + pglob->gl_matchc > limit->l_path_lim) { errno = 0; return (GLOB_NOSPACE); } @@ -731,6 +782,12 @@ globextend(const Char *path, glob_t *pglob, size_t *limit) for (p = path; *p++;) continue; len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ + limit->l_string_cnt += len; + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_string_cnt >= GLOB_LIMIT_STRING) { + errno = 0; + return (GLOB_NOSPACE); + } if ((copy = malloc(len)) != NULL) { if (g_Ctoc(path, copy, len)) { free(copy); diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh index 00cf19e9d736..dfb697860abd 100644 --- a/sys/conf/newvers.sh +++ b/sys/conf/newvers.sh @@ -32,7 +32,7 @@ TYPE="FreeBSD" REVISION="7.4" -BRANCH="RELEASE-p11" +BRANCH="RELEASE-p12" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi |