2009-10-24 15:34:31 +04:00
# Manipulate file NT ACLs
#
# Copyright Matthieu Patou 2010 <mat@matws.net>
#
# 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/>.
#
2023-10-05 05:18:32 +03:00
import optparse
2023-10-05 05:10:06 +03:00
import os
2009-10-24 15:34:31 +04:00
import samba . getopt as options
2023-10-05 05:10:06 +03:00
from samba import provision
from samba . auth import system_session
from samba . auth_util import system_session_unix
from samba . credentials import DONT_USE_KERBEROS
2012-08-22 12:32:18 +04:00
from samba . dcerpc import security , idmap
2023-06-22 08:02:47 +03:00
from samba . ndr import ndr_print
2023-10-05 05:10:06 +03:00
from samba . ntacls import setntacl , getntacl , getdosinfo
2018-10-11 02:40:50 +03:00
from samba . samba3 import param as s3param , passdb
2023-10-05 05:10:06 +03:00
from samba . samdb import SamDB
2009-10-24 15:34:31 +04:00
2023-10-05 05:10:06 +03:00
from . import Command , CommandError , SuperCommand , Option
2018-10-11 02:40:50 +03:00
2009-10-24 15:34:31 +04:00
2019-06-17 15:20:56 +03:00
def get_local_domain_sid ( lp ) :
is_ad_dc = False
server_role = lp . server_role ( )
if server_role == " ROLE_ACTIVE_DIRECTORY_DC " :
is_ad_dc = True
s3conf = s3param . get_context ( )
s3conf . load ( lp . configfile )
if is_ad_dc :
try :
samdb = SamDB ( session_info = system_session ( ) ,
lp = lp )
except Exception as e :
raise CommandError ( " Unable to open samdb: " , e )
# ensure we are using the right samba_dsdb passdb backend, no
# matter what
s3conf . set ( " passdb backend " , " samba_dsdb: %s " % samdb . url )
try :
if is_ad_dc :
domain_sid = security . dom_sid ( samdb . domain_sid )
else :
domain_sid = passdb . get_domain_sid ( )
except :
raise CommandError ( " Unable to read domain SID from configuration "
" files " )
return domain_sid
2011-08-31 01:19:59 +04:00
class cmd_ntacl_set ( Command ) :
2012-10-08 14:32:58 +04:00
""" Set ACLs on a file. """
2011-10-14 01:47:45 +04:00
2023-05-02 17:18:26 +03:00
synopsis = " % prog <acl> <path> [options] "
2009-10-24 15:34:31 +04:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2012-02-06 19:33:38 +04:00
2009-10-24 15:34:31 +04:00
takes_options = [
2023-05-02 17:18:26 +03:00
# --quiet is not used at all...
2023-10-05 05:18:32 +03:00
Option ( " -q " , " --quiet " , help = optparse . SUPPRESS_HELP , action = " store_true " ) ,
2023-05-02 17:18:26 +03:00
Option ( " -v " , " --verbose " , help = " Be verbose " , action = " store_true " ) ,
2009-10-24 15:34:31 +04:00
Option ( " --xattr-backend " , type = " choice " , help = " xattr backend type (native fs or tdb) " ,
2018-07-30 09:19:05 +03:00
choices = [ " native " , " tdb " ] ) ,
2009-10-24 15:34:31 +04:00
Option ( " --eadb-file " , help = " Name of the tdb file where attributes are stored " , type = " string " ) ,
2012-09-05 12:12:52 +04:00
Option ( " --use-ntvfs " , help = " Set the ACLs directly to the TDB or xattr for use with the ntvfs file server " , action = " store_true " ) ,
2012-12-15 14:24:26 +04:00
Option ( " --use-s3fs " , help = " Set the ACLs for use with the default s3fs file server via the VFS layer " , action = " store_true " ) ,
2023-05-02 17:18:51 +03:00
Option ( " --recursive " , help = " Set the ACLs for directories and their contents recursively " , action = " store_true " ) ,
Option ( " --follow-symlinks " , help = " Follow symlinks " , action = " store_true " ) ,
2012-12-15 14:24:26 +04:00
Option ( " --service " , help = " Name of the smb.conf service to use when applying the ACLs " , type = " string " )
2018-07-30 09:14:37 +03:00
]
2009-10-24 15:34:31 +04:00
2023-05-02 17:18:26 +03:00
takes_args = [ " acl " , " path " ]
2009-10-24 15:34:31 +04:00
2023-05-02 17:18:26 +03:00
def run ( self , acl , path , use_ntvfs = False , use_s3fs = False ,
quiet = False , verbose = False , xattr_backend = None , eadb_file = None ,
2012-12-15 14:24:26 +04:00
credopts = None , sambaopts = None , versionopts = None ,
2023-05-02 17:18:51 +03:00
recursive = False , follow_symlinks = False , service = None ) :
2012-09-05 12:12:52 +04:00
logger = self . get_logger ( )
2010-03-01 06:39:53 +03:00
lp = sambaopts . get_loadparm ( )
2019-06-17 15:20:56 +03:00
domain_sid = get_local_domain_sid ( lp )
2012-09-05 12:12:52 +04:00
if not use_ntvfs and not use_s3fs :
use_ntvfs = " smb " in lp . get ( " server services " )
elif use_s3fs :
use_ntvfs = False
2023-05-02 17:18:26 +03:00
def _setntacl_path ( _path ) :
2023-05-02 17:18:51 +03:00
if not follow_symlinks and os . path . islink ( _path ) :
if recursive :
self . outf . write ( " ignored symlink: %s \n " % _path )
return
raise CommandError ( " symlink: %s : requires --follow-symlinks " % ( _path ) )
2023-05-02 17:18:26 +03:00
if verbose :
if os . path . islink ( _path ) :
self . outf . write ( " symlink: %s \n " % _path )
elif os . path . isdir ( _path ) :
self . outf . write ( " dir: %s \n " % _path )
else :
self . outf . write ( " file: %s \n " % _path )
try :
2023-06-22 07:59:36 +03:00
setntacl ( lp ,
_path ,
acl ,
str ( domain_sid ) ,
system_session_unix ( ) ,
xattr_backend ,
eadb_file ,
use_ntvfs = use_ntvfs ,
service = service )
2023-05-02 17:18:26 +03:00
except Exception as e :
raise CommandError ( " Could not set acl for %s : %s " % ( _path , e ) )
_setntacl_path ( path )
2012-09-05 12:12:52 +04:00
2023-05-02 17:18:51 +03:00
if recursive and os . path . isdir ( path ) :
for root , dirs , files in os . walk ( path , followlinks = follow_symlinks ) :
for name in files :
_setntacl_path ( os . path . join ( root , name ) )
for name in dirs :
_setntacl_path ( os . path . join ( root , name ) )
2012-09-05 12:12:52 +04:00
if use_ntvfs :
logger . warning ( " Please note that POSIX permissions have NOT been changed, only the stored NT ACL " )
2009-10-24 15:34:31 +04:00
2011-08-31 01:19:59 +04:00
2015-09-26 02:16:50 +03:00
class cmd_dosinfo_get ( Command ) :
""" Get DOS info of a file from xattr. """
synopsis = " % prog <file> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2015-09-26 02:16:50 +03:00
takes_args = [ " file " ]
def run ( self , file , credopts = None , sambaopts = None , versionopts = None ) :
lp = sambaopts . get_loadparm ( )
s3conf = s3param . get_context ( )
s3conf . load ( lp . configfile )
dosinfo = getdosinfo ( lp , file )
if dosinfo :
self . outf . write ( ndr_print ( dosinfo ) )
2018-07-30 09:20:39 +03:00
2011-08-31 01:19:59 +04:00
class cmd_ntacl_get ( Command ) :
2012-10-08 14:32:58 +04:00
""" Get ACLs of a file. """
2011-10-14 01:27:22 +04:00
synopsis = " % prog <file> [options] "
2009-10-24 15:34:31 +04:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2012-02-06 19:33:38 +04:00
2009-10-24 15:34:31 +04:00
takes_options = [
Option ( " --as-sddl " , help = " Output ACL in the SDDL format " , action = " store_true " ) ,
Option ( " --xattr-backend " , type = " choice " , help = " xattr backend type (native fs or tdb) " ,
2018-07-30 09:19:05 +03:00
choices = [ " native " , " tdb " ] ) ,
2009-10-24 15:34:31 +04:00
Option ( " --eadb-file " , help = " Name of the tdb file where attributes are stored " , type = " string " ) ,
2012-09-05 12:12:52 +04:00
Option ( " --use-ntvfs " , help = " Get the ACLs directly from the TDB or xattr used with the ntvfs file server " , action = " store_true " ) ,
2012-12-15 14:24:26 +04:00
Option ( " --use-s3fs " , help = " Get the ACLs for use via the VFS layer used by the default s3fs file server " , action = " store_true " ) ,
Option ( " --service " , help = " Name of the smb.conf service to use when getting the ACLs " , type = " string " )
2018-07-30 09:14:37 +03:00
]
2009-10-24 15:34:31 +04:00
takes_args = [ " file " ]
2012-09-05 12:12:52 +04:00
def run ( self , file , use_ntvfs = False , use_s3fs = False ,
as_sddl = False , xattr_backend = None , eadb_file = None ,
2012-12-15 14:24:26 +04:00
credopts = None , sambaopts = None , versionopts = None ,
service = None ) :
2009-10-24 15:34:31 +04:00
lp = sambaopts . get_loadparm ( )
2019-06-17 15:20:56 +03:00
domain_sid = get_local_domain_sid ( lp )
2012-09-05 12:12:52 +04:00
if not use_ntvfs and not use_s3fs :
use_ntvfs = " smb " in lp . get ( " server services " )
elif use_s3fs :
use_ntvfs = False
2018-09-04 17:20:49 +03:00
acl = getntacl ( lp ,
file ,
2019-12-17 16:52:49 +03:00
system_session_unix ( ) ,
2018-09-04 17:20:49 +03:00
xattr_backend ,
eadb_file ,
direct_db_access = use_ntvfs ,
2019-12-17 16:52:49 +03:00
service = service )
2009-10-24 15:34:31 +04:00
if as_sddl :
2018-07-30 09:18:25 +03:00
self . outf . write ( acl . as_sddl ( domain_sid ) + " \n " )
2009-10-24 15:34:31 +04:00
else :
2012-09-05 11:06:33 +04:00
self . outf . write ( ndr_print ( acl ) )
2009-10-24 15:34:31 +04:00
2019-06-11 16:11:20 +03:00
class cmd_ntacl_changedomsid ( Command ) :
""" Change the domain SID for ACLs """
synopsis = " % prog <Orig-Domain-SID> <New-Domain-SID> <file> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
}
takes_options = [
Option (
" --service " ,
help = " Name of the smb.conf service to use " ,
type = " string " ) ,
Option (
" --use-ntvfs " ,
help = ( " Set the ACLs directly to the TDB or xattr for use with the "
" ntvfs file server " ) ,
action = " store_true " ) ,
Option (
" --use-s3fs " ,
help = ( " Set the ACLs for use with the default s3fs file server via "
" the VFS layer " ) ,
action = " store_true " ) ,
Option (
" --eadb-file " ,
help = " Name of the tdb file where attributes are stored " ,
type = " string " ) ,
Option (
" --xattr-backend " ,
type = " choice " ,
help = " xattr backend type (native fs or tdb) " ,
choices = [ " native " , " tdb " ] ) ,
Option (
" -r " ,
" --recursive " ,
help = " Set the ACLs for directories and their contents recursively " ,
action = " store_true " ) ,
Option (
" --follow-symlinks " ,
help = " Follow symlinks " ,
action = " store_true " ) ,
Option (
" -v " ,
" --verbose " ,
help = " Be verbose " ,
action = " store_true " ) ,
]
2023-05-16 14:57:51 +03:00
takes_args = [ " old_domain_sid " , " new_domain_sid " , " path " ]
2019-06-11 16:11:20 +03:00
def run ( self ,
old_domain_sid_str ,
new_domain_sid_str ,
2023-05-16 14:57:51 +03:00
path ,
2019-06-11 16:11:20 +03:00
use_ntvfs = False ,
use_s3fs = False ,
service = None ,
xattr_backend = None ,
eadb_file = None ,
sambaopts = None ,
recursive = False ,
follow_symlinks = False ,
verbose = False ) :
logger = self . get_logger ( )
lp = sambaopts . get_loadparm ( )
domain_sid = get_local_domain_sid ( lp )
if not use_ntvfs and not use_s3fs :
use_ntvfs = " smb " in lp . get ( " server services " )
elif use_s3fs :
use_ntvfs = False
if not use_ntvfs and not service :
raise CommandError (
" Must provide a share name with --service=<share> " )
try :
old_domain_sid = security . dom_sid ( old_domain_sid_str )
except Exception as e :
raise CommandError ( " Could not parse old sid %s : %s " %
( old_domain_sid_str , e ) )
try :
new_domain_sid = security . dom_sid ( new_domain_sid_str )
except Exception as e :
raise CommandError ( " Could not parse old sid %s : %s " %
( new_domain_sid_str , e ) )
2023-05-16 14:57:51 +03:00
def changedom_sids ( _path ) :
if not follow_symlinks and os . path . islink ( _path ) :
if recursive :
self . outf . write ( " ignored symlink: %s \n " % _path )
return
raise CommandError ( " symlink: %s : requires --follow-symlinks " % ( _path ) )
2019-06-11 16:11:20 +03:00
if verbose :
2023-05-16 14:57:51 +03:00
if os . path . islink ( _path ) :
self . outf . write ( " symlink: %s \n " % _path )
elif os . path . isdir ( _path ) :
self . outf . write ( " dir: %s \n " % _path )
else :
self . outf . write ( " file: %s \n " % _path )
2019-06-11 16:11:20 +03:00
try :
acl = getntacl ( lp ,
2023-05-16 14:57:51 +03:00
_path ,
2019-12-17 16:52:49 +03:00
system_session_unix ( ) ,
2019-06-11 16:11:20 +03:00
xattr_backend ,
eadb_file ,
direct_db_access = use_ntvfs ,
2019-12-17 16:52:49 +03:00
service = service )
2019-06-11 16:11:20 +03:00
except Exception as e :
2023-05-16 14:57:51 +03:00
raise CommandError ( " Could not get acl for %s : %s " % ( _path , e ) )
2019-06-11 16:11:20 +03:00
orig_sddl = acl . as_sddl ( domain_sid )
if verbose :
self . outf . write ( " before: \n %s \n " % orig_sddl )
def replace_domain_sid ( sid ) :
( dom , rid ) = sid . split ( )
if dom == old_domain_sid :
return security . dom_sid ( " %s - %i " % ( new_domain_sid , rid ) )
return sid
acl . owner_sid = replace_domain_sid ( acl . owner_sid )
acl . group_sid = replace_domain_sid ( acl . group_sid )
if acl . sacl :
for ace in acl . sacl . aces :
ace . trustee = replace_domain_sid ( ace . trustee )
if acl . dacl :
for ace in acl . dacl . aces :
ace . trustee = replace_domain_sid ( ace . trustee )
new_sddl = acl . as_sddl ( domain_sid )
if verbose :
self . outf . write ( " after: \n %s \n " % new_sddl )
if orig_sddl == new_sddl :
if verbose :
self . outf . write ( " nothing to do \n " )
return True
try :
setntacl ( lp ,
2023-05-16 14:57:51 +03:00
_path ,
2019-06-11 16:11:20 +03:00
acl ,
new_domain_sid ,
2019-12-17 16:49:42 +03:00
system_session_unix ( ) ,
2019-06-11 16:11:20 +03:00
xattr_backend ,
eadb_file ,
use_ntvfs = use_ntvfs ,
2019-12-17 16:49:42 +03:00
service = service )
2019-06-11 16:11:20 +03:00
except Exception as e :
2023-05-16 14:57:51 +03:00
raise CommandError ( " Could not set acl for %s : %s " % ( _path , e ) )
2019-06-11 16:11:20 +03:00
2023-05-16 14:57:51 +03:00
def recursive_changedom_sids ( _path ) :
for root , dirs , files in os . walk ( _path , followlinks = follow_symlinks ) :
2019-06-11 16:11:20 +03:00
for f in files :
changedom_sids ( os . path . join ( root , f ) )
for d in dirs :
changedom_sids ( os . path . join ( root , d ) )
2023-05-16 14:57:51 +03:00
changedom_sids ( path )
if recursive and os . path . isdir ( path ) :
recursive_changedom_sids ( path )
2019-06-11 16:11:20 +03:00
if use_ntvfs :
logger . warning ( " Please note that POSIX permissions have NOT been "
" changed, only the stored NT ACL. " )
2012-08-22 12:32:18 +04:00
class cmd_ntacl_sysvolreset ( Command ) :
2012-10-08 14:32:58 +04:00
""" Reset sysvol ACLs to defaults (including correct ACLs on GPOs). """
2012-08-22 12:32:18 +04:00
synopsis = " % prog <file> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2012-08-22 12:32:18 +04:00
takes_options = [
Option ( " --use-ntvfs " , help = " Set the ACLs for use with the ntvfs file server " , action = " store_true " ) ,
Option ( " --use-s3fs " , help = " Set the ACLs for use with the default s3fs file server " , action = " store_true " )
2018-07-30 09:14:37 +03:00
]
2012-08-22 12:32:18 +04:00
def run ( self , use_ntvfs = False , use_s3fs = False ,
credopts = None , sambaopts = None , versionopts = None ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp )
creds . set_kerberos_state ( DONT_USE_KERBEROS )
logger = self . get_logger ( )
sysvol = lp . get ( " path " , " sysvol " )
try :
2012-09-27 20:30:47 +04:00
samdb = SamDB ( session_info = system_session ( ) ,
2012-08-22 12:32:18 +04:00
lp = lp )
2018-02-14 00:07:23 +03:00
except Exception as e :
2012-08-22 12:32:18 +04:00
raise CommandError ( " Unable to open samdb: " , e )
if not use_ntvfs and not use_s3fs :
use_ntvfs = " smb " in lp . get ( " server services " )
elif use_s3fs :
use_ntvfs = False
domain_sid = security . dom_sid ( samdb . domain_sid )
s3conf = s3param . get_context ( )
s3conf . load ( lp . configfile )
2012-09-04 04:27:50 +04:00
# ensure we are using the right samba_dsdb passdb backend, no matter what
s3conf . set ( " passdb backend " , " samba_dsdb: %s " % samdb . url )
2012-08-22 12:32:18 +04:00
LA_sid = security . dom_sid ( str ( domain_sid )
2018-08-22 08:09:01 +03:00
+ " - " + str ( security . DOMAIN_RID_ADMINISTRATOR ) )
2012-08-22 12:32:18 +04:00
BA_sid = security . dom_sid ( security . SID_BUILTIN_ADMINISTRATORS )
s4_passdb = passdb . PDB ( s3conf . get ( " passdb backend " ) )
2015-03-05 20:08:43 +03:00
# These assertions correct for current ad_dc selftest
2012-08-22 12:32:18 +04:00
# configuration. When other environments have a broad range of
# groups mapped via passdb, we can relax some of these checks
2018-07-30 09:19:05 +03:00
( LA_uid , LA_type ) = s4_passdb . sid_to_id ( LA_sid )
2012-08-22 12:32:18 +04:00
if ( LA_type != idmap . ID_TYPE_UID and LA_type != idmap . ID_TYPE_BOTH ) :
raise CommandError ( " SID %s is not mapped to a UID " % LA_sid )
2018-07-30 09:19:05 +03:00
( BA_gid , BA_type ) = s4_passdb . sid_to_id ( BA_sid )
2012-08-22 12:32:18 +04:00
if ( BA_type != idmap . ID_TYPE_GID and BA_type != idmap . ID_TYPE_BOTH ) :
raise CommandError ( " SID %s is not mapped to a GID " % BA_sid )
if use_ntvfs :
logger . warning ( " Please note that POSIX permissions have NOT been changed, only the stored NT ACL " )
2012-09-27 20:30:47 +04:00
2022-08-11 02:26:44 +03:00
try :
2023-11-23 10:52:03 +03:00
provision . setsysvolacl ( samdb , sysvol ,
2022-08-11 02:26:44 +03:00
LA_uid , BA_gid , domain_sid ,
lp . get ( " realm " ) . lower ( ) , samdb . domain_dn ( ) ,
lp , use_ntvfs = use_ntvfs )
except OSError as e :
if not e . filename :
raise
raise CommandError ( f " Could not access { e . filename } : { e . strerror } " , e )
2012-08-22 12:32:18 +04:00
2018-07-30 09:20:39 +03:00
2012-08-23 04:37:46 +04:00
class cmd_ntacl_sysvolcheck ( Command ) :
2012-10-08 14:32:58 +04:00
""" Check sysvol ACLs match defaults (including correct ACLs on GPOs). """
2012-08-23 04:37:46 +04:00
synopsis = " % prog <file> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" credopts " : options . CredentialsOptions ,
" versionopts " : options . VersionOptions ,
2018-07-30 09:14:37 +03:00
}
2012-08-23 04:37:46 +04:00
2012-09-27 20:30:47 +04:00
def run ( self , credopts = None , sambaopts = None , versionopts = None ) :
2012-08-23 04:37:46 +04:00
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp )
creds . set_kerberos_state ( DONT_USE_KERBEROS )
netlogon = lp . get ( " path " , " netlogon " )
sysvol = lp . get ( " path " , " sysvol " )
try :
2012-09-27 20:30:47 +04:00
samdb = SamDB ( session_info = system_session ( ) , lp = lp )
2018-02-14 00:07:23 +03:00
except Exception as e :
2012-08-23 04:37:46 +04:00
raise CommandError ( " Unable to open samdb: " , e )
domain_sid = security . dom_sid ( samdb . domain_sid )
2022-08-11 02:26:44 +03:00
try :
provision . checksysvolacl ( samdb , netlogon , sysvol ,
domain_sid ,
lp . get ( " realm " ) . lower ( ) , samdb . domain_dn ( ) ,
lp )
except OSError as e :
if not e . filename :
raise
raise CommandError ( f " Could not access { e . filename } : { e . strerror } " , e )
2012-08-23 04:37:46 +04:00
2012-08-22 12:32:18 +04:00
2011-08-31 01:19:59 +04:00
class cmd_ntacl ( SuperCommand ) :
2012-10-09 13:53:21 +04:00
""" NT ACLs manipulation. """
2009-10-24 15:34:31 +04:00
subcommands = { }
2011-08-31 01:19:59 +04:00
subcommands [ " set " ] = cmd_ntacl_set ( )
subcommands [ " get " ] = cmd_ntacl_get ( )
2019-06-11 16:11:20 +03:00
subcommands [ " changedomsid " ] = cmd_ntacl_changedomsid ( )
2012-08-22 12:32:18 +04:00
subcommands [ " sysvolreset " ] = cmd_ntacl_sysvolreset ( )
2012-08-23 04:37:46 +04:00
subcommands [ " sysvolcheck " ] = cmd_ntacl_sysvolcheck ( )
2015-09-26 02:16:50 +03:00
subcommands [ " getdosinfo " ] = cmd_dosinfo_get ( )