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 };
48da6c28aaSamw
49c13be35aSGordon Ross static int smb_match_private(const char *, const char *, struct match_priv *);
50c13be35aSGordon Ross
51c13be35aSGordon Ross static const char smb_wildcards[] = "*?<>\"";
523a6c5f83SAlan Wright
53da6c28aaSamw /*
54c13be35aSGordon Ross * Return B_TRUE if pattern contains wildcards
55da6c28aaSamw */
56148c5f43SAlan Wright boolean_t
smb_contains_wildcards(const char * pattern)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.
66c13be35aSGordon Ross */
67c13be35aSGordon Ross boolean_t
smb_match(const char * p,const char * s,boolean_t ci)68c13be35aSGordon Ross smb_match(const char *p, const char *s, boolean_t ci)
69c13be35aSGordon Ross {
70c13be35aSGordon Ross struct match_priv priv;
71c13be35aSGordon Ross int rc;
72c13be35aSGordon Ross
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);
84c13be35aSGordon Ross
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++;
92c13be35aSGordon Ross
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);
102c13be35aSGordon Ross }
103c13be35aSGordon Ross
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);
111c13be35aSGordon Ross }
112c13be35aSGordon Ross
113c13be35aSGordon Ross /*
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:
1243a6c5f83SAlan Wright * 1 match
1253a6c5f83SAlan Wright * 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
smb_match_private(const char * pat,const char * str,struct match_priv * priv)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 */
1443a6c5f83SAlan Wright int rc;
145c13be35aSGordon Ross smb_wchar_t wcpat, wcstr; /* current wchar in pat, str */
146c13be35aSGordon Ross int nbpat, nbstr; /* multi-byte length of it */
1473a6c5f83SAlan Wright
148c13be35aSGordon Ross if (priv->depth >= SMB_MATCH_DEPTH_MAX)
1493a6c5f83SAlan Wright return (-1);
150da6c28aaSamw
1513a6c5f83SAlan Wright /*
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".
1553a6c5f83SAlan Wright *
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.
1593a6c5f83SAlan Wright */
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
166da6c28aaSamw /*
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.
169da6c28aaSamw */
170c13be35aSGordon Ross while ((pc = *pat) != '\0') {
171c13be35aSGordon Ross switch (pc) {
1723a6c5f83SAlan Wright
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);
181da6c28aaSamw
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;
196c13be35aSGordon Ross
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);
208c13be35aSGordon Ross }
209c13be35aSGordon Ross continue;
210c13be35aSGordon Ross
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;
228c13be35aSGordon Ross
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 */
239da6c28aaSamw return (0);
240da6c28aaSamw
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)
246bbf6f00cSJordan Brown return (-1);
247c13be35aSGordon Ross if (wcpat == wcstr) {
248c13be35aSGordon Ross pat += nbpat;
249c13be35aSGordon Ross str += nbstr;
250c13be35aSGordon Ross continue;
251da6c28aaSamw }
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;
258da6c28aaSamw continue;
259da6c28aaSamw }
260da6c28aaSamw }
261c13be35aSGordon Ross return (0); /* no-match */
262da6c28aaSamw }
26394fff790SAlan Wright }
264c13be35aSGordon Ross return (*str == '\0');
26594fff790SAlan Wright }
266