mumble-voip / mumble-scripts

Mumble scripts is a place for gathering various scripts written for the Mumble VoIP application.
https://www.mumble.info
48 stars 33 forks source link

LDAP authenticator: number_attr - use hash(username_attr) instead #10

Open stumbaumr opened 9 years ago

stumbaumr commented 9 years ago

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

hacst commented 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.

IronSavior commented 4 years ago

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)).

IronSavior commented 4 years ago

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
IronSavior commented 4 years ago

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.