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 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 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 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