1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/util/portreg.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,377 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * shexp.c: shell-like wildcard match routines 1.10 + * 1.11 + * See shexp.h for public documentation. 1.12 + */ 1.13 + 1.14 +#include "seccomon.h" 1.15 +#include "portreg.h" 1.16 + 1.17 +/* ----------------------------- shexp_valid ------------------------------ */ 1.18 + 1.19 + 1.20 +static int 1.21 +_valid_subexp(const char *exp, char stop1, char stop2) 1.22 +{ 1.23 + register int x; 1.24 + int nsc = 0; /* Number of special characters */ 1.25 + int np; /* Number of pipe characters in union */ 1.26 + int tld = 0; /* Number of tilde characters */ 1.27 + 1.28 + for (x = 0; exp[x] && (exp[x] != stop1) && (exp[x] != stop2); ++x) { 1.29 + switch(exp[x]) { 1.30 + case '~': 1.31 + if(tld) /* at most one exclusion */ 1.32 + return INVALID_SXP; 1.33 + if (stop1) /* no exclusions within unions */ 1.34 + return INVALID_SXP; 1.35 + if (!exp[x+1]) /* exclusion cannot be last character */ 1.36 + return INVALID_SXP; 1.37 + if (!x) /* exclusion cannot be first character */ 1.38 + return INVALID_SXP; 1.39 + ++tld; 1.40 + /* fall through */ 1.41 + case '*': 1.42 + case '?': 1.43 + case '$': 1.44 + ++nsc; 1.45 + break; 1.46 + case '[': 1.47 + ++nsc; 1.48 + if((!exp[++x]) || (exp[x] == ']')) 1.49 + return INVALID_SXP; 1.50 + for(; exp[x] && (exp[x] != ']'); ++x) { 1.51 + if(exp[x] == '\\' && !exp[++x]) 1.52 + return INVALID_SXP; 1.53 + } 1.54 + if(!exp[x]) 1.55 + return INVALID_SXP; 1.56 + break; 1.57 + case '(': 1.58 + ++nsc; 1.59 + if (stop1) /* no nested unions */ 1.60 + return INVALID_SXP; 1.61 + np = -1; 1.62 + do { 1.63 + int t = _valid_subexp(&exp[++x], ')', '|'); 1.64 + if(t == 0 || t == INVALID_SXP) 1.65 + return INVALID_SXP; 1.66 + x+=t; 1.67 + if(!exp[x]) 1.68 + return INVALID_SXP; 1.69 + ++np; 1.70 + } while (exp[x] == '|' ); 1.71 + if(np < 1) /* must be at least one pipe */ 1.72 + return INVALID_SXP; 1.73 + break; 1.74 + case ')': 1.75 + case '|': 1.76 + case ']': 1.77 + return INVALID_SXP; 1.78 + case '\\': 1.79 + ++nsc; 1.80 + if(!exp[++x]) 1.81 + return INVALID_SXP; 1.82 + break; 1.83 + default: 1.84 + break; 1.85 + } 1.86 + } 1.87 + if((!stop1) && (!nsc)) /* must be at least one special character */ 1.88 + return NON_SXP; 1.89 + return ((exp[x] == stop1 || exp[x] == stop2) ? x : INVALID_SXP); 1.90 +} 1.91 + 1.92 +int 1.93 +PORT_RegExpValid(const char *exp) 1.94 +{ 1.95 + int x; 1.96 + 1.97 + x = _valid_subexp(exp, '\0', '\0'); 1.98 + return (x < 0 ? x : VALID_SXP); 1.99 +} 1.100 + 1.101 + 1.102 +/* ----------------------------- shexp_match ----------------------------- */ 1.103 + 1.104 + 1.105 +#define MATCH 0 1.106 +#define NOMATCH 1 1.107 +#define ABORTED -1 1.108 + 1.109 +static int 1.110 +_shexp_match(const char *str, const char *exp, PRBool case_insensitive, 1.111 + unsigned int level); 1.112 + 1.113 +/* Count characters until we reach a NUL character or either of the 1.114 + * two delimiter characters, stop1 or stop2. If we encounter a bracketed 1.115 + * expression, look only for NUL or ']' inside it. Do not look for stop1 1.116 + * or stop2 inside it. Return ABORTED if bracketed expression is unterminated. 1.117 + * Handle all escaping. 1.118 + * Return index in input string of first stop found, or ABORTED if not found. 1.119 + * If "dest" is non-NULL, copy counted characters to it and NUL terminate. 1.120 + */ 1.121 +static int 1.122 +_scan_and_copy(const char *exp, char stop1, char stop2, char *dest) 1.123 +{ 1.124 + register int sx; /* source index */ 1.125 + register char cc; 1.126 + 1.127 + for (sx = 0; (cc = exp[sx]) && cc != stop1 && cc != stop2; sx++) { 1.128 + if (cc == '\\') { 1.129 + if (!exp[++sx]) 1.130 + return ABORTED; /* should be impossible */ 1.131 + } else if (cc == '[') { 1.132 + while ((cc = exp[++sx]) && cc != ']') { 1.133 + if(cc == '\\' && !exp[++sx]) 1.134 + return ABORTED; 1.135 + } 1.136 + if (!cc) 1.137 + return ABORTED; /* should be impossible */ 1.138 + } 1.139 + } 1.140 + if (dest && sx) { 1.141 + /* Copy all but the closing delimiter. */ 1.142 + memcpy(dest, exp, sx); 1.143 + dest[sx] = 0; 1.144 + } 1.145 + return cc ? sx : ABORTED; /* index of closing delimiter */ 1.146 +} 1.147 + 1.148 +/* On input, exp[0] is the opening parenthesis of a union. 1.149 + * See if any of the alternatives in the union matches as a pattern. 1.150 + * The strategy is to take each of the alternatives, in turn, and append 1.151 + * the rest of the expression (after the closing ')' that marks the end of 1.152 + * this union) to that alternative, and then see if the resultant expression 1.153 + * matches the input string. Repeat this until some alternative matches, 1.154 + * or we have an abort. 1.155 + */ 1.156 +static int 1.157 +_handle_union(const char *str, const char *exp, PRBool case_insensitive, 1.158 + unsigned int level) 1.159 +{ 1.160 + register int sx; /* source index */ 1.161 + int cp; /* source index of closing parenthesis */ 1.162 + int count; 1.163 + int ret = NOMATCH; 1.164 + char *e2; 1.165 + 1.166 + /* Find the closing parenthesis that ends this union in the expression */ 1.167 + cp = _scan_and_copy(exp, ')', '\0', NULL); 1.168 + if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */ 1.169 + return ABORTED; 1.170 + ++cp; /* now index of char after closing parenthesis */ 1.171 + e2 = (char *) PORT_Alloc(1 + strlen(exp)); 1.172 + if (!e2) 1.173 + return ABORTED; 1.174 + for (sx = 1; ; ++sx) { 1.175 + /* Here, exp[sx] is one character past the preceding '(' or '|'. */ 1.176 + /* Copy everything up to the next delimiter to e2 */ 1.177 + count = _scan_and_copy(exp + sx, ')', '|', e2); 1.178 + if (count == ABORTED || !count) { 1.179 + ret = ABORTED; 1.180 + break; 1.181 + } 1.182 + sx += count; 1.183 + /* Append everything after closing parenthesis to e2. This is safe. */ 1.184 + strcpy(e2+count, exp+cp); 1.185 + ret = _shexp_match(str, e2, case_insensitive, level + 1); 1.186 + if (ret != NOMATCH || !exp[sx] || exp[sx] == ')') 1.187 + break; 1.188 + } 1.189 + PORT_Free(e2); 1.190 + if (sx < 2) 1.191 + ret = ABORTED; 1.192 + return ret; 1.193 +} 1.194 + 1.195 +/* returns 1 if val is in range from start..end, case insensitive. */ 1.196 +static int 1.197 +_is_char_in_range(int start, int end, int val) 1.198 +{ 1.199 + char map[256]; 1.200 + memset(map, 0, sizeof map); 1.201 + while (start <= end) 1.202 + map[tolower(start++)] = 1; 1.203 + return map[tolower(val)]; 1.204 +} 1.205 + 1.206 +static int 1.207 +_shexp_match(const char *str, const char *exp, PRBool case_insensitive, 1.208 + unsigned int level) 1.209 +{ 1.210 + register int x; /* input string index */ 1.211 + register int y; /* expression index */ 1.212 + int ret,neg; 1.213 + 1.214 + if (level > 20) /* Don't let the stack get too deep. */ 1.215 + return ABORTED; 1.216 + for(x = 0, y = 0; exp[y]; ++y, ++x) { 1.217 + if((!str[x]) && (exp[y] != '$') && (exp[y] != '*')) { 1.218 + return NOMATCH; 1.219 + } 1.220 + switch(exp[y]) { 1.221 + case '$': 1.222 + if(str[x]) 1.223 + return NOMATCH; 1.224 + --x; /* we don't want loop to increment x */ 1.225 + break; 1.226 + case '*': 1.227 + while(exp[++y] == '*'){} 1.228 + if(!exp[y]) 1.229 + return MATCH; 1.230 + while(str[x]) { 1.231 + ret = _shexp_match(&str[x++], &exp[y], case_insensitive, 1.232 + level + 1); 1.233 + switch(ret) { 1.234 + case NOMATCH: 1.235 + continue; 1.236 + case ABORTED: 1.237 + return ABORTED; 1.238 + default: 1.239 + return MATCH; 1.240 + } 1.241 + } 1.242 + if((exp[y] == '$') && (exp[y+1] == '\0') && (!str[x])) 1.243 + return MATCH; 1.244 + else 1.245 + return NOMATCH; 1.246 + case '[': { 1.247 + int start, end = 0, i; 1.248 + neg = ((exp[++y] == '^') && (exp[y+1] != ']')); 1.249 + if (neg) 1.250 + ++y; 1.251 + i = y; 1.252 + start = (unsigned char)(exp[i++]); 1.253 + if (start == '\\') 1.254 + start = (unsigned char)(exp[i++]); 1.255 + if (isalnum(start) && exp[i++] == '-') { 1.256 + end = (unsigned char)(exp[i++]); 1.257 + if (end == '\\') 1.258 + end = (unsigned char)(exp[i++]); 1.259 + } 1.260 + if (isalnum(end) && exp[i] == ']') { 1.261 + /* This is a range form: a-b */ 1.262 + int val = (unsigned char)(str[x]); 1.263 + if (end < start) { /* swap them */ 1.264 + start ^= end; 1.265 + end ^= start; 1.266 + start ^= end; 1.267 + } 1.268 + if (case_insensitive && isalpha(val)) { 1.269 + val = _is_char_in_range(start, end, val); 1.270 + if (neg == val) 1.271 + return NOMATCH; 1.272 + } else if (neg != ((val < start) || (val > end))) { 1.273 + return NOMATCH; 1.274 + } 1.275 + y = i; 1.276 + } else { 1.277 + /* Not range form */ 1.278 + int matched = 0; 1.279 + for (; exp[y] != ']'; y++) { 1.280 + if (exp[y] == '\\') 1.281 + ++y; 1.282 + if(case_insensitive) { 1.283 + matched |= (toupper(str[x]) == toupper(exp[y])); 1.284 + } else { 1.285 + matched |= (str[x] == exp[y]); 1.286 + } 1.287 + } 1.288 + if (neg == matched) 1.289 + return NOMATCH; 1.290 + } 1.291 + } 1.292 + break; 1.293 + case '(': 1.294 + if (!exp[y+1]) 1.295 + return ABORTED; 1.296 + return _handle_union(&str[x], &exp[y], case_insensitive, level); 1.297 + case '?': 1.298 + break; 1.299 + case '|': 1.300 + case ']': 1.301 + case ')': 1.302 + return ABORTED; 1.303 + case '\\': 1.304 + ++y; 1.305 + /* fall through */ 1.306 + default: 1.307 + if(case_insensitive) { 1.308 + if(toupper(str[x]) != toupper(exp[y])) 1.309 + return NOMATCH; 1.310 + } else { 1.311 + if(str[x] != exp[y]) 1.312 + return NOMATCH; 1.313 + } 1.314 + break; 1.315 + } 1.316 + } 1.317 + return (str[x] ? NOMATCH : MATCH); 1.318 +} 1.319 + 1.320 +static int 1.321 +port_RegExpMatch(const char *str, const char *xp, PRBool case_insensitive) 1.322 +{ 1.323 + char *exp = 0; 1.324 + int x, ret = MATCH; 1.325 + 1.326 + if (!strchr(xp, '~')) 1.327 + return _shexp_match(str, xp, case_insensitive, 0); 1.328 + 1.329 + exp = PORT_Strdup(xp); 1.330 + if(!exp) 1.331 + return NOMATCH; 1.332 + 1.333 + x = _scan_and_copy(exp, '~', '\0', NULL); 1.334 + if (x != ABORTED && exp[x] == '~') { 1.335 + exp[x++] = '\0'; 1.336 + ret = _shexp_match(str, &exp[x], case_insensitive, 0); 1.337 + switch (ret) { 1.338 + case NOMATCH: ret = MATCH; break; 1.339 + case MATCH: ret = NOMATCH; break; 1.340 + default: break; 1.341 + } 1.342 + } 1.343 + if (ret == MATCH) 1.344 + ret = _shexp_match(str, exp, case_insensitive, 0); 1.345 + 1.346 + PORT_Free(exp); 1.347 + return ret; 1.348 +} 1.349 + 1.350 + 1.351 +/* ------------------------------ shexp_cmp ------------------------------- */ 1.352 + 1.353 +int 1.354 +PORT_RegExpSearch(const char *str, const char *exp) 1.355 +{ 1.356 + switch(PORT_RegExpValid(exp)) 1.357 + { 1.358 + case INVALID_SXP: 1.359 + return -1; 1.360 + case NON_SXP: 1.361 + return (strcmp(exp,str) ? 1 : 0); 1.362 + default: 1.363 + return port_RegExpMatch(str, exp, PR_FALSE); 1.364 + } 1.365 +} 1.366 + 1.367 +int 1.368 +PORT_RegExpCaseSearch(const char *str, const char *exp) 1.369 +{ 1.370 + switch(PORT_RegExpValid(exp)) 1.371 + { 1.372 + case INVALID_SXP: 1.373 + return -1; 1.374 + case NON_SXP: 1.375 + return (PORT_Strcasecmp(exp,str) ? 1 : 0); 1.376 + default: 1.377 + return port_RegExpMatch(str, exp, PR_TRUE); 1.378 + } 1.379 +} 1.380 +