Mercurial > illumos > illumos-gate
changeset 13839:2d7fbebd2923
1930 smb_match functions incorrectly handle multibyte characters
1931 smb_convert_wildcards incorrectly expands < at the end of the pattern
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
author | Gordon Ross <gwr@nexenta.com> |
---|---|
date | Thu, 27 Sep 2012 13:58:00 -0400 |
parents | eed531da8f67 |
children | 97fd5cdf328a |
files | usr/src/common/smbsrv/smb_match.c usr/src/common/smbsrv/smb_xdr.c usr/src/lib/smbsrv/libsmb/common/mapfile-vers usr/src/uts/common/fs/smbsrv/smb_delete.c usr/src/uts/common/fs/smbsrv/smb_kutil.c usr/src/uts/common/fs/smbsrv/smb_odir.c usr/src/uts/common/fs/smbsrv/smb_pathname.c usr/src/uts/common/smbsrv/string.h |
diffstat | 8 files changed, 240 insertions(+), 258 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/common/smbsrv/smb_match.c Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/common/smbsrv/smb_match.c Thu Sep 27 13:58:00 2012 -0400 @@ -18,8 +18,10 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #ifndef _KERNEL @@ -38,193 +40,225 @@ */ #define SMB_MATCH_DEPTH_MAX 32 -#define SMB_CRC_POLYNOMIAL 0xD8B5D8B5 +struct match_priv { + int depth; + boolean_t ci; +}; -static int smb_match_private(const char *, const char *, int *); -static int smb_match_ci_private(const char *, const char *, int *); +static int smb_match_private(const char *, const char *, struct match_priv *); + +static const char smb_wildcards[] = "*?<>\""; /* - * smb_match + * Return B_TRUE if pattern contains wildcards */ boolean_t -smb_match(char *patn, char *str) +smb_contains_wildcards(const char *pattern) { - int depth = 0; - return (smb_match_private(patn, str, &depth) == 1); + return (strpbrk(pattern, smb_wildcards) != NULL); } /* - * The '*' character matches multiple characters. - * The '?' character matches a single character. - * - * If the pattern has trailing '?'s then it matches the specified number - * of characters or less. For example, "x??" matches "xab", "xa" and "x", - * but not "xabc". - * - * Returns: - * 1 match - * 0 no-match - * -1 no-match, too many wildcards in pattern + * NT-compatible file name match function. [MS-FSA 3.1.4.4] + * Returns TRUE if there is a match. */ -static int -smb_match_private(const char *patn, const char *str, int *depth) +boolean_t +smb_match(const char *p, const char *s, boolean_t ci) { + struct match_priv priv; int rc; - for (;;) { - switch (*patn) { - case '\0': - return (*str == '\0'); - - case '?': - if (*str != 0) { - str++; - patn++; - continue; - } else { - return (0); - } - /*NOTREACHED*/ - - case '*': - patn += strspn(patn, "*"); - if (*patn == '\0') - return (1); - - if ((*depth)++ >= SMB_MATCH_DEPTH_MAX) - return (-1); + /* + * Optimize common patterns that match everything: + * ("*", "<\"*") That second one is the converted + * form of "*.*" after smb_convert_wildcards() does + * its work on it for an old LM client. Note that a + * plain "*.*" never gets this far. + */ + if (p[0] == '*' && p[1] == '\0') + return (B_TRUE); + if (p[0] == '<' && p[1] == '\"' && p[2] == '*' && p[3] == '\0') + return (B_TRUE); - while (*str) { - rc = smb_match_private(patn, str, depth); - if (rc != 0) - return (rc); - str++; - } - return (0); + /* + * Match string ".." as if "." This is Windows behavior + * (not mentioned in MS-FSA) that was determined using + * the Samba masktest program. + */ + if (s[0] == '.' && s[1] == '.' && s[2] == '\0') + s++; - default: - if (*str != *patn) - return (0); - str++; - patn++; - continue; - } + /* + * Optimize simple patterns (no wildcards) + */ + if (NULL == strpbrk(p, smb_wildcards)) { + if (ci) + rc = smb_strcasecmp(p, s, 0); + else + rc = strcmp(p, s); + return (rc == 0); } - /*NOTREACHED*/ -} -/* - * smb_match_ci - */ -boolean_t -smb_match_ci(char *patn, char *str) -{ - int depth = 0; - - return (smb_match_ci_private(patn, str, &depth) == 1); + /* + * Do real wildcard match. + */ + priv.depth = 0; + priv.ci = ci; + rc = smb_match_private(p, s, &priv); + return (rc == 1); } /* - * The '*' character matches multiple characters. - * The '?' character matches a single character. + * Internal file name match function. [MS-FSA 3.1.4.4] + * This does the full expression evaluation. * - * If the pattern has trailing '?'s then it matches the specified number - * of characters or less. For example, "x??" matches "xab", "xa" and "x", - * but not "xabc". + * '*' matches zero of more of any characters. + * '?' matches exactly one of any character. + * '<' matches any string up through the last dot or EOS. + * '>' matches any one char not a dot, dot at EOS, or EOS. + * '"' matches a dot, or EOS. * * Returns: - * 1 match - * 0 no-match - * -1 no-match, too many wildcards in pattern + * 1 match + * 0 no-match + * -1 no-match, error (illseq, too many wildcards in pattern, ...) + * + * Note that both the pattern and the string are in multi-byte form. + * + * The implementation of this is quite tricky. First note that it + * can call itself recursively, though it limits the recursion depth. + * Each switch case in the while loop can basically do one of three + * things: (a) return "Yes, match", (b) return "not a match", or + * continue processing the match pattern. The cases for wildcards + * that may match a variable number of characters ('*' and '<') do + * recursive calls, looking for a match of the remaining pattern, + * starting at the current and later positions in the string. */ static int -smb_match_ci_private(const char *patn, const char *str, int *depth) +smb_match_private(const char *pat, const char *str, struct match_priv *priv) { - const char *p; - smb_wchar_t wc1, wc2; - int nbytes1, nbytes2; + const char *limit; + char pc; /* current pattern char */ int rc; + smb_wchar_t wcpat, wcstr; /* current wchar in pat, str */ + int nbpat, nbstr; /* multi-byte length of it */ + + if (priv->depth >= SMB_MATCH_DEPTH_MAX) + return (-1); + + /* + * Advance over one multi-byte char, used in cases like + * '?' or '>' where "match one character" needs to be + * interpreted as "match one multi-byte sequence". + * + * This macro needs to consume the semicolon following + * each place it appears, so this is carefully written + * as an if/else with a missing semicolon at the end. + */ +#define ADVANCE(str) \ + if ((nbstr = smb_mbtowc(NULL, str, MTS_MB_CHAR_MAX)) < 1) \ + return (-1); \ + else \ + str += nbstr /* no ; */ /* - * "<" is a special pattern that matches only those names that do - * NOT have an extension. "." and ".." are ok. + * We move pat forward in each switch case so that the + * default case can move it by a whole multi-byte seq. */ - if (strcmp(patn, "<") == 0) { - if ((strcmp(str, ".") == 0) || (strcmp(str, "..") == 0)) - return (1); - if (strchr(str, '.') == 0) - return (1); - return (0); - } + while ((pc = *pat) != '\0') { + switch (pc) { - for (;;) { - switch (*patn) { - case '\0': - return (*str == '\0'); - - case '?': - if (*str != 0) { - str++; - patn++; + case '?': /* exactly one of any character */ + pat++; + if (*str != '\0') { + ADVANCE(str); continue; - } else { - p = patn; - p += strspn(p, "?"); - return ((*p == '\0') ? 1 : 0); } - /*NOTREACHED*/ - - case '*': - patn += strspn(patn, "*"); - if (*patn == '\0') - return (1); - - if ((*depth)++ >= SMB_MATCH_DEPTH_MAX) - return (-1); - - while (*str) { - rc = smb_match_ci_private(patn, str, depth); - if (rc != 0) - return (rc); - str++; - } + /* EOS: no-match */ return (0); - default: - nbytes1 = smb_mbtowc(&wc1, patn, MTS_MB_CHAR_MAX); - nbytes2 = smb_mbtowc(&wc2, str, MTS_MB_CHAR_MAX); - if ((nbytes1 == -1) || (nbytes2 == -1)) - return (-1); + case '*': /* zero or more of any characters */ + pat++; + /* Optimize '*' at end of pattern. */ + if (*pat == '\0') + return (1); /* match */ + while (*str != '\0') { + priv->depth++; + rc = smb_match_private(pat, str, priv); + priv->depth--; + if (rc != 0) + return (rc); /* match */ + ADVANCE(str); + } + continue; + + case '<': /* any string up through the last dot or EOS */ + pat++; + if ((limit = strrchr(str, '.')) != NULL) + limit++; + while (*str != '\0' && str != limit) { + priv->depth++; + rc = smb_match_private(pat, str, priv); + priv->depth--; + if (rc != 0) + return (rc); /* match */ + ADVANCE(str); + } + continue; - if (wc1 != wc2) { - wc1 = smb_tolower(wc1); - wc2 = smb_tolower(wc2); - if (wc1 != wc2) - return (0); + case '>': /* anything not a dot, dot at EOS, or EOS */ + pat++; + if (*str == '.') { + if (str[1] == '\0') { + /* dot at EOS */ + str++; /* ADVANCE over '.' */ + continue; + } + /* dot NOT at EOS: no-match */ + return (0); + } + if (*str != '\0') { + /* something not a dot */ + ADVANCE(str); + continue; + } + continue; + + case '\"': /* dot, or EOS */ + pat++; + if (*str == '.') { + str++; /* ADVANCE over '.' */ + continue; } + if (*str == '\0') { + continue; + } + /* something else: no-match */ + return (0); - patn += nbytes1; - str += nbytes2; - continue; + default: /* not a wildcard */ + nbpat = smb_mbtowc(&wcpat, pat, MTS_MB_CHAR_MAX); + nbstr = smb_mbtowc(&wcstr, str, MTS_MB_CHAR_MAX); + /* make sure we advance */ + if (nbpat < 1 || nbstr < 1) + return (-1); + if (wcpat == wcstr) { + pat += nbpat; + str += nbstr; + continue; + } + if (priv->ci) { + wcpat = smb_tolower(wcpat); + wcstr = smb_tolower(wcstr); + if (wcpat == wcstr) { + pat += nbpat; + str += nbstr; + continue; + } + } + return (0); /* no-match */ } } - /*NOTREACHED*/ + return (*str == '\0'); } - -uint32_t -smb_crc_gen(uint8_t *buf, size_t len) -{ - uint32_t crc = SMB_CRC_POLYNOMIAL; - uint8_t *p; - int i; - - for (p = buf, i = 0; i < len; ++i, ++p) { - crc = (crc ^ (uint32_t)*p) + (crc << 12); - - if (crc == 0 || crc == 0xFFFFFFFF) - crc = SMB_CRC_POLYNOMIAL; - } - - return (crc); -}
--- a/usr/src/common/smbsrv/smb_xdr.c Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/common/smbsrv/smb_xdr.c Thu Sep 27 13:58:00 2012 -0400 @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <sys/sunddi.h> @@ -734,3 +735,26 @@ return (TRUE); } + +/* + * The smbsrv ioctl callers include a CRC of the XDR encoded data, + * and kmod ioctl handler checks it. Both use this function. This + * is not really XDR related, but this is as good a place as any. + */ +#define SMB_CRC_POLYNOMIAL 0xD8B5D8B5 +uint32_t +smb_crc_gen(uint8_t *buf, size_t len) +{ + uint32_t crc = SMB_CRC_POLYNOMIAL; + uint8_t *p; + int i; + + for (p = buf, i = 0; i < len; ++i, ++p) { + crc = (crc ^ (uint32_t)*p) + (crc << 12); + + if (crc == 0 || crc == 0xFFFFFFFF) + crc = SMB_CRC_POLYNOMIAL; + } + + return (crc); +}
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers Thu Sep 27 13:58:00 2012 -0400 @@ -19,7 +19,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. +# Copyright 2012 Nexenta Systems, Inc. All rights reserved. # # @@ -269,7 +269,6 @@ smb_mac_init; smb_mac_sign; smb_match; - smb_match_ci; smb_match_netlogon_seqnum; smb_mbstos; smb_mbstowcs;
--- a/usr/src/uts/common/fs/smbsrv/smb_delete.c Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/uts/common/fs/smbsrv/smb_delete.c Thu Sep 27 13:58:00 2012 -0400 @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> @@ -553,7 +554,7 @@ /* fname component is, or resolves to, '.' (dot) */ if ((strcmp(pn->pn_fname, ".") == 0) || (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) && - (smb_match(pn->pn_fname, ".")))) { + (smb_match(pn->pn_fname, ".", B_FALSE)))) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (-1);
--- a/usr/src/uts/common/fs/smbsrv/smb_kutil.c Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/uts/common/fs/smbsrv/smb_kutil.c Thu Sep 27 13:58:00 2012 -0400 @@ -18,8 +18,10 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -98,116 +100,34 @@ } /* - * Return B_TRUE if pattern contains wildcards - */ -boolean_t -smb_contains_wildcards(const char *pattern) -{ - static const char *wildcards = "*?"; - - return (strpbrk(pattern, wildcards) != NULL); -} - -/* - * When converting wildcards a '.' in a name is treated as a base and - * extension separator even if the name is longer than 8.3. * - * The '*' character matches an entire part of the name. For example, - * "*.abc" matches any name with an extension of "abc". - * - * The '?' character matches a single character. - * If the base contains all ? (8 or more) then it is treated as *. - * If the extension contains all ? (3 or more) then it is treated as *. - * - * Clients convert ASCII wildcards to Unicode wildcards as follows: + * Convert old-style (DOS, LanMan) wildcard strings to NT style. + * This should ONLY happen to patterns that come from old clients, + * meaning dialect LANMAN2_1 etc. (dialect < NT_LM_0_12). * * ? is converted to > - * . is converted to " if it is followed by ? or * * * is converted to < if it is followed by . - * - * Note that clients convert "*." to '< and drop the '.' but "*.txt" - * is sent as "<.TXT", i.e. + * . is converted to " if it is followed by ? or * or end of pattern * - * dir *. -> dir < - * dir *.txt -> dir <.TXT - * - * Since " and < are illegal in Windows file names, we always convert - * these Unicode wildcards without checking the following character. + * Note: modifies pattern in place. */ void smb_convert_wildcards(char *pattern) { - static char *match_all[] = { - "*.", - "*.*" - }; - char *extension; char *p; - int len; - int i; - - /* - * Special case "<" for "dir *.", and fast-track for "*". - */ - if ((*pattern == '<') || (*pattern == '*')) { - if (*(pattern + 1) == '\0') { - *pattern = '*'; - return; - } - } - - for (p = pattern; *p != '\0'; ++p) { - switch (*p) { - case '<': - *p = '*'; - break; - case '>': - *p = '?'; - break; - case '\"': - *p = '.'; - break; - default: - break; - } - } - /* - * Replace "????????.ext" with "*.ext". - */ - p = pattern; - p += strspn(p, "?"); - if (*p == '.') { - *p = '\0'; - len = strlen(pattern); - *p = '.'; - if (len >= SMB_NAME83_BASELEN) { - *pattern = '*'; - (void) strlcpy(pattern + 1, p, MAXPATHLEN - 1); - } - } - - /* - * Replace "base.???" with 'base.*'. - */ - if ((extension = strrchr(pattern, '.')) != NULL) { - p = ++extension; - p += strspn(p, "?"); - if (*p == '\0') { - len = strlen(extension); - if (len >= SMB_NAME83_EXTLEN) { - *extension = '\0'; - (void) strlcat(pattern, "*", MAXPATHLEN); - } - } - } - - /* - * Replace anything that matches an entry in match_all with "*". - */ - for (i = 0; i < sizeof (match_all) / sizeof (match_all[0]); ++i) { - if (strcmp(pattern, match_all[i]) == 0) { - (void) strlcpy(pattern, "*", MAXPATHLEN); + for (p = pattern; *p != '\0'; p++) { + switch (*p) { + case '?': + *p = '>'; + break; + case '*': + if (p[1] == '.') + *p = '<'; + break; + case '.': + if (p[1] == '?' || p[1] == '*' || p[1] == '\0') + *p = '\"'; break; } }
--- a/usr/src/uts/common/fs/smbsrv/smb_odir.c Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/uts/common/fs/smbsrv/smb_odir.c Thu Sep 27 13:58:00 2012 -0400 @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -286,7 +287,8 @@ tree = sr->tid_tree; - smb_convert_wildcards(path); + if (sr->session->dialect < NT_LM_0_12) + smb_convert_wildcards(path); rc = smb_pathname_reduce(sr, sr->user_cr, path, tree->t_snode, tree->t_snode, &dnode, pattern); @@ -1278,22 +1280,23 @@ * - If shortnames are supported, generate the shortname from * odirent->od_name and check if it matches od->d_pattern. */ -boolean_t +static boolean_t smb_odir_match_name(smb_odir_t *od, smb_odirent_t *odirent) { char *name = odirent->od_name; char shortname[SMB_SHORTNAMELEN]; ino64_t ino = odirent->od_ino; + boolean_t ci = (od->d_flags & SMB_ODIR_FLAG_IGNORE_CASE) != 0; if (smb_is_reserved_dos_name(name)) return (B_FALSE); - if (smb_match_ci(od->d_pattern, name)) + if (smb_match(od->d_pattern, name, ci)) return (B_TRUE); if (od->d_flags & SMB_ODIR_FLAG_SHORTNAMES) { smb_mangle(name, ino, shortname, SMB_SHORTNAMELEN); - if (smb_match_ci(od->d_pattern, shortname)) + if (smb_match(od->d_pattern, shortname, ci)) return (B_TRUE); }
--- a/usr/src/uts/common/fs/smbsrv/smb_pathname.c Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/uts/common/fs/smbsrv/smb_pathname.c Thu Sep 27 13:58:00 2012 -0400 @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> @@ -732,8 +733,8 @@ return; } - /* perform unicode wildcard conversion */ - smb_convert_wildcards(pn->pn_path); + if (sr->session->dialect < NT_LM_0_12) + smb_convert_wildcards(pn->pn_path); /* treat '/' as '\\' */ (void) strsubst(pn->pn_path, '/', '\\');
--- a/usr/src/uts/common/smbsrv/string.h Wed Sep 26 17:40:45 2012 -0400 +++ b/usr/src/uts/common/smbsrv/string.h Thu Sep 27 13:58:00 2012 -0400 @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_STRING_H @@ -123,8 +124,7 @@ int smb_isstrlwr(const char *); int smb_strcasecmp(const char *, const char *, size_t); -boolean_t smb_match(char *, char *); -boolean_t smb_match_ci(char *, char *); +boolean_t smb_match(const char *, const char *, boolean_t); size_t smb_mbstowcs(smb_wchar_t *, const char *, size_t); size_t smb_wcstombs(char *, const smb_wchar_t *, size_t);