1 |
|
|
/* $OpenBSD: match.c,v 1.37 2017/03/10 04:24:55 djm Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 |
|
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 |
|
|
* All rights reserved |
6 |
|
|
* Simple pattern matching, with '*' and '?' as wildcards. |
7 |
|
|
* |
8 |
|
|
* As far as I am concerned, the code I have written for this software |
9 |
|
|
* can be used freely for any purpose. Any derived versions of this |
10 |
|
|
* software must be clearly marked as such, and if the derived work is |
11 |
|
|
* incompatible with the protocol description in the RFC file, it must be |
12 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
13 |
|
|
*/ |
14 |
|
|
/* |
15 |
|
|
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
16 |
|
|
* |
17 |
|
|
* Redistribution and use in source and binary forms, with or without |
18 |
|
|
* modification, are permitted provided that the following conditions |
19 |
|
|
* are met: |
20 |
|
|
* 1. Redistributions of source code must retain the above copyright |
21 |
|
|
* notice, this list of conditions and the following disclaimer. |
22 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
23 |
|
|
* notice, this list of conditions and the following disclaimer in the |
24 |
|
|
* documentation and/or other materials provided with the distribution. |
25 |
|
|
* |
26 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
27 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
28 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
29 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
30 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
31 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
32 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
33 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
34 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 |
|
|
*/ |
37 |
|
|
|
38 |
|
|
#include <sys/types.h> |
39 |
|
|
|
40 |
|
|
#include <ctype.h> |
41 |
|
|
#include <stdlib.h> |
42 |
|
|
#include <string.h> |
43 |
|
|
#include <stdio.h> |
44 |
|
|
|
45 |
|
|
#include "xmalloc.h" |
46 |
|
|
#include "match.h" |
47 |
|
|
#include "misc.h" |
48 |
|
|
|
49 |
|
|
/* |
50 |
|
|
* Returns true if the given string matches the pattern (which may contain ? |
51 |
|
|
* and * as wildcards), and zero if it does not match. |
52 |
|
|
*/ |
53 |
|
|
|
54 |
|
|
int |
55 |
|
|
match_pattern(const char *s, const char *pattern) |
56 |
|
|
{ |
57 |
|
219 |
for (;;) { |
58 |
|
|
/* If at end of pattern, accept if also at end of string. */ |
59 |
✗✓ |
196 |
if (!*pattern) |
60 |
|
|
return !*s; |
61 |
|
|
|
62 |
✓✓ |
196 |
if (*pattern == '*') { |
63 |
|
|
/* Skip the asterisk. */ |
64 |
|
1 |
pattern++; |
65 |
|
|
|
66 |
|
|
/* If at end of pattern, accept immediately. */ |
67 |
✓✗ |
1 |
if (!*pattern) |
68 |
|
1 |
return 1; |
69 |
|
|
|
70 |
|
|
/* If next character in pattern is known, optimize. */ |
71 |
|
|
if (*pattern != '?' && *pattern != '*') { |
72 |
|
|
/* |
73 |
|
|
* Look instances of the next character in |
74 |
|
|
* pattern, and try to match starting from |
75 |
|
|
* those. |
76 |
|
|
*/ |
77 |
|
|
for (; *s; s++) |
78 |
|
|
if (*s == *pattern && |
79 |
|
|
match_pattern(s + 1, pattern + 1)) |
80 |
|
|
return 1; |
81 |
|
|
/* Failed. */ |
82 |
|
|
return 0; |
83 |
|
|
} |
84 |
|
|
/* |
85 |
|
|
* Move ahead one character at a time and try to |
86 |
|
|
* match at each position. |
87 |
|
|
*/ |
88 |
|
|
for (; *s; s++) |
89 |
|
|
if (match_pattern(s, pattern)) |
90 |
|
|
return 1; |
91 |
|
|
/* Failed. */ |
92 |
|
|
return 0; |
93 |
|
|
} |
94 |
|
|
/* |
95 |
|
|
* There must be at least one more character in the string. |
96 |
|
|
* If we are at the end, fail. |
97 |
|
|
*/ |
98 |
✗✓ |
195 |
if (!*s) |
99 |
|
|
return 0; |
100 |
|
|
|
101 |
|
|
/* Check if the next character of the string is acceptable. */ |
102 |
✓✗✓✓
|
390 |
if (*pattern != '?' && *pattern != *s) |
103 |
|
22 |
return 0; |
104 |
|
|
|
105 |
|
|
/* Move to the next character, both in string and in pattern. */ |
106 |
|
173 |
s++; |
107 |
|
173 |
pattern++; |
108 |
|
|
} |
109 |
|
|
/* NOTREACHED */ |
110 |
|
23 |
} |
111 |
|
|
|
112 |
|
|
/* |
113 |
|
|
* Tries to match the string against the |
114 |
|
|
* comma-separated sequence of subpatterns (each possibly preceded by ! to |
115 |
|
|
* indicate negation). Returns -1 if negation matches, 1 if there is |
116 |
|
|
* a positive match, 0 if there is no match at all. |
117 |
|
|
*/ |
118 |
|
|
int |
119 |
|
|
match_pattern_list(const char *string, const char *pattern, int dolower) |
120 |
|
|
{ |
121 |
|
28 |
char sub[1024]; |
122 |
|
|
int negated; |
123 |
|
|
int got_positive; |
124 |
|
14 |
u_int i, subi, len = strlen(pattern); |
125 |
|
|
|
126 |
|
|
got_positive = 0; |
127 |
✓✓ |
51 |
for (i = 0; i < len;) { |
128 |
|
|
/* Check if the subpattern is negated. */ |
129 |
✗✓ |
23 |
if (pattern[i] == '!') { |
130 |
|
|
negated = 1; |
131 |
|
|
i++; |
132 |
|
|
} else |
133 |
|
|
negated = 0; |
134 |
|
|
|
135 |
|
|
/* |
136 |
|
|
* Extract the subpattern up to a comma or end. Convert the |
137 |
|
|
* subpattern to lowercase. |
138 |
|
|
*/ |
139 |
✓✓ |
606 |
for (subi = 0; |
140 |
✓✓✓✗
|
902 |
i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; |
141 |
|
287 |
subi++, i++) |
142 |
✗✓✗✗
|
861 |
sub[subi] = dolower && isupper((u_char)pattern[i]) ? |
143 |
|
287 |
tolower((u_char)pattern[i]) : pattern[i]; |
144 |
|
|
/* If subpattern too long, return failure (no match). */ |
145 |
✗✓ |
23 |
if (subi >= sizeof(sub) - 1) |
146 |
|
|
return 0; |
147 |
|
|
|
148 |
|
|
/* If the subpattern was terminated by a comma, then skip it. */ |
149 |
✓✓✓✗
|
32 |
if (i < len && pattern[i] == ',') |
150 |
|
9 |
i++; |
151 |
|
|
|
152 |
|
|
/* Null-terminate the subpattern. */ |
153 |
|
23 |
sub[subi] = '\0'; |
154 |
|
|
|
155 |
|
|
/* Try to match the subpattern against the string. */ |
156 |
✓✓ |
23 |
if (match_pattern(string, sub)) { |
157 |
✗✓ |
1 |
if (negated) |
158 |
|
|
return -1; /* Negative */ |
159 |
|
|
else |
160 |
|
|
got_positive = 1; /* Positive */ |
161 |
|
1 |
} |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
/* |
165 |
|
|
* Return success if got a positive match. If there was a negative |
166 |
|
|
* match, we have already returned -1 and never get here. |
167 |
|
|
*/ |
168 |
|
14 |
return got_positive; |
169 |
|
14 |
} |
170 |
|
|
|
171 |
|
|
/* |
172 |
|
|
* Tries to match the host name (which must be in all lowercase) against the |
173 |
|
|
* comma-separated sequence of subpatterns (each possibly preceded by ! to |
174 |
|
|
* indicate negation). Returns -1 if negation matches, 1 if there is |
175 |
|
|
* a positive match, 0 if there is no match at all. |
176 |
|
|
*/ |
177 |
|
|
int |
178 |
|
|
match_hostname(const char *host, const char *pattern) |
179 |
|
|
{ |
180 |
|
|
char *hostcopy = xstrdup(host); |
181 |
|
|
int r; |
182 |
|
|
|
183 |
|
|
lowercase(hostcopy); |
184 |
|
|
r = match_pattern_list(hostcopy, pattern, 1); |
185 |
|
|
free(hostcopy); |
186 |
|
|
return r; |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
/* |
190 |
|
|
* returns 0 if we get a negative match for the hostname or the ip |
191 |
|
|
* or if we get no match at all. returns -1 on error, or 1 on |
192 |
|
|
* successful match. |
193 |
|
|
*/ |
194 |
|
|
int |
195 |
|
|
match_host_and_ip(const char *host, const char *ipaddr, |
196 |
|
|
const char *patterns) |
197 |
|
|
{ |
198 |
|
|
int mhost, mip; |
199 |
|
|
|
200 |
|
|
if ((mip = addr_match_list(ipaddr, patterns)) == -2) |
201 |
|
|
return -1; /* error in ipaddr match */ |
202 |
|
|
else if (host == NULL || ipaddr == NULL || mip == -1) |
203 |
|
|
return 0; /* negative ip address match, or testing pattern */ |
204 |
|
|
|
205 |
|
|
/* negative hostname match */ |
206 |
|
|
if ((mhost = match_hostname(host, patterns)) == -1) |
207 |
|
|
return 0; |
208 |
|
|
/* no match at all */ |
209 |
|
|
if (mhost == 0 && mip == 0) |
210 |
|
|
return 0; |
211 |
|
|
return 1; |
212 |
|
|
} |
213 |
|
|
|
214 |
|
|
/* |
215 |
|
|
* Match user, user@host_or_ip, user@host_or_ip_list against pattern. |
216 |
|
|
* If user, host and ipaddr are all NULL then validate pattern/ |
217 |
|
|
* Returns -1 on invalid pattern, 0 on no match, 1 on match. |
218 |
|
|
*/ |
219 |
|
|
int |
220 |
|
|
match_user(const char *user, const char *host, const char *ipaddr, |
221 |
|
|
const char *pattern) |
222 |
|
|
{ |
223 |
|
|
char *p, *pat; |
224 |
|
|
int ret; |
225 |
|
|
|
226 |
|
|
/* test mode */ |
227 |
|
|
if (user == NULL && host == NULL && ipaddr == NULL) { |
228 |
|
|
if ((p = strchr(pattern, '@')) != NULL && |
229 |
|
|
match_host_and_ip(NULL, NULL, p + 1) < 0) |
230 |
|
|
return -1; |
231 |
|
|
return 0; |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
if ((p = strchr(pattern,'@')) == NULL) |
235 |
|
|
return match_pattern(user, pattern); |
236 |
|
|
|
237 |
|
|
pat = xstrdup(pattern); |
238 |
|
|
p = strchr(pat, '@'); |
239 |
|
|
*p++ = '\0'; |
240 |
|
|
|
241 |
|
|
if ((ret = match_pattern(user, pat)) == 1) |
242 |
|
|
ret = match_host_and_ip(host, ipaddr, p); |
243 |
|
|
free(pat); |
244 |
|
|
|
245 |
|
|
return ret; |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
/* |
249 |
|
|
* Returns first item from client-list that is also supported by server-list, |
250 |
|
|
* caller must free the returned string. |
251 |
|
|
*/ |
252 |
|
|
#define MAX_PROP 40 |
253 |
|
|
#define SEP "," |
254 |
|
|
char * |
255 |
|
|
match_list(const char *client, const char *server, u_int *next) |
256 |
|
|
{ |
257 |
|
22 |
char *sproposals[MAX_PROP]; |
258 |
|
11 |
char *c, *s, *p, *ret, *cp, *sp; |
259 |
|
|
int i, j, nproposals; |
260 |
|
|
|
261 |
|
11 |
c = cp = xstrdup(client); |
262 |
|
11 |
s = sp = xstrdup(server); |
263 |
|
|
|
264 |
✓✓✓✗
|
214 |
for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; |
265 |
|
48 |
(p = strsep(&sp, SEP)), i++) { |
266 |
|
48 |
if (i < MAX_PROP) |
267 |
|
48 |
sproposals[i] = p; |
268 |
|
|
else |
269 |
|
|
break; |
270 |
|
|
} |
271 |
|
|
nproposals = i; |
272 |
|
|
|
273 |
✓✓✓✗
|
87 |
for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; |
274 |
|
19 |
(p = strsep(&cp, SEP)), i++) { |
275 |
✓✓ |
162 |
for (j = 0; j < nproposals; j++) { |
276 |
✓✓ |
62 |
if (strcmp(p, sproposals[j]) == 0) { |
277 |
|
8 |
ret = xstrdup(p); |
278 |
✓✓ |
8 |
if (next != NULL) |
279 |
✓✓ |
6 |
*next = (cp == NULL) ? |
280 |
|
2 |
strlen(c) : (u_int)(cp - c); |
281 |
|
8 |
free(c); |
282 |
|
8 |
free(s); |
283 |
|
8 |
return ret; |
284 |
|
|
} |
285 |
|
|
} |
286 |
|
|
} |
287 |
✗✓ |
3 |
if (next != NULL) |
288 |
|
|
*next = strlen(c); |
289 |
|
3 |
free(c); |
290 |
|
3 |
free(s); |
291 |
|
3 |
return NULL; |
292 |
|
11 |
} |
293 |
|
|
|
294 |
|
|
/* |
295 |
|
|
* Filters a comma-separated list of strings, excluding any entry matching |
296 |
|
|
* the 'filter' pattern list. Caller must free returned string. |
297 |
|
|
*/ |
298 |
|
|
char * |
299 |
|
|
match_filter_list(const char *proposal, const char *filter) |
300 |
|
|
{ |
301 |
|
|
size_t len = strlen(proposal) + 1; |
302 |
|
|
char *fix_prop = malloc(len); |
303 |
|
|
char *orig_prop = strdup(proposal); |
304 |
|
|
char *cp, *tmp; |
305 |
|
|
|
306 |
|
|
if (fix_prop == NULL || orig_prop == NULL) { |
307 |
|
|
free(orig_prop); |
308 |
|
|
free(fix_prop); |
309 |
|
|
return NULL; |
310 |
|
|
} |
311 |
|
|
|
312 |
|
|
tmp = orig_prop; |
313 |
|
|
*fix_prop = '\0'; |
314 |
|
|
while ((cp = strsep(&tmp, ",")) != NULL) { |
315 |
|
|
if (match_pattern_list(cp, filter, 0) != 1) { |
316 |
|
|
if (*fix_prop != '\0') |
317 |
|
|
strlcat(fix_prop, ",", len); |
318 |
|
|
strlcat(fix_prop, cp, len); |
319 |
|
|
} |
320 |
|
|
} |
321 |
|
|
free(orig_prop); |
322 |
|
|
return fix_prop; |
323 |
|
|
} |
324 |
|
|
|