match.c revision 1.32
1/* $OpenBSD: match.c,v 1.32 2016/09/21 16:55:42 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
44#include "xmalloc.h"
45#include "match.h"
46
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
52int
53match_pattern(const char *s, const char *pattern)
54{
55	for (;;) {
56		/* If at end of pattern, accept if also at end of string. */
57		if (!*pattern)
58			return !*s;
59
60		if (*pattern == '*') {
61			/* Skip the asterisk. */
62			pattern++;
63
64			/* If at end of pattern, accept immediately. */
65			if (!*pattern)
66				return 1;
67
68			/* If next character in pattern is known, optimize. */
69			if (*pattern != '?' && *pattern != '*') {
70				/*
71				 * Look instances of the next character in
72				 * pattern, and try to match starting from
73				 * those.
74				 */
75				for (; *s; s++)
76					if (*s == *pattern &&
77					    match_pattern(s + 1, pattern + 1))
78						return 1;
79				/* Failed. */
80				return 0;
81			}
82			/*
83			 * Move ahead one character at a time and try to
84			 * match at each position.
85			 */
86			for (; *s; s++)
87				if (match_pattern(s, pattern))
88					return 1;
89			/* Failed. */
90			return 0;
91		}
92		/*
93		 * There must be at least one more character in the string.
94		 * If we are at the end, fail.
95		 */
96		if (!*s)
97			return 0;
98
99		/* Check if the next character of the string is acceptable. */
100		if (*pattern != '?' && *pattern != *s)
101			return 0;
102
103		/* Move to the next character, both in string and in pattern. */
104		s++;
105		pattern++;
106	}
107	/* NOTREACHED */
108}
109
110/*
111 * Tries to match the string against the
112 * comma-separated sequence of subpatterns (each possibly preceded by ! to
113 * indicate negation).  Returns -1 if negation matches, 1 if there is
114 * a positive match, 0 if there is no match at all.
115 */
116int
117match_pattern_list(const char *string, const char *pattern, int dolower)
118{
119	char sub[1024];
120	int negated;
121	int got_positive;
122	u_int i, subi, len = strlen(pattern);
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((u_char)pattern[i]) ?
141			    tolower((u_char)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 */
175int
176match_hostname(const char *host, const char *pattern)
177{
178	return match_pattern_list(host, pattern, 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 on error, or 1 on
184 * successful match.
185 */
186int
187match_host_and_ip(const char *host, const char *ipaddr,
188    const char *patterns)
189{
190	int mhost, mip;
191
192	/* error in ipaddr match */
193	if ((mip = addr_match_list(ipaddr, patterns)) == -2)
194		return -1;
195	else if (mip == -1) /* negative ip address match */
196		return 0;
197
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/*
208 * match user, user@host_or_ip, user@host_or_ip_list against pattern
209 */
210int
211match_user(const char *user, const char *host, const char *ipaddr,
212    const char *pattern)
213{
214	char *p, *pat;
215	int ret;
216
217	if ((p = strchr(pattern,'@')) == NULL)
218		return match_pattern(user, pattern);
219
220	pat = xstrdup(pattern);
221	p = strchr(pat, '@');
222	*p++ = '\0';
223
224	if ((ret = match_pattern(user, pat)) == 1)
225		ret = match_host_and_ip(host, ipaddr, p);
226	free(pat);
227
228	return ret;
229}
230
231/*
232 * Returns first item from client-list that is also supported by server-list,
233 * caller must free the returned string.
234 */
235#define	MAX_PROP	40
236#define	SEP	","
237char *
238match_list(const char *client, const char *server, u_int *next)
239{
240	char *sproposals[MAX_PROP];
241	char *c, *s, *p, *ret, *cp, *sp;
242	int i, j, nproposals;
243
244	c = cp = xstrdup(client);
245	s = sp = xstrdup(server);
246
247	for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0';
248	    (p = strsep(&sp, SEP)), i++) {
249		if (i < MAX_PROP)
250			sproposals[i] = p;
251		else
252			break;
253	}
254	nproposals = i;
255
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