Warning
This is a design page. It was used to design and discuss the initial implementation of the change. However, the state of this document does not necessarily correspond to the current state of the implementation since we do not keep this document up to date with further changes and bug fixes.
Netgroup NSS map support
Overview of Netgroups
Netgroups define network-wide groups used for permission checking when fielding requests for remote mounts, remote logins, and remote shells. For remote mounts, the information in netgroup is used to classify machines; for remote logins and remote shells, it is used to classify users.
Netgroups have a name, and contain one or more of the following members:
The name of another netgroup (supporting nested netgroups)
A three-tuple of (username,hostname,domainname) (parentheses included)
Overview of Netgroups in Name-Service Switch
The interface and behavior of netgroups in libc is a multi-step procedural interface as follows:
The user calls
setnetgrent(netgroupname)
This sets an internal, global iterator to the start of the list of members for the netgroup specified by netgroupname
The user calls
getnetgrent()
repeatedly until it returns failureThis returns one set of username, hostname and domainname for each call, until there are no more associated with the netgroupname
The user calls
endnetgrent()
This cleans up after itself
Internally, libraries providing netgroups in libc must unroll the nested
netgroups so that all results are returned by getnetgrent()
without
additional explicit calls.
Overview of Netgroups in LDAP
Netgroups in LDAP are entries containing the objectClass
nisNetgroup
. This objectClass specifies two options:
- nisNetgroupTriple
A netgroup, specified as a literal string. So it would be
(hostname,username,domainname)
- memberNisNetgroup
The name of another netgroup whose contents need to be rolled into this entry.
Complete example (taken from http://directory.fedoraproject.org/wiki/Howto:Netgroups):
dn: cn=LinuxTeam,ou=Netgroup,dc=example,dc=com
objectClass: nisNetgroup
objectClass: top
cn: LinuxTeam
nisNetgroupTriple: (,frank,example.com)
nisNetgroupTriple: (,jill,example.com)
memberNisNetgroup: QA
memberNisNetgroup: Development
memberNisNetgroup: Operations
description: The Linux Team
SSSD
Overview of approach
Netgroups will be processed similarly to how we handle enumerations in SSSD.
High level
When a
setnetgrent()
request arrives, we will first check the LDB cache and then we will go to the backends to update the cache.Once the cache is readied, we will then construct a result object that we can iterate through to return the result set.
Once the result object is ready, we will reply to the
setgetgrent()
request to notify the calling application that it can start callinggetnetgrent()
The calling application will issue
getnetgrent()
calls until there are no more members available.The calling application will call
endnetgrent()
Lower-level - setnetgrent
Incoming requests to the SSSD will behave similarly to the user and group enumeration code, except that the individual result objects for different netgroup names will be stored in a hash table keyed on the netgroup name.
During processing, if a netgroup contains nested netgroups, we will need to issue a recursive internal
setnetgrent()
request. This means we will need to have a nesting limit (and ideally, loop-detection)The response object must contain the complete unrolled results of all of its child netgroups, so that we do not need to maintain multiple iterators for reading through the children.
The acknowledgement response to the initial
setnetgrent()
request will need to happen only after all nested netgroups have been cached.
Handling nested netgroups
During setnetgrent()
processing, we will convert the results into a
collection object (see libcollection). For each nested group, we will
recurse into setnetgrent()
and create a new collection object that
can be added to the parent collection. In this way, we will be able to
unroll the groups easily.
Later, in getnetgrent()
processing, we will construct the response
from the stored collection object, rather than directly from the
ldb_result object as we do with user and group enumerations.
Public interfaces:
struct tevent_req setnetgrent_send(char *netgroupname, hash_table_t *nesting)
errno_t setnetgrent_recv(tevent_req *req, struct collection **entries)
Internally, the processing for setnetgrent_send()
is expected to
recurse into nested netgroups and add the resulting entries
to its
own list using the col_add_collection_to_collection()
interface with
the col_add_mode_clone
mode.
Tracking nesting limits
The biggest danger in nesting is the risk of loops in the memberships. To resolve this, I propose that we keep track of subrequests in a dhash table. This would behave as follows:
In
setnetgrent_send()
we would first check whether the hash_count of the hash table is equal to the nesting limit. If it is, we will return completion immediately.Next we will check whether netgroupname already exists in the hash table. If it does, then we know we have looped and will simply return completion immediately.
At this point, we will add the current netgroup name to the hash table (with a NULL associated value) and continue processing this request.
In
setnetgrent_recv()
we will remove the requested netgroupname from the hash table and amend the result collection.
This will allow us to protect against both loops and excessive nesting all at once.
Dangling Questions
Is it permissible for a single client to request multiple different netgroups concurrently?
My reading of the documentation for [set|get|end]netgrent leads me to believe that this is not permitted by libc.
Maybe this is too low-level at this time, but is a cleanup task planned?
Netgroups should be handled in the same way that users and groups are handled, so I will probably have to extend the existing cleanup task to also address the netgroups entries in the cache - sgallagh