Esempio n. 1
0
    def _connect(self):
        ld_p = ffi.new("LDAP **")
        err = libldap.ldap_initialize(ld_p, compat._encode_utf8(self._uri))
        handle_ldap_error(err)
        self._ld = ld_p[0]

        version_p = ffi.new("int *")
        version_p[0] = libldap.LDAP_VERSION3
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_PROTOCOL_VERSION, version_p)

        if self._cacertfile is not None:
            libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_X_TLS_CACERTFILE,
                    compat._encode_utf8(self._cacertfile))

        require_cert_p = ffi.new("int *")
        require_cert_p[0] = self._require_cert
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_X_TLS_REQUIRE_CERT,
                require_cert_p);

        # For TLS options to take effect, a context refresh seems to be needed.
        newctx_p = ffi.new("int *")
        newctx_p[0] = 0
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_X_TLS_NEWCTX, newctx_p)

        timelimit_p = ffi.new("int *")
        timelimit_p[0] = self._timelimit
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_TIMELIMIT, timelimit_p)

        err = libldap.ldap_simple_bind_s(self._ld,
                compat._encode_utf8(self._bind_dn),
                compat._encode_utf8(self._bind_password))
        handle_ldap_error(err)

        self._fetch_attribute_types()
Esempio n. 2
0
    def _connect(self):
        ld_p = ffi.new("LDAP **")
        err = libldap.ldap_initialize(ld_p, compat._encode_utf8(self._uri))
        handle_ldap_error(err)
        self._ld = ld_p[0]

        version_p = ffi.new("int *")
        version_p[0] = libldap.LDAP_VERSION3
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_PROTOCOL_VERSION, version_p)

        if self._cacertfile is not None:
            libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_X_TLS_CACERTFILE,
                    compat._encode_utf8(self._cacertfile))

        require_cert_p = ffi.new("int *")
        require_cert_p[0] = self._require_cert
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_X_TLS_REQUIRE_CERT,
                require_cert_p);

        # For TLS options to take effect, a context refresh seems to be needed.
        newctx_p = ffi.new("int *")
        newctx_p[0] = 0
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_X_TLS_NEWCTX, newctx_p)

        timelimit_p = ffi.new("int *")
        timelimit_p[0] = self._timelimit
        libldap.ldap_set_option(self._ld, libldap.LDAP_OPT_TIMELIMIT, timelimit_p)

        err = libldap.ldap_simple_bind_s(self._ld,
                compat._encode_utf8(self._bind_dn),
                compat._encode_utf8(self._bind_password))
        handle_ldap_error(err)

        if self._enable_attribute_type_mapping:
            self._fetch_attribute_types()
Esempio n. 3
0
    def set_password(self, entry, password):
        """Set the bind password for an entry.

        :param entry: The entry to set the password for.
        :type entry: ldapom.LDAPEntry
        :param password: The password to set.
        :type password: str
        """
        password_p = ffi.new("char[]", compat._encode_utf8(password))
        password_berval = libldap.ber_bvstr(password_p)
        entry_dn_p = ffi.new("char[]", compat._encode_utf8(entry.dn))
        entry_dn_berval = libldap.ber_bvstr(entry_dn_p)

        err = libldap.ldap_passwd_s(self._ld,
                entry_dn_berval,
                ffi.NULL,
                password_berval, password_berval,
                ffi.NULL, ffi.NULL)
        handle_ldap_error(err)
Esempio n. 4
0
    def set_password(self, entry, password):
        """Set the bind password for an entry.

        :param entry: The entry to set the password for.
        :type entry: ldapom.LDAPEntry
        :param password: The password to set.
        :type password: str
        """
        if self._read_only:
            raise error.LDAPomReadOnlyError
        password_p = ffi.new("char[]", compat._encode_utf8(password))
        password_berval = libldap.ber_bvstr(password_p)
        entry_dn_p = ffi.new("char[]", compat._encode_utf8(entry.dn))
        entry_dn_berval = libldap.ber_bvstr(entry_dn_p)

        err = libldap.ldap_passwd_s(self._ld,
                entry_dn_berval,
                ffi.NULL,
                password_berval, password_berval,
                ffi.NULL, ffi.NULL)
        handle_ldap_error(err)
Esempio n. 5
0
    def save(self, entry):
        """Save the given entry and its attribute values to the LDAP server.

        :param entry: The entry to save.
        :type entry: ldapom.LDAPEntry
        """
        entry_exists = entry.exists()
        # Refuse to save if attributes have not been fetched or set explicitly.
        if entry._attributes is None:
            raise error.LDAPomError("Cannot save without attributes "
                    "previously fetched or set.")

        if entry_exists:
            assert entry._fetched_attributes is not None
            changed_attributes = entry._attributes - entry._fetched_attributes
            # Deleted attributes are represented as empty attributes to the LDAP server.
            deleted_attribute_names = (frozenset(a.name for a in entry._fetched_attributes)
                    - frozenset(a.name for a in entry._attributes))
            for deleted_name in deleted_attribute_names:
                deleted_attribute_type = self.get_attribute_type(deleted_name)
                changed_attributes.add(deleted_attribute_type(deleted_name))
        else:
            # Don't try to save empty attributes as this fails if the entry does
            # not exist on the server yet.
            changed_attributes = set(filter(lambda attr: len(attr._values) > 0, entry._attributes))

        # Keep around references to pointers to owned memory with data that is
        # still needed.
        prevent_garbage_collection = []

        mods = ffi.new("LDAPMod*[{}]".format(len(changed_attributes) + 1))
        for i, attribute in enumerate(changed_attributes):
            mod = ffi.new("LDAPMod *")
            prevent_garbage_collection.append(mod)

            mod.mod_op = libldap.LDAP_MOD_REPLACE

            mod_type = ffi.new("char[]", compat._encode_utf8(attribute.name))
            prevent_garbage_collection.append(mod_type)
            mod.mod_type = mod_type

            modv_strvals = ffi.new("char*[{}]".format(len(attribute._values) + 1))
            prevent_garbage_collection.append(modv_strvals)
            for j, value in enumerate(attribute._get_ldap_values()):
                strval = ffi.new("char[]", value)
                prevent_garbage_collection.append(strval)
                modv_strvals[j] = strval
            modv_strvals[len(attribute._values)] = ffi.NULL
            mod.mod_vals = {"modv_strvals": modv_strvals}

            mods[i] = mod
        mods[len(changed_attributes)] = ffi.NULL

        if entry_exists:
            err = libldap.ldap_modify_ext_s(self._ld,
                    compat._encode_utf8(entry.dn),
                    mods,
                    ffi.NULL, ffi.NULL)
        else:
            err = libldap.ldap_add_ext_s(self._ld,
                    compat._encode_utf8(entry.dn),
                    mods,
                    ffi.NULL, ffi.NULL)
        handle_ldap_error(err)

        entry._fetched_attributes = copy.deepcopy(entry._attributes)
Esempio n. 6
0
    def _raw_search(self, search_filter=None, retrieve_attributes=None,
            base=None, scope=libldap.LDAP_SCOPE_SUBTREE):
        """
        Raw wrapper around OpenLDAP ldap_search_ext_s.

        :param search_filter: Filter expression to use. OpenLDAP default used
            if None is given.
        :type search_filter: List of str
        :param retrieve_attributes: List of attributes to retrieve. If None is
            given, all are retrieved.
        :type retrieve_attributes: List of str
        :param base: Search base for the query.
        :type base: str
        :param scope: The search scope in the LDAP tree
        """
        search_result_p = ffi.new("LDAPMessage **")

        # Keep around references to pointers to owned memory with data that is
        # still needed.
        prevent_garbage_collection = []

        if retrieve_attributes is not None:
            attrs_p = ffi.new("char*[{}]".format(len(retrieve_attributes) + 1))
            for i, a in enumerate(retrieve_attributes):
                attr_p = ffi.new("char[]", compat._encode_utf8(a))
                prevent_garbage_collection.append(attr_p)
                attrs_p[i] = attr_p
            attrs_p[len(retrieve_attributes)] = ffi.NULL
        else:
            attrs_p = ffi.NULL

        err = libldap.ldap_search_ext_s(
                self._ld,
                compat._encode_utf8(base or self._base),
                scope,
                (compat._encode_utf8(search_filter)
                    if search_filter is not None else ffi.NULL),
                attrs_p,
                0,
                ffi.NULL, ffi.NULL,
                ffi.NULL, # TODO: Implement timeouts
                0,#libldap.LDAP_NO_LIMIT,
                search_result_p)
        handle_ldap_error(err)
        search_result = search_result_p[0]

        current_entry = libldap.ldap_first_entry(self._ld, search_result)
        while current_entry != ffi.NULL:
            dn = ffi.string(libldap.ldap_get_dn(self._ld, current_entry))
            attribute_dict = {}

            ber_p = ffi.new("BerElement **")
            current_attribute = libldap.ldap_first_attribute(self._ld,
                    current_entry, ber_p)
            while current_attribute != ffi.NULL:
                current_attribute_str = ffi.string(current_attribute)
                attribute_dict[current_attribute_str] = []

                values_p = libldap.ldap_get_values(self._ld, current_entry,
                        current_attribute)
                for i in range(0, libldap.ldap_count_values(values_p)):
                    attribute_dict[current_attribute_str].append(
                            ffi.string(values_p[i]))

                current_attribute = libldap.ldap_next_attribute(self._ld,
                        current_entry, ber_p[0])
            # TODO: Call ber_free on ber_p[0]

            yield (dn, attribute_dict)
            current_entry = libldap.ldap_next_entry(self._ld, current_entry)
Esempio n. 7
0
    def save(self, entry):
        """Save the given entry and its attribute values to the LDAP server.

        :param entry: The entry to save.
        :type entry: ldapom.LDAPEntry
        """
        if self._read_only:
            raise error.LDAPomReadOnlyError
        entry_exists = entry.exists()
        # Refuse to save if attributes have not been fetched or set explicitly.
        if entry._attributes is None:
            raise error.LDAPomError("Cannot save without attributes "
                    "previously fetched or set.")

        if entry_exists:
            assert entry._fetched_attributes is not None
            changed_attributes = entry._attributes - entry._fetched_attributes
            # Deleted attributes are represented as empty attributes to the LDAP server.
            deleted_attribute_names = (frozenset(a.name for a in entry._fetched_attributes)
                    - frozenset(a.name for a in entry._attributes))
            for deleted_name in deleted_attribute_names:
                deleted_attribute_type = self.get_attribute_type(deleted_name)
                changed_attributes.add(deleted_attribute_type(deleted_name))
        else:
            # Don't try to save empty attributes as this fails if the entry does
            # not exist on the server yet.
            changed_attributes = set(filter(lambda attr: len(attr._values) > 0, entry._attributes))

        # Don't try to save an empty modification set
        if not changed_attributes:
            return

        # Keep around references to pointers to owned memory with data that is
        # still needed.
        prevent_garbage_collection = []

        mods = ffi.new("LDAPMod*[{}]".format(len(changed_attributes) + 1))
        for i, attribute in enumerate(changed_attributes):
            mod = ffi.new("LDAPMod *")
            prevent_garbage_collection.append(mod)

            mod.mod_op = libldap.LDAP_MOD_REPLACE | libldap.LDAP_MOD_BVALUES

            mod_type = ffi.new("char[]", compat._encode_utf8(attribute.name))
            prevent_garbage_collection.append(mod_type)
            mod.mod_type = mod_type

            modv_bvals = ffi.new("BerValue*[{}]".format(len(attribute._values) + 1))
            prevent_garbage_collection.append(modv_bvals)
            for j, value in enumerate(attribute._get_ldap_values()):
                modv_berval = ffi.new("BerValue *")
                prevent_garbage_collection.append(modv_berval)
                modv_berval.bv_len = len(value)
                bval = ffi.new("char[]", len(value))
                prevent_garbage_collection.append(bval)
                ffi.buffer(bval)[:] = value
                modv_berval.bv_val = bval
                modv_bvals[j] = modv_berval
            modv_bvals[len(attribute._values)] = ffi.NULL
            mod.mod_vals = {"modv_bvals": modv_bvals}
            mods[i] = mod
        mods[len(changed_attributes)] = ffi.NULL

        if entry_exists:
            err = libldap.ldap_modify_ext_s(self._ld,
                    compat._encode_utf8(entry.dn),
                    mods,
                    ffi.NULL, ffi.NULL)
        else:
            err = libldap.ldap_add_ext_s(self._ld,
                    compat._encode_utf8(entry.dn),
                    mods,
                    ffi.NULL, ffi.NULL)
        handle_ldap_error(err)

        entry._fetched_attributes = copy.deepcopy(entry._attributes)
Esempio n. 8
0
    def _raw_search(self, search_filter=None, retrieve_attributes=None,
            base=None, scope=libldap.LDAP_SCOPE_SUBTREE,
            retrieve_operational_attributes=False):
        """
        Raw wrapper around OpenLDAP ldap_search_ext_s.

        :param search_filter: Filter expression to use. OpenLDAP default used
            if None is given.
        :type search_filter: List of str
        :param retrieve_attributes: List of attributes to retrieve. If None is
            given, all user attributes are retrieved.
        :type retrieve_attributes: List of str
        :param base: Search base for the query.
        :type base: str
        :param scope: The search scope in the LDAP tree
        :param retrieve_operational_attributes: Retrieve operational attributes of entries in
            addition to user attributes if retrieve_attributes is not set.
        """
        search_result_p = ffi.new("LDAPMessage **")

        # Keep around references to pointers to owned memory with data that is
        # still needed.
        prevent_garbage_collection = []

        if retrieve_attributes is None:
            retrieve_attributes = [
                compat._decode_utf8(ffi.string(libldap.LDAP_ALL_USER_ATTRIBUTES))]
            if retrieve_operational_attributes:
                retrieve_attributes.append(
                    compat._decode_utf8(ffi.string(libldap.LDAP_ALL_OPERATIONAL_ATTRIBUTES)))

        attrs_p = ffi.new("char*[{}]".format(len(retrieve_attributes) + 1))
        for i, a in enumerate(retrieve_attributes):
            attr_p = ffi.new("char[]", compat._encode_utf8(a))
            prevent_garbage_collection.append(attr_p)
            attrs_p[i] = attr_p
        attrs_p[len(retrieve_attributes)] = ffi.NULL

        err = libldap.ldap_search_ext_s(
                self._ld,
                compat._encode_utf8(base or self._base),
                scope,
                (compat._encode_utf8(search_filter)
                    if search_filter is not None else ffi.NULL),
                attrs_p,
                0,
                ffi.NULL, ffi.NULL,
                ffi.NULL, # TODO: Implement timeouts
                0,#libldap.LDAP_NO_LIMIT,
                search_result_p)
        handle_ldap_error(err)
        search_result = search_result_p[0]

        current_entry = libldap.ldap_first_entry(self._ld, search_result)
        while current_entry != ffi.NULL:
            dn = ffi.string(libldap.ldap_get_dn(self._ld, current_entry))
            attribute_dict = {}

            ber_p = ffi.new("BerElement **")
            current_attribute = libldap.ldap_first_attribute(self._ld,
                    current_entry, ber_p)
            while current_attribute != ffi.NULL:
                current_attribute_str = ffi.string(current_attribute)
                attribute_dict[current_attribute_str] = []

                values_p = libldap.ldap_get_values_len(self._ld, current_entry,
                        current_attribute)
                for i in range(0, libldap.ldap_count_values_len(values_p)):
                    val = ffi.buffer(values_p[i].bv_val, values_p[i].bv_len)[:]
                    attribute_dict[current_attribute_str].append(val)

                libldap.ldap_memfree(current_attribute)
                current_attribute = libldap.ldap_next_attribute(self._ld,
                        current_entry, ber_p[0])
            libldap.ber_free(ber_p[0], 0)

            yield (dn, attribute_dict)
            current_entry = libldap.ldap_next_entry(self._ld, current_entry)

        libldap.ldap_msgfree(search_result)