match.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /* $OpenBSD: match.c,v 1.42 2020/07/05 23:59:45 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. #include "includes.h"
  38. #include <sys/types.h>
  39. #include <ctype.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <stdarg.h>
  43. #include <stdio.h>
  44. #include "xmalloc.h"
  45. #include "match.h"
  46. #include "misc.h"
  47. /*
  48. * Returns true if the given string matches the pattern (which may contain ?
  49. * and * as wildcards), and zero if it does not match.
  50. */
  51. int
  52. match_pattern(const char *s, const char *pattern)
  53. {
  54. for (;;) {
  55. /* If at end of pattern, accept if also at end of string. */
  56. if (!*pattern)
  57. return !*s;
  58. if (*pattern == '*') {
  59. /* Skip this and any consecutive asterisks. */
  60. while (*pattern == '*')
  61. pattern++;
  62. /* If at end of pattern, accept immediately. */
  63. if (!*pattern)
  64. return 1;
  65. /* If next character in pattern is known, optimize. */
  66. if (*pattern != '?' && *pattern != '*') {
  67. /*
  68. * Look instances of the next character in
  69. * pattern, and try to match starting from
  70. * those.
  71. */
  72. for (; *s; s++)
  73. if (*s == *pattern &&
  74. match_pattern(s + 1, pattern + 1))
  75. return 1;
  76. /* Failed. */
  77. return 0;
  78. }
  79. /*
  80. * Move ahead one character at a time and try to
  81. * match at each position.
  82. */
  83. for (; *s; s++)
  84. if (match_pattern(s, pattern))
  85. return 1;
  86. /* Failed. */
  87. return 0;
  88. }
  89. /*
  90. * There must be at least one more character in the string.
  91. * If we are at the end, fail.
  92. */
  93. if (!*s)
  94. return 0;
  95. /* Check if the next character of the string is acceptable. */
  96. if (*pattern != '?' && *pattern != *s)
  97. return 0;
  98. /* Move to the next character, both in string and in pattern. */
  99. s++;
  100. pattern++;
  101. }
  102. /* NOTREACHED */
  103. }
  104. /*
  105. * Tries to match the string against the
  106. * comma-separated sequence of subpatterns (each possibly preceded by ! to
  107. * indicate negation). Returns -1 if negation matches, 1 if there is
  108. * a positive match, 0 if there is no match at all.
  109. */
  110. int
  111. match_pattern_list(const char *string, const char *pattern, int dolower)
  112. {
  113. char sub[1024];
  114. int negated;
  115. int got_positive;
  116. u_int i, subi, len = strlen(pattern);
  117. got_positive = 0;
  118. for (i = 0; i < len;) {
  119. /* Check if the subpattern is negated. */
  120. if (pattern[i] == '!') {
  121. negated = 1;
  122. i++;
  123. } else
  124. negated = 0;
  125. /*
  126. * Extract the subpattern up to a comma or end. Convert the
  127. * subpattern to lowercase.
  128. */
  129. for (subi = 0;
  130. i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
  131. subi++, i++)
  132. sub[subi] = dolower && isupper((u_char)pattern[i]) ?
  133. tolower((u_char)pattern[i]) : pattern[i];
  134. /* If subpattern too long, return failure (no match). */
  135. if (subi >= sizeof(sub) - 1)
  136. return 0;
  137. /* If the subpattern was terminated by a comma, then skip it. */
  138. if (i < len && pattern[i] == ',')
  139. i++;
  140. /* Null-terminate the subpattern. */
  141. sub[subi] = '\0';
  142. /* Try to match the subpattern against the string. */
  143. if (match_pattern(string, sub)) {
  144. if (negated)
  145. return -1; /* Negative */
  146. else
  147. got_positive = 1; /* Positive */
  148. }
  149. }
  150. /*
  151. * Return success if got a positive match. If there was a negative
  152. * match, we have already returned -1 and never get here.
  153. */
  154. return got_positive;
  155. }
  156. /* Match a list representing users or groups. */
  157. int
  158. match_usergroup_pattern_list(const char *string, const char *pattern)
  159. {
  160. #ifdef HAVE_CYGWIN
  161. /* Windows usernames may be Unicode and are not case sensitive */
  162. return cygwin_ug_match_pattern_list(string, pattern);
  163. #else
  164. /* Case sensitive match */
  165. return match_pattern_list(string, pattern, 0);
  166. #endif
  167. }
  168. /*
  169. * Tries to match the host name (which must be in all lowercase) against the
  170. * comma-separated sequence of subpatterns (each possibly preceded by ! to
  171. * indicate negation). Returns -1 if negation matches, 1 if there is
  172. * a positive match, 0 if there is no match at all.
  173. */
  174. int
  175. match_hostname(const char *host, const char *pattern)
  176. {
  177. char *hostcopy = xstrdup(host);
  178. int r;
  179. lowercase(hostcopy);
  180. r = match_pattern_list(hostcopy, pattern, 1);
  181. free(hostcopy);
  182. return r;
  183. }
  184. /*
  185. * returns 0 if we get a negative match for the hostname or the ip
  186. * or if we get no match at all. returns -1 on error, or 1 on
  187. * successful match.
  188. */
  189. int
  190. match_host_and_ip(const char *host, const char *ipaddr,
  191. const char *patterns)
  192. {
  193. int mhost, mip;
  194. if ((mip = addr_match_list(ipaddr, patterns)) == -2)
  195. return -1; /* error in ipaddr match */
  196. else if (host == NULL || ipaddr == NULL || mip == -1)
  197. return 0; /* negative ip address match, or testing pattern */
  198. /* negative hostname match */
  199. if ((mhost = match_hostname(host, patterns)) == -1)
  200. return 0;
  201. /* no match at all */
  202. if (mhost == 0 && mip == 0)
  203. return 0;
  204. return 1;
  205. }
  206. /*
  207. * Match user, user@host_or_ip, user@host_or_ip_list against pattern.
  208. * If user, host and ipaddr are all NULL then validate pattern/
  209. * Returns -1 on invalid pattern, 0 on no match, 1 on match.
  210. */
  211. int
  212. match_user(const char *user, const char *host, const char *ipaddr,
  213. const char *pattern)
  214. {
  215. char *p, *pat;
  216. int ret;
  217. /* test mode */
  218. if (user == NULL && host == NULL && ipaddr == NULL) {
  219. if ((p = strchr(pattern, '@')) != NULL &&
  220. match_host_and_ip(NULL, NULL, p + 1) < 0)
  221. return -1;
  222. return 0;
  223. }
  224. if ((p = strchr(pattern, '@')) == NULL)
  225. return match_pattern(user, pattern);
  226. pat = xstrdup(pattern);
  227. p = strchr(pat, '@');
  228. *p++ = '\0';
  229. if ((ret = match_pattern(user, pat)) == 1)
  230. ret = match_host_and_ip(host, ipaddr, p);
  231. free(pat);
  232. return ret;
  233. }
  234. /*
  235. * Returns first item from client-list that is also supported by server-list,
  236. * caller must free the returned string.
  237. */
  238. #define MAX_PROP 140
  239. #define SEP ","
  240. char *
  241. match_list(const char *client, const char *server, u_int *next)
  242. {
  243. char *sproposals[MAX_PROP];
  244. char *c, *s, *p, *ret, *cp, *sp;
  245. int i, j, nproposals;
  246. c = cp = xstrdup(client);
  247. s = sp = xstrdup(server);
  248. for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0';
  249. (p = strsep(&sp, SEP)), i++) {
  250. if (i < MAX_PROP)
  251. sproposals[i] = p;
  252. else
  253. break;
  254. }
  255. nproposals = i;
  256. for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0';
  257. (p = strsep(&cp, SEP)), i++) {
  258. for (j = 0; j < nproposals; j++) {
  259. if (strcmp(p, sproposals[j]) == 0) {
  260. ret = xstrdup(p);
  261. if (next != NULL)
  262. *next = (cp == NULL) ?
  263. strlen(c) : (u_int)(cp - c);
  264. free(c);
  265. free(s);
  266. return ret;
  267. }
  268. }
  269. }
  270. if (next != NULL)
  271. *next = strlen(c);
  272. free(c);
  273. free(s);
  274. return NULL;
  275. }
  276. /*
  277. * Filter proposal using pattern-list filter.
  278. * "denylist" determines sense of filter:
  279. * non-zero indicates that items matching filter should be excluded.
  280. * zero indicates that only items matching filter should be included.
  281. * returns NULL on allocation error, otherwise caller must free result.
  282. */
  283. static char *
  284. filter_list(const char *proposal, const char *filter, int denylist)
  285. {
  286. size_t len = strlen(proposal) + 1;
  287. char *fix_prop = malloc(len);
  288. char *orig_prop = strdup(proposal);
  289. char *cp, *tmp;
  290. int r;
  291. if (fix_prop == NULL || orig_prop == NULL) {
  292. free(orig_prop);
  293. free(fix_prop);
  294. return NULL;
  295. }
  296. tmp = orig_prop;
  297. *fix_prop = '\0';
  298. while ((cp = strsep(&tmp, ",")) != NULL) {
  299. r = match_pattern_list(cp, filter, 0);
  300. if ((denylist && r != 1) || (!denylist && r == 1)) {
  301. if (*fix_prop != '\0')
  302. strlcat(fix_prop, ",", len);
  303. strlcat(fix_prop, cp, len);
  304. }
  305. }
  306. free(orig_prop);
  307. return fix_prop;
  308. }
  309. /*
  310. * Filters a comma-separated list of strings, excluding any entry matching
  311. * the 'filter' pattern list. Caller must free returned string.
  312. */
  313. char *
  314. match_filter_denylist(const char *proposal, const char *filter)
  315. {
  316. return filter_list(proposal, filter, 1);
  317. }
  318. /*
  319. * Filters a comma-separated list of strings, including only entries matching
  320. * the 'filter' pattern list. Caller must free returned string.
  321. */
  322. char *
  323. match_filter_allowlist(const char *proposal, const char *filter)
  324. {
  325. return filter_list(proposal, filter, 0);
  326. }