xref: /titanic_44/usr/src/common/smbsrv/smb_match.c (revision c13be35a2c14be5433f5d23a6c4f84e66439b7b6)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21*c13be35aSGordon Ross 
22da6c28aaSamw /*
23148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24*c13be35aSGordon Ross  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
25da6c28aaSamw  */
26da6c28aaSamw 
27da6c28aaSamw #ifndef _KERNEL
28da6c28aaSamw #include <stdlib.h>
29da6c28aaSamw #include <string.h>
30da6c28aaSamw #else
31da6c28aaSamw #include <sys/types.h>
32da6c28aaSamw #include <sys/sunddi.h>
33da6c28aaSamw #endif
34bbf6f00cSJordan Brown #include <smbsrv/string.h>
35bbf6f00cSJordan Brown #include <smbsrv/smb.h>
36da6c28aaSamw 
373a6c5f83SAlan Wright /*
383a6c5f83SAlan Wright  * Maximum recursion depth for the wildcard match functions.
393a6c5f83SAlan Wright  * These functions may recurse when processing a '*'.
403a6c5f83SAlan Wright  */
413a6c5f83SAlan Wright #define	SMB_MATCH_DEPTH_MAX	32
423a6c5f83SAlan Wright 
43*c13be35aSGordon Ross struct match_priv {
44*c13be35aSGordon Ross 	int depth;
45*c13be35aSGordon Ross 	boolean_t ci;
46*c13be35aSGordon Ross };
47da6c28aaSamw 
48*c13be35aSGordon Ross static int smb_match_private(const char *, const char *, struct match_priv *);
49*c13be35aSGordon Ross 
50*c13be35aSGordon Ross static const char smb_wildcards[] = "*?<>\"";
513a6c5f83SAlan Wright 
52da6c28aaSamw /*
53*c13be35aSGordon Ross  * Return B_TRUE if pattern contains wildcards
54da6c28aaSamw  */
55148c5f43SAlan Wright boolean_t
56*c13be35aSGordon Ross smb_contains_wildcards(const char *pattern)
57da6c28aaSamw {
583a6c5f83SAlan Wright 
59*c13be35aSGordon Ross 	return (strpbrk(pattern, smb_wildcards) != NULL);
603a6c5f83SAlan Wright }
613a6c5f83SAlan Wright 
623a6c5f83SAlan Wright /*
63*c13be35aSGordon Ross  * NT-compatible file name match function.  [MS-FSA 3.1.4.4]
64*c13be35aSGordon Ross  * Returns TRUE if there is a match.
65*c13be35aSGordon Ross  */
66*c13be35aSGordon Ross boolean_t
67*c13be35aSGordon Ross smb_match(const char *p, const char *s, boolean_t ci)
68*c13be35aSGordon Ross {
69*c13be35aSGordon Ross 	struct match_priv priv;
70*c13be35aSGordon Ross 	int rc;
71*c13be35aSGordon Ross 
72*c13be35aSGordon Ross 	/*
73*c13be35aSGordon Ross 	 * Optimize common patterns that match everything:
74*c13be35aSGordon Ross 	 * ("*", "<\"*")  That second one is the converted
75*c13be35aSGordon Ross 	 * form of "*.*" after smb_convert_wildcards() does
76*c13be35aSGordon Ross 	 * its work on it for an old LM client. Note that a
77*c13be35aSGordon Ross 	 * plain "*.*" never gets this far.
78*c13be35aSGordon Ross 	 */
79*c13be35aSGordon Ross 	if (p[0] == '*' && p[1] == '\0')
80*c13be35aSGordon Ross 		return (B_TRUE);
81*c13be35aSGordon Ross 	if (p[0] == '<' && p[1] == '\"' && p[2] == '*' && p[3] == '\0')
82*c13be35aSGordon Ross 		return (B_TRUE);
83*c13be35aSGordon Ross 
84*c13be35aSGordon Ross 	/*
85*c13be35aSGordon Ross 	 * Match string ".." as if "."  This is Windows behavior
86*c13be35aSGordon Ross 	 * (not mentioned in MS-FSA) that was determined using
87*c13be35aSGordon Ross 	 * the Samba masktest program.
88*c13be35aSGordon Ross 	 */
89*c13be35aSGordon Ross 	if (s[0] == '.' && s[1] == '.' && s[2] == '\0')
90*c13be35aSGordon Ross 		s++;
91*c13be35aSGordon Ross 
92*c13be35aSGordon Ross 	/*
93*c13be35aSGordon Ross 	 * Optimize simple patterns (no wildcards)
94*c13be35aSGordon Ross 	 */
95*c13be35aSGordon Ross 	if (NULL == strpbrk(p, smb_wildcards)) {
96*c13be35aSGordon Ross 		if (ci)
97*c13be35aSGordon Ross 			rc = smb_strcasecmp(p, s, 0);
98*c13be35aSGordon Ross 		else
99*c13be35aSGordon Ross 			rc = strcmp(p, s);
100*c13be35aSGordon Ross 		return (rc == 0);
101*c13be35aSGordon Ross 	}
102*c13be35aSGordon Ross 
103*c13be35aSGordon Ross 	/*
104*c13be35aSGordon Ross 	 * Do real wildcard match.
105*c13be35aSGordon Ross 	 */
106*c13be35aSGordon Ross 	priv.depth = 0;
107*c13be35aSGordon Ross 	priv.ci = ci;
108*c13be35aSGordon Ross 	rc = smb_match_private(p, s, &priv);
109*c13be35aSGordon Ross 	return (rc == 1);
110*c13be35aSGordon Ross }
111*c13be35aSGordon Ross 
112*c13be35aSGordon Ross /*
113*c13be35aSGordon Ross  * Internal file name match function.  [MS-FSA 3.1.4.4]
114*c13be35aSGordon Ross  * This does the full expression evaluation.
1153a6c5f83SAlan Wright  *
116*c13be35aSGordon Ross  * '*' matches zero of more of any characters.
117*c13be35aSGordon Ross  * '?' matches exactly one of any character.
118*c13be35aSGordon Ross  * '<' matches any string up through the last dot or EOS.
119*c13be35aSGordon Ross  * '>' matches any one char not a dot, dot at EOS, or EOS.
120*c13be35aSGordon Ross  * '"' matches a dot, or EOS.
1213a6c5f83SAlan Wright  *
1223a6c5f83SAlan Wright  * Returns:
1233a6c5f83SAlan Wright  *  1	match
1243a6c5f83SAlan Wright  *  0	no-match
125*c13be35aSGordon Ross  * -1	no-match, error (illseq, too many wildcards in pattern, ...)
126*c13be35aSGordon Ross  *
127*c13be35aSGordon Ross  * Note that both the pattern and the string are in multi-byte form.
128*c13be35aSGordon Ross  *
129*c13be35aSGordon Ross  * The implementation of this is quite tricky.  First note that it
130*c13be35aSGordon Ross  * can call itself recursively, though it limits the recursion depth.
131*c13be35aSGordon Ross  * Each switch case in the while loop can basically do one of three
132*c13be35aSGordon Ross  * things: (a) return "Yes, match", (b) return "not a match", or
133*c13be35aSGordon Ross  * continue processing the match pattern.  The cases for wildcards
134*c13be35aSGordon Ross  * that may match a variable number of characters ('*' and '<') do
135*c13be35aSGordon Ross  * recursive calls, looking for a match of the remaining pattern,
136*c13be35aSGordon Ross  * starting at the current and later positions in the string.
1373a6c5f83SAlan Wright  */
1383a6c5f83SAlan Wright static int
139*c13be35aSGordon Ross smb_match_private(const char *pat, const char *str, struct match_priv *priv)
1403a6c5f83SAlan Wright {
141*c13be35aSGordon Ross 	const char	*limit;
142*c13be35aSGordon Ross 	char		pc;		/* current pattern char */
1433a6c5f83SAlan Wright 	int		rc;
144*c13be35aSGordon Ross 	smb_wchar_t	wcpat, wcstr;	/* current wchar in pat, str */
145*c13be35aSGordon Ross 	int		nbpat, nbstr;	/* multi-byte length of it */
1463a6c5f83SAlan Wright 
147*c13be35aSGordon Ross 	if (priv->depth >= SMB_MATCH_DEPTH_MAX)
1483a6c5f83SAlan Wright 		return (-1);
149da6c28aaSamw 
1503a6c5f83SAlan Wright 	/*
151*c13be35aSGordon Ross 	 * Advance over one multi-byte char, used in cases like
152*c13be35aSGordon Ross 	 * '?' or '>' where "match one character" needs to be
153*c13be35aSGordon Ross 	 * interpreted as "match one multi-byte sequence".
1543a6c5f83SAlan Wright 	 *
155*c13be35aSGordon Ross 	 * This	macro needs to consume the semicolon following
156*c13be35aSGordon Ross 	 * each place it appears, so this is carefully written
157*c13be35aSGordon Ross 	 * as an if/else with a missing semicolon at the end.
1583a6c5f83SAlan Wright 	 */
159*c13be35aSGordon Ross #define	ADVANCE(str) \
160*c13be35aSGordon Ross 	if ((nbstr = smb_mbtowc(NULL, str, MTS_MB_CHAR_MAX)) < 1) \
161*c13be35aSGordon Ross 		return (-1); \
162*c13be35aSGordon Ross 	else \
163*c13be35aSGordon Ross 		str += nbstr	/* no ; */
1643a6c5f83SAlan Wright 
165da6c28aaSamw 	/*
166*c13be35aSGordon Ross 	 * We move pat forward in each switch case so that the
167*c13be35aSGordon Ross 	 * default case can move it by a whole multi-byte seq.
168da6c28aaSamw 	 */
169*c13be35aSGordon Ross 	while ((pc = *pat) != '\0') {
170*c13be35aSGordon Ross 		switch (pc) {
1713a6c5f83SAlan Wright 
172*c13be35aSGordon Ross 		case '?':	/* exactly one of any character */
173*c13be35aSGordon Ross 			pat++;
174*c13be35aSGordon Ross 			if (*str != '\0') {
175*c13be35aSGordon Ross 				ADVANCE(str);
176da6c28aaSamw 				continue;
177da6c28aaSamw 			}
178*c13be35aSGordon Ross 			/* EOS: no-match */
179*c13be35aSGordon Ross 			return (0);
180da6c28aaSamw 
181*c13be35aSGordon Ross 		case '*':	/* zero or more of any characters */
182*c13be35aSGordon Ross 			pat++;
183*c13be35aSGordon Ross 			/* Optimize '*' at end of pattern. */
184*c13be35aSGordon Ross 			if (*pat == '\0')
185*c13be35aSGordon Ross 				return (1); /* match */
186*c13be35aSGordon Ross 			while (*str != '\0') {
187*c13be35aSGordon Ross 				priv->depth++;
188*c13be35aSGordon Ross 				rc = smb_match_private(pat, str, priv);
189*c13be35aSGordon Ross 				priv->depth--;
1903a6c5f83SAlan Wright 				if (rc != 0)
191*c13be35aSGordon Ross 					return (rc); /* match */
192*c13be35aSGordon Ross 				ADVANCE(str);
193da6c28aaSamw 			}
194*c13be35aSGordon Ross 			continue;
195*c13be35aSGordon Ross 
196*c13be35aSGordon Ross 		case '<':	/* any string up through the last dot or EOS */
197*c13be35aSGordon Ross 			pat++;
198*c13be35aSGordon Ross 			if ((limit = strrchr(str, '.')) != NULL)
199*c13be35aSGordon Ross 				limit++;
200*c13be35aSGordon Ross 			while (*str != '\0' && str != limit) {
201*c13be35aSGordon Ross 				priv->depth++;
202*c13be35aSGordon Ross 				rc = smb_match_private(pat, str, priv);
203*c13be35aSGordon Ross 				priv->depth--;
204*c13be35aSGordon Ross 				if (rc != 0)
205*c13be35aSGordon Ross 					return (rc); /* match */
206*c13be35aSGordon Ross 				ADVANCE(str);
207*c13be35aSGordon Ross 			}
208*c13be35aSGordon Ross 			continue;
209*c13be35aSGordon Ross 
210*c13be35aSGordon Ross 		case '>':	/* anything not a dot, dot at EOS, or EOS */
211*c13be35aSGordon Ross 			pat++;
212*c13be35aSGordon Ross 			if (*str == '.') {
213*c13be35aSGordon Ross 				if (str[1] == '\0') {
214*c13be35aSGordon Ross 					/* dot at EOS */
215*c13be35aSGordon Ross 					str++;	/* ADVANCE over '.' */
216*c13be35aSGordon Ross 					continue;
217*c13be35aSGordon Ross 				}
218*c13be35aSGordon Ross 				/* dot NOT at EOS: no-match */
219*c13be35aSGordon Ross 				return (0);
220*c13be35aSGordon Ross 			}
221*c13be35aSGordon Ross 			if (*str != '\0') {
222*c13be35aSGordon Ross 				/* something not a dot */
223*c13be35aSGordon Ross 				ADVANCE(str);
224*c13be35aSGordon Ross 				continue;
225*c13be35aSGordon Ross 			}
226*c13be35aSGordon Ross 			continue;
227*c13be35aSGordon Ross 
228*c13be35aSGordon Ross 		case '\"':	/* dot, or EOS */
229*c13be35aSGordon Ross 			pat++;
230*c13be35aSGordon Ross 			if (*str == '.') {
231*c13be35aSGordon Ross 				str++;	/* ADVANCE over '.' */
232*c13be35aSGordon Ross 				continue;
233*c13be35aSGordon Ross 			}
234*c13be35aSGordon Ross 			if (*str == '\0') {
235*c13be35aSGordon Ross 				continue;
236*c13be35aSGordon Ross 			}
237*c13be35aSGordon Ross 			/* something else: no-match */
238da6c28aaSamw 			return (0);
239da6c28aaSamw 
240*c13be35aSGordon Ross 		default:	/* not a wildcard */
241*c13be35aSGordon Ross 			nbpat = smb_mbtowc(&wcpat, pat, MTS_MB_CHAR_MAX);
242*c13be35aSGordon Ross 			nbstr = smb_mbtowc(&wcstr, str, MTS_MB_CHAR_MAX);
243*c13be35aSGordon Ross 			/* make sure we advance */
244*c13be35aSGordon Ross 			if (nbpat < 1 || nbstr < 1)
245bbf6f00cSJordan Brown 				return (-1);
246*c13be35aSGordon Ross 			if (wcpat == wcstr) {
247*c13be35aSGordon Ross 				pat += nbpat;
248*c13be35aSGordon Ross 				str += nbstr;
249*c13be35aSGordon Ross 				continue;
250da6c28aaSamw 			}
251*c13be35aSGordon Ross 			if (priv->ci) {
252*c13be35aSGordon Ross 				wcpat = smb_tolower(wcpat);
253*c13be35aSGordon Ross 				wcstr = smb_tolower(wcstr);
254*c13be35aSGordon Ross 				if (wcpat == wcstr) {
255*c13be35aSGordon Ross 					pat += nbpat;
256*c13be35aSGordon Ross 					str += nbstr;
257da6c28aaSamw 					continue;
258da6c28aaSamw 				}
259da6c28aaSamw 			}
260*c13be35aSGordon Ross 			return (0); /* no-match */
261da6c28aaSamw 		}
26294fff790SAlan Wright 	}
263*c13be35aSGordon Ross 	return (*str == '\0');
26494fff790SAlan Wright }
265