Line | Count | Source |
1 | | /* |
2 | | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
3 | | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | | * All rights reserved |
5 | | * Simple pattern matching, with '*' and '?' as wildcards. |
6 | | * |
7 | | * As far as I am concerned, the code I have written for this software |
8 | | * can be used freely for any purpose. Any derived versions of this |
9 | | * software must be clearly marked as such, and if the derived work is |
10 | | * incompatible with the protocol description in the RFC file, it must be |
11 | | * called by a name other than "ssh" or "Secure Shell". |
12 | | */ |
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 "config.h" |
39 | | |
40 | | #include <ctype.h> |
41 | | #include <stdbool.h> |
42 | | #include <sys/types.h> |
43 | | #ifndef _WIN32 |
44 | | #include <arpa/inet.h> |
45 | | #include <netinet/in.h> |
46 | | #include <sys/socket.h> |
47 | | #endif |
48 | | |
49 | | /* for systems without IPv6 support matching should still work */ |
50 | | #ifndef INET6_ADDRSTRLEN |
51 | | #define INET6_ADDRSTRLEN 46 |
52 | | #endif |
53 | | |
54 | | #include "libssh/priv.h" |
55 | | |
56 | | /** |
57 | | * @brief Compare a string with a pattern containing wildcards `*` and `?` |
58 | | * |
59 | | * This function is an iterative replacement for the previously recursive |
60 | | * implementation to avoid exponential complexity (DoS) with specific patterns. |
61 | | * |
62 | | * @param[in] s The string to match. |
63 | | * @param[in] pattern The pattern to match against. |
64 | | * |
65 | | * @return 1 if the pattern matches, 0 otherwise. |
66 | | */ |
67 | | static int match_pattern(const char *s, const char *pattern) |
68 | 0 | { |
69 | 0 | const char *s_star = NULL; /* Position in s when last `*` was met */ |
70 | 0 | const char *p_star = NULL; /* Position in pattern after last `*` */ |
71 | |
|
72 | 0 | if (s == NULL || pattern == NULL) { |
73 | 0 | return 0; |
74 | 0 | } |
75 | | |
76 | 0 | while (*s) { |
77 | | /* Case 1: Exact match or '?' wildcard */ |
78 | 0 | if (*pattern == *s || *pattern == '?') { |
79 | 0 | s++; |
80 | 0 | pattern++; |
81 | 0 | continue; |
82 | 0 | } |
83 | | |
84 | | /* Case 2: '*' wildcard */ |
85 | 0 | if (*pattern == '*') { |
86 | | /* Record the position of the star and the current string position. |
87 | | * We optimistically assume * matches 0 characters first. |
88 | | */ |
89 | 0 | p_star = ++pattern; |
90 | 0 | s_star = s; |
91 | 0 | continue; |
92 | 0 | } |
93 | | |
94 | | /* Case 3: Mismatch */ |
95 | 0 | if (p_star) { |
96 | | /* If we have seen a star previously, backtrack. |
97 | | * We restore the pattern to just after the star, |
98 | | * but advance the string position (consume one more char for the |
99 | | * star). |
100 | | * No need to backtrack to previous stars as any match of the last |
101 | | * star could be eaten the same way by the previous star. |
102 | | */ |
103 | 0 | pattern = p_star; |
104 | 0 | s = ++s_star; |
105 | 0 | continue; |
106 | 0 | } |
107 | | |
108 | | /* Case 4: Mismatch and no star to backtrack to */ |
109 | 0 | return 0; |
110 | 0 | } |
111 | | |
112 | | /* Handle trailing stars in the pattern |
113 | | * (e.g., pattern "abc*" matching "abc") */ |
114 | 0 | while (*pattern == '*') { |
115 | 0 | pattern++; |
116 | 0 | } |
117 | | |
118 | | /* If we reached the end of the pattern, it's a match */ |
119 | 0 | return (*pattern == '\0'); |
120 | 0 | } |
121 | | |
122 | | /* |
123 | | * Tries to match the string against the comma-separated sequence of subpatterns |
124 | | * (each possibly preceded by ! to indicate negation). |
125 | | * Returns -1 if negation matches, 1 if there is a positive match, 0 if there is |
126 | | * no match at all. |
127 | | */ |
128 | | int match_pattern_list(const char *string, const char *pattern, |
129 | 0 | size_t len, int dolower) { |
130 | 0 | char sub[1024]; |
131 | 0 | int negated; |
132 | 0 | int got_positive; |
133 | 0 | size_t i, subi; |
134 | |
|
135 | 0 | got_positive = 0; |
136 | 0 | for (i = 0; i < len;) { |
137 | | /* Check if the subpattern is negated. */ |
138 | 0 | if (pattern[i] == '!') { |
139 | 0 | negated = 1; |
140 | 0 | i++; |
141 | 0 | } else { |
142 | 0 | negated = 0; |
143 | 0 | } |
144 | | |
145 | | /* |
146 | | * Extract the subpattern up to a comma or end. Convert the |
147 | | * subpattern to lowercase. |
148 | | */ |
149 | 0 | for (subi = 0; |
150 | 0 | i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; |
151 | 0 | subi++, i++) { |
152 | 0 | sub[subi] = dolower && isupper(pattern[i]) ? |
153 | 0 | (char)tolower(pattern[i]) : pattern[i]; |
154 | 0 | } |
155 | | |
156 | | /* If subpattern too long, return failure (no match). */ |
157 | 0 | if (subi >= sizeof(sub) - 1) { |
158 | 0 | return 0; |
159 | 0 | } |
160 | | |
161 | | /* If the subpattern was terminated by a comma, skip the comma. */ |
162 | 0 | if (i < len && pattern[i] == ',') { |
163 | 0 | i++; |
164 | 0 | } |
165 | | |
166 | | /* Null-terminate the subpattern. */ |
167 | 0 | sub[subi] = '\0'; |
168 | | |
169 | | /* Try to match the subpattern against the string. */ |
170 | 0 | if (match_pattern(string, sub)) { |
171 | 0 | if (negated) { |
172 | 0 | return -1; /* Negative */ |
173 | 0 | } else { |
174 | 0 | got_positive = 1; /* Positive */ |
175 | 0 | } |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | /* |
180 | | * Return success if got a positive match. If there was a negative |
181 | | * match, we have already returned -1 and never get here. |
182 | | */ |
183 | 0 | return got_positive; |
184 | 0 | } |
185 | | |
186 | | /* |
187 | | * Tries to match the host name (which must be in all lowercase) against the |
188 | | * comma-separated sequence of subpatterns (each possibly preceded by ! to |
189 | | * indicate negation). |
190 | | * Returns -1 if negation matches, 1 if there is a positive match, 0 if there |
191 | | * is no match at all. |
192 | | */ |
193 | | int |
194 | | match_hostname(const char *host, const char *pattern, size_t len) |
195 | 0 | { |
196 | 0 | return match_pattern_list(host, pattern, len, 1); |
197 | 0 | } |
198 | | |
199 | | #ifndef _WIN32 |
200 | | /** |
201 | | * @brief Tries to match the host IPv6 address against a given network address |
202 | | * with specified prefix length in CIDR notation. |
203 | | * |
204 | | * @param[in] host_addr The host address to verify. |
205 | | * |
206 | | * @param[in] net_addr The network id address against which the match is |
207 | | * being verified |
208 | | * |
209 | | * @param[in] bits The prefix length |
210 | | * |
211 | | * @return 0 on a negative match. |
212 | | * @return 1 on a positive match. |
213 | | */ |
214 | | static int |
215 | | cidr_match_6(struct in6_addr *host_addr, |
216 | | struct in6_addr *net_addr, |
217 | | unsigned int bits) |
218 | 0 | { |
219 | 0 | const uint8_t *a = host_addr->s6_addr; |
220 | 0 | const uint8_t *b = net_addr->s6_addr; |
221 | |
|
222 | 0 | unsigned int byte_whole, bits_left; |
223 | | |
224 | | /* The number of a complete byte covered by the prefix */ |
225 | 0 | byte_whole = bits / 8; |
226 | | |
227 | | /* |
228 | | * The number of bits remaining in the incomplete (last) byte |
229 | | * covered by the prefix |
230 | | */ |
231 | 0 | bits_left = bits % 8; |
232 | |
|
233 | 0 | if (byte_whole) { |
234 | 0 | if (memcmp(a, b, byte_whole) != 0) { |
235 | 0 | return 0; |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | 0 | if (bits_left) { |
240 | 0 | if ((a[byte_whole] ^ b[byte_whole]) & (0xFFu << (8 - bits_left))) { |
241 | 0 | return 0; |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | 0 | return 1; |
246 | 0 | } |
247 | | |
248 | | /** |
249 | | * @brief Tries to match the host IPv4 address against a given network address |
250 | | * with specified prefix length in CIDR notation. |
251 | | * |
252 | | * @param[in] host_addr The host address to verify. |
253 | | * |
254 | | * @param[in] net_addr The network id address against which the match is |
255 | | * being verified |
256 | | * |
257 | | * @param[in] bits The prefix length |
258 | | * |
259 | | * @return 0 on a negative match. |
260 | | * @return 1 on a positive match. |
261 | | */ |
262 | | static int |
263 | | cidr_match_4(struct in_addr *host_addr, |
264 | | struct in_addr *net_addr, |
265 | | unsigned int bits) |
266 | 0 | { |
267 | 0 | if (bits == 0) { |
268 | | /* C99 6.5.7 (3): u32 << 32 is undefined behaviour */ |
269 | 0 | return 1; |
270 | 0 | } |
271 | | |
272 | 0 | return !((host_addr->s_addr ^ net_addr->s_addr) & |
273 | 0 | htonl((0xFFFFFFFFu << (32 - bits)) & 0xFFFFFFFFu)); |
274 | 0 | } |
275 | | |
276 | | /** |
277 | | * @brief Checks if the mask length is valid according to the address family |
278 | | * (IPv4 or IPv6). |
279 | | * |
280 | | * @param[in] family The address family (e.g. AF_INET or AF_INET6) |
281 | | * |
282 | | * @param[in] mask The subnet mask (prefix) |
283 | | * |
284 | | * @return true if the mask length does not exceed the maximum valid length |
285 | | * according to the address family (IPv4 or IPv6). |
286 | | * @return false if the mask length exceeds the maximum valid length |
287 | | * or there is no match with IPv4 or IPv6 address family. |
288 | | */ |
289 | | static bool |
290 | | masklen_valid(int family, unsigned int mask) |
291 | 0 | { |
292 | 0 | switch (family) { |
293 | 0 | case AF_INET: |
294 | 0 | return mask <= 32; |
295 | 0 | case AF_INET6: |
296 | 0 | return mask <= 128; |
297 | 0 | default: |
298 | 0 | return false; |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | /** |
303 | | * @brief Extracts address family given a network address. |
304 | | * |
305 | | * @param[in] address The network address. |
306 | | * |
307 | | * @return The value of the address family if no errors. |
308 | | * @return -1 in case of errors. |
309 | | */ |
310 | | static int |
311 | | get_address_family(const char *address) |
312 | 0 | { |
313 | 0 | struct addrinfo hints, *ai = NULL; |
314 | 0 | int rc = -1, rv; |
315 | |
|
316 | 0 | ZERO_STRUCT(hints); |
317 | 0 | if (address == NULL) { |
318 | 0 | SSH_LOG(SSH_LOG_TRACE, "Bad arguments"); |
319 | 0 | goto out; |
320 | 0 | } |
321 | | |
322 | 0 | hints.ai_flags = AI_NUMERICHOST; |
323 | 0 | rv = getaddrinfo(address, NULL, &hints, &ai); |
324 | 0 | if (rv != 0) { |
325 | 0 | SSH_LOG(SSH_LOG_TRACE, |
326 | 0 | "Couldn't get address information - getaddrinfo() failed: %s", |
327 | 0 | gai_strerror(rv)); |
328 | 0 | goto out; |
329 | 0 | } |
330 | | |
331 | 0 | rc = ai->ai_family; |
332 | 0 | freeaddrinfo(ai); |
333 | |
|
334 | 0 | out: |
335 | 0 | return rc; |
336 | 0 | } |
337 | | |
338 | | /** |
339 | | * @brief Tries to match the host address against a CIDR list provided |
340 | | * by the user. If the host address family is unknown, it can be derived by |
341 | | * passing -1 as sa_family argument. |
342 | | * |
343 | | * It can be also used to validate a CIDR list when the passed address is NULL |
344 | | * and sa_family is -1. |
345 | | * |
346 | | * @param[in] address The host address to verify (NULL to validate CIDR list). |
347 | | * |
348 | | * @param[in] addrlist The CIDR list against which the match is being verified. |
349 | | * The CIDR list can contain both IPv4 and IPv6 addresses |
350 | | * and has to be comma separated |
351 | | * (',' only, space after comma not allowed). |
352 | | * |
353 | | * @param[in] sa_family The socket address family (e.g. AF_INET or AF_INET6, |
354 | | * -1 to validate CIDR list or unknown address family). |
355 | | * |
356 | | * @usage To validate CIDR list: match_cidr_address_list(NULL, addrlist, -1). |
357 | | * @usage To verify a match with unknown address family: |
358 | | * match_cidr_address_list(address, addrlist, -1). |
359 | | * @return 1 only on positive match. |
360 | | * @return 0 on negative match or valid CIDR list. |
361 | | * @return -1 on errors or invalid CIDR list. |
362 | | */ |
363 | | int |
364 | | match_cidr_address_list(const char *address, |
365 | | const char *addrlist, |
366 | | int sa_family) |
367 | 0 | { |
368 | 0 | char *list = NULL, *cp = NULL, *a = NULL, *b = NULL, *sp = NULL; |
369 | 0 | char addr_buffer[64], addr[NI_MAXHOST]; |
370 | 0 | struct in_addr try_addr, match_addr; |
371 | 0 | struct in6_addr try_addr6, match_addr6; |
372 | 0 | unsigned long mask_len; |
373 | 0 | size_t addr_len, tmp_len; |
374 | 0 | int rc = 0, r, ai_family; |
375 | |
|
376 | 0 | ZERO_STRUCT(try_addr); |
377 | 0 | ZERO_STRUCT(try_addr6); |
378 | 0 | ZERO_STRUCT(match_addr); |
379 | 0 | ZERO_STRUCT(match_addr6); |
380 | |
|
381 | 0 | if (sa_family != AF_INET && sa_family != AF_INET6 && sa_family != -1) { |
382 | 0 | SSH_LOG(SSH_LOG_TRACE, |
383 | 0 | "Invalid argument: sa_family %d is not valid", |
384 | 0 | sa_family); |
385 | 0 | return -1; |
386 | 0 | } |
387 | | |
388 | 0 | if (address != NULL) { |
389 | 0 | strlcpy(addr, address, sizeof(addr)); |
390 | | |
391 | | /* Remove interface in case of IPv6 address: addr%interface */ |
392 | 0 | a = strchr(addr, '%'); |
393 | 0 | if (a != NULL) { |
394 | 0 | *a = '\0'; |
395 | 0 | } |
396 | | |
397 | | /* |
398 | | * If sa_family is set to -1 and address is not NULL then |
399 | | * the socket address family should be derived |
400 | | */ |
401 | 0 | if (sa_family == -1) { |
402 | 0 | r = get_address_family(addr); |
403 | 0 | if (r == -1) { |
404 | 0 | SSH_LOG(SSH_LOG_TRACE, |
405 | 0 | "Failed to derive address family for address " |
406 | 0 | "\"%.100s\"", |
407 | 0 | addr); |
408 | 0 | return -1; |
409 | 0 | } |
410 | 0 | sa_family = r; |
411 | 0 | } |
412 | | |
413 | | /* |
414 | | * Translate host address from dot notation to binary network format |
415 | | * according to family type, |
416 | | * i.e. IPv4 (store in in_addr) or IPv6 (store in in6_addr) |
417 | | */ |
418 | 0 | if (sa_family == AF_INET) { |
419 | 0 | if (inet_pton(AF_INET, addr, &try_addr) == 0) { |
420 | 0 | SSH_LOG(SSH_LOG_TRACE, |
421 | 0 | "Couldn't parse IPv4 address \"%.100s\"", |
422 | 0 | addr); |
423 | 0 | return -1; |
424 | 0 | } |
425 | 0 | } else if (sa_family == AF_INET6) { |
426 | 0 | if (inet_pton(AF_INET6, addr, &try_addr6) == 0) { |
427 | 0 | SSH_LOG(SSH_LOG_TRACE, |
428 | 0 | "Couldn't parse IPv6 address \"%.100s\"", |
429 | 0 | addr); |
430 | 0 | return -1; |
431 | 0 | } |
432 | 0 | } else { |
433 | 0 | SSH_LOG(SSH_LOG_TRACE, |
434 | 0 | "Address family %d for address \"%.100s\" " |
435 | 0 | "is not recognized", |
436 | 0 | sa_family, |
437 | 0 | addr); |
438 | 0 | return -1; |
439 | 0 | } |
440 | 0 | } |
441 | | |
442 | 0 | b = list = strdup(addrlist); |
443 | 0 | if (b == NULL) { |
444 | 0 | return -1; |
445 | 0 | } |
446 | | |
447 | 0 | while ((cp = strsep(&list, ",")) != NULL) { |
448 | 0 | if (*cp == '\0') { |
449 | 0 | SSH_LOG(SSH_LOG_TRACE, "Empty entry in list \"%.100s\"", b); |
450 | 0 | rc = -1; |
451 | 0 | break; |
452 | 0 | } |
453 | | |
454 | | /* |
455 | | * Stop junk from reaching address translation. +3 for the "/prefix". |
456 | | * INET6_ADDRSTRLEN is 46 and includes space for '\0' terminator. The |
457 | | * maximum IPv6 address printable is the one that carries IPv4 too. |
458 | | * E.g. ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255 is 46 chars |
459 | | * long ('\0' included) and the maximum prefix length possible is 96. |
460 | | * This explains why +3. All the other IPv6 addresses with maximum /127 |
461 | | * prefix length (39 + 4) are covered just by INET6_ADDRSTRLEN itself |
462 | | */ |
463 | 0 | addr_len = strlen(cp); |
464 | 0 | if (addr_len > INET6_ADDRSTRLEN + 3) { |
465 | 0 | SSH_LOG(SSH_LOG_TRACE, |
466 | 0 | "List entry \"%.100s\" too long: %zu > %d (MAX ALLOWED)", |
467 | 0 | cp, |
468 | 0 | addr_len, |
469 | 0 | INET6_ADDRSTRLEN + 3); |
470 | 0 | rc = -1; |
471 | 0 | break; |
472 | 0 | } |
473 | | |
474 | 0 | #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" |
475 | 0 | tmp_len = strspn(cp, VALID_CIDR_CHARS); |
476 | 0 | if (tmp_len != addr_len) { |
477 | 0 | SSH_LOG(SSH_LOG_TRACE, |
478 | 0 | "List entry \"%.100s\" contains invalid characters " |
479 | 0 | "-> \"%c\" is an invalid character", |
480 | 0 | cp, |
481 | 0 | cp[tmp_len]); |
482 | 0 | rc = -1; |
483 | 0 | break; |
484 | 0 | } |
485 | 0 | #undef VALID_CIDR_CHARS |
486 | | |
487 | 0 | strlcpy(addr_buffer, cp, sizeof(addr_buffer)); |
488 | 0 | sp = strchr(addr_buffer, '/'); |
489 | 0 | if (sp != NULL) { |
490 | 0 | *sp = '\0'; |
491 | 0 | sp++; |
492 | 0 | mask_len = strtoul(sp, &cp, 10); |
493 | 0 | if (*sp < '0' || *sp > '9' || *cp != '\0') { |
494 | 0 | SSH_LOG(SSH_LOG_TRACE, "Error while parsing prefix: %s", sp); |
495 | 0 | rc = -1; |
496 | 0 | break; |
497 | 0 | } |
498 | 0 | if (mask_len > 128) { |
499 | 0 | SSH_LOG(SSH_LOG_TRACE, |
500 | 0 | "Invalid prefix: %lu exceeds the maximum allowed " |
501 | 0 | "(>128)", |
502 | 0 | mask_len); |
503 | 0 | rc = -1; |
504 | 0 | break; |
505 | 0 | } |
506 | 0 | } else { |
507 | 0 | SSH_LOG(SSH_LOG_TRACE, |
508 | 0 | "Missing prefix length for list entry \"%.100s\"", |
509 | 0 | addr_buffer); |
510 | 0 | rc = -1; |
511 | 0 | break; |
512 | 0 | } |
513 | | |
514 | 0 | ai_family = get_address_family(addr_buffer); |
515 | 0 | if (ai_family == -1) { |
516 | 0 | SSH_LOG(SSH_LOG_TRACE, |
517 | 0 | "Couldn't get address family for \"%.100s\"", |
518 | 0 | addr_buffer); |
519 | 0 | rc = -1; |
520 | 0 | break; |
521 | 0 | } |
522 | | |
523 | 0 | if (ai_family == AF_INET) { |
524 | 0 | if (inet_pton(AF_INET, addr_buffer, &match_addr) == 0) { |
525 | 0 | SSH_LOG(SSH_LOG_TRACE, |
526 | 0 | "Couldn't parse IPv4 address \"%.100s\"", |
527 | 0 | addr_buffer); |
528 | 0 | rc = -1; |
529 | 0 | break; |
530 | 0 | } |
531 | 0 | } else if (ai_family == AF_INET6) { |
532 | 0 | if (inet_pton(AF_INET6, addr_buffer, &match_addr6) == 0) { |
533 | 0 | SSH_LOG(SSH_LOG_TRACE, |
534 | 0 | "Couldn't parse IPv6 address \"%.100s\"", |
535 | 0 | addr_buffer); |
536 | 0 | rc = -1; |
537 | 0 | break; |
538 | 0 | } |
539 | 0 | } else { |
540 | 0 | SSH_LOG(SSH_LOG_TRACE, |
541 | 0 | "Address family %d for address \"%.100s\" " |
542 | 0 | "is not recognized", |
543 | 0 | ai_family, |
544 | 0 | addr_buffer); |
545 | 0 | rc = -1; |
546 | 0 | break; |
547 | 0 | } |
548 | | |
549 | 0 | if (masklen_valid(ai_family, mask_len) != true) { |
550 | 0 | SSH_LOG(SSH_LOG_TRACE, |
551 | 0 | "Invalid mask length %lu for list entry \"%.100s\"", |
552 | 0 | mask_len, |
553 | 0 | addr_buffer); |
554 | 0 | rc = -1; |
555 | 0 | break; |
556 | 0 | } |
557 | | |
558 | | /* Verify match between host address and network address*/ |
559 | 0 | if (((ai_family == AF_INET && sa_family == AF_INET) && |
560 | 0 | cidr_match_4(&try_addr, &match_addr, mask_len)) || |
561 | 0 | ((ai_family == AF_INET6 && sa_family == AF_INET6) && |
562 | 0 | cidr_match_6(&try_addr6, &match_addr6, mask_len))) { |
563 | 0 | rc = 1; |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | } |
567 | 0 | SAFE_FREE(b); |
568 | |
|
569 | 0 | return rc; |
570 | 0 | } |
571 | | #endif /* _WIN32 */ |
572 | | |
573 | | /** |
574 | | * @brief Tries to match an object against a comma separated group of objects |
575 | | * |
576 | | * The characters '*' and '?' are NOT considered wildcards and an object in the |
577 | | * group preceded by a ! does NOT indicate negation. The characters '*', '?' |
578 | | * and '!' are treated normally like other characters, only ',' (comma) is |
579 | | * treated specially and is considered as a delimiter that separates objects in |
580 | | * the group. |
581 | | * |
582 | | * @param[in] group Group of objects (comma separated) to match against. |
583 | | * |
584 | | * @param[in] object Object to match. |
585 | | * |
586 | | * @returns 1 if there is a match, 0 if there is no match at all. |
587 | | */ |
588 | | int match_group(const char *group, const char *object) |
589 | 0 | { |
590 | 0 | const char *a = NULL; |
591 | 0 | const char *z = NULL; |
592 | |
|
593 | 0 | if (group == NULL || object == NULL) { |
594 | 0 | return 0; |
595 | 0 | } |
596 | | |
597 | 0 | z = group; |
598 | 0 | do { |
599 | 0 | a = strchr(z, ','); |
600 | 0 | if (a == NULL) { |
601 | 0 | if (strcmp(z, object) == 0) { |
602 | 0 | return 1; |
603 | 0 | } |
604 | 0 | return 0; |
605 | 0 | } else { |
606 | 0 | if (strncmp(z, object, a - z) == 0) { |
607 | 0 | return 1; |
608 | 0 | } |
609 | 0 | } |
610 | 0 | z = a + 1; |
611 | 0 | } while (1); |
612 | | |
613 | | /* not reached */ |
614 | 0 | return 0; |
615 | 0 | } |