Open stumbaumr opened 9 years ago
The mumble user ID is a signed 32 bit Integer. The negative space is reserved for unregistered users and 0 is superuser. That leaves you with 2^31-1 numbers minus whatever you exclude for the offset you configure. Imho this is not really enough for collision resistant hashing of usernames in a long living server. With a thousand accounts over the server life-time you are already at p > 10^-4 for a collision.
Are you sure it's 32 bits wide? I was given to believe it was 64. Been working on something like the following:
@lru_cache(maxsize=1000)
def dn_to_uid(dn, max_bits=64, make_hash=hashlib.sha1):
"""
This function maps LDAP distinguished names to Murmur user IDs. It works by computing a hash of the given DN, taking
the unsigned integer value of the least significant 8 bytes of the hash, and then mapping that value into the signed
integer space of the same bit width. SQLite integer columns can hold signed integers up to 8 bytes wide.
Yeah, there's faster ways to do this, but this implementation is portable--which is desirable when you consider the
UIDs returned to Murmur are saved in an external database.
"""
def hash_integer_value():
h = make_hash()
h.update(dn)
return int(h.hexdigest(), 16)
bitmask = 2 ** max_bits - 1
return (hash_integer_value() & bitmask) - (bitmask >> 1)
Don't have the thing working yet because I can't get Ice to load the slice file. Complains: ImportError: No module named Ice_SliceChecksumDict_ice
even though I'm passing it the correct include directory in Ice.loadSlice("-I/usr/share/Ice/slice {}".format(slice_name))
.
Did more testing and found that the UID value must be an unsigned integer less than 31-bits wide. I'm guessing it has to be 31 bits unsigned because the value at a low level is 32 bits signed, but negative UIDs are not valid. Correct DN-to-UID mapping function with decently low risk of collisions:
@lru_cache(maxsize=1000)
def dn_to_uid(dn, max_bits=31, make_hash=hashlib.sha1):
"""
This function maps LDAP distinguished names to Murmur user IDs. It works by computing a hash of the given DN, taking
the unsigned integer value of the least significant 31-bits of the hash.
"""
def hash_integer_value():
h = make_hash()
h.update(dn)
return int(h.hexdigest(), 16)
bitmask = 2 ** max_bits - 1
return hash_integer_value() & bitmask
By the way, the authenticator process would sometimes get killed if we returned a value out of range. When that happens, it allows any user to connect unauthenticated.
Hi, instead of using a possibly unused field in LDAP why do you not just use a hash function to generate a unique number from the unique username_attr instead.
This would also save time when creating user accounts.
Thanks Rainer