[Pkg-apache-commits] r1317 - in /branches/squeeze-apr: changelog patches/00list patches/028_fnmatch_CVE-2011-0419.dpatch

sf at alioth.debian.org sf at alioth.debian.org
Thu May 19 05:40:31 UTC 2011


Author: sf
Date: Thu May 19 05:40:28 2011
New Revision: 1317

URL: http://svn.debian.org/wsvn/pkg-apache/?sc=1&rev=1317
Log:
commit 1.4.2-6+squeeze1

Added:
    branches/squeeze-apr/patches/028_fnmatch_CVE-2011-0419.dpatch
Modified:
    branches/squeeze-apr/changelog
    branches/squeeze-apr/patches/00list

Modified: branches/squeeze-apr/changelog
URL: http://svn.debian.org/wsvn/pkg-apache/branches/squeeze-apr/changelog?rev=1317&op=diff
==============================================================================
--- branches/squeeze-apr/changelog (original)
+++ branches/squeeze-apr/changelog Thu May 19 05:40:28 2011
@@ -1,3 +1,10 @@
+apr (1.4.2-6+squeeze1) stable-security; urgency=high
+
+  * Fix DoS in apr_fnmatch (CVE-2011-0419) which can be exploited via
+    Apache HTTPD's mod_autoindex.
+
+ -- Stefan Fritsch <sf at debian.org>  Fri, 13 May 2011 23:52:15 +0200
+
 apr (1.4.2-6) unstable; urgency=low
 
   * Also disable robust pthread mutexes on alpha, to fix hang in the test

Modified: branches/squeeze-apr/patches/00list
URL: http://svn.debian.org/wsvn/pkg-apache/branches/squeeze-apr/patches/00list?rev=1317&op=diff
==============================================================================
--- branches/squeeze-apr/patches/00list (original)
+++ branches/squeeze-apr/patches/00list Thu May 19 05:40:28 2011
@@ -8,3 +8,4 @@
 025_GNU_SOURCE_earlier.dpatch
 026_omit_extra_libs.dpatch
 027_apr_socket_addr_get_lifetime.dpatch
+028_fnmatch_CVE-2011-0419.dpatch

Added: branches/squeeze-apr/patches/028_fnmatch_CVE-2011-0419.dpatch
URL: http://svn.debian.org/wsvn/pkg-apache/branches/squeeze-apr/patches/028_fnmatch_CVE-2011-0419.dpatch?rev=1317&op=file
==============================================================================
--- branches/squeeze-apr/patches/028_fnmatch_CVE-2011-0419.dpatch (added)
+++ branches/squeeze-apr/patches/028_fnmatch_CVE-2011-0419.dpatch Thu May 19 05:40:28 2011
@@ -1,0 +1,761 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: merge apr_fnmatch from apr 1.4.4 to fix DoS CVE-2011-0419
+ at DPATCH@
+--- a/strings/apr_fnmatch.c	2006-10-08 22:15:56.014230272 +0200
++++ a/strings/apr_fnmatch.c	2011-05-03 06:51:24.000000000 +0200
+@@ -1,50 +1,58 @@
+-/*
+- * Copyright (c) 1989, 1993, 1994
+- *      The Regents of the University of California.  All rights reserved.
+- *
+- * This code is derived from software contributed to Berkeley by
+- * Guido van Rossum.
+- *
+- * Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions
+- * are met:
+- * 1. Redistributions of source code must retain the above copyright
+- *    notice, this list of conditions and the following disclaimer.
+- * 2. Redistributions in binary form must reproduce the above copyright
+- *    notice, this list of conditions and the following disclaimer in the
+- *    documentation and/or other materials provided with the distribution.
+- * 3. All advertising materials mentioning features or use of this software
+- *    must display the following acknowledgement:
+- *      This product includes software developed by the University of
+- *      California, Berkeley and its contributors.
+- * 4. Neither the name of the University nor the names of its contributors
+- *    may be used to endorse or promote products derived from this software
+- *    without specific prior written permission.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+- * SUCH DAMAGE.
+- */
++/* Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */ 
++
+ 
+-#if defined(LIBC_SCCS) && !defined(lint)
+-static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
+-#endif /* LIBC_SCCS and not lint */
+-
+-/*
+- * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+- * Compares a filename or pathname to a pattern.
++/* Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008
++ * as described in;
++ *   http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
++ *
++ * Filename pattern matches defined in section 2.13, "Pattern Matching Notation"
++ * from chapter 2. "Shell Command Language"
++ *   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
++ * where; 1. A bracket expression starting with an unquoted <circumflex> '^' 
++ * character CONTINUES to specify a non-matching list; 2. an explicit <period> '.' 
++ * in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading 
++ * <period> in a filename; 3. a <left-square-bracket> '[' which does not introduce
++ * a valid bracket expression is treated as an ordinary character; 4. a differing
++ * number of consecutive slashes within pattern and string will NOT match;
++ * 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character.
++ *
++ * Bracket expansion defined in section 9.3.5, "RE Bracket Expression",
++ * from chapter 9, "Regular Expressions"
++ *   http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
++ * with no support for collating symbols, equivalence class expressions or 
++ * character class expressions.  A partial range expression with a leading 
++ * hyphen following a valid range expression will match only the ordinary
++ * <hyphen> and the ending character (e.g. "[a-m-z]" will match characters 
++ * 'a' through 'm', a <hyphen> '-', or a 'z').
++ *
++ * NOTE: Only POSIX/C single byte locales are correctly supported at this time.
++ * Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results,
++ * particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and
++ * nonalpha characters within a range.
++ *
++ * XXX comments below indicate porting required for multi-byte character sets
++ * and non-POSIX locale collation orders; requires mbr* APIs to track shift
++ * state of pattern and string (rewinding pattern and string repeatedly).
++ *
++ * Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g.
++ * UTF-8, SHIFT-JIS, etc).  Any implementation allowing '\' as an alternate
++ * path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS.
+  */
+-#ifndef WIN32
+-#include "apr_private.h"
+-#endif
++
+ #include "apr_file_info.h"
+ #include "apr_fnmatch.h"
+ #include "apr_tables.h"
+@@ -55,196 +63,355 @@
+ # include <ctype.h>
+ #endif
+ 
+-#define	EOS	'\0'
+-
+-static const char *rangematch(const char *, int, int);
+ 
+-APR_DECLARE(apr_status_t) apr_fnmatch(const char *pattern, const char *string, int flags)
++/* Most MBCS/collation/case issues handled here.  Wildcard '*' is not handled.
++ * EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over, 
++ * however the "\/" sequence is advanced to '/'.
++ *
++ * Both pattern and string are **char to support pointer increment of arbitrary
++ * multibyte characters for the given locale, in a later iteration of this code
++ */
++static APR_INLINE int fnmatch_ch(const char **pattern, const char **string, int flags)
+ {
+-    const char *stringstart;
+-    char c, test;
+-
+-    for (stringstart = string;;) {
+-	switch (c = *pattern++) {
+-	case EOS:
+-	    return (*string == EOS ? APR_SUCCESS : APR_FNM_NOMATCH);
+-	case '?':
+-	    if (*string == EOS) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-	    if (*string == '/' && (flags & APR_FNM_PATHNAME)) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
+-		(string == stringstart ||
+-		 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-	    ++string;
+-	    break;
+-	case '*':
+-	    c = *pattern;
+-	    /* Collapse multiple stars. */
+-	    while (c == '*') {
+-		c = *++pattern;
+-	    }
+-
+-	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
+-		(string == stringstart ||
+-		 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-
+-	    /* Optimize for pattern with * at end or before /. */
+-	    if (c == EOS) {
+-		if (flags & APR_FNM_PATHNAME) {
+-		    return (strchr(string, '/') == NULL ? APR_SUCCESS : APR_FNM_NOMATCH);
+-		}
+-		else {
+-		    return (APR_SUCCESS);
+-		}
+-	    }
+-	    else if (c == '/' && flags & APR_FNM_PATHNAME) {
+-	        if ((string = strchr(string, '/')) == NULL) {
+-		    return (APR_FNM_NOMATCH);
+-		}
+-		break;
+-	    }
+-
+-	    /* General case, use recursion. */
+-	    while ((test = *string) != EOS) {
+-	        if (!apr_fnmatch(pattern, string, flags & ~APR_FNM_PERIOD)) {
+-		    return (APR_SUCCESS);
+-		}
+-		if (test == '/' && flags & APR_FNM_PATHNAME) {
+-		    break;
+-		}
+-		++string;
+-	    }
+-	    return (APR_FNM_NOMATCH);
+-	case '[':
+-	    if (*string == EOS) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-	    if (*string == '/' && flags & APR_FNM_PATHNAME) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
+-		(string == stringstart ||
+-		 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
+-	        return (APR_FNM_NOMATCH);
+-	    }
+-	    if ((pattern = rangematch(pattern, *string, flags)) == NULL) {
+-		return (APR_FNM_NOMATCH);
+-	    }
+-	    ++string;
+-	    break;
+-	case '\\':
+-	    if (!(flags & APR_FNM_NOESCAPE)) {
+-		if ((c = *pattern++) == EOS) {
+-		    c = '\\';
+-		    --pattern;
+-		}
+-	    }
+-	    /* FALLTHROUGH */
+-	default:
+-	    if (flags & APR_FNM_CASE_BLIND) {
+-	        if (apr_tolower(c) != apr_tolower(*string)) {
+-		    return (APR_FNM_NOMATCH);
+-		}
+-	    }
+-	    else if (c != *string) {
+-	        return (APR_FNM_NOMATCH);
+-	    }
+-	    string++;
+-	    break;
+-	}
+-    /* NOTREACHED */
++    const char * const mismatch = *pattern;
++    const int nocase = !!(flags & APR_FNM_CASE_BLIND);
++    const int escape = !(flags & APR_FNM_NOESCAPE);
++    const int slash = !!(flags & APR_FNM_PATHNAME);
++    int result = APR_FNM_NOMATCH;
++    const char *startch;
++    int negate;
++
++    if (**pattern == '[')
++    {
++        ++*pattern;
++
++        /* Handle negation, either leading ! or ^ operators (never both) */
++        negate = ((**pattern == '!') || (**pattern == '^'));
++        if (negate)
++            ++*pattern;
++
++        while (**pattern)
++        {
++            /* ']' is an ordinary character at the start of the range pattern */
++            if ((**pattern == ']') && (*pattern > mismatch)) {
++                ++*pattern;
++                /* XXX: Fix for MBCS character width */
++                ++*string;
++                return (result ^ negate);
++            }
++
++            if (escape && (**pattern == '\\')) {
++                ++*pattern;
++
++                /* Patterns must be terminated with ']', not EOS */
++                if (!**pattern)
++                    break;
++            }
++
++            /* Patterns must be terminated with ']' not '/' */
++            if (slash && (**pattern == '/'))
++                break;
++
++            /* Look at only well-formed range patterns; ']' is allowed only if escaped,
++             * while '/' is not allowed at all in FNM_PATHNAME mode.
++             */
++            /* XXX: Fix for locale/MBCS character width */
++            if (((*pattern)[1] == '-') && (*pattern)[2] 
++                    && ((escape && ((*pattern)[2] != '\\'))
++                          ? (((*pattern)[2] != ']') && (!slash || ((*pattern)[2] != '/')))
++                          : (((*pattern)[3]) && (!slash || ((*pattern)[3] != '/'))))) {
++                startch = *pattern;
++                *pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
++
++                /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
++                if ((**string >= *startch) && (**string <= **pattern))
++                    result = 0;
++                else if (nocase && (isupper(**string) || isupper(*startch)
++                                                      || isupper(**pattern))
++                            && (tolower(**string) >= tolower(*startch)) 
++                            && (tolower(**string) <= tolower(**pattern)))
++                    result = 0;
++
++                ++*pattern;
++                continue;
++            }
++
++            /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
++            if ((**string == **pattern))
++                result = 0;
++            else if (nocase && (isupper(**string) || isupper(**pattern))
++                            && (tolower(**string) == tolower(**pattern)))
++                result = 0;
++
++            ++*pattern;
++        }
++
++        /* NOT a properly balanced [expr] pattern; Rewind to test '[' literal */
++        *pattern = mismatch;
++        result = APR_FNM_NOMATCH;
+     }
++    else if (**pattern == '?') {
++        /* Optimize '?' match before unescaping **pattern */
++        if (!**string || (slash && (**string == '/')))
++            return APR_FNM_NOMATCH;
++        result = 0;
++        goto fnmatch_ch_success;
++    }
++    else if (escape && (**pattern == '\\') && (*pattern)[1]) {
++        ++*pattern;
++    }
++
++    /* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
++    if (**string == **pattern)
++        result = 0;
++    else if (nocase && (isupper(**string) || isupper(**pattern))
++                    && (tolower(**string) == tolower(**pattern)))
++        result = 0;
++
++    /* Refuse to advance over trailing slash or nulls
++     */
++    if (!**string || !**pattern || (slash && ((**string == '/') || (**pattern == '/'))))
++        return result;
++
++fnmatch_ch_success:
++    ++*pattern;
++    ++*string;
++    return result;
+ }
+ 
+-static const char *rangematch(const char *pattern, int test, int flags)
+-{
+-    int negate, ok;
+-    char c, c2;
+ 
+-    /*
+-     * A bracket expression starting with an unquoted circumflex
+-     * character produces unspecified results (IEEE 1003.2-1992,
+-     * 3.13.2).  This implementation treats it like '!', for
+-     * consistency with the regular expression syntax.
+-     * J.T. Conklin (conklin at ngai.kaleida.com)
++APR_DECLARE(int) apr_fnmatch(const char *pattern, const char *string, int flags)
++{
++    static const char dummystring[2] = {' ', 0};
++    const int escape = !(flags & APR_FNM_NOESCAPE);
++    const int slash = !!(flags & APR_FNM_PATHNAME);
++    const char *strendseg;
++    const char *dummyptr;
++    const char *matchptr;
++    int wild;
++    /* For '*' wild processing only; surpress 'used before initialization'
++     * warnings with dummy initialization values;
+      */
+-    if ((negate = (*pattern == '!' || *pattern == '^'))) {
+-	++pattern;
++    const char *strstartseg = NULL;
++    const char *mismatch = NULL;
++    int matchlen = 0;
++
++    while (*pattern)
++    {
++        /* Match balanced slashes, starting a new segment pattern
++         */
++        if (slash && escape && (*pattern == '\\') && (pattern[1] == '/'))
++            ++pattern;
++        if (slash && (*pattern == '/') && (*string == '/')) {
++            ++pattern;
++            ++string;
++        }            
++
++        /* At the beginning of each segment, validate leading period behavior.
++         */
++        if ((flags & APR_FNM_PERIOD) && (*string == '.'))
++        {
++            if (*pattern == '.')
++                ++pattern;
++            else if (escape && (*pattern == '\\') && (pattern[1] == '.'))
++                pattern += 2;
++            else
++                return APR_FNM_NOMATCH;
++            ++string;
++        }
++
++        /* Determine the end of string segment
++         *
++         * Presumes '/' character is unique, not composite in any MBCS encoding
++         */
++        if (slash) {
++            strendseg = strchr(string, '/');
++            if (!strendseg)
++                strendseg = strchr(string, '\0');
++        }
++        else {
++            strendseg = strchr(string, '\0');
++        }
++
++        /* Allow pattern '*' to be consumed even with no remaining string to match
++         */
++        while (*pattern && !(slash && ((*pattern == '/')
++                                       || (escape && (*pattern == '\\')
++                                                  && (pattern[1] == '/'))))
++                        && ((string < strendseg)
++                            || ((*pattern == '*') && (string == strendseg))))
++        {
++            /* Reduce groups of '*' and '?' to n '?' matches
++             * followed by one '*' test for simplicity
++             */
++            for (wild = 0; ((*pattern == '*') || (*pattern == '?')); ++pattern)
++            {
++                if (*pattern == '*') {
++                    wild = 1;
++                }
++                else if (string < strendseg) {  /* && (*pattern == '?') */
++                    /* XXX: Advance 1 char for MBCS locale */
++                    ++string;
++                }
++                else {  /* (string >= strendseg) && (*pattern == '?') */
++                    return APR_FNM_NOMATCH;
++                }
++            }
++
++            if (wild)
++            {
++                strstartseg = string;
++                mismatch = pattern;
++
++                /* Count fixed (non '*') char matches remaining in pattern
++                 * excluding '/' (or "\/") and '*'
++                 */
++                for (matchptr = pattern, matchlen = 0; 1; ++matchlen)
++                {
++                    if ((*matchptr == '\0') 
++                        || (slash && ((*matchptr == '/')
++                                      || (escape && (*matchptr == '\\')
++                                                 && (matchptr[1] == '/')))))
++                    {
++                        /* Compare precisely this many trailing string chars,
++                         * the resulting match needs no wildcard loop
++                         */
++                        /* XXX: Adjust for MBCS */
++                        if (string + matchlen > strendseg)
++                            return APR_FNM_NOMATCH;
++
++                        string = strendseg - matchlen;
++                        wild = 0;
++                        break;
++                    }
++
++                    if (*matchptr == '*')
++                    {
++                        /* Ensure at least this many trailing string chars remain
++                         * for the first comparison
++                         */
++                        /* XXX: Adjust for MBCS */
++                        if (string + matchlen > strendseg)
++                            return APR_FNM_NOMATCH;
++
++                        /* Begin first wild comparison at the current position */
++                        break;
++                    }
++
++                    /* Skip forward in pattern by a single character match
++                     * Use a dummy fnmatch_ch() test to count one "[range]" escape
++                     */ 
++                    /* XXX: Adjust for MBCS */
++                    if (escape && (*matchptr == '\\') && matchptr[1]) {
++                        matchptr += 2;
++                    }
++                    else if (*matchptr == '[') {
++                        dummyptr = dummystring;
++                        fnmatch_ch(&matchptr, &dummyptr, flags);
++                    }
++                    else {
++                        ++matchptr;
++                    }
++                }
++            }
++
++            /* Incrementally match string against the pattern
++             */
++            while (*pattern && (string < strendseg))
++            {
++                /* Success; begin a new wild pattern search
++                 */
++                if (*pattern == '*')
++                    break;
++
++                if (slash && ((*string == '/') || (*pattern == '/')
++                                               || (escape && (*pattern == '\\')
++                                                   && (pattern[1] == '/'))))
++                    break;
++
++                /* Compare ch's (the pattern is advanced over "\/" to the '/',
++                 * but slashes will mismatch, and are not consumed)
++                 */
++                if (!fnmatch_ch(&pattern, &string, flags))
++                    continue;
++
++                /* Failed to match, loop against next char offset of string segment 
++                 * until not enough string chars remain to match the fixed pattern
++                 */
++                if (wild) {
++                    /* XXX: Advance 1 char for MBCS locale */
++                    string = ++strstartseg;
++                    if (string + matchlen > strendseg)
++                        return APR_FNM_NOMATCH;
++
++                    pattern = mismatch;
++                    continue;
++                }
++                else
++                    return APR_FNM_NOMATCH;
++            }
++        }
++
++        if (*string && (!slash || (*string != '/')))
++            return APR_FNM_NOMATCH;
++
++        if (*pattern && (!slash || ((*pattern != '/')
++                                    && (!escape || (*pattern != '\\')
++                                                || (pattern[1] != '/')))))
++            return APR_FNM_NOMATCH;
+     }
+ 
+-    for (ok = 0; (c = *pattern++) != ']';) {
+-        if (c == '\\' && !(flags & APR_FNM_NOESCAPE)) {
+-	    c = *pattern++;
+-	}
+-	if (c == EOS) {
+-	    return (NULL);
+-	}
+-	if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
+-	    pattern += 2;
+-	    if (c2 == '\\' && !(flags & APR_FNM_NOESCAPE)) {
+-		c2 = *pattern++;
+-	    }
+-	    if (c2 == EOS) {
+-		return (NULL);
+-	    }
+-	    if ((c <= test && test <= c2)
+-		|| ((flags & APR_FNM_CASE_BLIND)
+-		    && ((apr_tolower(c) <= apr_tolower(test))
+-			&& (apr_tolower(test) <= apr_tolower(c2))))) {
+-		ok = 1;
+-	    }
+-	}
+-	else if ((c == test)
+-		 || ((flags & APR_FNM_CASE_BLIND)
+-		     && (apr_tolower(c) == apr_tolower(test)))) {
+-	    ok = 1;
+-	}
+-    }
+-    return (ok == negate ? NULL : pattern);
++    /* pattern is at EOS; if string is also, declare success
++     */
++    if (!*string)
++        return 0;
++
++    /* pattern didn't match to the end of string */
++    return APR_FNM_NOMATCH;
+ }
+ 
+ 
+-/* This function is an Apache addition */
+-/* return non-zero if pattern has any glob chars in it */
++/* This function is an Apache addition
++ * return non-zero if pattern has any glob chars in it
++ * @bug Function does not distinguish for FNM_PATHNAME mode, which renders
++ * a false positive for test[/]this (which is not a range, but 
++ * seperate test[ and ]this segments and no glob.)
++ * @bug Function does not distinguish for non-FNM_ESCAPE mode.
++ * @bug Function does not parse []] correctly
++ * Solution may be to use fnmatch_ch() to walk the patterns?
++ */
+ APR_DECLARE(int) apr_fnmatch_test(const char *pattern)
+ {
+     int nesting;
+ 
+     nesting = 0;
+     while (*pattern) {
+-	switch (*pattern) {
+-	case '?':
+-	case '*':
+-	    return 1;
+-
+-	case '\\':
+-	    if (*++pattern == '\0') {
+-		return 0;
+-	    }
+-	    break;
+-
+-	case '[':	/* '[' is only a glob if it has a matching ']' */
+-	    ++nesting;
+-	    break;
+-
+-	case ']':
+-	    if (nesting) {
+-		return 1;
+-	    }
+-	    break;
+-	}
+-	++pattern;
+-    }
++        switch (*pattern) {
++        case '?':
++        case '*':
++            return 1;
++
++        case '\\':
++            if (*++pattern == '\0') {
++                return 0;
++            }
++            break;
++
++        case '[':         /* '[' is only a glob if it has a matching ']' */
++            ++nesting;
++            break;
++
++        case ']':
++            if (nesting) {
++                return 1;
++            }
++            break;
++        }
++        ++pattern;    }
+     return 0;
+ }
+ 
++
+ /* Find all files matching the specified pattern */
+ APR_DECLARE(apr_status_t) apr_match_glob(const char *pattern, 
+                                          apr_array_header_t **result,
+--- a/test/testfnmatch.c	2007-11-18 01:35:57.000000000 +0100
++++ a/test/testfnmatch.c	2011-05-03 06:39:57.000000000 +0200
+@@ -25,6 +25,114 @@
+ 
+ #define NUM_FILES (5)
+ 
++#define APR_FNM_BITS    15
++#define APR_FNM_FAILBIT 256
++
++#define FAILS_IF(X)     0, X
++#define SUCCEEDS_IF(X)  X, 256
++#define SUCCEEDS        0, 256
++#define FAILS           256, 0
++
++static struct pattern_s {
++    const char *pattern;
++    const char *string;
++    int         require_flags;
++    int         fail_flags;
++} patterns[] = {
++
++/*   Pattern,  String to Test,          Flags to Match  */
++    {"", "test",                        FAILS},
++    {"", "*",                           FAILS},
++    {"test", "*",                       FAILS},
++    {"test", "test",                    SUCCEEDS},
++
++     /* Remember C '\\' is a single backslash in pattern */
++    {"te\\st", "test",                  FAILS_IF(APR_FNM_NOESCAPE)},
++    {"te\\\\st", "te\\st",              FAILS_IF(APR_FNM_NOESCAPE)},
++    {"te\\*t", "te*t",                  FAILS_IF(APR_FNM_NOESCAPE)},
++    {"te\\*t", "test",                  FAILS},
++    {"te\\?t", "te?t",                  FAILS_IF(APR_FNM_NOESCAPE)},
++    {"te\\?t", "test",                  FAILS},
++
++    {"tesT", "test",                    SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
++    {"test", "Test",                    SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
++    {"tEst", "teSt",                    SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
++
++    {"?est", "test",                    SUCCEEDS},
++    {"te?t", "test",                    SUCCEEDS},
++    {"tes?", "test",                    SUCCEEDS},
++    {"test?", "test",                   FAILS},
++
++    {"*", "",                           SUCCEEDS},
++    {"*", "test",                       SUCCEEDS},
++    {"*test", "test",                   SUCCEEDS},
++    {"*est", "test",                    SUCCEEDS},
++    {"*st", "test",                     SUCCEEDS},
++    {"t*t", "test",                     SUCCEEDS},
++    {"te*t", "test",                    SUCCEEDS},
++    {"te*st", "test",                   SUCCEEDS},
++    {"te*", "test",                     SUCCEEDS},
++    {"tes*", "test",                    SUCCEEDS},
++    {"test*", "test",                   SUCCEEDS},
++
++    {"test/this", "test/",              FAILS},
++    {"test/", "test/this",              FAILS},
++    {"test*/this", "test/this",         SUCCEEDS},
++    {"test/*this", "test/this",         SUCCEEDS},
++
++    {".*", ".this",                     SUCCEEDS},
++    {"*", ".this",                      FAILS_IF(APR_FNM_PERIOD)},
++    {"?this", ".this",                  FAILS_IF(APR_FNM_PERIOD)},
++    {"[.]this", ".this",                FAILS_IF(APR_FNM_PERIOD)},
++
++    {"test/this", "test/this",          SUCCEEDS},
++    {"test?this", "test/this",          FAILS_IF(APR_FNM_PATHNAME)},
++    {"test*this", "test/this",          FAILS_IF(APR_FNM_PATHNAME)},
++    {"test[/]this", "test/this",        FAILS_IF(APR_FNM_PATHNAME)},
++
++    {"test/.*", "test/.this",           SUCCEEDS},
++    {"test/*", "test/.this",            FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
++    {"test/?this", "test/.this",        FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
++    {"test/[.]this", "test/.this",      FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
++
++    {NULL, NULL, 0}
++};
++
++
++
++static void test_fnmatch(abts_case *tc, void *data)
++{
++    struct pattern_s *test = patterns;
++    char buf[80];
++    int i = APR_FNM_BITS + 1;
++    int res;
++
++    for (test = patterns; test->pattern; ++test)
++    {
++        for (i = 0; i <= APR_FNM_BITS; ++i)
++        {
++            res = apr_fnmatch(test->pattern, test->string, i);
++            if (((i & test->require_flags) != test->require_flags)
++                || ((i & test->fail_flags) == test->fail_flags)) {
++                if (res != APR_FNM_NOMATCH)
++                    break;
++            }
++            else {
++                if (res != 0)
++                    break;
++            }
++        }
++        if (i <= APR_FNM_BITS)
++            break;
++    }
++
++    if (i <= APR_FNM_BITS) {
++        sprintf(buf, "apr_fnmatch(\"%s\", \"%s\", %d) returns %d\n",
++                test->pattern, test->string, i, res);
++        abts_fail(tc, buf, __LINE__);
++    }
++}
++
+ static void test_glob(abts_case *tc, void *data)
+ {
+     int i;
+@@ -68,6 +176,7 @@
+ {
+     suite = ADD_SUITE(suite)
+ 
++    abts_run_test(suite, test_fnmatch, NULL);
+     abts_run_test(suite, test_glob, NULL);
+     abts_run_test(suite, test_glob_currdir, NULL);
+ 




More information about the Pkg-apache-commits mailing list