-
Notifications
You must be signed in to change notification settings - Fork 59
Description
Currently the code seems to assume that the value of a member attribute is the DN of a user, and treats the RDN as the uid (even if it happens to be called cn and ldap_uidattr is unset, and even if the LDAP member entity doesn't have the posixAccount objectClass, which I'd say is a bug).
Under rfc2307bis, group members can also be groups; libnss-ldap also supports this.
I think nsscache should perform a transitive closure on the groups it obtains from LDAP. I wrote a shell script that does this, as a proof of concept; it's a bit crude, but it works. It allows arbitrary group nesting (even across groups that don't have the posixGroup objectClass). It doesn't prevent infinite recursion though, and it's very slow.
Ironically, getent group gets it right if nss is configured to use libnss-ldap, so that instead of running nsscache, we could just use getent -- if that wouldn't defeat the purpose of using nss-cache. :)
#!/bin/zsh
#
# Purpose: get groups from LDAP recursively and extract their members.
typeset -A isgroup
typeset -A isposixgroup
typeset -A members # Contains DNs
typeset -A memberuids
typeset -A processed_DNs
typeset -A uids
typeset -A groups
typeset -A memberfilter
GROUPBASE="$1"
GROUPFILTER="$2" # only print groups matching this filter; use objectClass=posixGroup
MEMBERFILTER="$3" # only print members matching this filter; use objectClass=posixAccount
function process_ldapsearch() {
local currentdn=""
local memberdn=""
local memberuid=""
while read attrib val; do case "$attrib $val" in
dn:*)
currentdn="$val"
;;
objectClass:\ groupOfNames)
isgroup[$currentdn]=1
;;
objectClass:\ posixGroup)
isposixgroup[$currentdn]=1
;;
member:\ *)
memberdn="$val"
# Do we already have this member in the current group? If not, add it.
[[ "$members[$currentdn]" =~ \b${memberdn}\b ]] || members[$currentdn]="$members[$currentdn] $memberdn"
[[ "${memberdn/,$GROUPBASE/}" = "${memberdn}" ]] && # Is this member located directly under our specified GROUPBASE dn?
[[ "${memberdn/uid=/}" = "${memberdn}" ]] && { # Process members that are outside our search base and that are not obviously users
[[ $processed_DNs[$memberdn] = "" ]] && ldapsearch -LLL -x -o ldif-wrap=no -b "${memberdn}" | process_ldapsearch
processed_DNs[$memberdn]=1
}
;;
memberUid:*)
memberuid="$val"
# Do we already have this member in the current group? If not, add it.
[[ "$memberuids[$currentdn]" =~ \b$memberuid\b ]] || memberuids[$currentdn]="$memberuids[$currentdn] $memberuid"
;;
*)
;;
esac; done
}
function memberfilter_match() {
[[ -z "$MEMBERFILTER" ]] && return 0
[[ -n "$memberfilter[$1]" ]] && return $memberfilter[$1]
if [[ $(ldapsearch -s base -LLL -x -o ldif-wrap=no -b "$i" "($MEMBERFILTER)" dn) = "dn: $i" ]]; then
memberfilter[$1]="0"
else
memberfilter[$1]="1"
fi
return $memberfilter[$1]
}
function getmembers() {
local group=$1
local -A curmembers
local i
local j
for i in ${(z)members[$group]}; do
if (( ${+isgroup[$i]} || ${+isposixgroup[$i]} )); then
for j in $(getmembers $i); do
curmembers[$j]=1
done
else
if memberfilter_match $i; then
i=${i/uid=/}
i=${i/,*/}
curmembers[$i]=1
fi
fi
done
for i in ${(z)memberuids[$group]}; do
curmembers[$i]=1
done
echo ${(k)curmembers}
return 0;
}
# main()
ldapsearch -LLL -x -b "$GROUPBASE" -o ldif-wrap=no | process_ldapsearch
# Merge the list of posixGroups and groupOfNames groups; the keys of the groups hash will form a list of all groups
for group in ${(k)isposixgroup} ${(k)isgroup}; do
groups[$group]=1
done
for group in ${(k)groups}; do
if [[ -z "$GROUPFILTER" ]] || [[ $(ldapsearch -s base -LLL -x -b "$group" -o ldif-wrap=no "($GROUPFILTER)" dn) = "dn: $group" ]] then
groupname=${group/cn=/}
if ! [[ "${groupname/,$GROUPBASE/}" = "${groupname}" ]]; then # Was this particular group inside our search base or outside it?
groupname=${groupname/,*/}
echo $groupname $(getmembers $group | tr ' ' '\n' | sort | tr '\n' ' ')
fi
fi
done