xref: /freebsd/crypto/openssh/match.c (revision 56ca39961bd1c9946a505c41c3fc634ef63fdd42)
1 /*
2  *
3  * match.c
4  *
5  * Author: Tatu Ylonen <ylo@cs.hut.fi>
6  *
7  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8  *                    All rights reserved
9  *
10  * Created: Thu Jun 22 01:17:50 1995 ylo
11  *
12  * Simple pattern matching, with '*' and '?' as wildcards.
13  *
14  */
15 
16 #include "includes.h"
17 RCSID("$Id: match.c,v 1.5 2000/03/23 22:15:33 markus Exp $");
18 
19 #include "ssh.h"
20 
21 /*
22  * Returns true if the given string matches the pattern (which may contain ?
23  * and * as wildcards), and zero if it does not match.
24  */
25 
26 int
27 match_pattern(const char *s, const char *pattern)
28 {
29 	for (;;) {
30 		/* If at end of pattern, accept if also at end of string. */
31 		if (!*pattern)
32 			return !*s;
33 
34 		if (*pattern == '*') {
35 			/* Skip the asterisk. */
36 			pattern++;
37 
38 			/* If at end of pattern, accept immediately. */
39 			if (!*pattern)
40 				return 1;
41 
42 			/* If next character in pattern is known, optimize. */
43 			if (*pattern != '?' && *pattern != '*') {
44 				/*
45 				 * Look instances of the next character in
46 				 * pattern, and try to match starting from
47 				 * those.
48 				 */
49 				for (; *s; s++)
50 					if (*s == *pattern &&
51 					    match_pattern(s + 1, pattern + 1))
52 						return 1;
53 				/* Failed. */
54 				return 0;
55 			}
56 			/*
57 			 * Move ahead one character at a time and try to
58 			 * match at each position.
59 			 */
60 			for (; *s; s++)
61 				if (match_pattern(s, pattern))
62 					return 1;
63 			/* Failed. */
64 			return 0;
65 		}
66 		/*
67 		 * There must be at least one more character in the string.
68 		 * If we are at the end, fail.
69 		 */
70 		if (!*s)
71 			return 0;
72 
73 		/* Check if the next character of the string is acceptable. */
74 		if (*pattern != '?' && *pattern != *s)
75 			return 0;
76 
77 		/* Move to the next character, both in string and in pattern. */
78 		s++;
79 		pattern++;
80 	}
81 	/* NOTREACHED */
82 }
83 
84 /*
85  * Tries to match the host name (which must be in all lowercase) against the
86  * comma-separated sequence of subpatterns (each possibly preceded by ! to
87  * indicate negation).  Returns true if there is a positive match; zero
88  * otherwise.
89  */
90 
91 int
92 match_hostname(const char *host, const char *pattern, unsigned int len)
93 {
94 	char sub[1024];
95 	int negated;
96 	int got_positive;
97 	unsigned int i, subi;
98 
99 	got_positive = 0;
100 	for (i = 0; i < len;) {
101 		/* Check if the subpattern is negated. */
102 		if (pattern[i] == '!') {
103 			negated = 1;
104 			i++;
105 		} else
106 			negated = 0;
107 
108 		/*
109 		 * Extract the subpattern up to a comma or end.  Convert the
110 		 * subpattern to lowercase.
111 		 */
112 		for (subi = 0;
113 		     i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
114 		     subi++, i++)
115 			sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
116 		/* If subpattern too long, return failure (no match). */
117 		if (subi >= sizeof(sub) - 1)
118 			return 0;
119 
120 		/* If the subpattern was terminated by a comma, skip the comma. */
121 		if (i < len && pattern[i] == ',')
122 			i++;
123 
124 		/* Null-terminate the subpattern. */
125 		sub[subi] = '\0';
126 
127 		/* Try to match the subpattern against the host name. */
128 		if (match_pattern(host, sub)) {
129 			if (negated)
130 				return 0;	/* Fail */
131 			else
132 				got_positive = 1;
133 		}
134 	}
135 
136 	/*
137 	 * Return success if got a positive match.  If there was a negative
138 	 * match, we have already returned zero and never get here.
139 	 */
140 	return got_positive;
141 }
142