|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
# group.py - group entry lookup routines
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# Copyright (C) 2010-2017 Arthur de Jong
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
# modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
# License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
# version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
# Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
# License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
# 02110-1301 USA
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
import logging
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
from ldap.filter import escape_filter_chars
|
|
Packit |
6bd9ab |
import ldap
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
import cache
|
|
Packit |
6bd9ab |
import cfg
|
|
Packit |
6bd9ab |
import common
|
|
Packit |
6bd9ab |
import constants
|
|
Packit |
6bd9ab |
import passwd
|
|
Packit |
6bd9ab |
import search
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def clean(lst):
|
|
Packit |
6bd9ab |
if lst:
|
|
Packit |
6bd9ab |
for i in lst:
|
|
Packit |
6bd9ab |
yield i.replace('\0', '')
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
attmap = common.Attributes(cn='cn',
|
|
Packit |
6bd9ab |
userPassword='"*"',
|
|
Packit |
6bd9ab |
gidNumber='gidNumber',
|
|
Packit |
6bd9ab |
memberUid='memberUid',
|
|
Packit |
6bd9ab |
member='member')
|
|
Packit |
6bd9ab |
filter = '(objectClass=posixGroup)'
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class Search(search.LDAPSearch):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
case_sensitive = ('cn', )
|
|
Packit |
6bd9ab |
limit_attributes = ('cn', 'gidNumber')
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def __init__(self, *args, **kwargs):
|
|
Packit |
6bd9ab |
super(Search, self).__init__(*args, **kwargs)
|
|
Packit |
6bd9ab |
if (cfg.nss_getgrent_skipmembers or
|
|
Packit |
6bd9ab |
'memberUid' in self.parameters or
|
|
Packit |
6bd9ab |
'member' in self.parameters):
|
|
Packit |
6bd9ab |
# set up our own attributes that leave out membership attributes
|
|
Packit |
6bd9ab |
self.attributes = list(self.attributes)
|
|
Packit |
6bd9ab |
if attmap['memberUid'] in self.attributes:
|
|
Packit |
6bd9ab |
self.attributes.remove(attmap['memberUid'])
|
|
Packit |
6bd9ab |
if attmap['member'] in self.attributes:
|
|
Packit |
6bd9ab |
self.attributes.remove(attmap['member'])
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def mk_filter(self):
|
|
Packit |
6bd9ab |
# we still need a custom mk_filter because this is an | query
|
|
Packit |
6bd9ab |
if attmap['member'] and 'memberUid' in self.parameters:
|
|
Packit |
6bd9ab |
memberuid = self.parameters['memberUid']
|
|
Packit |
6bd9ab |
entry = passwd.uid2entry(self.conn, memberuid)
|
|
Packit |
6bd9ab |
if entry:
|
|
Packit |
6bd9ab |
return '(&%s(|(%s=%s)(%s=%s)))' % (
|
|
Packit |
6bd9ab |
self.filter,
|
|
Packit |
6bd9ab |
attmap['memberUid'], escape_filter_chars(memberuid),
|
|
Packit |
6bd9ab |
attmap['member'], escape_filter_chars(entry[0])
|
|
Packit |
6bd9ab |
)
|
|
Packit |
6bd9ab |
if 'gidNumber' in self.parameters:
|
|
Packit |
6bd9ab |
self.parameters['gidNumber'] -= cfg.nss_gid_offset
|
|
Packit |
6bd9ab |
return super(Search, self).mk_filter()
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class Cache(cache.Cache):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
tables = ('group_cache', 'group_member_cache')
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
create_sql = '''
|
|
Packit |
6bd9ab |
CREATE TABLE IF NOT EXISTS `group_cache`
|
|
Packit |
6bd9ab |
( `cn` TEXT PRIMARY KEY,
|
|
Packit |
6bd9ab |
`userPassword` TEXT,
|
|
Packit |
6bd9ab |
`gidNumber` INTEGER NOT NULL UNIQUE,
|
|
Packit |
6bd9ab |
`mtime` TIMESTAMP NOT NULL );
|
|
Packit |
6bd9ab |
CREATE TABLE IF NOT EXISTS `group_member_cache`
|
|
Packit |
6bd9ab |
( `group` TEXT NOT NULL,
|
|
Packit |
6bd9ab |
`memberUid` TEXT NOT NULL,
|
|
Packit |
6bd9ab |
FOREIGN KEY(`group`) REFERENCES `group_cache`(`cn`)
|
|
Packit |
6bd9ab |
ON DELETE CASCADE ON UPDATE CASCADE );
|
|
Packit |
6bd9ab |
CREATE INDEX IF NOT EXISTS `group_member_idx` ON `group_member_cache`(`group`);
|
|
Packit |
6bd9ab |
'''
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
retrieve_sql = '''
|
|
Packit |
6bd9ab |
SELECT `group_cache`.`cn` AS `cn`, `userPassword`, `gidNumber`,
|
|
Packit |
6bd9ab |
`memberUid`, `mtime`
|
|
Packit |
6bd9ab |
FROM `group_cache`
|
|
Packit |
6bd9ab |
LEFT JOIN `group_member_cache`
|
|
Packit |
6bd9ab |
ON `group_member_cache`.`group` = `group_cache`.`cn`
|
|
Packit |
6bd9ab |
'''
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
retrieve_by = dict(
|
|
Packit |
6bd9ab |
memberUid='''
|
|
Packit |
6bd9ab |
`cn` IN (
|
|
Packit |
6bd9ab |
SELECT `a`.`group`
|
|
Packit |
6bd9ab |
FROM `group_member_cache` `a`
|
|
Packit |
6bd9ab |
WHERE `a`.`memberUid` = ?)
|
|
Packit |
6bd9ab |
''',
|
|
Packit |
6bd9ab |
)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
group_by = (0, ) # cn
|
|
Packit |
6bd9ab |
group_columns = (3, ) # memberUid
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class GroupRequest(common.Request):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def write(self, name, passwd, gid, members):
|
|
Packit |
6bd9ab |
self.fp.write_string(name)
|
|
Packit |
6bd9ab |
self.fp.write_string(passwd)
|
|
Packit |
6bd9ab |
self.fp.write_int32(gid)
|
|
Packit |
6bd9ab |
self.fp.write_stringlist(members)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def get_members(self, attributes, members, subgroups, seen):
|
|
Packit |
6bd9ab |
# add the memberUid values
|
|
Packit |
6bd9ab |
for member in clean(attributes['memberUid']):
|
|
Packit |
6bd9ab |
if common.is_valid_name(member):
|
|
Packit |
6bd9ab |
members.add(member)
|
|
Packit |
6bd9ab |
# translate and add the member values
|
|
Packit |
6bd9ab |
if attmap['member']:
|
|
Packit |
6bd9ab |
for memberdn in clean(attributes['member']):
|
|
Packit |
6bd9ab |
if memberdn in seen:
|
|
Packit |
6bd9ab |
continue
|
|
Packit |
6bd9ab |
seen.add(memberdn)
|
|
Packit |
6bd9ab |
member = passwd.dn2uid(self.conn, memberdn)
|
|
Packit |
6bd9ab |
if member and common.is_valid_name(member):
|
|
Packit |
6bd9ab |
members.add(member)
|
|
Packit |
6bd9ab |
elif cfg.nss_nested_groups:
|
|
Packit |
6bd9ab |
subgroups.append(memberdn)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def convert(self, dn, attributes, parameters):
|
|
Packit |
6bd9ab |
# get group names and check against requested group name
|
|
Packit |
6bd9ab |
names = attributes['cn']
|
|
Packit |
6bd9ab |
# get group password
|
|
Packit |
6bd9ab |
try:
|
|
Packit |
6bd9ab |
passwd = attributes['userPassword'][0]
|
|
Packit |
6bd9ab |
except IndexError:
|
|
Packit |
6bd9ab |
passwd = None
|
|
Packit |
6bd9ab |
if not passwd or self.calleruid != 0:
|
|
Packit |
6bd9ab |
passwd = '*'
|
|
Packit |
6bd9ab |
# get group id(s)
|
|
Packit |
6bd9ab |
gids = [int(x) + cfg.nss_gid_offset for x in attributes['gidNumber']]
|
|
Packit |
6bd9ab |
# build member list
|
|
Packit |
6bd9ab |
members = set()
|
|
Packit |
6bd9ab |
subgroups = []
|
|
Packit |
6bd9ab |
seen = set([dn])
|
|
Packit |
6bd9ab |
self.get_members(attributes, members, subgroups, seen)
|
|
Packit |
6bd9ab |
# go over subgroups to find more members
|
|
Packit |
6bd9ab |
while subgroups:
|
|
Packit |
6bd9ab |
memberdn = subgroups.pop(0)
|
|
Packit |
6bd9ab |
for dn2, attributes2 in self.search(self.conn, base=memberdn, scope=ldap.SCOPE_BASE):
|
|
Packit |
6bd9ab |
self.get_members(attributes2, members, subgroups, seen)
|
|
Packit |
6bd9ab |
# actually return the results
|
|
Packit |
6bd9ab |
for name in names:
|
|
Packit |
6bd9ab |
if not common.is_valid_name(name):
|
|
Packit |
6bd9ab |
logging.warning('%s: %s: denied by validnames option', dn,
|
|
Packit |
6bd9ab |
attmap['cn'])
|
|
Packit |
6bd9ab |
else:
|
|
Packit |
6bd9ab |
for gid in gids:
|
|
Packit |
6bd9ab |
yield (name, passwd, gid, members)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class GroupByNameRequest(GroupRequest):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
action = constants.NSLCD_ACTION_GROUP_BYNAME
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def read_parameters(self, fp):
|
|
Packit |
6bd9ab |
name = fp.read_string()
|
|
Packit |
6bd9ab |
common.validate_name(name)
|
|
Packit |
6bd9ab |
return dict(cn=name)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class GroupByGidRequest(GroupRequest):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
action = constants.NSLCD_ACTION_GROUP_BYGID
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def read_parameters(self, fp):
|
|
Packit |
6bd9ab |
return dict(gidNumber=fp.read_int32())
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class GroupByMemberRequest(GroupRequest):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
action = constants.NSLCD_ACTION_GROUP_BYMEMBER
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def read_parameters(self, fp):
|
|
Packit |
6bd9ab |
memberuid = fp.read_string()
|
|
Packit |
6bd9ab |
common.validate_name(memberuid)
|
|
Packit |
6bd9ab |
return dict(memberUid=memberuid)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def get_results(self, parameters):
|
|
Packit |
6bd9ab |
seen = set()
|
|
Packit |
6bd9ab |
for dn, attributes in self.search(self.conn, parameters=parameters):
|
|
Packit |
6bd9ab |
seen.add(dn)
|
|
Packit |
6bd9ab |
for values in self.convert(dn, attributes, parameters):
|
|
Packit |
6bd9ab |
yield values
|
|
Packit |
6bd9ab |
if cfg.nss_nested_groups and attmap['member']:
|
|
Packit |
6bd9ab |
tocheck = list(seen)
|
|
Packit |
6bd9ab |
# find parent groups
|
|
Packit |
6bd9ab |
while tocheck:
|
|
Packit |
6bd9ab |
group = tocheck.pop(0)
|
|
Packit |
6bd9ab |
for dn, attributes in self.search(self.conn, parameters=dict(member=group)):
|
|
Packit |
6bd9ab |
if dn not in seen:
|
|
Packit |
6bd9ab |
seen.add(dn)
|
|
Packit |
6bd9ab |
tocheck.append(dn)
|
|
Packit |
6bd9ab |
for result in self.convert(dn, attributes, parameters):
|
|
Packit |
6bd9ab |
yield result
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def handle_request(self, parameters):
|
|
Packit |
6bd9ab |
# check whether requested user is in nss_initgroups_ignoreusers
|
|
Packit |
6bd9ab |
if parameters['memberUid'] in cfg.nss_initgroups_ignoreusers:
|
|
Packit |
6bd9ab |
# write the final result code to signify empty results
|
|
Packit |
6bd9ab |
self.fp.write_int32(constants.NSLCD_RESULT_END)
|
|
Packit |
6bd9ab |
return
|
|
Packit |
6bd9ab |
return super(GroupByMemberRequest, self).handle_request(parameters)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class GroupAllRequest(GroupRequest):
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
action = constants.NSLCD_ACTION_GROUP_ALL
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def handle_request(self, parameters):
|
|
Packit |
6bd9ab |
if not cfg.nss_disable_enumeration:
|
|
Packit |
6bd9ab |
return super(GroupAllRequest, self).handle_request(parameters)
|