Index: source/smbd/mangle_common.c =================================================================== --- source/smbd/mangle_common.c (revision 0) +++ source/smbd/mangle_common.c (revision 0) @@ -0,0 +1,260 @@ +/* + Unix SMB/CIFS implementation. + Name mangling + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Simo Sorce 2001 + Copyright (C) Andrew Bartlett 2002 + Copyright (C) SATOH Fumiyasu 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* these tables are used to provide fast tests for characters */ +static unsigned char char_flags[256]; + +/* the list of reserved dos names - all of these are illegal */ +static const char *reserved_names[] = { + "AUX", "LOCK$", "CON", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + "NUL", "PRN", NULL +}; + +/* these flags are used to mark characters in as having particular + properties */ +#define FLAG_BASECHAR 1 +#define FLAG_ASCII 2 +#define FLAG_ILLEGAL 4 +#define FLAG_WILDCARD 8 + +/* the "possible" flags are used as a fast way to find possible DOS + reserved filenames */ +#define FLAG_POSSIBLE1 16 +#define FLAG_POSSIBLE2 32 +#define FLAG_POSSIBLE3 64 +#define FLAG_POSSIBLE4 128 + +#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag)) + +/* initialise the flags table + + we allow only a very restricted set of characters as 'ascii' in this + mangling backend. This isn't a significant problem as modern clients + use the 'long' filenames anyway, and those don't have these + restrictions. +*/ +void mangle_common_init(void) +{ + int i; + + memset(char_flags, 0, sizeof(char_flags)); + + for (i=1; i<128; i++) { + if ((i >= '0' && i <= '9') || + (i >= 'a' && i <= 'z') || + (i >= 'A' && i <= 'Z')) { + char_flags[i] |= FLAG_ASCII | FLAG_BASECHAR; + } + if (strchr("_-$~", i)) { + char_flags[i] |= FLAG_ASCII; + } + + if (strchr("*\\/?<>|\":", i)) { + char_flags[i] |= FLAG_ILLEGAL; + } + + if (strchr("*?\"<>", i)) { + char_flags[i] |= FLAG_WILDCARD; + } + } + + /* fill in the reserved names flags. These are used as a very + fast filter for finding possible DOS reserved filenames */ + for (i=0; reserved_names[i]; i++) { + unsigned char c1, c2, c3, c4; + + c1 = (unsigned char)reserved_names[i][0]; + c2 = (unsigned char)reserved_names[i][1]; + c3 = (unsigned char)reserved_names[i][2]; + c4 = (unsigned char)reserved_names[i][3]; + + char_flags[c1] |= FLAG_POSSIBLE1; + char_flags[c2] |= FLAG_POSSIBLE2; + char_flags[c3] |= FLAG_POSSIBLE3; + char_flags[c4] |= FLAG_POSSIBLE4; + char_flags[tolower(c1)] |= FLAG_POSSIBLE1; + char_flags[tolower(c2)] |= FLAG_POSSIBLE2; + char_flags[tolower(c3)] |= FLAG_POSSIBLE3; + char_flags[tolower(c4)] |= FLAG_POSSIBLE4; + + char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4; + } +} + +/* + look for a DOS reserved name +*/ +BOOL mangle_common_is_reserved_name(const char *name) +{ + if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) && + FLAG_CHECK(name[1], FLAG_POSSIBLE2) && + FLAG_CHECK(name[2], FLAG_POSSIBLE3) && + /* FIXME: false-negative if (name[3] == NULL) -- fumiya */ + FLAG_CHECK(name[3], FLAG_POSSIBLE4)) { + /* a likely match, scan the lot */ + int i; + for (i=0; reserved_names[i]; i++) { + int len = strlen(reserved_names[i]); + /* note that we match on COM1 as well as COM1.foo */ + if (strnequal(name, reserved_names[i], len) && + (name[len] == '.' || name[len] == 0)) { + return True; + } + } + } + + return False; +} + +/* + See if a filename is a legal long filename. + A filename ending in a '.' is not legal unless it's "." or "..". JRA. +*/ +BOOL mangle_common_is_legal_name_dos(const char *name_dos) +{ + const char *dot_pos = NULL; + BOOL alldots = True; + size_t numdots = 0; + + while (*name_dos) { + if (((unsigned int)name_dos[0]) >= 128 && name_dos[1] != '\0') { + /* mb character */ + name_dos += 2; + continue; + } + + if (FLAG_CHECK(name_dos[0], FLAG_ILLEGAL)) { + return False; + } + if (name_dos[0] == '.') { + dot_pos = name_dos; + numdots++; + } else { + alldots = False; + } + name_dos++; + } + + if (dot_pos) { + if (alldots && (numdots == 1 || numdots == 2)) + return True; /* . or .. is a valid name */ + + /* A valid long name cannot end in '.' */ + if (dot_pos[1] == '\0') + return False; + } + + return True; +} + +/* + see if a filename is an allowable 8.3 name. + + we are only going to allow ascii characters in 8.3 names, as this + simplifies things greatly (it means that we know the string won't + get larger when converted from UNIX to DOS formats) +*/ +BOOL mangle_common_is_8_3_dos(const char *name_dos, BOOL check_case, BOOL allow_wildcards) +{ + int len; + char *dot_p; + + /* as a special case, the names '.' and '..' are allowable 8.3 names */ + if (name_dos[0] == '.') { + if (!name_dos[1] || (name_dos[1] == '.' && !name_dos[2])) { + return True; + } + } + + /* the simplest test is on the overall length of the + filename. Note that we deliberately use the ascii string + length (not the multi-byte one) as it is faster, and gives us + the result we need in this case. Using strlen_m would not + only be slower, it would be incorrect */ + len = strlen(name_dos); + if (len > 12) + return False; + + /* find the '.'. Note that once again we use the non-multibyte + function */ + dot_p = strchr(name_dos, '.'); + + if (!dot_p) { + /* if the name doesn't contain a '.' then its length + must be less than 8 */ + if (len > 8) { + return False; + } + } else { + int prefix_len, suffix_len; + + /* if it does contain a dot then the prefix must be <= + 8 and the suffix <= 3 in length */ + prefix_len = PTR_DIFF(dot_p, name_dos); + suffix_len = len - (prefix_len+1); + + if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) { + return False; + } + + /* a 8.3 name cannot contain more than 1 '.' */ + if (strchr(dot_p+1, '.')) { + return False; + } + } + + /* the length are all OK. Now check to see if the characters themselves are OK */ + while (*name_dos) { + if (((unsigned int)name_dos[0]) >= 128 && name_dos[1] != '\0') { + /* mb character */ + name_dos += 2; + continue; + } + + /* note that we may allow wildcard petterns! */ + if (!FLAG_CHECK(name_dos[0], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) && name_dos[0] != '.') { + return False; + } + name_dos++; + } + + /* it is a good 8.3 name */ + return True; +} + +BOOL mangle_common_is_8_3(const char *name, BOOL check_case, BOOL allow_wildcards) +{ + fstring name_dos; + BOOL ret; + + push_ascii_fstring(name_dos, name); + + ret = mangle_common_is_8_3_dos(name_dos, check_case, allow_wildcards); + + return ret; +} + Index: source/smbd/mangle_hash.c =================================================================== --- source/smbd/mangle_hash.c (revision 2179) +++ source/smbd/mangle_hash.c (working copy) @@ -103,216 +103,6 @@ static BOOL ct_initialized = Fa static TDB_CONTEXT *tdb_mangled_cache; -/* -------------------------------------------------------------------- */ - -static NTSTATUS has_valid_83_chars(const smb_ucs2_t *s, BOOL allow_wildcards) -{ - if (!s || !*s) - return NT_STATUS_INVALID_PARAMETER; - - /* CHECK: this should not be necessary if the ms wild chars - are not valid in valid.dat --- simo */ - if (!allow_wildcards && ms_has_wild_w(s)) - return NT_STATUS_UNSUCCESSFUL; - - while (*s) { - if(!isvalid83_w(*s)) - return NT_STATUS_UNSUCCESSFUL; - s++; - } - - return NT_STATUS_OK; -} - -/* return False if something fail and - * return 2 alloced unicode strings that contain prefix and extension - */ - -static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix, - smb_ucs2_t **extension, BOOL allow_wildcards) -{ - size_t ext_len; - smb_ucs2_t *p; - - *extension = 0; - *prefix = strdup_w(ucs2_string); - if (!*prefix) { - return NT_STATUS_NO_MEMORY; - } - if ((p = strrchr_w(*prefix, UCS2_CHAR('.')))) { - ext_len = strlen_w(p+1); - if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) && - (NT_STATUS_IS_OK(has_valid_83_chars(p+1,allow_wildcards)))) /* check extension */ { - *p = 0; - *extension = strdup_w(p+1); - if (!*extension) { - SAFE_FREE(*prefix); - return NT_STATUS_NO_MEMORY; - } - } - } - return NT_STATUS_OK; -} - -/* ************************************************************************** ** - * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name. - * - * Input: fname - String containing the name to be tested. - * - * Output: NT_STATUS_UNSUCCESSFUL, if the name matches one of the list of reserved names. - * - * Notes: This is a static function called by is_8_3(), below. - * - * ************************************************************************** ** - */ - -static NTSTATUS is_valid_name(const smb_ucs2_t *fname, BOOL allow_wildcards, BOOL only_8_3) -{ - smb_ucs2_t *str, *p; - NTSTATUS ret = NT_STATUS_OK; - - if (!fname || !*fname) - return NT_STATUS_INVALID_PARAMETER; - - /* . and .. are valid names. */ - if (strcmp_wa(fname, ".")==0 || strcmp_wa(fname, "..")==0) - return NT_STATUS_OK; - - /* Name cannot start with '.' */ - if (*fname == UCS2_CHAR('.')) - return NT_STATUS_UNSUCCESSFUL; - - if (only_8_3) { - ret = has_valid_83_chars(fname, allow_wildcards); - if (!NT_STATUS_IS_OK(ret)) - return ret; - } - - str = strdup_w(fname); - p = strchr_w(str, UCS2_CHAR('.')); - if (p && p[1] == UCS2_CHAR(0)) { - /* Name cannot end in '.' */ - SAFE_FREE(str); - return NT_STATUS_UNSUCCESSFUL; - } - if (p) - *p = 0; - strupper_w(str); - p = &(str[1]); - - switch(str[0]) - { - case UCS2_CHAR('A'): - if(strcmp_wa(p, "UX") == 0) - ret = NT_STATUS_UNSUCCESSFUL; - break; - case UCS2_CHAR('C'): - if((strcmp_wa(p, "LOCK$") == 0) - || (strcmp_wa(p, "ON") == 0) - || (strcmp_wa(p, "OM1") == 0) - || (strcmp_wa(p, "OM2") == 0) - || (strcmp_wa(p, "OM3") == 0) - || (strcmp_wa(p, "OM4") == 0) - ) - ret = NT_STATUS_UNSUCCESSFUL; - break; - case UCS2_CHAR('L'): - if((strcmp_wa(p, "PT1") == 0) - || (strcmp_wa(p, "PT2") == 0) - || (strcmp_wa(p, "PT3") == 0) - ) - ret = NT_STATUS_UNSUCCESSFUL; - break; - case UCS2_CHAR('N'): - if(strcmp_wa(p, "UL") == 0) - ret = NT_STATUS_UNSUCCESSFUL; - break; - case UCS2_CHAR('P'): - if(strcmp_wa(p, "RN") == 0) - ret = NT_STATUS_UNSUCCESSFUL; - break; - default: - break; - } - - SAFE_FREE(str); - return ret; -} - -static NTSTATUS is_8_3_w(const smb_ucs2_t *fname, BOOL allow_wildcards) -{ - smb_ucs2_t *pref = 0, *ext = 0; - size_t plen; - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - - if (!fname || !*fname) - return NT_STATUS_INVALID_PARAMETER; - - if (strlen_w(fname) > 12) - return NT_STATUS_UNSUCCESSFUL; - - if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0) - return NT_STATUS_OK; - - if (!NT_STATUS_IS_OK(is_valid_name(fname, allow_wildcards, True))) - goto done; - - if (!NT_STATUS_IS_OK(mangle_get_prefix(fname, &pref, &ext, allow_wildcards))) - goto done; - plen = strlen_w(pref); - - if (strchr_wa(pref, '.')) - goto done; - if (plen < 1 || plen > 8) - goto done; - if (ext && (strlen_w(ext) > 3)) - goto done; - - ret = NT_STATUS_OK; - -done: - SAFE_FREE(pref); - SAFE_FREE(ext); - return ret; -} - -static BOOL is_8_3(const char *fname, BOOL check_case, BOOL allow_wildcards) -{ - const char *f; - smb_ucs2_t *ucs2name; - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - size_t size; - - if (!fname || !*fname) - return False; - if ((f = strrchr(fname, '/')) == NULL) - f = fname; - else - f++; - - if (strlen(f) > 12) - return False; - - size = push_ucs2_allocate(&ucs2name, f); - if (size == (size_t)-1) { - DEBUG(0,("is_8_3: internal error push_ucs2_allocate() failed!\n")); - goto done; - } - - ret = is_8_3_w(ucs2name, allow_wildcards); - -done: - SAFE_FREE(ucs2name); - - if (!NT_STATUS_IS_OK(ret)) { - return False; - } - - return True; -} - - - /* -------------------------------------------------------------------------- ** * Functions... */ @@ -515,8 +305,9 @@ static BOOL check_cache( char *s, size_t * the buffer must be able to hold 13 characters (including the null) ***************************************************************************** */ -static void to_8_3(char *s, int default_case) +static void to_8_3_dos(char *name_dos, int default_case) { + fstring name_unix; int csum; char *p; char extension[4]; @@ -527,28 +318,39 @@ static void to_8_3(char *s, int default_ extension[0] = 0; base[0] = 0; - p = strrchr(s,'.'); - if( p && (strlen(p+1) < (size_t)4) ) { - BOOL all_normal = ( strisnormal(p+1, default_case) ); /* XXXXXXXXX */ + pull_ascii_fstring(name_unix, name_dos); + + p = strrchr(name_dos, '.'); + if(p && (strlen(p+1) < (size_t)4)) { + char *p2 = strrchr(name_unix, '.'); + BOOL all_normal = (strisnormal(p2+1, default_case)); /* XXXXXXXXX */ if( all_normal && p[1] != 0 ) { - *p = 0; - csum = str_checksum( s ); - *p = '.'; + *p2 = 0; + csum = str_checksum(name_unix); + *p2 = '.'; } else - csum = str_checksum(s); + csum = str_checksum(name_unix); } else - csum = str_checksum(s); - - strupper_m( s ); + csum = str_checksum(name_unix); - if( p ) { - if( p == s ) - safe_strcpy( extension, "___", 3 ); + if (p) { + if (p == name_dos) + safe_strcpy(extension, "___", 3 ); else { *p++ = 0; - while( *p && extlen < 3 ) { - if ( *p != '.') { + while (*p && extlen < 3) { + if (*p != '.') { + if ((unsigned int)p[0] >= 128 && p[1] != '\0') { + /* mb character */ + if (extlen < 2) { + extension[extlen++] = p[0]; + extension[extlen++] = p[1]; + } + p += 2; + continue; + } + extension[extlen++] = p[0]; } p++; @@ -556,25 +358,37 @@ static void to_8_3(char *s, int default_ extension[extlen] = 0; } } - - p = s; - while( *p && baselen < 5 ) { + strupper_m(name_unix); + push_ascii_fstring(name_dos, name_unix); + + p = name_dos; + while (*p && baselen < 5) { if (*p != '.') { + if ((unsigned int)p[0] >= 128 && p[1] != '\0') { + /* mb character */ + if (baselen < 4) { + base[baselen++] = p[0]; + base[baselen++] = p[1]; + } + p += 2; + continue; + } + base[baselen++] = p[0]; } p++; } base[baselen] = 0; - + csum = csum % (MANGLE_BASE*MANGLE_BASE); - - (void)slprintf(s, 12, "%s%c%c%c", - base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) ); - - if( *extension ) { - (void)pstrcat( s, "." ); - (void)pstrcat( s, extension ); + + (void)slprintf(name_dos, 12, "%s%c%c%c", + base, magic_char, mangle(csum/MANGLE_BASE), mangle(csum)); + + if (*extension) { + fstrcat(name_dos, "."); + fstrcat(name_dos, extension); } } @@ -604,38 +418,44 @@ static void to_8_3(char *s, int default_ * **************************************************************************** */ -static void name_map(char *OutName, BOOL need83, BOOL cache83, int default_case) +static void name_map(fstring name, BOOL need83, BOOL cache83, int default_case) { - smb_ucs2_t *OutName_ucs2; - DEBUG(5,("name_map( %s, need83 = %s, cache83 = %s)\n", OutName, - need83 ? "True" : "False", cache83 ? "True" : "False")); - - if (push_ucs2_allocate(&OutName_ucs2, OutName) == (size_t)-1) { - DEBUG(0, ("push_ucs2_allocate failed!\n")); - return; - } - - if( !need83 && !NT_STATUS_IS_OK(is_valid_name(OutName_ucs2, False, False))) - need83 = True; + fstring name_orig; + fstring name_dos; + DEBUG(0, ("name_map( %s (%p), need83 = %s, cache83 = %s)\n", name, name, + need83 ? "True" : "False", cache83 ? "True" : "False")); + DEBUG(5, ("name_map( %s, need83 = %s, cache83 = %s)\n", name, + need83 ? "True" : "False", cache83 ? "True" : "False")); + + push_ascii_fstring(name_dos, name); + + /* reserved names are handled specially */ + if (!mangle_common_is_reserved_name(name)) { + /* if the name is already a valid 8.3 name then we don't need to + do anything */ + if (mangle_common_is_8_3_dos(name_dos, False, False)) { + return; + } - /* check if it's already in 8.3 format */ - if (need83 && !NT_STATUS_IS_OK(is_8_3_w(OutName_ucs2, False))) { - char *tmp = NULL; - - /* mangle it into 8.3 */ - if (cache83) - tmp = strdup(OutName); - - to_8_3(OutName, default_case); - - if(tmp != NULL) { - cache_mangled_name(OutName, tmp); - SAFE_FREE(tmp); + /* if the caller doesn't strictly need 8.3 then just check for illegal + filenames */ + if (!need83 && mangle_common_is_legal_name_dos(name_dos)) { + return; } } - DEBUG(5,("name_map() ==> [%s]\n", OutName)); - SAFE_FREE(OutName_ucs2); + if (cache83) + fstrcpy(name_orig, name); + + /* mangle it into 8.3 */ + to_8_3_dos(name_dos, default_case); + pull_ascii_fstring(name, name_dos); + DEBUG(5, ("name_map() ==> [%s]\n", name)); + + if (cache83) + cache_mangled_name(name, name_orig); + + return; } /* @@ -644,7 +464,7 @@ static void name_map(char *OutName, BOOL */ static struct mangle_fns mangle_fns = { is_mangled, - is_8_3, + mangle_common_is_8_3, mangle_reset, check_cache, name_map @@ -653,6 +473,7 @@ static struct mangle_fns mangle_fns = { /* return the methods for this mangling implementation */ struct mangle_fns *mangle_hash_init(void) { + mangle_common_init(); mangle_reset(); /* Create the in-memory tdb using our custom hash function. */ Index: source/Makefile.in =================================================================== --- source/Makefile.in (revision 2179) +++ source/Makefile.in (working copy) @@ -365,7 +365,8 @@ AUTH_OBJ = auth/auth.o @AUTH_STATIC@ aut auth/auth_ntlmssp.o \ $(PLAINTEXT_AUTH_OBJ) $(SLCACHE_OBJ) $(DCUTIL_OBJ) -MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o +MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o \ + smbd/mangle_common.o SMBD_OBJ_MAIN = smbd/server.o