2010-06-07 20:10:28 +04:00
# Copyright Jelmer Vernooij 2008
#
# Based on the original in EJS:
# Copyright Andrew Tridgell 2005
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import samba . getopt as options
from samba . netcmd import Command , SuperCommand , CommandError , Option
import ldb
2022-02-10 07:14:56 +03:00
from samba . ndr import ndr_pack , ndr_unpack
2012-05-02 00:17:33 +04:00
from samba . dcerpc import security
2010-06-07 20:10:28 +04:00
from samba . auth import system_session
from samba . samdb import SamDB
from samba . dsdb import (
2018-04-26 06:59:06 +03:00
ATYPE_SECURITY_GLOBAL_GROUP ,
2022-02-10 07:14:56 +03:00
DS_GUID_USERS_CONTAINER ,
2013-03-11 23:47:19 +04:00
GTYPE_SECURITY_BUILTIN_LOCAL_GROUP ,
2010-06-07 20:10:28 +04:00
GTYPE_SECURITY_DOMAIN_LOCAL_GROUP ,
GTYPE_SECURITY_GLOBAL_GROUP ,
GTYPE_SECURITY_UNIVERSAL_GROUP ,
GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP ,
GTYPE_DISTRIBUTION_GLOBAL_GROUP ,
GTYPE_DISTRIBUTION_UNIVERSAL_GROUP ,
2022-02-10 07:14:56 +03:00
SYSTEM_FLAG_DISALLOW_DELETE ,
SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE ,
SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME ,
2020-12-23 17:51:12 +03:00
UF_ACCOUNTDISABLE ,
2010-06-07 20:10:28 +04:00
)
2018-10-18 07:08:32 +03:00
from collections import defaultdict
2019-03-13 23:20:29 +03:00
from subprocess import check_call , CalledProcessError
2022-02-10 07:14:56 +03:00
from samba . common import get_bytes , normalise_int32
2019-03-13 23:20:29 +03:00
import os
import tempfile
2019-11-25 16:13:37 +03:00
from . import common
2010-06-07 20:10:28 +04:00
2013-03-11 23:47:19 +04:00
security_group = dict ( { " Builtin " : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP ,
" Domain " : GTYPE_SECURITY_DOMAIN_LOCAL_GROUP ,
" Global " : GTYPE_SECURITY_GLOBAL_GROUP ,
" Universal " : GTYPE_SECURITY_UNIVERSAL_GROUP } )
distribution_group = dict ( { " Domain " : GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP ,
" Global " : GTYPE_DISTRIBUTION_GLOBAL_GROUP ,
" Universal " : GTYPE_DISTRIBUTION_UNIVERSAL_GROUP } )
2010-06-07 20:10:28 +04:00
class cmd_group_add ( Command ) :
2012-10-08 14:32:58 +04:00
""" Creates a new AD group.
2011-11-10 01:33:37 +04:00
2020-08-26 16:07:16 +03:00
This command adds a new Active Directory group . The groupname specified on the command is a unique sAMAccountName .
2011-11-10 01:33:37 +04:00
2020-08-26 16:07:16 +03:00
An Active Directory group may contain user and computer accounts as well as other groups . An administrator adds a new group and adds members to that group so they can be managed as a single entity . This helps to simplify security and system administration .
2011-11-10 01:33:37 +04:00
Groups may also be used to establish email distribution lists , using - - group - type = Distribution .
Groups are located in domains in organizational units ( OUs ) . The group ' s scope is a characteristic of the group that designates the extent to which the group is applied within the domain tree or forest.
The group location ( OU ) , type ( security or distribution ) and scope may all be specified on the samba - tool command when the group is created .
The command may be run from the root userid or another authorized userid . The
- H or - - URL = option can be used to execute the command on a remote server .
Example1 :
samba - tool group add Group1 - H ldap : / / samba . samdom . example . com - - description = ' Simple group '
Example1 adds a new group with the name Group1 added to the Users container on a remote LDAP server . The - U parameter is used to pass the userid and password of a user that exists on the remote server and is authorized to issue the command on that server . It defaults to the security type and global scope .
Example2 :
sudo samba - tool group add Group2 - - group - type = Distribution
Example2 adds a new distribution group to the local server . The command is run under root using the sudo command .
2014-10-18 02:34:35 +04:00
Example3 :
2015-01-24 17:59:40 +03:00
samba - tool group add Group3 - - nis - domain = samdom - - gid - number = 12345
2014-10-18 02:34:35 +04:00
Example3 adds a new RFC2307 enabled group for NIS domain samdom and GID 12345 ( both options are required to enable this feature ) .
2011-11-10 01:33:37 +04:00
"""
2010-06-07 20:10:28 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <groupname> [options] "
2010-06-07 20:10:28 +04:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-06-07 20:10:28 +04:00
takes_options = [
2011-07-25 19:56:10 +04:00
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
2010-06-07 20:10:28 +04:00
Option ( " --groupou " ,
2018-07-30 09:16:12 +03:00
help = " Alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created " ,
type = str ) ,
2010-06-07 20:10:28 +04:00
Option ( " --group-scope " , type = " choice " , choices = [ " Domain " , " Global " , " Universal " ] ,
2018-07-30 09:16:12 +03:00
help = " Group scope (Domain | Global | Universal) " ) ,
2010-06-07 20:10:28 +04:00
Option ( " --group-type " , type = " choice " , choices = [ " Security " , " Distribution " ] ,
2018-07-30 09:16:12 +03:00
help = " Group type (Security | Distribution) " ) ,
2010-06-07 20:10:28 +04:00
Option ( " --description " , help = " Group ' s description " , type = str ) ,
Option ( " --mail-address " , help = " Group ' s email address " , type = str ) ,
2023-11-21 02:40:03 +03:00
Option ( " --notes " , help = " Group ' s notes " , type = str ) ,
2014-10-18 02:34:35 +04:00
Option ( " --gid-number " , help = " Group ' s Unix/RFC2307 GID number " , type = int ) ,
Option ( " --nis-domain " , help = " SFU30 NIS Domain " , type = str ) ,
2022-02-10 07:14:56 +03:00
Option ( " --special " , help = " Add a special predefined group " , action = " store_true " , default = False ) ,
2010-06-07 20:10:28 +04:00
]
takes_args = [ " groupname " ]
def run ( self , groupname , credopts = None , sambaopts = None ,
versionopts = None , H = None , groupou = None , group_scope = None ,
2022-02-10 07:14:56 +03:00
group_type = None , description = None , mail_address = None , notes = None , gid_number = None , nis_domain = None ,
special = False ) :
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if ( group_type or " Security " ) == " Security " :
2012-07-03 05:27:21 +04:00
gtype = security_group . get ( group_scope , GTYPE_SECURITY_GLOBAL_GROUP )
2010-06-07 20:10:28 +04:00
else :
2012-07-03 05:27:21 +04:00
gtype = distribution_group . get ( group_scope , GTYPE_DISTRIBUTION_GLOBAL_GROUP )
2010-06-07 20:10:28 +04:00
2014-10-18 02:34:35 +04:00
if ( gid_number is None and nis_domain is not None ) or ( gid_number is not None and nis_domain is None ) :
raise CommandError ( ' Both --gid-number and --nis-domain have to be set for a RFC2307-enabled group. Operation cancelled. ' )
2010-06-07 20:10:28 +04:00
lp = sambaopts . get_loadparm ( )
2010-12-08 00:20:54 +03:00
creds = credopts . get_credentials ( lp , fallback_machine = True )
2010-06-07 20:10:28 +04:00
try :
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
2022-02-10 07:14:56 +03:00
except Exception as e :
# FIXME: catch more specific exception
raise CommandError ( f ' Failed to add group " { groupname } " ' , e )
if special :
invalid_option = None
if group_scope is not None :
invalid_option = ' group-scope '
elif group_type is not None :
invalid_option = ' group-type '
elif description is not None :
invalid_option = ' description '
elif mail_address is not None :
invalid_option = ' mail-address '
elif notes is not None :
invalid_option = ' notes '
elif gid_number is not None :
invalid_option = ' gid-number '
elif nis_domain is not None :
invalid_option = ' nis-domain '
if invalid_option is not None :
raise CommandError ( f ' Superfluous option -- { invalid_option } '
f ' specified with --special ' )
if not samdb . am_pdc ( ) :
raise CommandError ( ' Adding special groups is only permitted '
' against the PDC! ' )
special_groups = {
# On Windows, this group is added automatically when the PDC
# role is held by a DC running Windows Server 2012 R2 or later.
# https://docs.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group#BKMK_Requirements
' Protected Users ' . lower ( ) : (
' Protected Users ' ,
GTYPE_SECURITY_GLOBAL_GROUP ,
security . DOMAIN_RID_PROTECTED_USERS ,
' Members of this group are afforded additional '
' protections against authentication security threats ' ) ,
}
special_group = special_groups . get ( groupname . lower ( ) )
if special_group is None :
raise CommandError ( f ' Unknown special group " { groupname } " . ' )
groupname , gtype , rid , description = special_group
group_type = normalise_int32 ( gtype )
group_dn = samdb . get_default_basedn ( )
if gtype == GTYPE_SECURITY_GLOBAL_GROUP :
object_sid = security . dom_sid (
f ' { samdb . get_domain_sid ( ) } - { rid } ' )
system_flags = None
if not groupou :
group_dn = samdb . get_wellknown_dn ( group_dn ,
DS_GUID_USERS_CONTAINER )
elif gtype == GTYPE_SECURITY_BUILTIN_LOCAL_GROUP :
object_sid = security . dom_sid ( f ' S-1-5-32- { rid } ' )
system_flags = ( SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE |
SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME |
SYSTEM_FLAG_DISALLOW_DELETE )
2022-09-30 01:50:30 +03:00
if not groupou :
try :
group_dn . add_child ( ' CN=Builtin ' )
except ldb . LdbError :
raise RuntimeError ( ' Error getting Builtin objects DN ' )
2022-02-10 07:14:56 +03:00
else :
raise RuntimeError ( f ' Unknown group type { gtype } ' )
2022-09-30 01:50:30 +03:00
if groupou :
try :
group_dn . add_child ( groupou )
except ldb . LdbError :
raise CommandError ( f ' Invalid group OU " { groupou } " ' )
2022-02-10 07:14:56 +03:00
2022-09-30 01:50:30 +03:00
try :
group_dn . add_child ( f ' CN= { groupname } ' )
except ldb . LdbError :
2022-02-10 07:14:56 +03:00
raise CommandError ( f ' Invalid group name " { groupname } " ' )
msg = {
' dn ' : group_dn ,
' sAMAccountName ' : groupname ,
' objectClass ' : ' group ' ,
' groupType ' : group_type ,
' description ' : description ,
' objectSid ' : ndr_pack ( object_sid ) ,
' isCriticalSystemObject ' : ' TRUE ' ,
}
if system_flags is not None :
msg [ ' systemFlags ' ] = system_flags
try :
samdb . add ( msg , controls = [ ' relax:0 ' ] )
except ldb . LdbError as e :
num , estr = e . args
if num == ldb . ERR_CONSTRAINT_VIOLATION :
try :
res = samdb . search (
expression = f ' (objectSid= { object_sid } ) ' ,
attrs = [ ' sAMAccountName ' ] )
except ldb . LdbError :
raise CommandError (
f ' Failed to add group " { groupname } " ' , e )
if len ( res ) != 1 :
raise CommandError (
f ' Failed to add group " { groupname } " ' , e )
name = res [ 0 ] . get ( ' sAMAccountName ' , idx = 0 )
if name :
with_name = f ' with name " { name } " '
else :
with_name = ' '
raise CommandError (
f ' Failed to add group " { groupname } " - Special group '
f ' already exists { with_name } at " { res [ 0 ] . dn } " . ' )
elif num == ldb . ERR_ENTRY_ALREADY_EXISTS :
try :
res = samdb . search ( base = group_dn ,
scope = ldb . SCOPE_BASE ,
attrs = [ ' sAMAccountName ' ,
' objectSid ' ,
' groupType ' ] )
except ldb . LdbError :
try :
res = samdb . search (
expression = f ' (sAMAccountName= { groupname } ) ' ,
attrs = [ ' sAMAccountName ' ,
' objectSid ' ,
' groupType ' ] )
except ldb . LdbError :
raise CommandError (
f ' Failed to add group " { groupname } " ' , e )
if len ( res ) != 1 :
raise CommandError (
f ' Failed to add group " { groupname } " ' , e )
got_name = res [ 0 ] . get ( ' sAMAccountName ' , idx = 0 )
if got_name :
named = f ' named " { got_name } " '
else :
named = ' with no name '
got_group_type = res [ 0 ] . get ( ' groupType ' ,
idx = 0 ) . decode ( ' utf-8 ' )
if group_type != got_group_type :
raise CommandError (
f ' Failed to add group " { groupname } " - An object '
f ' { named } at " { res [ 0 ] . dn } " already exists, but it '
f ' is not a security group. Rename or remove this '
f ' existing object before attempting to add this '
f ' special group. ' )
sid = res [ 0 ] . get ( ' objectSid ' , idx = 0 )
if sid is None :
raise CommandError (
f ' Failed to add group " { groupname } " - A security '
f ' group { named } at " { res [ 0 ] . dn } " already exists, '
f ' but it lacks a SID. Rename or remove this '
f ' existing object before attempting to add this '
f ' special group. ' )
else :
sid = ndr_unpack ( security . dom_sid , sid )
if sid == object_sid :
raise CommandError (
f ' Failed to add group " { groupname } " - The '
f ' security group { named } at " { res [ 0 ] . dn } " '
f ' already exists. ' )
else :
raise CommandError (
f ' Failed to add group " { groupname } " - A '
f ' security group { named } at " { res [ 0 ] . dn } " '
f ' already exists, but it has the wrong SID, '
f ' and will not function as expected. Rename '
f ' or remove this existing object before '
f ' attempting to add this special group. ' )
else :
raise CommandError ( f ' Failed to add group " { groupname } " ' , e )
else :
self . outf . write ( f ' Added group { groupname } \n ' )
return
try :
2018-07-30 09:19:21 +03:00
samdb . newgroup ( groupname , groupou = groupou , grouptype = gtype ,
2018-07-30 09:16:12 +03:00
description = description , mailaddress = mail_address , notes = notes ,
gidnumber = gid_number , nisdomain = nis_domain )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-10-13 02:36:44 +04:00
# FIXME: catch more specific exception
2020-08-26 16:07:16 +03:00
raise CommandError ( ' Failed to add group " %s " ' % groupname , e )
2011-10-13 02:36:44 +04:00
self . outf . write ( " Added group %s \n " % groupname )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
2010-06-07 20:10:28 +04:00
class cmd_group_delete ( Command ) :
2012-10-08 14:32:58 +04:00
""" Deletes an AD group.
2011-11-10 01:33:37 +04:00
The command deletes an existing AD group from the Active Directory domain . The groupname specified on the command is the sAMAccountName .
Deleting a group is a permanent operation . When a group is deleted , all permissions and rights that users in the group had inherited from the group account are deleted as well .
The command may be run from the root userid or another authorized userid . The - H or - - URL option can be used to execute the command on a remote server .
Example1 :
samba - tool group delete Group1 - H ldap : / / samba . samdom . example . com - Uadministrator % passw0rd
Example1 shows how to delete an AD group from a remote LDAP server . The - U parameter is used to pass the userid and password of a user that exists on the remote server and is authorized to issue the command on that server .
Example2 :
sudo samba - tool group delete Group2
Example2 deletes group Group2 from the local server . The command is run under root using the sudo command .
"""
2010-06-07 20:10:28 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <groupname> [options] "
2010-06-07 20:10:28 +04:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-06-07 20:10:28 +04:00
takes_options = [
2011-07-25 19:56:10 +04:00
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
2010-06-07 20:10:28 +04:00
]
takes_args = [ " groupname " ]
def run ( self , groupname , credopts = None , sambaopts = None , versionopts = None , H = None ) :
lp = sambaopts . get_loadparm ( )
2010-12-08 00:20:54 +03:00
creds = credopts . get_credentials ( lp , fallback_machine = True )
2016-09-28 21:28:23 +03:00
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
filter = ( " (&(sAMAccountName= %s )(objectClass=group)) " %
2020-02-26 15:55:01 +03:00
ldb . binary_encode ( groupname ) )
2010-06-07 20:10:28 +04:00
try :
2016-09-28 21:28:23 +03:00
res = samdb . search ( base = samdb . domain_dn ( ) ,
scope = ldb . SCOPE_SUBTREE ,
expression = filter ,
attrs = [ " dn " ] )
group_dn = res [ 0 ] . dn
except IndexError :
raise CommandError ( ' Unable to find group " %s " ' % ( groupname ) )
try :
samdb . delete ( group_dn )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-10-13 02:36:44 +04:00
# FIXME: catch more specific exception
2010-11-29 06:15:57 +03:00
raise CommandError ( ' Failed to remove group " %s " ' % groupname , e )
2011-10-13 02:36:44 +04:00
self . outf . write ( " Deleted group %s \n " % groupname )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
2010-06-07 20:10:28 +04:00
class cmd_group_add_members ( Command ) :
2012-10-08 14:32:58 +04:00
""" Add members to an AD group.
2011-11-10 01:33:37 +04:00
2013-05-15 20:15:18 +04:00
This command adds one or more members to an existing Active Directory group . The command accepts one or more group member names separated by commas . A group member may be a user or computer account or another Active Directory group .
2011-11-10 01:33:37 +04:00
When a member is added to a group the member may inherit permissions and rights from the group . Likewise , when permission or rights of a group are changed , the changes may reflect in the members through inheritance .
2017-06-07 17:57:53 +03:00
The member names specified on the command must be the sAMaccountName .
2011-11-10 01:33:37 +04:00
Example1 :
samba - tool group addmembers supergroup Group1 , Group2 , User1 - H ldap : / / samba . samdom . example . com - Uadministrator % passw0rd
Example1 shows how to add two groups , Group1 and Group2 and one user account , User1 , to the existing AD group named supergroup . The command will be run on a remote server specified with the - H . The - U parameter is used to pass the userid and password of a user authorized to issue the command on the remote server .
Example2 :
sudo samba - tool group addmembers supergroup User2
Example2 shows how to add a single user account , User2 , to the supergroup AD group . It uses the sudo command to run as root when issuing the command .
"""
2010-06-07 20:10:28 +04:00
2019-12-17 18:26:23 +03:00
synopsis = " % prog <groupname> (<listofmembers>]|--member-dn=<member-dn>) [options] "
2010-06-07 20:10:28 +04:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-06-07 20:10:28 +04:00
takes_options = [
2011-07-25 19:56:10 +04:00
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
2019-12-17 18:26:23 +03:00
Option ( " --member-dn " ,
help = ( " DN of the new group member to be added. \n "
" The --object-types option will be ignored. " ) ,
2019-12-18 15:35:16 +03:00
type = str ,
action = " append " ) ,
2019-08-09 18:07:07 +03:00
Option ( " --object-types " ,
help = ( " Comma separated list of object types. \n "
" The types are used to filter the search for the "
" specified members. \n "
" Valid values are: user, group, computer, serviceaccount, "
" contact and all. \n "
" Default: user,group,computer " ) ,
default = " user,group,computer " ,
type = str ) ,
2019-12-30 16:54:32 +03:00
Option ( " --member-base-dn " ,
help = ( " Base DN for group member search. \n "
" Default is the domain DN. " ) ,
type = str ) ,
2010-06-07 20:10:28 +04:00
]
2019-12-17 18:26:23 +03:00
takes_args = [ " groupname " , " listofmembers? " ]
2010-06-07 20:10:28 +04:00
2019-08-09 18:07:07 +03:00
def run ( self ,
groupname ,
2019-12-17 18:26:23 +03:00
listofmembers = None ,
2019-08-09 18:07:07 +03:00
credopts = None ,
sambaopts = None ,
versionopts = None ,
H = None ,
2019-12-30 16:54:32 +03:00
member_base_dn = None ,
2019-12-17 18:26:23 +03:00
member_dn = None ,
2019-08-09 18:07:07 +03:00
object_types = " user,group,computer " ) :
2010-06-07 20:10:28 +04:00
lp = sambaopts . get_loadparm ( )
2010-12-08 00:20:54 +03:00
creds = credopts . get_credentials ( lp , fallback_machine = True )
2010-06-07 20:10:28 +04:00
2019-12-18 15:35:16 +03:00
if member_dn is None and listofmembers is None :
self . usage ( )
raise CommandError (
' Either listofmembers or --member-dn must be specified. ' )
2010-06-07 20:10:28 +04:00
try :
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
2019-12-18 15:35:16 +03:00
groupmembers = [ ]
2019-12-17 18:26:23 +03:00
if member_dn is not None :
2019-12-18 15:35:16 +03:00
groupmembers + = member_dn
if listofmembers is not None :
groupmembers + = listofmembers . split ( ' , ' )
2019-08-09 18:07:07 +03:00
group_member_types = object_types . split ( ' , ' )
2019-12-18 15:35:16 +03:00
2019-12-30 16:54:32 +03:00
if member_base_dn is not None :
member_base_dn = samdb . normalize_dn_in_domain ( member_base_dn )
2012-06-19 14:43:08 +04:00
samdb . add_remove_group_members ( groupname , groupmembers ,
2019-08-09 18:07:07 +03:00
add_members_operation = True ,
2019-12-30 16:54:32 +03:00
member_types = group_member_types ,
member_base_dn = member_base_dn )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-10-13 02:36:44 +04:00
# FIXME: catch more specific exception
2020-01-21 15:56:29 +03:00
raise CommandError ( ' Failed to add members %r to group " %s " - %s ' % (
groupmembers , groupname , e ) )
2011-10-13 02:36:44 +04:00
self . outf . write ( " Added members to group %s \n " % groupname )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
2010-06-07 20:10:28 +04:00
class cmd_group_remove_members ( Command ) :
2012-10-08 14:32:58 +04:00
""" Remove members from an AD group.
2011-11-10 01:33:37 +04:00
2013-05-15 20:15:18 +04:00
This command removes one or more members from an existing Active Directory group . The command accepts one or more group member names separated by commas . A group member may be a user or computer account or another Active Directory group that is a member of the group specified on the command .
2011-11-10 01:33:37 +04:00
When a member is removed from a group , inherited permissions and rights will no longer apply to the member .
Example1 :
samba - tool group removemembers supergroup Group1 - H ldap : / / samba . samdom . example . com - Uadministrator % passw0rd
Example1 shows how to remove Group1 from supergroup . The command will run on the remote server specified on the - H parameter . The - U parameter is used to pass the userid and password of a user authorized to issue the command on the remote server .
Example2 :
sudo samba - tool group removemembers supergroup User1
Example2 shows how to remove a single user account , User2 , from the supergroup AD group . It uses the sudo command to run as root when issuing the command .
"""
2010-06-07 20:10:28 +04:00
2019-12-17 18:27:32 +03:00
synopsis = " % prog <groupname> (<listofmembers>]|--member-dn=<member-dn>) [options] "
2010-06-07 20:10:28 +04:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-06-07 20:10:28 +04:00
takes_options = [
2011-07-25 19:56:10 +04:00
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
2019-12-17 18:27:32 +03:00
Option ( " --member-dn " ,
help = ( " DN of the group member to be removed. \n "
" The --object-types option will be ignored. " ) ,
2019-12-18 15:35:16 +03:00
type = str ,
action = " append " ) ,
2019-12-17 18:27:32 +03:00
Option ( " --object-types " ,
help = ( " Comma separated list of object types. \n "
" The types are used to filter the search for the "
" specified members. \n "
" Valid values are: user, group, computer, serviceaccount, "
" contact and all. \n "
" Default: user,group,computer " ) ,
default = " user,group,computer " ,
type = str ) ,
2019-12-30 16:54:32 +03:00
Option ( " --member-base-dn " ,
help = ( " Base DN for group member search. \n "
" Default is the domain DN. " ) ,
type = str ) ,
2010-06-07 20:10:28 +04:00
]
2019-12-17 18:27:32 +03:00
takes_args = [ " groupname " , " listofmembers? " ]
2010-06-07 20:10:28 +04:00
2019-12-17 18:27:32 +03:00
def run ( self ,
groupname ,
listofmembers = None ,
credopts = None ,
sambaopts = None ,
versionopts = None ,
H = None ,
2019-12-30 16:54:32 +03:00
member_base_dn = None ,
2019-12-17 18:27:32 +03:00
member_dn = None ,
object_types = " user,group,computer " ) :
2010-06-07 20:10:28 +04:00
lp = sambaopts . get_loadparm ( )
2010-12-08 00:20:54 +03:00
creds = credopts . get_credentials ( lp , fallback_machine = True )
2010-06-07 20:10:28 +04:00
2019-12-18 15:35:16 +03:00
if member_dn is None and listofmembers is None :
self . usage ( )
raise CommandError (
' Either listofmembers or --member-dn must be specified. ' )
2010-06-07 20:10:28 +04:00
try :
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
2019-12-18 15:35:16 +03:00
groupmembers = [ ]
2019-12-17 18:27:32 +03:00
if member_dn is not None :
2019-12-18 15:35:16 +03:00
groupmembers + = member_dn
if listofmembers is not None :
groupmembers + = listofmembers . split ( ' , ' )
group_member_types = object_types . split ( ' , ' )
2019-12-30 16:54:32 +03:00
if member_base_dn is not None :
member_base_dn = samdb . normalize_dn_in_domain ( member_base_dn )
2019-12-17 18:27:32 +03:00
samdb . add_remove_group_members ( groupname ,
groupmembers ,
add_members_operation = False ,
2019-12-30 16:54:32 +03:00
member_types = group_member_types ,
member_base_dn = member_base_dn )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-10-13 02:36:44 +04:00
# FIXME: Catch more specific exception
2019-12-18 15:35:16 +03:00
raise CommandError ( ' Failed to remove members %r from group " %s " ' % ( listofmembers , groupname ) , e )
2011-10-13 02:36:44 +04:00
self . outf . write ( " Removed members from group %s \n " % groupname )
2010-06-07 20:10:28 +04:00
2012-05-02 00:17:33 +04:00
2012-03-09 01:39:24 +04:00
class cmd_group_list ( Command ) :
2012-10-08 14:32:58 +04:00
""" List all groups. """
2012-03-09 01:39:24 +04:00
synopsis = " % prog [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
2013-03-11 23:47:19 +04:00
Option ( " -v " , " --verbose " ,
help = " Verbose output, showing group type and group scope. " ,
action = " store_true " ) ,
2019-08-12 21:46:47 +03:00
Option ( " -b " , " --base-dn " ,
help = " Specify base DN to use. " ,
type = str ) ,
2019-08-12 21:43:48 +03:00
Option ( " --full-dn " , dest = " full_dn " ,
default = False ,
action = ' store_true ' ,
help = " Display DN instead of the sAMAccountName. " ) ,
2018-07-30 09:14:37 +03:00
]
2012-03-09 01:39:24 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2012-03-09 01:39:24 +04:00
2019-08-12 21:43:48 +03:00
def run ( self ,
sambaopts = None ,
credopts = None ,
versionopts = None ,
H = None ,
verbose = False ,
2019-08-12 21:46:47 +03:00
base_dn = None ,
2019-08-12 21:43:48 +03:00
full_dn = False ) :
2012-03-09 01:39:24 +04:00
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
2018-07-30 09:16:12 +03:00
credentials = creds , lp = lp )
2018-10-18 06:59:24 +03:00
attrs = [ " samaccountname " ]
2012-03-09 01:39:24 +04:00
2018-10-18 06:59:24 +03:00
if verbose :
attrs + = [ " grouptype " , " member " ]
2012-03-09 01:39:24 +04:00
domain_dn = samdb . domain_dn ( )
2019-08-12 21:46:47 +03:00
if base_dn :
domain_dn = samdb . normalize_dn_in_domain ( base_dn )
2012-03-09 01:39:24 +04:00
res = samdb . search ( domain_dn , scope = ldb . SCOPE_SUBTREE ,
2018-07-30 09:16:12 +03:00
expression = ( " (objectClass=group) " ) ,
2018-10-18 06:59:24 +03:00
attrs = attrs )
2012-03-09 01:39:24 +04:00
if ( len ( res ) == 0 ) :
return
2013-03-11 23:47:19 +04:00
if verbose :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Group Name Group Type Group Scope Members \n " )
self . outf . write ( " -------------------------------------------------------------------------------- \n " )
2013-03-11 23:47:19 +04:00
for msg in res :
self . outf . write ( " %-44s " % msg . get ( " samaccountname " , idx = 0 ) )
hgtype = hex ( int ( " %s " % msg [ " grouptype " ] ) & 0x00000000FFFFFFFF )
if ( hgtype == hex ( int ( security_group . get ( " Builtin " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Security Builtin " )
2013-03-11 23:47:19 +04:00
elif ( hgtype == hex ( int ( security_group . get ( " Domain " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Security Domain " )
2013-03-11 23:47:19 +04:00
elif ( hgtype == hex ( int ( security_group . get ( " Global " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Security Global " )
2013-03-11 23:47:19 +04:00
elif ( hgtype == hex ( int ( security_group . get ( " Universal " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Security Universal " )
2013-03-11 23:47:19 +04:00
elif ( hgtype == hex ( int ( distribution_group . get ( " Global " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Distribution Global " )
2013-03-11 23:47:19 +04:00
elif ( hgtype == hex ( int ( distribution_group . get ( " Domain " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Distribution Domain " )
2013-03-11 23:47:19 +04:00
elif ( hgtype == hex ( int ( distribution_group . get ( " Universal " ) ) ) ) :
2018-10-18 06:59:24 +03:00
self . outf . write ( " Distribution Universal " )
2013-03-11 23:47:19 +04:00
else :
2018-10-18 06:59:24 +03:00
self . outf . write ( " " )
2018-11-27 01:45:51 +03:00
num_members = len ( msg . get ( " member " , default = [ ] ) )
self . outf . write ( " %6u \n " % num_members )
2013-03-11 23:47:19 +04:00
else :
for msg in res :
2019-08-12 21:43:48 +03:00
if full_dn :
self . outf . write ( " %s \n " % msg . get ( " dn " ) )
continue
2013-03-11 23:47:19 +04:00
self . outf . write ( " %s \n " % msg . get ( " samaccountname " , idx = 0 ) )
2012-05-02 00:17:33 +04:00
2018-07-30 09:20:39 +03:00
2012-05-02 00:17:33 +04:00
class cmd_group_list_members ( Command ) :
2012-10-08 14:32:58 +04:00
""" List all members of an AD group.
2012-05-02 00:17:33 +04:00
This command lists members from an existing Active Directory group . The command accepts one group name .
Example1 :
samba - tool group listmembers \" Domain Users \" -H ldap://samba.samdom.example.com -Uadministrator % passw0rd
"""
synopsis = " % prog <groupname> [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
2020-12-23 17:51:12 +03:00
Option ( " --hide-expired " ,
help = " Do not list expired group members " ,
default = False ,
action = ' store_true ' ) ,
Option ( " --hide-disabled " ,
default = False ,
action = ' store_true ' ,
help = " Do not list disabled group members " ) ,
2019-08-22 16:39:37 +03:00
Option ( " --full-dn " , dest = " full_dn " ,
default = False ,
action = ' store_true ' ,
help = " Display DN instead of the sAMAccountName. " )
2018-07-30 09:14:37 +03:00
]
2012-05-02 00:17:33 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2012-05-02 00:17:33 +04:00
takes_args = [ " groupname " ]
2019-08-22 16:39:37 +03:00
def run ( self ,
groupname ,
credopts = None ,
sambaopts = None ,
versionopts = None ,
H = None ,
2020-12-23 17:51:12 +03:00
hide_expired = False ,
hide_disabled = False ,
2019-08-22 16:39:37 +03:00
full_dn = False ) :
2012-05-02 00:17:33 +04:00
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
try :
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
2020-02-26 15:38:50 +03:00
search_filter = ( " (&(objectClass=group)(sAMAccountName= %s )) " %
ldb . binary_encode ( groupname ) )
2020-02-26 15:08:43 +03:00
try :
res = samdb . search ( samdb . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = ( search_filter ) ,
attrs = [ " objectSid " ] )
2020-02-26 15:39:44 +03:00
group_sid_binary = res [ 0 ] . get ( ' objectSid ' , idx = 0 )
2020-02-26 15:08:43 +03:00
except IndexError :
raise CommandError ( ' Unable to find group " %s " ' % ( groupname ) )
2012-05-02 00:17:33 +04:00
2020-02-26 15:39:44 +03:00
group_sid = ndr_unpack ( security . dom_sid , group_sid_binary )
( group_dom_sid , rid ) = group_sid . split ( )
group_sid_dn = " <SID= %s > " % ( group_sid )
2012-05-02 00:17:33 +04:00
2020-12-23 17:51:12 +03:00
filter_expires = " "
if hide_expired is True :
current_nttime = samdb . get_nttime ( )
2021-04-23 19:03:53 +03:00
filter_expires = ( " (| "
" (!(accountExpires=*)) "
" (accountExpires=0) "
" (accountExpires>= %u ) "
" ) " % ( current_nttime ) )
2020-12-23 17:51:12 +03:00
filter_disabled = " "
if hide_disabled is True :
filter_disabled = " (!(userAccountControl: %s := %u )) " % (
ldb . OID_COMPARATOR_AND , UF_ACCOUNTDISABLE )
filter = " (&(|(primaryGroupID= %s )(memberOf= %s )) %s %s ) " % (
rid , group_sid_dn , filter_disabled , filter_expires )
2012-05-02 00:17:33 +04:00
res = samdb . search ( samdb . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2020-12-23 17:51:12 +03:00
expression = filter ,
2012-05-09 17:24:01 +04:00
attrs = [ " samAccountName " , " cn " ] )
2012-05-02 00:17:33 +04:00
if ( len ( res ) == 0 ) :
return
for msg in res :
2019-08-22 16:39:37 +03:00
if full_dn :
self . outf . write ( " %s \n " % msg . get ( " dn " ) )
continue
2012-05-09 17:24:01 +04:00
member_name = msg . get ( " samAccountName " , idx = 0 )
if member_name is None :
member_name = msg . get ( " cn " , idx = 0 )
self . outf . write ( " %s \n " % member_name )
2012-05-02 00:17:33 +04:00
2018-02-14 00:07:23 +03:00
except Exception as e :
2020-02-26 15:05:16 +03:00
raise CommandError ( ' Failed to list members of " %s " group - %s ' %
( groupname , e ) )
2012-05-02 00:17:33 +04:00
2018-07-30 09:20:39 +03:00
2017-11-27 23:00:07 +03:00
class cmd_group_move ( Command ) :
""" Move a group to an organizational unit/container.
This command moves a group object into the specified organizational unit
or container .
The groupname specified on the command is the sAMAccountName .
The name of the organizational unit or container can be specified as a
full DN or without the domainDN component .
The command may be run from the root userid or another authorized userid .
The - H or - - URL = option can be used to execute the command against a remote
server .
Example1 :
2019-03-21 16:15:22 +03:00
samba - tool group move Group1 ' OU=OrgUnit,DC=samdom.DC=example,DC=com ' \\
2017-11-27 23:00:07 +03:00
- H ldap : / / samba . samdom . example . com - U administrator
Example1 shows how to move a group Group1 into the ' OrgUnit ' organizational
unit on a remote LDAP server .
The - H parameter is used to specify the remote target server .
Example2 :
samba - tool group move Group1 CN = Users
Example2 shows how to move a group Group1 back into the CN = Users container
on the local server .
"""
synopsis = " % prog <groupname> <new_parent_dn> [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " ,
type = str , metavar = " URL " , dest = " H " ) ,
]
2018-07-30 09:17:02 +03:00
takes_args = [ " groupname " , " new_parent_dn " ]
2017-11-27 23:00:07 +03:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2017-11-27 23:00:07 +03:00
def run ( self , groupname , new_parent_dn , credopts = None , sambaopts = None ,
versionopts = None , H = None ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
domain_dn = ldb . Dn ( samdb , samdb . domain_dn ( ) )
filter = ( " (&(sAMAccountName= %s )(objectClass=group)) " %
2020-02-26 15:40:50 +03:00
ldb . binary_encode ( groupname ) )
2017-11-27 23:00:07 +03:00
try :
res = samdb . search ( base = domain_dn ,
expression = filter ,
scope = ldb . SCOPE_SUBTREE )
group_dn = res [ 0 ] . dn
except IndexError :
raise CommandError ( ' Unable to find group " %s " ' % ( groupname ) )
try :
full_new_parent_dn = samdb . normalize_dn_in_domain ( new_parent_dn )
2018-02-14 00:07:23 +03:00
except Exception as e :
2017-11-27 23:00:07 +03:00
raise CommandError ( ' Invalid new_parent_dn " %s " : %s ' %
( new_parent_dn , e . message ) )
full_new_group_dn = ldb . Dn ( samdb , str ( group_dn ) )
2018-07-30 09:18:25 +03:00
full_new_group_dn . remove_base_components ( len ( group_dn ) - 1 )
2017-11-27 23:00:07 +03:00
full_new_group_dn . add_base ( full_new_parent_dn )
try :
samdb . rename ( group_dn , full_new_group_dn )
2018-02-14 00:07:23 +03:00
except Exception as e :
2017-11-27 23:00:07 +03:00
raise CommandError ( ' Failed to move group " %s " ' % groupname , e )
self . outf . write ( ' Moved group " %s " into " %s " \n ' %
( groupname , full_new_parent_dn ) )
2012-05-02 00:17:33 +04:00
2018-07-30 09:20:39 +03:00
2018-04-26 06:59:06 +03:00
class cmd_group_show ( Command ) :
""" Display a group AD object.
This command displays a group object and it ' s attributes in the Active
Directory domain .
The group name specified on the command is the sAMAccountName of the group .
The command may be run from the root userid or another authorized userid .
The - H or - - URL = option can be used to execute the command against a remote
server .
Example1 :
2019-03-21 16:15:22 +03:00
samba - tool group show Group1 - H ldap : / / samba . samdom . example . com \\
- U administrator - - password = passw1rd
2018-04-26 06:59:06 +03:00
2019-03-21 16:15:22 +03:00
Example1 shows how to display a group ' s attributes in the domain against a
remote LDAP server .
2018-04-26 06:59:06 +03:00
The - H parameter is used to specify the remote target server .
Example2 :
samba - tool group show Group2
Example2 shows how to display a group ' s attributes in the domain against a local
LDAP server .
Example3 :
samba - tool group show Group3 - - attributes = member , objectGUID
2019-11-25 18:36:03 +03:00
Example3 shows how to display a groups objectGUID and member attributes .
2018-04-26 06:59:06 +03:00
"""
synopsis = " % prog <group name> [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " ,
type = str , metavar = " URL " , dest = " H " ) ,
Option ( " --attributes " ,
help = ( " Comma separated list of attributes, "
" which will be printed. " ) ,
type = str , dest = " group_attrs " ) ,
]
takes_args = [ " groupname " ]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2018-04-26 06:59:06 +03:00
def run ( self , groupname , credopts = None , sambaopts = None , versionopts = None ,
H = None , group_attrs = None ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
attrs = None
if group_attrs :
attrs = group_attrs . split ( " , " )
2020-04-02 11:29:18 +03:00
filter = ( " (&(objectCategory=group)(sAMAccountName= %s )) " %
ldb . binary_encode ( groupname ) )
2018-04-26 06:59:06 +03:00
domaindn = samdb . domain_dn ( )
try :
res = samdb . search ( base = domaindn , expression = filter ,
scope = ldb . SCOPE_SUBTREE , attrs = attrs )
user_dn = res [ 0 ] . dn
except IndexError :
raise CommandError ( ' Unable to find group " %s " ' % ( groupname ) )
for msg in res :
2019-11-25 16:13:37 +03:00
group_ldif = common . get_ldif_for_editor ( samdb , msg )
2019-11-25 18:36:03 +03:00
self . outf . write ( group_ldif )
2018-04-26 06:59:06 +03:00
2018-07-30 09:20:39 +03:00
2018-10-18 07:08:32 +03:00
class cmd_group_stats ( Command ) :
""" Summary statistics about group memberships. """
synopsis = " % prog [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
}
def num_in_range ( self , range_min , range_max , group_freqs ) :
total_count = 0
for members , count in group_freqs . items ( ) :
if range_min < = members and members < = range_max :
total_count + = count
return total_count
def run ( self , sambaopts = None , credopts = None , versionopts = None , H = None ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
domain_dn = samdb . domain_dn ( )
res = samdb . search ( domain_dn , scope = ldb . SCOPE_SUBTREE ,
expression = ( " (objectClass=group) " ) ,
attrs = [ " samaccountname " , " member " ] )
# first count up how many members each group has
group_assignments = { }
total_memberships = 0
for msg in res :
name = str ( msg . get ( " samaccountname " ) )
2018-11-27 01:45:51 +03:00
num_members = len ( msg . get ( " member " , default = [ ] ) )
group_assignments [ name ] = num_members
total_memberships + = num_members
2018-10-18 07:08:32 +03:00
2018-11-27 01:45:51 +03:00
num_groups = res . count
2018-10-18 07:08:32 +03:00
self . outf . write ( " Group membership statistics* \n " )
self . outf . write ( " ------------------------------------------------- \n " )
2018-11-27 01:45:51 +03:00
self . outf . write ( " Total groups: {0} \n " . format ( num_groups ) )
2018-10-18 07:08:32 +03:00
self . outf . write ( " Total memberships: {0} \n " . format ( total_memberships ) )
2018-11-27 01:45:51 +03:00
average = total_memberships / float ( num_groups )
2018-10-18 07:08:32 +03:00
self . outf . write ( " Average members per group: %.2f \n " % average )
2018-11-27 01:45:51 +03:00
# find the max and median memberships (note that some default groups
# always have zero members, so displaying the min is not very helpful)
2018-10-18 07:08:32 +03:00
group_names = list ( group_assignments . keys ( ) )
group_members = list ( group_assignments . values ( ) )
idx = group_members . index ( max ( group_members ) )
max_members = group_members [ idx ]
2018-11-27 01:45:51 +03:00
self . outf . write ( " Max members: {0} ( {1} ) \n " . format ( max_members ,
group_names [ idx ] ) )
group_members . sort ( )
midpoint = num_groups / / 2
median = group_members [ midpoint ]
if num_groups % 2 == 0 :
median = ( median + group_members [ midpoint - 1 ] ) / 2
self . outf . write ( " Median members per group: {0} \n \n " . format ( median ) )
2018-10-18 07:08:32 +03:00
# convert this to the frequency of group membership, i.e. how many
# groups have 5 members, how many have 6 members, etc
group_freqs = defaultdict ( int )
2018-11-27 01:45:51 +03:00
for group , num_members in group_assignments . items ( ) :
group_freqs [ num_members ] + = 1
2018-10-18 07:08:32 +03:00
# now squash this down even further, so that we just display the number
# of groups that fall into one of the following membership bands
2018-11-27 01:45:51 +03:00
bands = [ ( 0 , 1 ) , ( 2 , 4 ) , ( 5 , 9 ) , ( 10 , 14 ) , ( 15 , 19 ) , ( 20 , 24 ) ,
( 25 , 29 ) , ( 30 , 39 ) , ( 40 , 49 ) , ( 50 , 59 ) , ( 60 , 69 ) , ( 70 , 79 ) ,
( 80 , 89 ) , ( 90 , 99 ) , ( 100 , 149 ) , ( 150 , 199 ) , ( 200 , 249 ) ,
( 250 , 299 ) , ( 300 , 399 ) , ( 400 , 499 ) , ( 500 , 999 ) , ( 1000 , 1999 ) ,
2018-10-18 07:08:32 +03:00
( 2000 , 2999 ) , ( 3000 , 3999 ) , ( 4000 , 4999 ) , ( 5000 , 9999 ) ,
( 10000 , max_members ) ]
self . outf . write ( " Members Number of Groups \n " )
self . outf . write ( " ------------------------------------------------- \n " )
for band in bands :
band_start = band [ 0 ]
band_end = band [ 1 ]
if band_start > max_members :
break
num_groups = self . num_in_range ( band_start , band_end , group_freqs )
if num_groups != 0 :
band_str = " {0} - {1} " . format ( band_start , band_end )
self . outf . write ( " %13s %u \n " % ( band_str , num_groups ) )
self . outf . write ( " \n * Note this does not include nested group memberships \n " )
2019-03-13 23:20:29 +03:00
class cmd_group_edit ( Command ) :
""" Modify Group AD object.
This command will allow editing of a group account in the Active Directory
domain . You will then be able to add or change attributes and their values .
The groupname specified on the command is the sAMAccountName .
The command may be run from the root userid or another authorized userid .
The - H or - - URL = option can be used to execute the command against a remote
server .
Example1 :
samba - tool group edit Group1 - H ldap : / / samba . samdom . example . com \\
- U administrator - - password = passw1rd
Example1 shows how to edit a groups attributes in the domain against a
remote LDAP server .
The - H parameter is used to specify the remote target server .
Example2 :
samba - tool group edit Group2
Example2 shows how to edit a groups attributes in the domain against a local
server .
Example3 :
samba - tool group edit Group3 - - editor = nano
Example3 shows how to edit a groups attributes in the domain against a local
server using the ' nano ' editor .
"""
synopsis = " % prog <groupname> [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " ,
type = str , metavar = " URL " , dest = " H " ) ,
Option ( " --editor " , help = " Editor to use instead of the system default, "
" or ' vi ' if no system default is set. " , type = str ) ,
]
takes_args = [ " groupname " ]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
}
def run ( self , groupname , credopts = None , sambaopts = None , versionopts = None ,
H = None , editor = None ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
2020-02-26 15:56:14 +03:00
filter = ( " (&(sAMAccountName= %s )(objectClass=group)) " %
ldb . binary_encode ( groupname ) )
2019-03-13 23:20:29 +03:00
domaindn = samdb . domain_dn ( )
try :
res = samdb . search ( base = domaindn ,
expression = filter ,
scope = ldb . SCOPE_SUBTREE )
group_dn = res [ 0 ] . dn
except IndexError :
raise CommandError ( ' Unable to find group " %s " ' % ( groupname ) )
if len ( res ) != 1 :
raise CommandError ( ' Invalid number of results: for " %s " : %d ' %
( ( groupname ) , len ( res ) ) )
msg = res [ 0 ]
result_ldif = common . get_ldif_for_editor ( samdb , msg )
if editor is None :
editor = os . environ . get ( ' EDITOR ' )
if editor is None :
editor = ' vi '
with tempfile . NamedTemporaryFile ( suffix = " .tmp " ) as t_file :
t_file . write ( get_bytes ( result_ldif ) )
t_file . flush ( )
try :
check_call ( [ editor , t_file . name ] )
except CalledProcessError as e :
raise CalledProcessError ( " ERROR: " , e )
with open ( t_file . name ) as edited_file :
edited_message = edited_file . read ( )
msgs_edited = samdb . parse_ldif ( edited_message )
msg_edited = next ( msgs_edited ) [ 1 ]
res_msg_diff = samdb . msg_diff ( msg , msg_edited )
if len ( res_msg_diff ) == 0 :
self . outf . write ( " Nothing to do \n " )
return
try :
samdb . modify ( res_msg_diff )
except Exception as e :
raise CommandError ( " Failed to modify group ' %s ' : " % groupname , e )
self . outf . write ( " Modified group ' %s ' successfully \n " % groupname )
2019-07-02 15:41:34 +03:00
class cmd_group_add_unix_attrs ( Command ) :
""" Add RFC2307 attributes to a group.
This command adds Unix attributes to a group account in the Active
Directory domain .
The groupname specified on the command is the sAMaccountName .
Unix ( RFC2307 ) attributes will be added to the group account .
Add ' idmap_ldb:use rfc2307 = Yes ' to smb . conf to use these attributes for
UID / GID mapping .
The command may be run from the root userid or another authorized userid .
The - H or - - URL = option can be used to execute the command against a
remote server .
Example1 :
samba - tool group addunixattrs Group1 10000
Example1 shows how to add RFC2307 attributes to a domain enabled group
account .
The groups Unix ID will be set to ' 10000 ' , provided this ID isn ' t already
in use .
"""
synopsis = " % prog <groupname> <gidnumber> [options] "
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " ,
type = str , metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " groupname " , " gidnumber " ]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
}
def run ( self , groupname , gidnumber , credopts = None , sambaopts = None ,
versionopts = None , H = None ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
domaindn = samdb . domain_dn ( )
# Check group exists and doesn't have a gidNumber
filter = " (samaccountname= {} ) " . format ( ldb . binary_encode ( groupname ) )
res = samdb . search ( domaindn ,
scope = ldb . SCOPE_SUBTREE ,
expression = filter )
if ( len ( res ) == 0 ) :
raise CommandError ( " Unable to find group ' {} ' " . format ( groupname ) )
group_dn = res [ 0 ] . dn
if " gidNumber " in res [ 0 ] :
raise CommandError ( " Group {} is a Unix group. " . format ( groupname ) )
# Check if supplied gidnumber isn't already being used
filter = " (&(objectClass=group)(gidNumber= {} )) " . format ( gidnumber )
res = samdb . search ( domaindn ,
scope = ldb . SCOPE_SUBTREE ,
expression = filter )
if ( len ( res ) != 0 ) :
raise CommandError ( ' gidNumber {} already used. ' . format ( gidnumber ) )
if not lp . get ( " idmap_ldb:use rfc2307 " ) :
self . outf . write ( " You are setting a Unix/RFC2307 GID. "
" You may want to set ' idmap_ldb:use rfc2307 = Yes ' "
" in smb.conf to use the attributes for "
" XID/SID-mapping. \n " )
group_mod = """
dn : { 0 }
changetype : modify
add : gidNumber
gidNumber : { 1 }
""" .format(group_dn, gidnumber)
try :
samdb . modify_ldif ( group_mod )
except ldb . LdbError as e :
raise CommandError ( " Failed to modify group ' {0} ' : {1} "
. format ( groupname , e ) )
self . outf . write ( " Modified Group ' {} ' successfully \n " . format ( groupname ) )
2020-08-06 16:39:54 +03:00
class cmd_group_rename ( Command ) :
""" Rename a group and related attributes.
This command allows to set the group ' s name related attributes. The
group ' s CN will be renamed automatically.
The group ' s CN will be the sAMAccountName.
Use the - - force - new - cn option to specify the new CN manually and the
- - reset - cn to reset this change .
Use an empty attribute value to remove the specified attribute .
The groupname specified on the command is the sAMAccountName .
The command may be run locally from the root userid or another authorized
userid .
The - H or - - URL = option can be used to execute the command against a remote
server .
Example1 :
samba - tool group rename employees - - samaccountname = staff
Example1 shows how to change the samaccountname of a group ' employees ' to
' staff ' . The CN of the group employees will also be changed to ' staff ' ,
if the previous CN was the previous sAMAccountName .
Example2 :
samba - tool group rename employees - - mail - address = ' staff@company.com ' \\
- H ldap : / / samba . samdom . example . com - U administrator
Example2 shows how to rename the mail address of a group ' employees ' to
' staff@company.com ' .
The - H parameter is used to specify the remote target server .
"""
synopsis = " % prog <groupname> [options] "
takes_options = [
Option ( " -H " , " --URL " ,
help = " LDB URL for database or target server " ,
type = str , metavar = " URL " , dest = " H " ) ,
Option ( " --force-new-cn " ,
help = " Specify a new CN (RND) instead of using the sAMAccountName. " ,
type = str ) ,
Option ( " --reset-cn " ,
help = " Set the CN (RDN) to the sAMAccountName. Use this option "
" to reset the changes made with the --force-new-cn option. " ,
action = " store_true " ) ,
Option ( " --mail-address " ,
help = " New mail address " ,
type = str ) ,
Option ( " --samaccountname " ,
help = " New account name (sAMAccountName/logon name) " ,
type = str )
]
takes_args = [ " groupname " ]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
}
def run ( self , groupname , credopts = None , sambaopts = None , versionopts = None ,
H = None , mail_address = None , samaccountname = None , force_new_cn = None ,
reset_cn = None ) :
# illegal options
if force_new_cn and reset_cn :
raise CommandError ( " It is not allowed to specify --force-new-cn "
" together with --reset-cn. " )
if force_new_cn == " " :
raise CommandError ( " Failed to rename group - delete protected "
" attribute ' CN ' " )
if samaccountname == " " :
raise CommandError ( " Failed to rename group - delete protected "
" attribute ' sAMAccountName ' " )
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp , fallback_machine = True )
samdb = SamDB ( url = H , session_info = system_session ( ) ,
credentials = creds , lp = lp )
domain_dn = ldb . Dn ( samdb , samdb . domain_dn ( ) )
filter = ( " (&(objectClass=group)(samaccountname= %s )) " %
ldb . binary_encode ( groupname ) )
try :
res = samdb . search ( base = domain_dn ,
scope = ldb . SCOPE_SUBTREE ,
expression = filter ,
attrs = [ " sAMAccountName " ,
" cn " ,
" mail " ]
)
old_group = res [ 0 ]
group_dn = old_group . dn
except IndexError :
raise CommandError ( ' Unable to find group " %s " ' % ( groupname ) )
group_parent_dn = group_dn . parent ( )
old_cn = old_group [ " cn " ] [ 0 ]
# get the actual and the new group cn and the new dn
if force_new_cn is not None :
new_cn = force_new_cn
elif samaccountname is not None :
new_cn = samaccountname
else :
new_cn = old_group [ " sAMAccountName " ]
# CN must change, if the new CN is different and the old CN is the
# standard CN or the change is forced with force-new-cn or reset-cn
expected_cn = old_group [ " sAMAccountName " ]
must_change_cn = str ( old_cn ) != str ( new_cn ) and \
( str ( old_cn ) == str ( expected_cn ) or \
reset_cn or bool ( force_new_cn ) )
new_group_dn = ldb . Dn ( samdb , " CN= %s " % new_cn )
new_group_dn . add_base ( group_parent_dn )
# format given attributes
group_attrs = ldb . Message ( )
group_attrs . dn = group_dn
samdb . prepare_attr_replace ( group_attrs , old_group , " sAMAccountName " ,
samaccountname )
samdb . prepare_attr_replace ( group_attrs , old_group , " mail " , mail_address )
group_attributes_changed = len ( group_attrs ) > 0
# update the group with formatted attributes
samdb . transaction_start ( )
try :
if group_attributes_changed :
samdb . modify ( group_attrs )
if must_change_cn :
samdb . rename ( group_dn , new_group_dn )
except Exception as e :
samdb . transaction_cancel ( )
raise CommandError ( ' Failed to rename group " %s " ' % groupname , e )
samdb . transaction_commit ( )
if must_change_cn :
self . outf . write ( ' Renamed CN of group " %s " from " %s " to " %s " '
' successfully \n ' % ( groupname , old_cn , new_cn ) )
if group_attributes_changed :
self . outf . write ( ' Following attributes of group " %s " have been '
' changed successfully: \n ' % ( groupname ) )
for attr in group_attrs . keys ( ) :
if attr == " dn " :
continue
self . outf . write ( ' %s : %s \n ' % ( attr , group_attrs [ attr ]
if group_attrs [ attr ] else ' [removed] ' ) )
2010-06-07 20:10:28 +04:00
class cmd_group ( SuperCommand ) :
2012-10-09 13:53:21 +04:00
""" Group management. """
2010-06-07 20:10:28 +04:00
subcommands = { }
subcommands [ " add " ] = cmd_group_add ( )
2020-08-26 16:07:16 +03:00
subcommands [ " create " ] = cmd_group_add ( )
2010-06-07 20:10:28 +04:00
subcommands [ " delete " ] = cmd_group_delete ( )
2019-03-13 23:20:29 +03:00
subcommands [ " edit " ] = cmd_group_edit ( )
2010-06-07 20:10:28 +04:00
subcommands [ " addmembers " ] = cmd_group_add_members ( )
subcommands [ " removemembers " ] = cmd_group_remove_members ( )
2012-03-09 01:39:24 +04:00
subcommands [ " list " ] = cmd_group_list ( )
2012-05-02 00:17:33 +04:00
subcommands [ " listmembers " ] = cmd_group_list_members ( )
2017-11-27 23:00:07 +03:00
subcommands [ " move " ] = cmd_group_move ( )
2018-04-26 06:59:06 +03:00
subcommands [ " show " ] = cmd_group_show ( )
2018-10-18 07:08:32 +03:00
subcommands [ " stats " ] = cmd_group_stats ( )
2019-07-02 15:41:34 +03:00
subcommands [ " addunixattrs " ] = cmd_group_add_unix_attrs ( )
2020-08-06 16:39:54 +03:00
subcommands [ " rename " ] = cmd_group_rename ( )