security/nss/lib/util/portreg.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /* 
     6  * shexp.c: shell-like wildcard match routines
     7  *
     8  * See shexp.h for public documentation.
     9  */
    11 #include "seccomon.h"
    12 #include "portreg.h"
    14 /* ----------------------------- shexp_valid ------------------------------ */
    17 static int 
    18 _valid_subexp(const char *exp, char stop1, char stop2) 
    19 {
    20     register int x;
    21     int nsc = 0;     /* Number of special characters */
    22     int np;          /* Number of pipe characters in union */
    23     int tld = 0;     /* Number of tilde characters */
    25     for (x = 0; exp[x] && (exp[x] != stop1) && (exp[x] != stop2); ++x) {
    26         switch(exp[x]) {
    27 	case '~':
    28             if(tld)                 /* at most one exclusion */
    29                 return INVALID_SXP;
    30             if (stop1)              /* no exclusions within unions */
    31                 return INVALID_SXP;
    32             if (!exp[x+1])          /* exclusion cannot be last character */
    33                 return INVALID_SXP;
    34             if (!x)                 /* exclusion cannot be first character */
    35                 return INVALID_SXP;
    36             ++tld;
    37 	    /* fall through */
    38 	case '*':
    39 	case '?':
    40 	case '$':
    41             ++nsc;
    42             break;
    43 	case '[':
    44             ++nsc;
    45             if((!exp[++x]) || (exp[x] == ']'))
    46                 return INVALID_SXP;
    47             for(; exp[x] && (exp[x] != ']'); ++x) {
    48                 if(exp[x] == '\\' && !exp[++x])
    49                     return INVALID_SXP;
    50             }
    51             if(!exp[x])
    52                 return INVALID_SXP;
    53             break;
    54 	case '(':
    55             ++nsc;
    56 	    if (stop1)			/* no nested unions */
    57 		return INVALID_SXP;
    58             np = -1;
    59             do {
    60                 int t = _valid_subexp(&exp[++x], ')', '|');
    61                 if(t == 0 || t == INVALID_SXP)
    62                     return INVALID_SXP;
    63                 x+=t;
    64 		if(!exp[x])
    65 		    return INVALID_SXP;
    66 		++np;
    67             } while (exp[x] == '|' );
    68 	    if(np < 1)  /* must be at least one pipe */
    69 		return INVALID_SXP;
    70             break;
    71 	case ')':
    72 	case '|':
    73 	case ']':
    74             return INVALID_SXP;
    75 	case '\\':
    76 	    ++nsc;
    77             if(!exp[++x])
    78                 return INVALID_SXP;
    79             break;
    80 	default:
    81             break;
    82         }
    83     }
    84     if((!stop1) && (!nsc)) /* must be at least one special character */
    85         return NON_SXP;
    86     return ((exp[x] == stop1 || exp[x] == stop2) ? x : INVALID_SXP);
    87 }
    89 int 
    90 PORT_RegExpValid(const char *exp) 
    91 {
    92     int x;
    94     x = _valid_subexp(exp, '\0', '\0');
    95     return (x < 0 ? x : VALID_SXP);
    96 }
    99 /* ----------------------------- shexp_match ----------------------------- */
   102 #define MATCH 0
   103 #define NOMATCH 1
   104 #define ABORTED -1
   106 static int 
   107 _shexp_match(const char *str, const char *exp, PRBool case_insensitive,
   108              unsigned int level);
   110 /* Count characters until we reach a NUL character or either of the 
   111  * two delimiter characters, stop1 or stop2.  If we encounter a bracketed 
   112  * expression, look only for NUL or ']' inside it.  Do not look for stop1 
   113  * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
   114  * Handle all escaping.
   115  * Return index in input string of first stop found, or ABORTED if not found.
   116  * If "dest" is non-NULL, copy counted characters to it and NUL terminate.
   117  */
   118 static int 
   119 _scan_and_copy(const char *exp, char stop1, char stop2, char *dest)
   120 {
   121     register int sx;     /* source index */
   122     register char cc;
   124     for (sx = 0; (cc = exp[sx]) && cc != stop1 && cc != stop2; sx++) {
   125 	if (cc == '\\') {
   126 	    if (!exp[++sx])
   127 		return ABORTED; /* should be impossible */
   128 	} else if (cc == '[') {
   129 	    while ((cc = exp[++sx]) && cc != ']') {
   130 		if(cc == '\\' && !exp[++sx])
   131 		    return ABORTED;
   132 	    }
   133 	    if (!cc) 
   134 		return ABORTED; /* should be impossible */
   135 	}
   136     }
   137     if (dest && sx) {
   138 	/* Copy all but the closing delimiter. */
   139 	memcpy(dest, exp, sx);
   140 	dest[sx] = 0;
   141     }
   142     return cc ? sx : ABORTED; /* index of closing delimiter */
   143 }
   145 /* On input, exp[0] is the opening parenthesis of a union.
   146  * See if any of the alternatives in the union matches as a pattern.
   147  * The strategy is to take each of the alternatives, in turn, and append
   148  * the rest of the expression (after the closing ')' that marks the end of 
   149  * this union) to that alternative, and then see if the resultant expression
   150  * matches the input string.  Repeat this until some alternative matches, 
   151  * or we have an abort. 
   152  */
   153 static int 
   154 _handle_union(const char *str, const char *exp, PRBool case_insensitive,
   155               unsigned int level) 
   156 {
   157     register int sx;     /* source index */
   158     int cp;              /* source index of closing parenthesis */
   159     int count;
   160     int ret   = NOMATCH;
   161     char *e2;
   163     /* Find the closing parenthesis that ends this union in the expression */
   164     cp = _scan_and_copy(exp, ')', '\0', NULL);
   165     if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */
   166     	return ABORTED;
   167     ++cp;                /* now index of char after closing parenthesis */
   168     e2 = (char *) PORT_Alloc(1 + strlen(exp));
   169     if (!e2)
   170     	return ABORTED;
   171     for (sx = 1; ; ++sx) {
   172 	/* Here, exp[sx] is one character past the preceding '(' or '|'. */
   173 	/* Copy everything up to the next delimiter to e2 */
   174 	count = _scan_and_copy(exp + sx, ')', '|', e2);
   175 	if (count == ABORTED || !count) {
   176 	    ret = ABORTED;
   177 	    break;
   178 	}
   179 	sx += count;
   180 	/* Append everything after closing parenthesis to e2. This is safe. */
   181 	strcpy(e2+count, exp+cp);
   182         ret = _shexp_match(str, e2, case_insensitive, level + 1);
   183 	if (ret != NOMATCH || !exp[sx] || exp[sx] == ')')
   184             break;
   185     }
   186     PORT_Free(e2);
   187     if (sx < 2)
   188     	ret = ABORTED;
   189     return ret;
   190 }
   192 /* returns 1 if val is in range from start..end, case insensitive. */
   193 static int
   194 _is_char_in_range(int start, int end, int val)
   195 {
   196     char map[256];
   197     memset(map, 0, sizeof map);
   198     while (start <= end)
   199 	map[tolower(start++)] = 1;
   200     return map[tolower(val)];
   201 }
   203 static int 
   204 _shexp_match(const char *str, const char *exp, PRBool case_insensitive, 
   205              unsigned int level) 
   206 {
   207     register int x;   /* input string index */
   208     register int y;   /* expression index */
   209     int ret,neg;
   211     if (level > 20)      /* Don't let the stack get too deep. */
   212     	return ABORTED;
   213     for(x = 0, y = 0; exp[y]; ++y, ++x) {
   214         if((!str[x]) && (exp[y] != '$') && (exp[y] != '*')) {
   215             return NOMATCH;
   216 	}
   217 	switch(exp[y]) {
   218 	case '$':
   219 	    if(str[x])
   220 		return NOMATCH;
   221 	    --x;                 /* we don't want loop to increment x */
   222 	    break;
   223 	case '*':
   224 	    while(exp[++y] == '*'){}
   225 	    if(!exp[y])
   226 		return MATCH;
   227 	    while(str[x]) {
   228 	        ret = _shexp_match(&str[x++], &exp[y], case_insensitive, 
   229 				   level + 1);
   230 		switch(ret) {
   231 		case NOMATCH:
   232 		    continue;
   233 		case ABORTED:
   234 		    return ABORTED;
   235 		default:
   236 		    return MATCH;
   237 		}
   238 	    }
   239 	    if((exp[y] == '$') && (exp[y+1] == '\0') && (!str[x]))
   240 		return MATCH;
   241 	    else
   242 		return NOMATCH;
   243 	case '[': {
   244 	    int start, end = 0, i;
   245 	    neg = ((exp[++y] == '^') && (exp[y+1] != ']'));
   246 	    if (neg)
   247 		++y;
   248 	    i = y;
   249 	    start = (unsigned char)(exp[i++]);
   250 	    if (start == '\\')
   251 	    	start = (unsigned char)(exp[i++]);
   252 	    if (isalnum(start) && exp[i++] == '-') {
   253 		end = (unsigned char)(exp[i++]); 
   254 		if (end == '\\')
   255 		    end = (unsigned char)(exp[i++]);
   256 	    }
   257 	    if (isalnum(end) && exp[i] == ']') {
   258 		/* This is a range form: a-b */
   259 		int val   = (unsigned char)(str[x]);
   260 		if (end < start) { /* swap them */
   261 		    start ^= end;
   262 		    end ^= start;
   263 		    start ^= end;
   264 		}
   265 		if (case_insensitive && isalpha(val)) {
   266 		    val = _is_char_in_range(start, end, val);
   267 		    if (neg == val)
   268 			return NOMATCH;
   269 		} else if (neg != ((val < start) || (val > end))) {
   270 		    return NOMATCH;
   271 		}
   272 		y = i;
   273 	    } else {
   274 		/* Not range form */
   275 		int matched = 0;
   276 		for (; exp[y] != ']'; y++) {
   277 		    if (exp[y] == '\\')
   278 			++y;
   279 		    if(case_insensitive) {
   280 			matched |= (toupper(str[x]) == toupper(exp[y]));
   281 		    } else {
   282 			matched |= (str[x] == exp[y]);
   283 		    }
   284 		}
   285 		if (neg == matched)
   286 		    return NOMATCH;
   287 	    }
   288 	}
   289 	break;
   290 	case '(':
   291 	    if (!exp[y+1])
   292 	    	return ABORTED;
   293 	    return _handle_union(&str[x], &exp[y], case_insensitive, level);
   294 	case '?':
   295 	    break;
   296 	case '|':
   297 	case ']':
   298 	case ')':
   299 	    return ABORTED;
   300 	case '\\':
   301 	    ++y;
   302 	    /* fall through */
   303 	default:
   304 	    if(case_insensitive) {
   305 		if(toupper(str[x]) != toupper(exp[y]))
   306 		    return NOMATCH;
   307 	    } else {
   308 		if(str[x] != exp[y])
   309 		    return NOMATCH;
   310 	    }
   311 	    break;
   312 	}
   313     }
   314     return (str[x] ? NOMATCH : MATCH);
   315 }
   317 static int 
   318 port_RegExpMatch(const char *str, const char *xp, PRBool case_insensitive) 
   319 {
   320     char *exp = 0;
   321     int x, ret = MATCH;
   323     if (!strchr(xp, '~'))
   324     	return _shexp_match(str, xp, case_insensitive, 0);
   326     exp = PORT_Strdup(xp);
   327     if(!exp)
   328 	return NOMATCH;
   330     x = _scan_and_copy(exp, '~', '\0', NULL);
   331     if (x != ABORTED && exp[x] == '~') {
   332 	exp[x++] = '\0';
   333 	ret = _shexp_match(str, &exp[x], case_insensitive, 0);
   334 	switch (ret) {
   335 	case NOMATCH: ret = MATCH;   break;
   336 	case MATCH:   ret = NOMATCH; break;
   337 	default:                     break;
   338         }
   339     }
   340     if (ret == MATCH)
   341 	ret = _shexp_match(str, exp, case_insensitive, 0);
   343     PORT_Free(exp);
   344     return ret;
   345 }
   348 /* ------------------------------ shexp_cmp ------------------------------- */
   350 int 
   351 PORT_RegExpSearch(const char *str, const char *exp)
   352 {
   353     switch(PORT_RegExpValid(exp)) 
   354 	  {
   355         case INVALID_SXP:
   356             return -1;
   357         case NON_SXP:
   358             return (strcmp(exp,str) ? 1 : 0);
   359         default:
   360             return port_RegExpMatch(str, exp, PR_FALSE);
   361       }
   362 }
   364 int
   365 PORT_RegExpCaseSearch(const char *str, const char *exp)
   366 {
   367     switch(PORT_RegExpValid(exp))
   368       {
   369         case INVALID_SXP:
   370             return -1;
   371         case NON_SXP:
   372             return (PORT_Strcasecmp(exp,str) ? 1 : 0);
   373         default:
   374             return port_RegExpMatch(str, exp, PR_TRUE);
   375       }
   376 }

mercurial