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