| 1 | /*
 | 
|---|
| 2 |    Unix SMB/CIFS implementation.
 | 
|---|
| 3 |    Name mangling
 | 
|---|
| 4 |    Copyright (C) Andrew Tridgell 1992-2002
 | 
|---|
| 5 |    Copyright (C) Simo Sorce 2001
 | 
|---|
| 6 |    Copyright (C) Andrew Bartlett 2002
 | 
|---|
| 7 |    Copyright (C) Jeremy Allison 2007
 | 
|---|
| 8 | 
 | 
|---|
| 9 |    This program is free software; you can redistribute it and/or modify
 | 
|---|
| 10 |    it under the terms of the GNU General Public License as published by
 | 
|---|
| 11 |    the Free Software Foundation; either version 3 of the License, or
 | 
|---|
| 12 |    (at your option) any later version.
 | 
|---|
| 13 | 
 | 
|---|
| 14 |    This program is distributed in the hope that it will be useful,
 | 
|---|
| 15 |    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 16 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 17 |    GNU General Public License for more details.
 | 
|---|
| 18 | 
 | 
|---|
| 19 |    You should have received a copy of the GNU General Public License
 | 
|---|
| 20 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
|---|
| 21 | */
 | 
|---|
| 22 | 
 | 
|---|
| 23 | #include "includes.h"
 | 
|---|
| 24 | #include "smbd/globals.h"
 | 
|---|
| 25 | 
 | 
|---|
| 26 | /* -------------------------------------------------------------------------- **
 | 
|---|
| 27 |  * Other stuff...
 | 
|---|
| 28 |  *
 | 
|---|
| 29 |  * magic_char     - This is the magic char used for mangling.  It's
 | 
|---|
| 30 |  *                  global.  There is a call to lp_magicchar() in server.c
 | 
|---|
| 31 |  *                  that is used to override the initial value.
 | 
|---|
| 32 |  *
 | 
|---|
| 33 |  * MANGLE_BASE    - This is the number of characters we use for name mangling.
 | 
|---|
| 34 |  *
 | 
|---|
| 35 |  * basechars      - The set characters used for name mangling.  This
 | 
|---|
| 36 |  *                  is static (scope is this file only).
 | 
|---|
| 37 |  *
 | 
|---|
| 38 |  * mangle()       - Macro used to select a character from basechars (i.e.,
 | 
|---|
| 39 |  *                  mangle(n) will return the nth digit, modulo MANGLE_BASE).
 | 
|---|
| 40 |  *
 | 
|---|
| 41 |  * chartest       - array 0..255.  The index range is the set of all possible
 | 
|---|
| 42 |  *                  values of a byte.  For each byte value, the content is a
 | 
|---|
| 43 |  *                  two nibble pair.  See BASECHAR_MASK below.
 | 
|---|
| 44 |  *
 | 
|---|
| 45 |  * ct_initialized - False until the chartest array has been initialized via
 | 
|---|
| 46 |  *                  a call to init_chartest().
 | 
|---|
| 47 |  *
 | 
|---|
| 48 |  * BASECHAR_MASK  - Masks the upper nibble of a one-byte value.
 | 
|---|
| 49 |  *
 | 
|---|
| 50 |  * isbasecahr()   - Given a character, check the chartest array to see
 | 
|---|
| 51 |  *                  if that character is in the basechars set.  This is
 | 
|---|
| 52 |  *                  faster than using strchr_m().
 | 
|---|
| 53 |  *
 | 
|---|
| 54 |  */
 | 
|---|
| 55 | 
 | 
|---|
| 56 | static const char basechars[43]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
 | 
|---|
| 57 | #define MANGLE_BASE       (sizeof(basechars)/sizeof(char)-1)
 | 
|---|
| 58 | 
 | 
|---|
| 59 | #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
 | 
|---|
| 60 | #define BASECHAR_MASK 0xf0
 | 
|---|
| 61 | #define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK )
 | 
|---|
| 62 | 
 | 
|---|
| 63 | /* -------------------------------------------------------------------- */
 | 
|---|
| 64 | 
 | 
|---|
| 65 | static NTSTATUS has_valid_83_chars(const smb_ucs2_t *s, bool allow_wildcards)
 | 
|---|
| 66 | {
 | 
|---|
| 67 |         if (!*s) {
 | 
|---|
| 68 |                 return NT_STATUS_INVALID_PARAMETER;
 | 
|---|
| 69 |         }
 | 
|---|
| 70 | 
 | 
|---|
| 71 |         if (!allow_wildcards && ms_has_wild_w(s)) {
 | 
|---|
| 72 |                 return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 73 |         }
 | 
|---|
| 74 | 
 | 
|---|
| 75 |         while (*s) {
 | 
|---|
| 76 |                 if(!isvalid83_w(*s)) {
 | 
|---|
| 77 |                         return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 78 |                 }
 | 
|---|
| 79 |                 s++;
 | 
|---|
| 80 |         }
 | 
|---|
| 81 | 
 | 
|---|
| 82 |         return NT_STATUS_OK;
 | 
|---|
| 83 | }
 | 
|---|
| 84 | 
 | 
|---|
| 85 | static NTSTATUS has_illegal_chars(const smb_ucs2_t *s, bool allow_wildcards)
 | 
|---|
| 86 | {
 | 
|---|
| 87 |         if (!allow_wildcards && ms_has_wild_w(s)) {
 | 
|---|
| 88 |                 return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 89 |         }
 | 
|---|
| 90 | 
 | 
|---|
| 91 |         while (*s) {
 | 
|---|
| 92 |                 if (*s <= 0x1f) {
 | 
|---|
| 93 |                         /* Control characters. */
 | 
|---|
| 94 |                         return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 95 |                 }
 | 
|---|
| 96 |                 switch(*s) {
 | 
|---|
| 97 |                         case UCS2_CHAR('\\'):
 | 
|---|
| 98 |                         case UCS2_CHAR('/'):
 | 
|---|
| 99 |                         case UCS2_CHAR('|'):
 | 
|---|
| 100 |                         case UCS2_CHAR(':'):
 | 
|---|
| 101 |                                 return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 102 |                 }
 | 
|---|
| 103 |                 s++;
 | 
|---|
| 104 |         }
 | 
|---|
| 105 | 
 | 
|---|
| 106 |         return NT_STATUS_OK;
 | 
|---|
| 107 | }
 | 
|---|
| 108 | 
 | 
|---|
| 109 | /* return False if something fail and
 | 
|---|
| 110 |  * return 2 alloced unicode strings that contain prefix and extension
 | 
|---|
| 111 |  */
 | 
|---|
| 112 | 
 | 
|---|
| 113 | static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix,
 | 
|---|
| 114 |                 smb_ucs2_t **extension, bool allow_wildcards)
 | 
|---|
| 115 | {
 | 
|---|
| 116 |         size_t ext_len;
 | 
|---|
| 117 |         smb_ucs2_t *p;
 | 
|---|
| 118 | 
 | 
|---|
| 119 |         *extension = 0;
 | 
|---|
| 120 |         *prefix = strdup_w(ucs2_string);
 | 
|---|
| 121 |         if (!*prefix) {
 | 
|---|
| 122 |                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 123 |         }
 | 
|---|
| 124 |         if ((p = strrchr_w(*prefix, UCS2_CHAR('.')))) {
 | 
|---|
| 125 |                 ext_len = strlen_w(p+1);
 | 
|---|
| 126 |                 if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) &&
 | 
|---|
| 127 |                     (NT_STATUS_IS_OK(has_valid_83_chars(p+1,allow_wildcards)))) /* check extension */ {
 | 
|---|
| 128 |                         *p = 0;
 | 
|---|
| 129 |                         *extension = strdup_w(p+1);
 | 
|---|
| 130 |                         if (!*extension) {
 | 
|---|
| 131 |                                 SAFE_FREE(*prefix);
 | 
|---|
| 132 |                                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 133 |                         }
 | 
|---|
| 134 |                 }
 | 
|---|
| 135 |         }
 | 
|---|
| 136 |         return NT_STATUS_OK;
 | 
|---|
| 137 | }
 | 
|---|
| 138 | 
 | 
|---|
| 139 | /* ************************************************************************** **
 | 
|---|
| 140 |  * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name.
 | 
|---|
| 141 |  * or contains illegal characters.
 | 
|---|
| 142 |  *
 | 
|---|
| 143 |  *  Input:  fname - String containing the name to be tested.
 | 
|---|
| 144 |  *
 | 
|---|
| 145 |  *  Output: NT_STATUS_UNSUCCESSFUL, if the condition above is true.
 | 
|---|
| 146 |  *
 | 
|---|
| 147 |  *  Notes:  This is a static function called by is_8_3(), below.
 | 
|---|
| 148 |  *
 | 
|---|
| 149 |  * ************************************************************************** **
 | 
|---|
| 150 |  */
 | 
|---|
| 151 | 
 | 
|---|
| 152 | static NTSTATUS is_valid_name(const smb_ucs2_t *fname, bool allow_wildcards, bool only_8_3)
 | 
|---|
| 153 | {
 | 
|---|
| 154 |         smb_ucs2_t *str, *p;
 | 
|---|
| 155 |         size_t num_ucs2_chars;
 | 
|---|
| 156 |         NTSTATUS ret = NT_STATUS_OK;
 | 
|---|
| 157 | 
 | 
|---|
| 158 |         if (!fname || !*fname)
 | 
|---|
| 159 |                 return NT_STATUS_INVALID_PARAMETER;
 | 
|---|
| 160 | 
 | 
|---|
| 161 |         /* . and .. are valid names. */
 | 
|---|
| 162 |         if (strcmp_wa(fname, ".")==0 || strcmp_wa(fname, "..")==0)
 | 
|---|
| 163 |                 return NT_STATUS_OK;
 | 
|---|
| 164 | 
 | 
|---|
| 165 |         if (only_8_3) {
 | 
|---|
| 166 |                 ret = has_valid_83_chars(fname, allow_wildcards);
 | 
|---|
| 167 |                 if (!NT_STATUS_IS_OK(ret))
 | 
|---|
| 168 |                         return ret;
 | 
|---|
| 169 |         }
 | 
|---|
| 170 | 
 | 
|---|
| 171 |         ret = has_illegal_chars(fname, allow_wildcards);
 | 
|---|
| 172 |         if (!NT_STATUS_IS_OK(ret))
 | 
|---|
| 173 |                 return ret;
 | 
|---|
| 174 | 
 | 
|---|
| 175 |         /* Name can't end in '.' or ' ' */
 | 
|---|
| 176 |         num_ucs2_chars = strlen_w(fname);
 | 
|---|
| 177 |         if (fname[num_ucs2_chars-1] == UCS2_CHAR('.') || fname[num_ucs2_chars-1] == UCS2_CHAR(' ')) {
 | 
|---|
| 178 |                 return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 179 |         }
 | 
|---|
| 180 | 
 | 
|---|
| 181 |         str = strdup_w(fname);
 | 
|---|
| 182 | 
 | 
|---|
| 183 |         /* Truncate copy after the first dot. */
 | 
|---|
| 184 |         p = strchr_w(str, UCS2_CHAR('.'));
 | 
|---|
| 185 |         if (p) {
 | 
|---|
| 186 |                 *p = 0;
 | 
|---|
| 187 |         }
 | 
|---|
| 188 | 
 | 
|---|
| 189 |         strupper_w(str);
 | 
|---|
| 190 |         p = &str[1];
 | 
|---|
| 191 | 
 | 
|---|
| 192 |         switch(str[0])
 | 
|---|
| 193 |         {
 | 
|---|
| 194 |         case UCS2_CHAR('A'):
 | 
|---|
| 195 |                 if(strcmp_wa(p, "UX") == 0)
 | 
|---|
| 196 |                         ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 197 |                 break;
 | 
|---|
| 198 |         case UCS2_CHAR('C'):
 | 
|---|
| 199 |                 if((strcmp_wa(p, "LOCK$") == 0)
 | 
|---|
| 200 |                 || (strcmp_wa(p, "ON") == 0)
 | 
|---|
| 201 |                 || (strcmp_wa(p, "OM1") == 0)
 | 
|---|
| 202 |                 || (strcmp_wa(p, "OM2") == 0)
 | 
|---|
| 203 |                 || (strcmp_wa(p, "OM3") == 0)
 | 
|---|
| 204 |                 || (strcmp_wa(p, "OM4") == 0)
 | 
|---|
| 205 |                 )
 | 
|---|
| 206 |                         ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 207 |                 break;
 | 
|---|
| 208 |         case UCS2_CHAR('L'):
 | 
|---|
| 209 |                 if((strcmp_wa(p, "PT1") == 0)
 | 
|---|
| 210 |                 || (strcmp_wa(p, "PT2") == 0)
 | 
|---|
| 211 |                 || (strcmp_wa(p, "PT3") == 0)
 | 
|---|
| 212 |                 )
 | 
|---|
| 213 |                         ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 214 |                 break;
 | 
|---|
| 215 |         case UCS2_CHAR('N'):
 | 
|---|
| 216 |                 if(strcmp_wa(p, "UL") == 0)
 | 
|---|
| 217 |                         ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 218 |                 break;
 | 
|---|
| 219 |         case UCS2_CHAR('P'):
 | 
|---|
| 220 |                 if(strcmp_wa(p, "RN") == 0)
 | 
|---|
| 221 |                         ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 222 |                 break;
 | 
|---|
| 223 |         default:
 | 
|---|
| 224 |                 break;
 | 
|---|
| 225 |         }
 | 
|---|
| 226 | 
 | 
|---|
| 227 |         SAFE_FREE(str);
 | 
|---|
| 228 |         return ret;
 | 
|---|
| 229 | }
 | 
|---|
| 230 | 
 | 
|---|
| 231 | static NTSTATUS is_8_3_w(const smb_ucs2_t *fname, bool allow_wildcards)
 | 
|---|
| 232 | {
 | 
|---|
| 233 |         smb_ucs2_t *pref = 0, *ext = 0;
 | 
|---|
| 234 |         size_t plen;
 | 
|---|
| 235 |         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 236 | 
 | 
|---|
| 237 |         if (!fname || !*fname)
 | 
|---|
| 238 |                 return NT_STATUS_INVALID_PARAMETER;
 | 
|---|
| 239 | 
 | 
|---|
| 240 |         if (strlen_w(fname) > 12)
 | 
|---|
| 241 |                 return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 242 | 
 | 
|---|
| 243 |         if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0)
 | 
|---|
| 244 |                 return NT_STATUS_OK;
 | 
|---|
| 245 | 
 | 
|---|
| 246 |         /* Name cannot start with '.' */
 | 
|---|
| 247 |         if (*fname == UCS2_CHAR('.'))
 | 
|---|
| 248 |                 return NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 249 | 
 | 
|---|
| 250 |         if (!NT_STATUS_IS_OK(is_valid_name(fname, allow_wildcards, True)))
 | 
|---|
| 251 |                 goto done;
 | 
|---|
| 252 | 
 | 
|---|
| 253 |         if (!NT_STATUS_IS_OK(mangle_get_prefix(fname, &pref, &ext, allow_wildcards)))
 | 
|---|
| 254 |                 goto done;
 | 
|---|
| 255 |         plen = strlen_w(pref);
 | 
|---|
| 256 | 
 | 
|---|
| 257 |         if (strchr_wa(pref, '.'))
 | 
|---|
| 258 |                 goto done;
 | 
|---|
| 259 |         if (plen < 1 || plen > 8)
 | 
|---|
| 260 |                 goto done;
 | 
|---|
| 261 |         if (ext && (strlen_w(ext) > 3))
 | 
|---|
| 262 |                 goto done;
 | 
|---|
| 263 | 
 | 
|---|
| 264 |         ret = NT_STATUS_OK;
 | 
|---|
| 265 | 
 | 
|---|
| 266 | done:
 | 
|---|
| 267 |         SAFE_FREE(pref);
 | 
|---|
| 268 |         SAFE_FREE(ext);
 | 
|---|
| 269 |         return ret;
 | 
|---|
| 270 | }
 | 
|---|
| 271 | 
 | 
|---|
| 272 | static bool is_8_3(const char *fname, bool check_case, bool allow_wildcards,
 | 
|---|
| 273 |                    const struct share_params *p)
 | 
|---|
| 274 | {
 | 
|---|
| 275 |         const char *f;
 | 
|---|
| 276 |         smb_ucs2_t *ucs2name;
 | 
|---|
| 277 |         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
 | 
|---|
| 278 |         size_t size;
 | 
|---|
| 279 |         char magic_char;
 | 
|---|
| 280 | 
 | 
|---|
| 281 |         magic_char = lp_magicchar(p);
 | 
|---|
| 282 | 
 | 
|---|
| 283 |         if (!fname || !*fname)
 | 
|---|
| 284 |                 return False;
 | 
|---|
| 285 |         if ((f = strrchr(fname, '/')) == NULL)
 | 
|---|
| 286 |                 f = fname;
 | 
|---|
| 287 |         else
 | 
|---|
| 288 |                 f++;
 | 
|---|
| 289 | 
 | 
|---|
| 290 |         if (strlen(f) > 12)
 | 
|---|
| 291 |                 return False;
 | 
|---|
| 292 | 
 | 
|---|
| 293 |         if (!push_ucs2_talloc(NULL, &ucs2name, f, &size)) {
 | 
|---|
| 294 |                 DEBUG(0,("is_8_3: internal error push_ucs2_talloc() failed!\n"));
 | 
|---|
| 295 |                 goto done;
 | 
|---|
| 296 |         }
 | 
|---|
| 297 | 
 | 
|---|
| 298 |         ret = is_8_3_w(ucs2name, allow_wildcards);
 | 
|---|
| 299 | 
 | 
|---|
| 300 | done:
 | 
|---|
| 301 |         TALLOC_FREE(ucs2name);
 | 
|---|
| 302 | 
 | 
|---|
| 303 |         if (!NT_STATUS_IS_OK(ret)) {
 | 
|---|
| 304 |                 return False;
 | 
|---|
| 305 |         }
 | 
|---|
| 306 | 
 | 
|---|
| 307 |         return True;
 | 
|---|
| 308 | }
 | 
|---|
| 309 | 
 | 
|---|
| 310 | /* -------------------------------------------------------------------------- **
 | 
|---|
| 311 |  * Functions...
 | 
|---|
| 312 |  */
 | 
|---|
| 313 | 
 | 
|---|
| 314 | /* ************************************************************************** **
 | 
|---|
| 315 |  * Initialize the static character test array.
 | 
|---|
| 316 |  *
 | 
|---|
| 317 |  *  Input:  none
 | 
|---|
| 318 |  *
 | 
|---|
| 319 |  *  Output: none
 | 
|---|
| 320 |  *
 | 
|---|
| 321 |  *  Notes:  This function changes (loads) the contents of the <chartest>
 | 
|---|
| 322 |  *          array.  The scope of <chartest> is this file.
 | 
|---|
| 323 |  *
 | 
|---|
| 324 |  * ************************************************************************** **
 | 
|---|
| 325 |  */
 | 
|---|
| 326 | 
 | 
|---|
| 327 | static void init_chartest( void )
 | 
|---|
| 328 | {
 | 
|---|
| 329 |         const unsigned char *s;
 | 
|---|
| 330 | 
 | 
|---|
| 331 |         chartest = SMB_MALLOC_ARRAY(unsigned char, 256);
 | 
|---|
| 332 | 
 | 
|---|
| 333 |         SMB_ASSERT(chartest != NULL);
 | 
|---|
| 334 |         memset(chartest, '\0', 256);
 | 
|---|
| 335 | 
 | 
|---|
| 336 |         for( s = (const unsigned char *)basechars; *s; s++ ) {
 | 
|---|
| 337 |                 chartest[*s] |= BASECHAR_MASK;
 | 
|---|
| 338 |         }
 | 
|---|
| 339 | }
 | 
|---|
| 340 | 
 | 
|---|
| 341 | /* ************************************************************************** **
 | 
|---|
| 342 |  * Return True if the name *could be* a mangled name.
 | 
|---|
| 343 |  *
 | 
|---|
| 344 |  *  Input:  s - A path name - in UNIX pathname format.
 | 
|---|
| 345 |  *
 | 
|---|
| 346 |  *  Output: True if the name matches the pattern described below in the
 | 
|---|
| 347 |  *          notes, else False.
 | 
|---|
| 348 |  *
 | 
|---|
| 349 |  *  Notes:  The input name is *not* tested for 8.3 compliance.  This must be
 | 
|---|
| 350 |  *          done separately.  This function returns true if the name contains
 | 
|---|
| 351 |  *          a magic character followed by excactly two characters from the
 | 
|---|
| 352 |  *          basechars list (above), which in turn are followed either by the
 | 
|---|
| 353 |  *          nul (end of string) byte or a dot (extension) or by a '/' (end of
 | 
|---|
| 354 |  *          a directory name).
 | 
|---|
| 355 |  *
 | 
|---|
| 356 |  * ************************************************************************** **
 | 
|---|
| 357 |  */
 | 
|---|
| 358 | 
 | 
|---|
| 359 | static bool is_mangled(const char *s, const struct share_params *p)
 | 
|---|
| 360 | {
 | 
|---|
| 361 |         char *magic;
 | 
|---|
| 362 |         char magic_char;
 | 
|---|
| 363 | 
 | 
|---|
| 364 |         magic_char = lp_magicchar(p);
 | 
|---|
| 365 | 
 | 
|---|
| 366 |         if (chartest == NULL) {
 | 
|---|
| 367 |                 init_chartest();
 | 
|---|
| 368 |         }
 | 
|---|
| 369 | 
 | 
|---|
| 370 |         magic = strchr_m( s, magic_char );
 | 
|---|
| 371 |         while( magic && magic[1] && magic[2] ) {         /* 3 chars, 1st is magic. */
 | 
|---|
| 372 |                 if( ('.' == magic[3] || '/' == magic[3] || !(magic[3]))          /* Ends with '.' or nul or '/' ?  */
 | 
|---|
| 373 |                                 && isbasechar( toupper_ascii(magic[1]) )           /* is 2nd char basechar?  */
 | 
|---|
| 374 |                                 && isbasechar( toupper_ascii(magic[2]) ) )         /* is 3rd char basechar?  */
 | 
|---|
| 375 |                         return( True );                           /* If all above, then true, */
 | 
|---|
| 376 |                 magic = strchr_m( magic+1, magic_char );      /*    else seek next magic. */
 | 
|---|
| 377 |         }
 | 
|---|
| 378 |         return( False );
 | 
|---|
| 379 | }
 | 
|---|
| 380 | 
 | 
|---|
| 381 | /***************************************************************************
 | 
|---|
| 382 |  Initializes or clears the mangled cache.
 | 
|---|
| 383 | ***************************************************************************/
 | 
|---|
| 384 | 
 | 
|---|
| 385 | static void mangle_reset( void )
 | 
|---|
| 386 | {
 | 
|---|
| 387 |         /* We could close and re-open the tdb here... should we ? The old code did
 | 
|---|
| 388 |            the equivalent... JRA. */
 | 
|---|
| 389 | }
 | 
|---|
| 390 | 
 | 
|---|
| 391 | /***************************************************************************
 | 
|---|
| 392 |  Add a mangled name into the cache.
 | 
|---|
| 393 |  If the extension of the raw name maps directly to the
 | 
|---|
| 394 |  extension of the mangled name, then we'll store both names
 | 
|---|
| 395 |  *without* extensions.  That way, we can provide consistent
 | 
|---|
| 396 |  reverse mangling for all names that match.  The test here is
 | 
|---|
| 397 |  a bit more careful than the one done in earlier versions of
 | 
|---|
| 398 |  mangle.c:
 | 
|---|
| 399 | 
 | 
|---|
| 400 |     - the extension must exist on the raw name,
 | 
|---|
| 401 |     - it must be all lower case
 | 
|---|
| 402 |     - it must match the mangled extension (to prove that no
 | 
|---|
| 403 |       mangling occurred).
 | 
|---|
| 404 |   crh 07-Apr-1998
 | 
|---|
| 405 | **************************************************************************/
 | 
|---|
| 406 | 
 | 
|---|
| 407 | static void cache_mangled_name( const char mangled_name[13],
 | 
|---|
| 408 |                                 const char *raw_name )
 | 
|---|
| 409 | {
 | 
|---|
| 410 |         TDB_DATA data_val;
 | 
|---|
| 411 |         char mangled_name_key[13];
 | 
|---|
| 412 |         char *s1 = NULL;
 | 
|---|
| 413 |         char *s2 = NULL;
 | 
|---|
| 414 | 
 | 
|---|
| 415 |         /* If the cache isn't initialized, give up. */
 | 
|---|
| 416 |         if( !tdb_mangled_cache )
 | 
|---|
| 417 |                 return;
 | 
|---|
| 418 | 
 | 
|---|
| 419 |         /* Init the string lengths. */
 | 
|---|
| 420 |         safe_strcpy(mangled_name_key, mangled_name, sizeof(mangled_name_key)-1);
 | 
|---|
| 421 | 
 | 
|---|
| 422 |         /* See if the extensions are unmangled.  If so, store the entry
 | 
|---|
| 423 |          * without the extension, thus creating a "group" reverse map.
 | 
|---|
| 424 |          */
 | 
|---|
| 425 |         s1 = strrchr( mangled_name_key, '.' );
 | 
|---|
| 426 |         if( s1 && (s2 = strrchr( raw_name, '.' )) ) {
 | 
|---|
| 427 |                 size_t i = 1;
 | 
|---|
| 428 |                 while( s1[i] && (tolower_ascii( s1[i] ) == s2[i]) )
 | 
|---|
| 429 |                         i++;
 | 
|---|
| 430 |                 if( !s1[i] && !s2[i] ) {
 | 
|---|
| 431 |                         /* Truncate at the '.' */
 | 
|---|
| 432 |                         *s1 = '\0';
 | 
|---|
| 433 |                         /*
 | 
|---|
| 434 |                          * DANGER WILL ROBINSON - this
 | 
|---|
| 435 |                          * is changing a const string via
 | 
|---|
| 436 |                          * an aliased pointer ! Remember to
 | 
|---|
| 437 |                          * put it back once we've used it.
 | 
|---|
| 438 |                          * JRA
 | 
|---|
| 439 |                          */
 | 
|---|
| 440 |                         *s2 = '\0';
 | 
|---|
| 441 |                 }
 | 
|---|
| 442 |         }
 | 
|---|
| 443 | 
 | 
|---|
| 444 |         /* Allocate a new cache entry.  If the allocation fails, just return. */
 | 
|---|
| 445 |         data_val = string_term_tdb_data(raw_name);
 | 
|---|
| 446 |         if (tdb_store_bystring(tdb_mangled_cache, mangled_name_key, data_val, TDB_REPLACE) != 0) {
 | 
|---|
| 447 |                 DEBUG(0,("cache_mangled_name: Error storing entry %s -> %s\n", mangled_name_key, raw_name));
 | 
|---|
| 448 |         } else {
 | 
|---|
| 449 |                 DEBUG(5,("cache_mangled_name: Stored entry %s -> %s\n", mangled_name_key, raw_name));
 | 
|---|
| 450 |         }
 | 
|---|
| 451 |         /* Restore the change we made to the const string. */
 | 
|---|
| 452 |         if (s2) {
 | 
|---|
| 453 |                 *s2 = '.';
 | 
|---|
| 454 |         }
 | 
|---|
| 455 | }
 | 
|---|
| 456 | 
 | 
|---|
| 457 | /* ************************************************************************** **
 | 
|---|
| 458 |  * Check for a name on the mangled name stack
 | 
|---|
| 459 |  *
 | 
|---|
| 460 |  *  Input:  s - Input *and* output string buffer.
 | 
|---|
| 461 |  *          maxlen - space in i/o string buffer.
 | 
|---|
| 462 |  *  Output: True if the name was found in the cache, else False.
 | 
|---|
| 463 |  *
 | 
|---|
| 464 |  *  Notes:  If a reverse map is found, the function will overwrite the string
 | 
|---|
| 465 |  *          space indicated by the input pointer <s>.  This is frightening.
 | 
|---|
| 466 |  *          It should be rewritten to return NULL if the long name was not
 | 
|---|
| 467 |  *          found, and a pointer to the long name if it was found.
 | 
|---|
| 468 |  *
 | 
|---|
| 469 |  * ************************************************************************** **
 | 
|---|
| 470 |  */
 | 
|---|
| 471 | 
 | 
|---|
| 472 | static bool lookup_name_from_8_3(TALLOC_CTX *ctx,
 | 
|---|
| 473 |                                 const char *in,
 | 
|---|
| 474 |                                 char **out, /* talloced on the given context. */
 | 
|---|
| 475 |                                 const struct share_params *p)
 | 
|---|
| 476 | {
 | 
|---|
| 477 |         TDB_DATA data_val;
 | 
|---|
| 478 |         char *saved_ext = NULL;
 | 
|---|
| 479 |         char *s = talloc_strdup(ctx, in);
 | 
|---|
| 480 |         char magic_char;
 | 
|---|
| 481 | 
 | 
|---|
| 482 |         magic_char = lp_magicchar(p);
 | 
|---|
| 483 | 
 | 
|---|
| 484 |         /* If the cache isn't initialized, give up. */
 | 
|---|
| 485 |         if(!s || !tdb_mangled_cache ) {
 | 
|---|
| 486 |                 TALLOC_FREE(s);
 | 
|---|
| 487 |                 return False;
 | 
|---|
| 488 |         }
 | 
|---|
| 489 | 
 | 
|---|
| 490 |         data_val = tdb_fetch_bystring(tdb_mangled_cache, s);
 | 
|---|
| 491 | 
 | 
|---|
| 492 |         /* If we didn't find the name *with* the extension, try without. */
 | 
|---|
| 493 |         if(data_val.dptr == NULL || data_val.dsize == 0) {
 | 
|---|
| 494 |                 char *ext_start = strrchr( s, '.' );
 | 
|---|
| 495 |                 if( ext_start ) {
 | 
|---|
| 496 |                         if((saved_ext = talloc_strdup(ctx,ext_start)) == NULL) {
 | 
|---|
| 497 |                                 TALLOC_FREE(s);
 | 
|---|
| 498 |                                 return False;
 | 
|---|
| 499 |                         }
 | 
|---|
| 500 | 
 | 
|---|
| 501 |                         *ext_start = '\0';
 | 
|---|
| 502 |                         data_val = tdb_fetch_bystring(tdb_mangled_cache, s);
 | 
|---|
| 503 |                         /*
 | 
|---|
| 504 |                          * At this point s is the name without the
 | 
|---|
| 505 |                          * extension. We re-add the extension if saved_ext
 | 
|---|
| 506 |                          * is not null, before freeing saved_ext.
 | 
|---|
| 507 |                          */
 | 
|---|
| 508 |                 }
 | 
|---|
| 509 |         }
 | 
|---|
| 510 | 
 | 
|---|
| 511 |         /* Okay, if we haven't found it we're done. */
 | 
|---|
| 512 |         if(data_val.dptr == NULL || data_val.dsize == 0) {
 | 
|---|
| 513 |                 TALLOC_FREE(saved_ext);
 | 
|---|
| 514 |                 TALLOC_FREE(s);
 | 
|---|
| 515 |                 return False;
 | 
|---|
| 516 |         }
 | 
|---|
| 517 | 
 | 
|---|
| 518 |         /* If we *did* find it, we need to talloc it on the given ctx. */
 | 
|---|
| 519 |         if (saved_ext) {
 | 
|---|
| 520 |                 *out = talloc_asprintf(ctx, "%s%s",
 | 
|---|
| 521 |                                         (char *)data_val.dptr,
 | 
|---|
| 522 |                                         saved_ext);
 | 
|---|
| 523 |         } else {
 | 
|---|
| 524 |                 *out = talloc_strdup(ctx, (char *)data_val.dptr);
 | 
|---|
| 525 |         }
 | 
|---|
| 526 | 
 | 
|---|
| 527 |         TALLOC_FREE(s);
 | 
|---|
| 528 |         TALLOC_FREE(saved_ext);
 | 
|---|
| 529 |         SAFE_FREE(data_val.dptr);
 | 
|---|
| 530 | 
 | 
|---|
| 531 |         return *out ? True : False;
 | 
|---|
| 532 | }
 | 
|---|
| 533 | 
 | 
|---|
| 534 | /*****************************************************************************
 | 
|---|
| 535 |  Do the actual mangling to 8.3 format.
 | 
|---|
| 536 | *****************************************************************************/
 | 
|---|
| 537 | 
 | 
|---|
| 538 | static bool to_8_3(char magic_char, const char *in, char out[13], int default_case)
 | 
|---|
| 539 | {
 | 
|---|
| 540 |         int csum;
 | 
|---|
| 541 |         char *p;
 | 
|---|
| 542 |         char extension[4];
 | 
|---|
| 543 |         char base[9];
 | 
|---|
| 544 |         int baselen = 0;
 | 
|---|
| 545 |         int extlen = 0;
 | 
|---|
| 546 |         char *s = SMB_STRDUP(in);
 | 
|---|
| 547 | 
 | 
|---|
| 548 |         extension[0] = 0;
 | 
|---|
| 549 |         base[0] = 0;
 | 
|---|
| 550 | 
 | 
|---|
| 551 |         if (!s) {
 | 
|---|
| 552 |                 return False;
 | 
|---|
| 553 |         }
 | 
|---|
| 554 | 
 | 
|---|
| 555 |         p = strrchr(s,'.');
 | 
|---|
| 556 |         if( p && (strlen(p+1) < (size_t)4) ) {
 | 
|---|
| 557 |                 bool all_normal = ( strisnormal(p+1, default_case) ); /* XXXXXXXXX */
 | 
|---|
| 558 | 
 | 
|---|
| 559 |                 if( all_normal && p[1] != 0 ) {
 | 
|---|
| 560 |                         *p = 0;
 | 
|---|
| 561 |                         csum = str_checksum( s );
 | 
|---|
| 562 |                         *p = '.';
 | 
|---|
| 563 |                 } else
 | 
|---|
| 564 |                         csum = str_checksum(s);
 | 
|---|
| 565 |         } else
 | 
|---|
| 566 |                 csum = str_checksum(s);
 | 
|---|
| 567 | 
 | 
|---|
| 568 |         strupper_m( s );
 | 
|---|
| 569 | 
 | 
|---|
| 570 |         if( p ) {
 | 
|---|
| 571 |                 if( p == s )
 | 
|---|
| 572 |                         safe_strcpy( extension, "___", 3 );
 | 
|---|
| 573 |                 else {
 | 
|---|
| 574 |                         *p++ = 0;
 | 
|---|
| 575 |                         while( *p && extlen < 3 ) {
 | 
|---|
| 576 |                                 if ( *p != '.') {
 | 
|---|
| 577 |                                         extension[extlen++] = p[0];
 | 
|---|
| 578 |                                 }
 | 
|---|
| 579 |                                 p++;
 | 
|---|
| 580 |                         }
 | 
|---|
| 581 |                         extension[extlen] = 0;
 | 
|---|
| 582 |                 }
 | 
|---|
| 583 |         }
 | 
|---|
| 584 | 
 | 
|---|
| 585 |         p = s;
 | 
|---|
| 586 | 
 | 
|---|
| 587 |         while( *p && baselen < 5 ) {
 | 
|---|
| 588 |                 if (isbasechar(*p)) {
 | 
|---|
| 589 |                         base[baselen++] = p[0];
 | 
|---|
| 590 |                 }
 | 
|---|
| 591 |                 p++;
 | 
|---|
| 592 |         }
 | 
|---|
| 593 |         base[baselen] = 0;
 | 
|---|
| 594 | 
 | 
|---|
| 595 |         csum = csum % (MANGLE_BASE*MANGLE_BASE);
 | 
|---|
| 596 | 
 | 
|---|
| 597 |         memcpy(out, base, baselen);
 | 
|---|
| 598 |         out[baselen] = magic_char;
 | 
|---|
| 599 |         out[baselen+1] = mangle( csum/MANGLE_BASE );
 | 
|---|
| 600 |         out[baselen+2] = mangle( csum );
 | 
|---|
| 601 | 
 | 
|---|
| 602 |         if( *extension ) {
 | 
|---|
| 603 |                 out[baselen+3] = '.';
 | 
|---|
| 604 |                 safe_strcpy(&out[baselen+4], extension, 3);
 | 
|---|
| 605 |         }
 | 
|---|
| 606 | 
 | 
|---|
| 607 |         SAFE_FREE(s);
 | 
|---|
| 608 |         return True;
 | 
|---|
| 609 | }
 | 
|---|
| 610 | 
 | 
|---|
| 611 | static bool must_mangle(const char *name,
 | 
|---|
| 612 |                         const struct share_params *p)
 | 
|---|
| 613 | {
 | 
|---|
| 614 |         smb_ucs2_t *name_ucs2 = NULL;
 | 
|---|
| 615 |         NTSTATUS status;
 | 
|---|
| 616 |         size_t converted_size;
 | 
|---|
| 617 |         char magic_char;
 | 
|---|
| 618 | 
 | 
|---|
| 619 |         magic_char = lp_magicchar(p);
 | 
|---|
| 620 | 
 | 
|---|
| 621 |         if (!push_ucs2_talloc(NULL, &name_ucs2, name, &converted_size)) {
 | 
|---|
| 622 |                 DEBUG(0, ("push_ucs2_talloc failed!\n"));
 | 
|---|
| 623 |                 return False;
 | 
|---|
| 624 |         }
 | 
|---|
| 625 |         status = is_valid_name(name_ucs2, False, False);
 | 
|---|
| 626 |         TALLOC_FREE(name_ucs2);
 | 
|---|
| 627 |         /* We return true if we *must* mangle, so if it's
 | 
|---|
| 628 |          * a valid name (status == OK) then we must return
 | 
|---|
| 629 |          * false. Bug #6939. */
 | 
|---|
| 630 |         return !NT_STATUS_IS_OK(status);
 | 
|---|
| 631 | }
 | 
|---|
| 632 | 
 | 
|---|
| 633 | /*****************************************************************************
 | 
|---|
| 634 |  * Convert a filename to DOS format.  Return True if successful.
 | 
|---|
| 635 |  *  Input:  in        Incoming name.
 | 
|---|
| 636 |  *
 | 
|---|
| 637 |  *          out       8.3 DOS name.
 | 
|---|
| 638 |  *
 | 
|---|
| 639 |  *          cache83 - If False, the mangled name cache will not be updated.
 | 
|---|
| 640 |  *                    This is usually used to prevent that we overwrite
 | 
|---|
| 641 |  *                    a conflicting cache entry prematurely, i.e. before
 | 
|---|
| 642 |  *                    we know whether the client is really interested in the
 | 
|---|
| 643 |  *                    current name.  (See PR#13758).  UKD.
 | 
|---|
| 644 |  *
 | 
|---|
| 645 |  * ****************************************************************************
 | 
|---|
| 646 |  */
 | 
|---|
| 647 | 
 | 
|---|
| 648 | static bool hash_name_to_8_3(const char *in,
 | 
|---|
| 649 |                         char out[13],
 | 
|---|
| 650 |                         bool cache83,
 | 
|---|
| 651 |                         int default_case,
 | 
|---|
| 652 |                         const struct share_params *p)
 | 
|---|
| 653 | {
 | 
|---|
| 654 |         smb_ucs2_t *in_ucs2 = NULL;
 | 
|---|
| 655 |         size_t converted_size;
 | 
|---|
| 656 |         char magic_char;
 | 
|---|
| 657 | 
 | 
|---|
| 658 |         magic_char = lp_magicchar(p);
 | 
|---|
| 659 | 
 | 
|---|
| 660 |         DEBUG(5,("hash_name_to_8_3( %s, cache83 = %s)\n", in,
 | 
|---|
| 661 |                  cache83 ? "True" : "False"));
 | 
|---|
| 662 | 
 | 
|---|
| 663 |         if (!push_ucs2_talloc(NULL, &in_ucs2, in, &converted_size)) {
 | 
|---|
| 664 |                 DEBUG(0, ("push_ucs2_talloc failed!\n"));
 | 
|---|
| 665 |                 return False;
 | 
|---|
| 666 |         }
 | 
|---|
| 667 | 
 | 
|---|
| 668 |         /* If it's already 8.3, just copy. */
 | 
|---|
| 669 |         if (NT_STATUS_IS_OK(is_valid_name(in_ucs2, False, False)) &&
 | 
|---|
| 670 |                                 NT_STATUS_IS_OK(is_8_3_w(in_ucs2, False))) {
 | 
|---|
| 671 |                 TALLOC_FREE(in_ucs2);
 | 
|---|
| 672 |                 safe_strcpy(out, in, 12);
 | 
|---|
| 673 |                 return True;
 | 
|---|
| 674 |         }
 | 
|---|
| 675 | 
 | 
|---|
| 676 |         TALLOC_FREE(in_ucs2);
 | 
|---|
| 677 |         if (!to_8_3(magic_char, in, out, default_case)) {
 | 
|---|
| 678 |                 return False;
 | 
|---|
| 679 |         }
 | 
|---|
| 680 | 
 | 
|---|
| 681 |         cache_mangled_name(out, in);
 | 
|---|
| 682 | 
 | 
|---|
| 683 |         DEBUG(5,("hash_name_to_8_3(%s) ==> [%s]\n", in, out));
 | 
|---|
| 684 |         return True;
 | 
|---|
| 685 | }
 | 
|---|
| 686 | 
 | 
|---|
| 687 | /*
 | 
|---|
| 688 |   the following provides the abstraction layer to make it easier
 | 
|---|
| 689 |   to drop in an alternative mangling implementation
 | 
|---|
| 690 | */
 | 
|---|
| 691 | static const struct mangle_fns mangle_hash_fns = {
 | 
|---|
| 692 |         mangle_reset,
 | 
|---|
| 693 |         is_mangled,
 | 
|---|
| 694 |         must_mangle,
 | 
|---|
| 695 |         is_8_3,
 | 
|---|
| 696 |         lookup_name_from_8_3,
 | 
|---|
| 697 |         hash_name_to_8_3
 | 
|---|
| 698 | };
 | 
|---|
| 699 | 
 | 
|---|
| 700 | /* return the methods for this mangling implementation */
 | 
|---|
| 701 | const struct mangle_fns *mangle_hash_init(void)
 | 
|---|
| 702 | {
 | 
|---|
| 703 |         mangle_reset();
 | 
|---|
| 704 | 
 | 
|---|
| 705 |         /* Create the in-memory tdb using our custom hash function. */
 | 
|---|
| 706 |         tdb_mangled_cache = tdb_open_ex("mangled_cache", 1031, TDB_INTERNAL,
 | 
|---|
| 707 |                                 (O_RDWR|O_CREAT), 0644, NULL, fast_string_hash);
 | 
|---|
| 708 | 
 | 
|---|
| 709 |         return &mangle_hash_fns;
 | 
|---|
| 710 | }
 | 
|---|