xref: /illumos-gate/usr/src/common/smbsrv/smb_match.c (revision b819cea2)
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  */
21c13be35aSGordon Ross 
22da6c28aaSamw /*
23148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24*b819cea2SGordon Ross  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25da6c28aaSamw  */
26da6c28aaSamw 
27*b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
28da6c28aaSamw #include <stdlib.h>
29da6c28aaSamw #include <string.h>
30da6c28aaSamw #else
31da6c28aaSamw #include <sys/types.h>
32*b819cea2SGordon Ross #include <sys/systm.h>
33da6c28aaSamw #include <sys/sunddi.h>
34da6c28aaSamw #endif
35bbf6f00cSJordan Brown #include <smbsrv/string.h>
36bbf6f00cSJordan Brown #include <smbsrv/smb.h>
37da6c28aaSamw 
383a6c5f83SAlan Wright /*
393a6c5f83SAlan Wright  * Maximum recursion depth for the wildcard match functions.
403a6c5f83SAlan Wright  * These functions may recurse when processing a '*'.
413a6c5f83SAlan Wright  */
423a6c5f83SAlan Wright #define	SMB_MATCH_DEPTH_MAX	32
433a6c5f83SAlan Wright 
44c13be35aSGordon Ross struct match_priv {
45c13be35aSGordon Ross 	int depth;
46c13be35aSGordon Ross 	boolean_t ci;
47c13be35aSGordon Ross };
48c13be35aSGordon Ross 
49c13be35aSGordon Ross static int smb_match_private(const char *, const char *, struct match_priv *);
50da6c28aaSamw 
51c13be35aSGordon Ross static const char smb_wildcards[] = "*?<>\"";
523a6c5f83SAlan Wright 
53da6c28aaSamw /*
54c13be35aSGordon Ross  * Return B_TRUE if pattern contains wildcards
55da6c28aaSamw  */
56148c5f43SAlan Wright boolean_t
57c13be35aSGordon Ross smb_contains_wildcards(const char *pattern)
58da6c28aaSamw {
593a6c5f83SAlan Wright 
60c13be35aSGordon Ross 	return (strpbrk(pattern, smb_wildcards) != NULL);
613a6c5f83SAlan Wright }
623a6c5f83SAlan Wright 
633a6c5f83SAlan Wright /*
64c13be35aSGordon Ross  * NT-compatible file name match function.  [MS-FSA 3.1.4.4]
65c13be35aSGordon Ross  * Returns TRUE if there is a match.
663a6c5f83SAlan Wright  */
67c13be35aSGordon Ross boolean_t
68c13be35aSGordon Ross smb_match(const char *p, const char *s, boolean_t ci)
693a6c5f83SAlan Wright {
70c13be35aSGordon Ross 	struct match_priv priv;
713a6c5f83SAlan Wright 	int rc;
723a6c5f83SAlan Wright 
73c13be35aSGordon Ross 	/*
74c13be35aSGordon Ross 	 * Optimize common patterns that match everything:
75c13be35aSGordon Ross 	 * ("*", "<\"*")  That second one is the converted
76c13be35aSGordon Ross 	 * form of "*.*" after smb_convert_wildcards() does
77c13be35aSGordon Ross 	 * its work on it for an old LM client. Note that a
78c13be35aSGordon Ross 	 * plain "*.*" never gets this far.
79c13be35aSGordon Ross 	 */
80c13be35aSGordon Ross 	if (p[0] == '*' && p[1] == '\0')
81c13be35aSGordon Ross 		return (B_TRUE);
82c13be35aSGordon Ross 	if (p[0] == '<' && p[1] == '\"' && p[2] == '*' && p[3] == '\0')
83c13be35aSGordon Ross 		return (B_TRUE);
84da6c28aaSamw 
85c13be35aSGordon Ross 	/*
86c13be35aSGordon Ross 	 * Match string ".." as if "."  This is Windows behavior
87c13be35aSGordon Ross 	 * (not mentioned in MS-FSA) that was determined using
88c13be35aSGordon Ross 	 * the Samba masktest program.
89c13be35aSGordon Ross 	 */
90c13be35aSGordon Ross 	if (s[0] == '.' && s[1] == '.' && s[2] == '\0')
91c13be35aSGordon Ross 		s++;
92da6c28aaSamw 
93c13be35aSGordon Ross 	/*
94c13be35aSGordon Ross 	 * Optimize simple patterns (no wildcards)
95c13be35aSGordon Ross 	 */
96c13be35aSGordon Ross 	if (NULL == strpbrk(p, smb_wildcards)) {
97c13be35aSGordon Ross 		if (ci)
98c13be35aSGordon Ross 			rc = smb_strcasecmp(p, s, 0);
99c13be35aSGordon Ross 		else
100c13be35aSGordon Ross 			rc = strcmp(p, s);
101c13be35aSGordon Ross 		return (rc == 0);
102da6c28aaSamw 	}
103da6c28aaSamw 
104c13be35aSGordon Ross 	/*
105c13be35aSGordon Ross 	 * Do real wildcard match.
106c13be35aSGordon Ross 	 */
107c13be35aSGordon Ross 	priv.depth = 0;
108c13be35aSGordon Ross 	priv.ci = ci;
109c13be35aSGordon Ross 	rc = smb_match_private(p, s, &priv);
110c13be35aSGordon Ross 	return (rc == 1);
1113a6c5f83SAlan Wright }
1123a6c5f83SAlan Wright 
1133a6c5f83SAlan Wright /*
114c13be35aSGordon Ross  * Internal file name match function.  [MS-FSA 3.1.4.4]
115c13be35aSGordon Ross  * This does the full expression evaluation.
1163a6c5f83SAlan Wright  *
117c13be35aSGordon Ross  * '*' matches zero of more of any characters.
118c13be35aSGordon Ross  * '?' matches exactly one of any character.
119c13be35aSGordon Ross  * '<' matches any string up through the last dot or EOS.
120c13be35aSGordon Ross  * '>' matches any one char not a dot, dot at EOS, or EOS.
121c13be35aSGordon Ross  * '"' matches a dot, or EOS.
1223a6c5f83SAlan Wright  *
1233a6c5f83SAlan Wright  * Returns:
124c13be35aSGordon Ross  *  1	match
125c13be35aSGordon Ross  *  0	no-match
126c13be35aSGordon Ross  * -1	no-match, error (illseq, too many wildcards in pattern, ...)
127c13be35aSGordon Ross  *
128c13be35aSGordon Ross  * Note that both the pattern and the string are in multi-byte form.
129c13be35aSGordon Ross  *
130c13be35aSGordon Ross  * The implementation of this is quite tricky.  First note that it
131c13be35aSGordon Ross  * can call itself recursively, though it limits the recursion depth.
132c13be35aSGordon Ross  * Each switch case in the while loop can basically do one of three
133c13be35aSGordon Ross  * things: (a) return "Yes, match", (b) return "not a match", or
134c13be35aSGordon Ross  * continue processing the match pattern.  The cases for wildcards
135c13be35aSGordon Ross  * that may match a variable number of characters ('*' and '<') do
136c13be35aSGordon Ross  * recursive calls, looking for a match of the remaining pattern,
137c13be35aSGordon Ross  * starting at the current and later positions in the string.
1383a6c5f83SAlan Wright  */
1393a6c5f83SAlan Wright static int
140c13be35aSGordon Ross smb_match_private(const char *pat, const char *str, struct match_priv *priv)
1413a6c5f83SAlan Wright {
142c13be35aSGordon Ross 	const char	*limit;
143c13be35aSGordon Ross 	char		pc;		/* current pattern char */
144bbf6f00cSJordan Brown 	int		rc;
145c13be35aSGordon Ross 	smb_wchar_t	wcpat, wcstr;	/* current wchar in pat, str */
146c13be35aSGordon Ross 	int		nbpat, nbstr;	/* multi-byte length of it */
147c13be35aSGordon Ross 
148c13be35aSGordon Ross 	if (priv->depth >= SMB_MATCH_DEPTH_MAX)
149c13be35aSGordon Ross 		return (-1);
1503a6c5f83SAlan Wright 
151da6c28aaSamw 	/*
152c13be35aSGordon Ross 	 * Advance over one multi-byte char, used in cases like
153c13be35aSGordon Ross 	 * '?' or '>' where "match one character" needs to be
154c13be35aSGordon Ross 	 * interpreted as "match one multi-byte sequence".
155c13be35aSGordon Ross 	 *
156c13be35aSGordon Ross 	 * This	macro needs to consume the semicolon following
157c13be35aSGordon Ross 	 * each place it appears, so this is carefully written
158c13be35aSGordon Ross 	 * as an if/else with a missing semicolon at the end.
159da6c28aaSamw 	 */
160c13be35aSGordon Ross #define	ADVANCE(str) \
161c13be35aSGordon Ross 	if ((nbstr = smb_mbtowc(NULL, str, MTS_MB_CHAR_MAX)) < 1) \
162c13be35aSGordon Ross 		return (-1); \
163c13be35aSGordon Ross 	else \
164c13be35aSGordon Ross 		str += nbstr	/* no ; */
1653a6c5f83SAlan Wright 
166c13be35aSGordon Ross 	/*
167c13be35aSGordon Ross 	 * We move pat forward in each switch case so that the
168c13be35aSGordon Ross 	 * default case can move it by a whole multi-byte seq.
169c13be35aSGordon Ross 	 */
170c13be35aSGordon Ross 	while ((pc = *pat) != '\0') {
171c13be35aSGordon Ross 		switch (pc) {
172da6c28aaSamw 
173c13be35aSGordon Ross 		case '?':	/* exactly one of any character */
174c13be35aSGordon Ross 			pat++;
175c13be35aSGordon Ross 			if (*str != '\0') {
176c13be35aSGordon Ross 				ADVANCE(str);
177da6c28aaSamw 				continue;
178da6c28aaSamw 			}
179c13be35aSGordon Ross 			/* EOS: no-match */
180c13be35aSGordon Ross 			return (0);
1813a6c5f83SAlan Wright 
182c13be35aSGordon Ross 		case '*':	/* zero or more of any characters */
183c13be35aSGordon Ross 			pat++;
184c13be35aSGordon Ross 			/* Optimize '*' at end of pattern. */
185c13be35aSGordon Ross 			if (*pat == '\0')
186c13be35aSGordon Ross 				return (1); /* match */
187c13be35aSGordon Ross 			while (*str != '\0') {
188c13be35aSGordon Ross 				priv->depth++;
189c13be35aSGordon Ross 				rc = smb_match_private(pat, str, priv);
190c13be35aSGordon Ross 				priv->depth--;
1913a6c5f83SAlan Wright 				if (rc != 0)
192c13be35aSGordon Ross 					return (rc); /* match */
193c13be35aSGordon Ross 				ADVANCE(str);
194da6c28aaSamw 			}
195c13be35aSGordon Ross 			continue;
196da6c28aaSamw 
197c13be35aSGordon Ross 		case '<':	/* any string up through the last dot or EOS */
198c13be35aSGordon Ross 			pat++;
199c13be35aSGordon Ross 			if ((limit = strrchr(str, '.')) != NULL)
200c13be35aSGordon Ross 				limit++;
201c13be35aSGordon Ross 			while (*str != '\0' && str != limit) {
202c13be35aSGordon Ross 				priv->depth++;
203c13be35aSGordon Ross 				rc = smb_match_private(pat, str, priv);
204c13be35aSGordon Ross 				priv->depth--;
205c13be35aSGordon Ross 				if (rc != 0)
206c13be35aSGordon Ross 					return (rc); /* match */
207c13be35aSGordon Ross 				ADVANCE(str);
208da6c28aaSamw 			}
209da6c28aaSamw 			continue;
21094fff790SAlan Wright 
211c13be35aSGordon Ross 		case '>':	/* anything not a dot, dot at EOS, or EOS */
212c13be35aSGordon Ross 			pat++;
213c13be35aSGordon Ross 			if (*str == '.') {
214c13be35aSGordon Ross 				if (str[1] == '\0') {
215c13be35aSGordon Ross 					/* dot at EOS */
216c13be35aSGordon Ross 					str++;	/* ADVANCE over '.' */
217c13be35aSGordon Ross 					continue;
218c13be35aSGordon Ross 				}
219c13be35aSGordon Ross 				/* dot NOT at EOS: no-match */
220c13be35aSGordon Ross 				return (0);
221c13be35aSGordon Ross 			}
222c13be35aSGordon Ross 			if (*str != '\0') {
223c13be35aSGordon Ross 				/* something not a dot */
224c13be35aSGordon Ross 				ADVANCE(str);
225c13be35aSGordon Ross 				continue;
226c13be35aSGordon Ross 			}
227c13be35aSGordon Ross 			continue;
22894fff790SAlan Wright 
229c13be35aSGordon Ross 		case '\"':	/* dot, or EOS */
230c13be35aSGordon Ross 			pat++;
231c13be35aSGordon Ross 			if (*str == '.') {
232c13be35aSGordon Ross 				str++;	/* ADVANCE over '.' */
233c13be35aSGordon Ross 				continue;
234c13be35aSGordon Ross 			}
235c13be35aSGordon Ross 			if (*str == '\0') {
236c13be35aSGordon Ross 				continue;
237c13be35aSGordon Ross 			}
238c13be35aSGordon Ross 			/* something else: no-match */
239c13be35aSGordon Ross 			return (0);
24094fff790SAlan Wright 
241c13be35aSGordon Ross 		default:	/* not a wildcard */
242c13be35aSGordon Ross 			nbpat = smb_mbtowc(&wcpat, pat, MTS_MB_CHAR_MAX);
243c13be35aSGordon Ross 			nbstr = smb_mbtowc(&wcstr, str, MTS_MB_CHAR_MAX);
244c13be35aSGordon Ross 			/* make sure we advance */
245c13be35aSGordon Ross 			if (nbpat < 1 || nbstr < 1)
246c13be35aSGordon Ross 				return (-1);
247c13be35aSGordon Ross 			if (wcpat == wcstr) {
248c13be35aSGordon Ross 				pat += nbpat;
249c13be35aSGordon Ross 				str += nbstr;
250c13be35aSGordon Ross 				continue;
251c13be35aSGordon Ross 			}
252c13be35aSGordon Ross 			if (priv->ci) {
253c13be35aSGordon Ross 				wcpat = smb_tolower(wcpat);
254c13be35aSGordon Ross 				wcstr = smb_tolower(wcstr);
255c13be35aSGordon Ross 				if (wcpat == wcstr) {
256c13be35aSGordon Ross 					pat += nbpat;
257c13be35aSGordon Ross 					str += nbstr;
258c13be35aSGordon Ross 					continue;
259c13be35aSGordon Ross 				}
260c13be35aSGordon Ross 			}
261c13be35aSGordon Ross 			return (0); /* no-match */
262c13be35aSGordon Ross 		}
26394fff790SAlan Wright 	}
264c13be35aSGordon Ross 	return (*str == '\0');
26594fff790SAlan Wright }
266