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 * Copyright (c) 2000 Markus Friedl. All rights reserved. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include "includes.h" 38 RCSID("$OpenBSD: match.c,v 1.19 2002/03/01 13:12:10 markus Exp $"); 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include "match.h" 43 #include "xmalloc.h" 44 45 /* 46 * Returns true if the given string matches the pattern (which may contain ? 47 * and * as wildcards), and zero if it does not match. 48 */ 49 50 int 51 match_pattern(const char *s, const char *pattern) 52 { 53 for (;;) { 54 /* If at end of pattern, accept if also at end of string. */ 55 if (!*pattern) 56 return !*s; 57 58 if (*pattern == '*') { 59 /* Skip the asterisk. */ 60 pattern++; 61 62 /* If at end of pattern, accept immediately. */ 63 if (!*pattern) 64 return 1; 65 66 /* If next character in pattern is known, optimize. */ 67 if (*pattern != '?' && *pattern != '*') { 68 /* 69 * Look instances of the next character in 70 * pattern, and try to match starting from 71 * those. 72 */ 73 for (; *s; s++) 74 if (*s == *pattern && 75 match_pattern(s + 1, pattern + 1)) 76 return 1; 77 /* Failed. */ 78 return 0; 79 } 80 /* 81 * Move ahead one character at a time and try to 82 * match at each position. 83 */ 84 for (; *s; s++) 85 if (match_pattern(s, pattern)) 86 return 1; 87 /* Failed. */ 88 return 0; 89 } 90 /* 91 * There must be at least one more character in the string. 92 * If we are at the end, fail. 93 */ 94 if (!*s) 95 return 0; 96 97 /* Check if the next character of the string is acceptable. */ 98 if (*pattern != '?' && *pattern != *s) 99 return 0; 100 101 /* Move to the next character, both in string and in pattern. */ 102 s++; 103 pattern++; 104 } 105 /* NOTREACHED */ 106 } 107 108 /* 109 * Tries to match the string against the 110 * comma-separated sequence of subpatterns (each possibly preceded by ! to 111 * indicate negation). Returns -1 if negation matches, 1 if there is 112 * a positive match, 0 if there is no match at all. 113 */ 114 115 int 116 match_pattern_list(const char *string, const char *pattern, u_int len, 117 int dolower) 118 { 119 char sub[1024]; 120 int negated; 121 int got_positive; 122 u_int i, subi; 123 124 got_positive = 0; 125 for (i = 0; i < len;) { 126 /* Check if the subpattern is negated. */ 127 if (pattern[i] == '!') { 128 negated = 1; 129 i++; 130 } else 131 negated = 0; 132 133 /* 134 * Extract the subpattern up to a comma or end. Convert the 135 * subpattern to lowercase. 136 */ 137 for (subi = 0; 138 i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; 139 subi++, i++) 140 sub[subi] = dolower && isupper(pattern[i]) ? 141 tolower(pattern[i]) : pattern[i]; 142 /* If subpattern too long, return failure (no match). */ 143 if (subi >= sizeof(sub) - 1) 144 return 0; 145 146 /* If the subpattern was terminated by a comma, skip the comma. */ 147 if (i < len && pattern[i] == ',') 148 i++; 149 150 /* Null-terminate the subpattern. */ 151 sub[subi] = '\0'; 152 153 /* Try to match the subpattern against the string. */ 154 if (match_pattern(string, sub)) { 155 if (negated) 156 return -1; /* Negative */ 157 else 158 got_positive = 1; /* Positive */ 159 } 160 } 161 162 /* 163 * Return success if got a positive match. If there was a negative 164 * match, we have already returned -1 and never get here. 165 */ 166 return got_positive; 167 } 168 169 /* 170 * Tries to match the host name (which must be in all lowercase) against the 171 * comma-separated sequence of subpatterns (each possibly preceded by ! to 172 * indicate negation). Returns -1 if negation matches, 1 if there is 173 * a positive match, 0 if there is no match at all. 174 */ 175 int 176 match_hostname(const char *host, const char *pattern, u_int len) 177 { 178 return match_pattern_list(host, pattern, len, 1); 179 } 180 181 /* 182 * returns 0 if we get a negative match for the hostname or the ip 183 * or if we get no match at all. returns 1 otherwise. 184 */ 185 int 186 match_host_and_ip(const char *host, const char *ipaddr, 187 const char *patterns) 188 { 189 int mhost, mip; 190 191 /* negative ipaddr match */ 192 if ((mip = match_hostname(ipaddr, patterns, strlen(patterns))) == -1) 193 return 0; 194 /* negative hostname match */ 195 if ((mhost = match_hostname(host, patterns, strlen(patterns))) == -1) 196 return 0; 197 /* no match at all */ 198 if (mhost == 0 && mip == 0) 199 return 0; 200 return 1; 201 } 202 203 /* 204 * match user, user@host_or_ip, user@host_or_ip_list against pattern 205 */ 206 int 207 match_user(const char *user, const char *host, const char *ipaddr, 208 const char *pattern) 209 { 210 char *p, *pat; 211 int ret; 212 213 if ((p = strchr(pattern,'@')) == NULL) 214 return match_pattern(user, pattern); 215 216 pat = xstrdup(pattern); 217 p = strchr(pat, '@'); 218 *p++ = '\0'; 219 220 if ((ret = match_pattern(user, pat)) == 1) 221 ret = match_host_and_ip(host, ipaddr, p); 222 xfree(pat); 223 224 return ret; 225 } 226 227 /* 228 * Returns first item from client-list that is also supported by server-list, 229 * caller must xfree() returned string. 230 */ 231 #define MAX_PROP 40 232 #define SEP "," 233 char * 234 match_list(const char *client, const char *server, u_int *next) 235 { 236 char *sproposals[MAX_PROP]; 237 char *c, *s, *p, *ret, *cp, *sp; 238 int i, j, nproposals; 239 240 c = cp = xstrdup(client); 241 s = sp = xstrdup(server); 242 243 for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; 244 (p = strsep(&sp, SEP)), i++) { 245 if (i < MAX_PROP) 246 sproposals[i] = p; 247 else 248 break; 249 } 250 nproposals = i; 251 252 for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; 253 (p = strsep(&cp, SEP)), i++) { 254 for (j = 0; j < nproposals; j++) { 255 if (strcmp(p, sproposals[j]) == 0) { 256 ret = xstrdup(p); 257 if (next != NULL) 258 *next = (cp == NULL) ? 259 strlen(c) : cp - c; 260 xfree(c); 261 xfree(s); 262 return ret; 263 } 264 } 265 } 266 if (next != NULL) 267 *next = strlen(c); 268 xfree(c); 269 xfree(s); 270 return NULL; 271 } 272