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

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

mercurial