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 #include "match.h" 41 #include "xmalloc.h" 42 43 /* 44 * Returns true if the given string matches the pattern (which may contain ? 45 * and * as wildcards), and zero if it does not match. 46 */ 47 48 int 49 match_pattern(const char *s, const char *pattern) 50 { 51 for (;;) { 52 /* If at end of pattern, accept if also at end of string. */ 53 if (!*pattern) 54 return !*s; 55 56 if (*pattern == '*') { 57 /* Skip the asterisk. */ 58 pattern++; 59 60 /* If at end of pattern, accept immediately. */ 61 if (!*pattern) 62 return 1; 63 64 /* If next character in pattern is known, optimize. */ 65 if (*pattern != '?' && *pattern != '*') { 66 /* 67 * Look instances of the next character in 68 * pattern, and try to match starting from 69 * those. 70 */ 71 for (; *s; s++) 72 if (*s == *pattern && 73 match_pattern(s + 1, pattern + 1)) 74 return 1; 75 /* Failed. */ 76 return 0; 77 } 78 /* 79 * Move ahead one character at a time and try to 80 * match at each position. 81 */ 82 for (; *s; s++) 83 if (match_pattern(s, pattern)) 84 return 1; 85 /* Failed. */ 86 return 0; 87 } 88 /* 89 * There must be at least one more character in the string. 90 * If we are at the end, fail. 91 */ 92 if (!*s) 93 return 0; 94 95 /* Check if the next character of the string is acceptable. */ 96 if (*pattern != '?' && *pattern != *s) 97 return 0; 98 99 /* Move to the next character, both in string and in pattern. */ 100 s++; 101 pattern++; 102 } 103 /* NOTREACHED */ 104 } 105 106 /* 107 * Tries to match the string against the 108 * comma-separated sequence of subpatterns (each possibly preceded by ! to 109 * indicate negation). Returns -1 if negation matches, 1 if there is 110 * a positive match, 0 if there is no match at all. 111 */ 112 113 int 114 match_pattern_list(const char *string, const char *pattern, u_int len, 115 int dolower) 116 { 117 char sub[1024]; 118 int negated; 119 int got_positive; 120 u_int i, subi; 121 122 got_positive = 0; 123 for (i = 0; i < len;) { 124 /* Check if the subpattern is negated. */ 125 if (pattern[i] == '!') { 126 negated = 1; 127 i++; 128 } else 129 negated = 0; 130 131 /* 132 * Extract the subpattern up to a comma or end. Convert the 133 * subpattern to lowercase. 134 */ 135 for (subi = 0; 136 i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; 137 subi++, i++) 138 sub[subi] = dolower && isupper(pattern[i]) ? 139 tolower(pattern[i]) : pattern[i]; 140 /* If subpattern too long, return failure (no match). */ 141 if (subi >= sizeof(sub) - 1) 142 return 0; 143 144 /* If the subpattern was terminated by a comma, skip the comma. */ 145 if (i < len && pattern[i] == ',') 146 i++; 147 148 /* Null-terminate the subpattern. */ 149 sub[subi] = '\0'; 150 151 /* Try to match the subpattern against the string. */ 152 if (match_pattern(string, sub)) { 153 if (negated) 154 return -1; /* Negative */ 155 else 156 got_positive = 1; /* Positive */ 157 } 158 } 159 160 /* 161 * Return success if got a positive match. If there was a negative 162 * match, we have already returned -1 and never get here. 163 */ 164 return got_positive; 165 } 166 167 /* 168 * Tries to match the host name (which must be in all lowercase) against the 169 * comma-separated sequence of subpatterns (each possibly preceded by ! to 170 * indicate negation). Returns -1 if negation matches, 1 if there is 171 * a positive match, 0 if there is no match at all. 172 */ 173 int 174 match_hostname(const char *host, const char *pattern, u_int len) 175 { 176 return match_pattern_list(host, pattern, len, 1); 177 } 178 179 /* 180 * returns 0 if we get a negative match for the hostname or the ip 181 * or if we get no match at all. returns 1 otherwise. 182 */ 183 int 184 match_host_and_ip(const char *host, const char *ipaddr, 185 const char *patterns) 186 { 187 int mhost, mip; 188 189 /* negative ipaddr match */ 190 if ((mip = match_hostname(ipaddr, patterns, strlen(patterns))) == -1) 191 return 0; 192 /* negative hostname match */ 193 if ((mhost = match_hostname(host, patterns, strlen(patterns))) == -1) 194 return 0; 195 /* no match at all */ 196 if (mhost == 0 && mip == 0) 197 return 0; 198 return 1; 199 } 200 201 /* 202 * match user, user@host_or_ip, user@host_or_ip_list against pattern 203 */ 204 int 205 match_user(const char *user, const char *host, const char *ipaddr, 206 const char *pattern) 207 { 208 char *p, *pat; 209 int ret; 210 211 if ((p = strchr(pattern,'@')) == NULL) 212 return match_pattern(user, pattern); 213 214 pat = xstrdup(pattern); 215 p = strchr(pat, '@'); 216 *p++ = '\0'; 217 218 if ((ret = match_pattern(user, pat)) == 1) 219 ret = match_host_and_ip(host, ipaddr, p); 220 xfree(pat); 221 222 return ret; 223 } 224 225 /* 226 * Returns first item from client-list that is also supported by server-list, 227 * caller must xfree() returned string. 228 */ 229 #define MAX_PROP 40 230 #define SEP "," 231 char * 232 match_list(const char *client, const char *server, u_int *next) 233 { 234 char *sproposals[MAX_PROP]; 235 char *c, *s, *p, *ret, *cp, *sp; 236 int i, j, nproposals; 237 238 c = cp = xstrdup(client); 239 s = sp = xstrdup(server); 240 241 for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; 242 (p = strsep(&sp, SEP)), i++) { 243 if (i < MAX_PROP) 244 sproposals[i] = p; 245 else 246 break; 247 } 248 nproposals = i; 249 250 for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; 251 (p = strsep(&cp, SEP)), i++) { 252 for (j = 0; j < nproposals; j++) { 253 if (strcmp(p, sproposals[j]) == 0) { 254 ret = xstrdup(p); 255 if (next != NULL) 256 *next = (cp == NULL) ? 257 strlen(c) : cp - c; 258 xfree(c); 259 xfree(s); 260 return ret; 261 } 262 } 263 } 264 if (next != NULL) 265 *next = strlen(c); 266 xfree(c); 267 xfree(s); 268 return NULL; 269 } 270