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 failure- This 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