Check error returns from strupper_m() (in all reasonable places).
[kai/samba.git] / source3 / smbd / mangle_hash.c
index 44fbe8a9e80b6b0e2bee189975e52e36eeb66077..8a44ea2d6a2c0a64aa637989757cb80674a6d76b 100644 (file)
 */
 
 #include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "mangle.h"
+#include "util_tdb.h"
+#include "lib/param/loadparm.h"
 
 /* -------------------------------------------------------------------------- **
  * Other stuff...
  *
  */
 
-char magic_char = '~';
-
-static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
+static const char basechars[43]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
 #define MANGLE_BASE       (sizeof(basechars)/sizeof(char)-1)
 
-static unsigned char chartest[256]  = { 0 };
-static bool          ct_initialized = False;
-
 #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
 #define BASECHAR_MASK 0xf0
 #define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK )
 
-static TDB_CONTEXT *tdb_mangled_cache;
-
 /* -------------------------------------------------------------------- */
 
+
+/*******************************************************************
+ Determine if a character is valid in a 8.3 name.
+********************************************************************/
+
+/**
+ * Load the valid character map table from <tt>valid.dat</tt> or
+ * create from the configured codepage.
+ *
+ * This function is called whenever the configuration is reloaded.
+ * However, the valid character table is not changed if it's loaded
+ * from a file, because we can't unmap files.
+ **/
+
+static uint8 *valid_table;
+static void init_valid_table(void)
+{
+       if (valid_table) {
+               return;
+       }
+
+       valid_table = (uint8 *)map_file(data_path(talloc_tos(), "valid.dat"), 0x10000);
+       if (!valid_table) {
+               smb_panic("Could not load valid.dat file required for mangle method=hash");
+               return;
+       }
+}
+
+static bool isvalid83_w(smb_ucs2_t c)
+{
+       init_valid_table();
+       return valid_table[SVAL(&c,0)] != 0;
+}
+
 static NTSTATUS has_valid_83_chars(const smb_ucs2_t *s, bool allow_wildcards)
 {
        if (!*s) {
@@ -112,6 +145,25 @@ static NTSTATUS has_illegal_chars(const smb_ucs2_t *s, bool allow_wildcards)
        return NT_STATUS_OK;
 }
 
+/*******************************************************************
+ Duplicate string.
+********************************************************************/
+
+static smb_ucs2_t *strdup_w(const smb_ucs2_t *src)
+{
+       smb_ucs2_t *dest;
+       size_t len = strlen_w(src);
+       dest = SMB_MALLOC_ARRAY(smb_ucs2_t, len + 1);
+       if (!dest) {
+               DEBUG(0,("strdup_w: out of memory!\n"));
+               return NULL;
+       }
+
+       memcpy(dest, src, len * sizeof(smb_ucs2_t));
+       dest[len] = 0;
+       return dest;
+}
+
 /* return False if something fail and
  * return 2 alloced unicode strings that contain prefix and extension
  */
@@ -283,8 +335,6 @@ static bool is_8_3(const char *fname, bool check_case, bool allow_wildcards,
        NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
        size_t size;
 
-       magic_char = lp_magicchar(p);
-
        if (!fname || !*fname)
                return False;
        if ((f = strrchr(fname, '/')) == NULL)
@@ -295,16 +345,15 @@ static bool is_8_3(const char *fname, bool check_case, bool allow_wildcards,
        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"));
+       if (!push_ucs2_talloc(NULL, &ucs2name, f, &size)) {
+               DEBUG(0,("is_8_3: internal error push_ucs2_talloc() failed!\n"));
                goto done;
        }
 
        ret = is_8_3_w(ucs2name, allow_wildcards);
 
 done:
-       SAFE_FREE(ucs2name);
+       TALLOC_FREE(ucs2name);
 
        if (!NT_STATUS_IS_OK(ret)) {
                return False;
@@ -334,13 +383,14 @@ static void init_chartest( void )
 {
        const unsigned char *s;
 
-       memset( (char *)chartest, '\0', 256 );
+       chartest = SMB_MALLOC_ARRAY(unsigned char, 256);
+
+       SMB_ASSERT(chartest != NULL);
+       memset(chartest, '\0', 256);
 
        for( s = (const unsigned char *)basechars; *s; s++ ) {
                chartest[*s] |= BASECHAR_MASK;
        }
-
-       ct_initialized = True;
 }
 
 /* ************************************************************************** **
@@ -364,17 +414,19 @@ static void init_chartest( void )
 static bool is_mangled(const char *s, const struct share_params *p)
 {
        char *magic;
+       char magic_char;
 
        magic_char = lp_magicchar(p);
 
-       if( !ct_initialized )
+       if (chartest == NULL) {
                init_chartest();
+       }
 
        magic = strchr_m( s, magic_char );
        while( magic && magic[1] && magic[2] ) {         /* 3 chars, 1st is magic. */
                if( ('.' == magic[3] || '/' == magic[3] || !(magic[3]))          /* Ends with '.' or nul or '/' ?  */
-                               && isbasechar( toupper_ascii(magic[1]) )           /* is 2nd char basechar?  */
-                               && isbasechar( toupper_ascii(magic[2]) ) )         /* is 3rd char basechar?  */
+                               && isbasechar( toupper_m(magic[1]) )           /* is 2nd char basechar?  */
+                               && isbasechar( toupper_m(magic[2]) ) )         /* is 3rd char basechar?  */
                        return( True );                           /* If all above, then true, */
                magic = strchr_m( magic+1, magic_char );      /*    else seek next magic. */
        }
@@ -412,15 +464,15 @@ static void cache_mangled_name( const char mangled_name[13],
 {
        TDB_DATA data_val;
        char mangled_name_key[13];
-       char *s1;
-       char *s2;
+       char *s1 = NULL;
+       char *s2 = NULL;
 
        /* If the cache isn't initialized, give up. */
        if( !tdb_mangled_cache )
                return;
 
        /* Init the string lengths. */
-       safe_strcpy(mangled_name_key, mangled_name, sizeof(mangled_name_key)-1);
+       strlcpy(mangled_name_key, mangled_name, sizeof(mangled_name_key));
 
        /* See if the extensions are unmangled.  If so, store the entry
         * without the extension, thus creating a "group" reverse map.
@@ -428,11 +480,18 @@ static void cache_mangled_name( const char mangled_name[13],
        s1 = strrchr( mangled_name_key, '.' );
        if( s1 && (s2 = strrchr( raw_name, '.' )) ) {
                size_t i = 1;
-               while( s1[i] && (tolower_ascii( s1[i] ) == s2[i]) )
+               while( s1[i] && (tolower_m( s1[i] ) == s2[i]) )
                        i++;
                if( !s1[i] && !s2[i] ) {
                        /* Truncate at the '.' */
                        *s1 = '\0';
+                       /*
+                        * DANGER WILL ROBINSON - this
+                        * is changing a const string via
+                        * an aliased pointer ! Remember to
+                        * put it back once we've used it.
+                        * JRA
+                        */
                        *s2 = '\0';
                }
        }
@@ -444,6 +503,10 @@ static void cache_mangled_name( const char mangled_name[13],
        } else {
                DEBUG(5,("cache_mangled_name: Stored entry %s -> %s\n", mangled_name_key, raw_name));
        }
+       /* Restore the change we made to the const string. */
+       if (s2) {
+               *s2 = '.';
+       }
 }
 
 /* ************************************************************************** **
@@ -470,8 +533,6 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx,
        char *saved_ext = NULL;
        char *s = talloc_strdup(ctx, in);
 
-       magic_char = lp_magicchar(p);
-
        /* If the cache isn't initialized, give up. */
        if(!s || !tdb_mangled_cache ) {
                TALLOC_FREE(s);
@@ -522,11 +583,24 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx,
        return *out ? True : False;
 }
 
+/**
+ Check if a string is in "normal" case.
+**/
+
+static bool strisnormal(const char *s, int case_default)
+{
+       if (case_default == CASE_UPPER)
+               return(!strhaslower(s));
+
+       return(!strhasupper(s));
+}
+
+
 /*****************************************************************************
  Do the actual mangling to 8.3 format.
 *****************************************************************************/
 
-static bool to_8_3(const char *in, char out[13], int default_case)
+static bool to_8_3(char magic_char, const char *in, char out[13], int default_case)
 {
        int csum;
        char *p;
@@ -556,11 +630,14 @@ static bool to_8_3(const char *in, char out[13], int default_case)
        } else
                csum = str_checksum(s);
 
-       strupper_m( s );
+       if (!strupper_m( s )) {
+               SAFE_FREE(s);
+               return false;
+       }
 
        if( p ) {
                if( p == s )
-                       safe_strcpy( extension, "___", 3 );
+                       strlcpy( extension, "___", 4);
                else {
                        *p++ = 0;
                        while( *p && extlen < 3 ) {
@@ -592,7 +669,7 @@ static bool to_8_3(const char *in, char out[13], int default_case)
 
        if( *extension ) {
                out[baselen+3] = '.';
-               safe_strcpy(&out[baselen+4], extension, 3);
+               strlcpy(&out[baselen+4], extension, 4);
        }
 
        SAFE_FREE(s);
@@ -604,15 +681,18 @@ static bool must_mangle(const char *name,
 {
        smb_ucs2_t *name_ucs2 = NULL;
        NTSTATUS status;
-       magic_char = lp_magicchar(p);
+       size_t converted_size;
 
-       if (push_ucs2_allocate(&name_ucs2, name) == (size_t)-1) {
-               DEBUG(0, ("push_ucs2_allocate failed!\n"));
+       if (!push_ucs2_talloc(NULL, &name_ucs2, name, &converted_size)) {
+               DEBUG(0, ("push_ucs2_talloc failed!\n"));
                return False;
        }
        status = is_valid_name(name_ucs2, False, False);
-       SAFE_FREE(name_ucs2);
-       return NT_STATUS_IS_OK(status);
+       TALLOC_FREE(name_ucs2);
+       /* We return true if we *must* mangle, so if it's
+        * a valid name (status == OK) then we must return
+        * false. Bug #6939. */
+       return !NT_STATUS_IS_OK(status);
 }
 
 /*****************************************************************************
@@ -637,26 +717,29 @@ static bool hash_name_to_8_3(const char *in,
                        const struct share_params *p)
 {
        smb_ucs2_t *in_ucs2 = NULL;
+       size_t converted_size;
+       char magic_char;
+
        magic_char = lp_magicchar(p);
 
        DEBUG(5,("hash_name_to_8_3( %s, cache83 = %s)\n", in,
                 cache83 ? "True" : "False"));
 
-       if (push_ucs2_allocate(&in_ucs2, in) == (size_t)-1) {
-               DEBUG(0, ("push_ucs2_allocate failed!\n"));
+       if (!push_ucs2_talloc(NULL, &in_ucs2, in, &converted_size)) {
+               DEBUG(0, ("push_ucs2_talloc failed!\n"));
                return False;
        }
 
        /* If it's already 8.3, just copy. */
        if (NT_STATUS_IS_OK(is_valid_name(in_ucs2, False, False)) &&
                                NT_STATUS_IS_OK(is_8_3_w(in_ucs2, False))) {
-               SAFE_FREE(in_ucs2);
-               safe_strcpy(out, in, 12);
+               TALLOC_FREE(in_ucs2);
+               strlcpy(out, in, 13);
                return True;
        }
 
-       SAFE_FREE(in_ucs2);
-       if (!to_8_3(in, out, default_case)) {
+       TALLOC_FREE(in_ucs2);
+       if (!to_8_3(magic_char, in, out, default_case)) {
                return False;
        }
 
@@ -670,7 +753,7 @@ static bool hash_name_to_8_3(const char *in,
   the following provides the abstraction layer to make it easier
   to drop in an alternative mangling implementation
 */
-static struct mangle_fns mangle_fns = {
+static const struct mangle_fns mangle_hash_fns = {
        mangle_reset,
        is_mangled,
        must_mangle,
@@ -680,7 +763,7 @@ static struct mangle_fns mangle_fns = {
 };
 
 /* return the methods for this mangling implementation */
-struct mangle_fns *mangle_hash_init(void)
+const struct mangle_fns *mangle_hash_init(void)
 {
        mangle_reset();
 
@@ -688,5 +771,5 @@ struct mangle_fns *mangle_hash_init(void)
        tdb_mangled_cache = tdb_open_ex("mangled_cache", 1031, TDB_INTERNAL,
                                (O_RDWR|O_CREAT), 0644, NULL, fast_string_hash);
 
-       return &mangle_fns;
+       return &mangle_hash_fns;
 }