2021-01-22 20:09:55 +03:00
# vgp_files_ext samba gpo policy
# Copyright (C) David Mulder <dmulder@suse.com> 2020
#
# 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/>.
2021-01-25 18:13:18 +03:00
import os , pwd , grp
2022-11-29 18:04:35 +03:00
from samba . gp . gpclass import gp_xml_ext , check_safe_path , gp_file_applier
2021-01-25 18:13:18 +03:00
from tempfile import NamedTemporaryFile
from shutil import copyfile , move
2022-03-02 12:23:51 +03:00
from samba . gp . util . logging import log
2021-01-25 18:13:18 +03:00
def calc_mode ( entry ) :
mode = 0o000
for permissions in entry . findall ( ' permissions ' ) :
ptype = permissions . get ( ' type ' )
if ptype == ' user ' :
if permissions . find ( ' read ' ) is not None :
mode | = 0o400
if permissions . find ( ' write ' ) is not None :
mode | = 0o200
if permissions . find ( ' execute ' ) is not None :
mode | = 0o100
elif ptype == ' group ' :
if permissions . find ( ' read ' ) is not None :
mode | = 0o040
if permissions . find ( ' write ' ) is not None :
mode | = 0o020
if permissions . find ( ' execute ' ) is not None :
mode | = 0o010
elif ptype == ' other ' :
if permissions . find ( ' read ' ) is not None :
mode | = 0o004
if permissions . find ( ' write ' ) is not None :
mode | = 0o002
if permissions . find ( ' execute ' ) is not None :
mode | = 0o001
return mode
def stat_from_mode ( mode ) :
stat = ' - '
for i in range ( 6 , - 1 , - 3 ) :
mask = { 0o4 : ' r ' , 0o2 : ' w ' , 0o1 : ' x ' }
for x in mask . keys ( ) :
if mode & ( x << i ) :
stat + = mask [ x ]
else :
stat + = ' - '
return stat
2021-01-22 20:09:55 +03:00
2022-11-29 18:04:35 +03:00
def source_file_change ( fname ) :
if os . path . exists ( fname ) :
return b ' %d ' % os . stat ( fname ) . st_ctime
class vgp_files_ext ( gp_xml_ext , gp_file_applier ) :
2021-01-25 18:13:18 +03:00
def __str__ ( self ) :
2021-04-28 19:32:44 +03:00
return ' VGP/Unix Settings/Files '
2021-01-25 18:13:18 +03:00
2021-01-22 20:09:55 +03:00
def process_group_policy ( self , deleted_gpo_list , changed_gpo_list ) :
2021-01-25 18:13:18 +03:00
for guid , settings in deleted_gpo_list :
if str ( self ) in settings :
for attribute , _ in settings [ str ( self ) ] . items ( ) :
2022-11-29 18:04:35 +03:00
self . unapply ( guid , attribute , attribute )
2021-01-25 18:13:18 +03:00
for gpo in changed_gpo_list :
if gpo . file_sys_path :
xml = ' MACHINE/VGP/VTLA/Unix/Files/manifest.xml '
path = os . path . join ( gpo . file_sys_path , xml )
xml_conf = self . parse ( path )
if not xml_conf :
continue
policy = xml_conf . find ( ' policysetting ' )
data = policy . find ( ' data ' )
for entry in data . findall ( ' file_properties ' ) :
local_path = self . lp . cache_path ( ' gpo_cache ' )
source = entry . find ( ' source ' ) . text
source_file = os . path . join ( local_path ,
os . path . dirname ( check_safe_path ( path ) ) . upper ( ) ,
source . upper ( ) )
if not os . path . exists ( source_file ) :
2022-03-02 12:23:51 +03:00
log . warn ( ' Source file does not exist ' , source_file )
2021-01-25 18:13:18 +03:00
continue
target = entry . find ( ' target ' ) . text
user = entry . find ( ' user ' ) . text
group = entry . find ( ' group ' ) . text
mode = calc_mode ( entry )
2022-11-29 18:04:35 +03:00
# The attribute is simply the target file.
attribute = target
# The value hash is generated from the source file last
# change stamp, the user, the group, and the mode, ensuring
# any changes to this GPO will cause the file to be
# rewritten.
value_hash = self . generate_value_hash (
source_file_change ( source_file ) ,
user , group , b ' %d ' % mode )
def applier_func ( source_file , target , user , group , mode ) :
with NamedTemporaryFile ( dir = os . path . dirname ( target ) ,
delete = False ) as f :
copyfile ( source_file , f . name )
os . chown ( f . name , pwd . getpwnam ( user ) . pw_uid ,
grp . getgrnam ( group ) . gr_gid )
os . chmod ( f . name , mode )
move ( f . name , target )
return [ target ]
self . apply ( gpo . name , attribute , value_hash , applier_func ,
source_file , target , user , group , mode )
2021-01-22 20:09:55 +03:00
def rsop ( self , gpo ) :
output = { }
2021-01-25 18:13:18 +03:00
xml = ' MACHINE/VGP/VTLA/Unix/Files/manifest.xml '
if gpo . file_sys_path :
path = os . path . join ( gpo . file_sys_path , xml )
xml_conf = self . parse ( path )
if not xml_conf :
return output
policy = xml_conf . find ( ' policysetting ' )
data = policy . find ( ' data ' )
for entry in data . findall ( ' file_properties ' ) :
source = entry . find ( ' source ' ) . text
target = entry . find ( ' target ' ) . text
user = entry . find ( ' user ' ) . text
group = entry . find ( ' group ' ) . text
mode = calc_mode ( entry )
p = ' %s \t %s \t %s \t %s -> %s ' % \
( stat_from_mode ( mode ) , user , group , target , source )
if str ( self ) not in output . keys ( ) :
output [ str ( self ) ] = [ ]
output [ str ( self ) ] . append ( p )
2021-01-22 20:09:55 +03:00
return output