Commit ba6f1c51 authored by owsla's avatar owsla

Add error handling and logging to Windows ACL support; fixes Windows backup to

SMB share. Improve test in fs_abilities to determine if Windows ACLs are
supported.


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@944 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 72ed1ad2
New in v1.2.2 (????/??/??) New in v1.2.2 (????/??/??)
--------------------------- ---------------------------
Add error handling and logging to Windows ACL support; fixes Windows backup to
SMB share. Improve test in fs_abilities to determine if Windows ACLs are
supported. (Andrew Ferguson)
Add a warning message if extended attributes support is broken by the Add a warning message if extended attributes support is broken by the
filesystem (such as with older EncFS versions). (Andrew Ferguson) filesystem (such as with older EncFS versions). (Andrew Ferguson)
......
...@@ -122,7 +122,7 @@ class FSAbilities: ...@@ -122,7 +122,7 @@ class FSAbilities:
self.read_only = 1 self.read_only = 1
self.set_eas(rp, 0) self.set_eas(rp, 0)
self.set_acls(rp) self.set_acls(rp)
self.set_win_acls(rp) self.set_win_acls(rp, 0)
self.set_resource_fork_readonly(rp) self.set_resource_fork_readonly(rp)
self.set_carbonfile() self.set_carbonfile()
self.set_case_sensitive_readonly(rp) self.set_case_sensitive_readonly(rp)
...@@ -154,7 +154,7 @@ class FSAbilities: ...@@ -154,7 +154,7 @@ class FSAbilities:
self.set_fsync_dirs(subdir) self.set_fsync_dirs(subdir)
self.set_eas(subdir, 1) self.set_eas(subdir, 1)
self.set_acls(subdir) self.set_acls(subdir)
self.set_win_acls(subdir) self.set_win_acls(subdir, 1)
self.set_dir_inc_perms(subdir) self.set_dir_inc_perms(subdir)
self.set_resource_fork_readwrite(subdir) self.set_resource_fork_readwrite(subdir)
self.set_carbonfile() self.set_carbonfile()
...@@ -368,27 +368,53 @@ class FSAbilities: ...@@ -368,27 +368,53 @@ class FSAbilities:
self.eas = 0 self.eas = 0
except AssertionError: except AssertionError:
log.Log("Extended attributes support is broken on filesystem at " log.Log("Extended attributes support is broken on filesystem at "
"%s. Please upgrade the filesystem driver, contact the " "%s.\nPlease upgrade the filesystem driver, contact the "
"developers, or use the --no-eas option to disable " "developers,\nor use the --no-eas option to disable "
"extended attributes support and suppress this message." "extended attributes\nsupport and suppress this message."
% (rp.path,), 1) % (rp.path,), 1)
self.eas = 0 self.eas = 0
else: self.eas = 1 else: self.eas = 1
def set_win_acls(self, dir_rp): def set_win_acls(self, dir_rp, write):
"""Test if windows access control lists are supported""" """Test if windows access control lists are supported"""
assert Globals.local_connection is dir_rp.conn
assert dir_rp.lstat()
try: try:
import win32security import win32security, pywintypes
except ImportError: except ImportError:
log.Log("Unable to import win32security module. Windows ACLs\n" log.Log("Unable to import win32security module. Windows ACLs\n"
"not supported by filesystem at %s" % dir_rp.path, 4) "not supported by filesystem at %s" % dir_rp.path, 4)
self.win_acls = 0 self.win_acls = 0
return return
try:
sd = win32security.GetNamedSecurityInfo(dir_rp.path,
win32security.SE_FILE_OBJECT,
win32security.OWNER_SECURITY_INFORMATION |
win32security.GROUP_SECURITY_INFORMATION |
win32security.DACL_SECURITY_INFORMATION)
acl = sd.GetSecurityDescriptorDacl()
n = acl.GetAceCount()
if write:
win32security.SetNamedSecurityInfo(dir_rp.path,
win32security.SE_FILE_OBJECT,
win32security.OWNER_SECURITY_INFORMATION |
win32security.GROUP_SECURITY_INFORMATION |
win32security.DACL_SECURITY_INFORMATION,
sd.GetSecurityDescriptorOwner(),
sd.GetSecurityDescriptorGroup(),
sd.GetSecurityDescriptorDacl(),
None)
except (OSError, AttributeError, pywintypes.error):
log.Log("Unable to load a Windows ACL.\nWindows ACLs not supported "
"by filesystem at %s" % dir_rp.path, 4)
self.win_acls = 0
return
try: try:
win_acls.init_acls() win_acls.init_acls()
except OSError: except (OSError, AttributeError, pywintypes.error):
log.Log("Windows ACLs not supported by filesystem\n" log.Log("Unable to init win_acls.\nWindows ACLs not supported by "
"at %s" % dir_rp.path, 4) "filesystem at %s" % dir_rp.path, 4)
self.win_acls = 0 self.win_acls = 0
return return
self.win_acls = 1 self.win_acls = 1
...@@ -414,7 +440,7 @@ class FSAbilities: ...@@ -414,7 +440,7 @@ class FSAbilities:
try: try:
import Carbon.File import Carbon.File
import MacOS import MacOS
except: except (ImportError, AttributeError):
self.carbonfile = 0 self.carbonfile = 0
return return
......
...@@ -17,11 +17,12 @@ ...@@ -17,11 +17,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA # USA
import C, metadata, re, rorpiter, rpath import C, metadata, re, rorpiter, rpath, log
try: try:
from win32security import * from win32security import *
except: import pywintypes
except ImportError:
GROUP_SECURITY_INFORMATION = 0 GROUP_SECURITY_INFORMATION = 0
OWNER_SECURITY_INFORMATION = 0 OWNER_SECURITY_INFORMATION = 0
DACL_SECURITY_INFORMATION = 0 DACL_SECURITY_INFORMATION = 0
...@@ -40,8 +41,11 @@ class ACL: ...@@ -40,8 +41,11 @@ class ACL:
def load_from_rp(self, rp, skip_inherit_only = True): def load_from_rp(self, rp, skip_inherit_only = True):
self.index = rp.index self.index = rp.index
try: try:
sd = rp.conn.win32security.GetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags) sd = rp.conn.win32security. \
except: GetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags)
except (OSError, IOError, pywintypes.error), exc:
log.Log("Warning: unable to read ACL from %s: %s"
% (repr(rp.path), exc), 4)
return return
if skip_inherit_only: if skip_inherit_only:
...@@ -70,18 +74,30 @@ class ACL: ...@@ -70,18 +74,30 @@ class ACL:
sd.SetSecurityDescriptorSacl(1, acl, 0) sd.SetSecurityDescriptorSacl(1, acl, 0)
if not sd.GetSecurityDescriptorDacl(): if not sd.GetSecurityDescriptorDacl():
sd.SetSecurityDescriptorDacl(0, None, 0) sd.SetSecurityDescriptorDacl(0, None, 0)
if not sd.GetSecurityDescriptorSacl(): if (ACL.flags & SACL_SECURITY_INFORMATION) and not \
sd.GetSecurityDescriptorSacl():
sd.SetSecurityDescriptorSacl(0, None, 0) sd.SetSecurityDescriptorSacl(0, None, 0)
self.__acl = \ try:
rp.conn.win32security.ConvertSecurityDescriptorToStringSecurityDescriptor(sd, self.__acl = \
rp.conn.win32security. \
ConvertSecurityDescriptorToStringSecurityDescriptor(sd,
SDDL_REVISION_1, ACL.flags) SDDL_REVISION_1, ACL.flags)
except (OSError, IOError, pywintypes.error), exc:
log.Log("Warning: unable to convert ACL from %s to string: %s"
% (repr(rp.path), exc), 4)
self.__acl = ''
def clear_rp(self, rp): def clear_rp(self, rp):
# not sure how to interpret this # not sure how to interpret this
# I'll jus clear all acl-s from rp.path # I'll just clear all acl-s from rp.path
sd = rp.conn.win32security.GetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags) try:
sd = rp.conn.win32security. \
GetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags)
except (OSError, IOError, pywintypes.error), exc:
log.Log("Warning: unable to read ACL from %s for clearing: %s"
% (repr(rp.path), exc), 4)
acl = sd.GetSecurityDescriptorDacl() acl = sd.GetSecurityDescriptorDacl()
if acl: if acl:
...@@ -102,42 +118,65 @@ class ACL: ...@@ -102,42 +118,65 @@ class ACL:
acl.DeleteAce(n) acl.DeleteAce(n)
sd.SetSecurityDescriptorSacl(0, acl, 0) sd.SetSecurityDescriptorSacl(0, acl, 0)
rp.conn.win32security.SetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags, try:
sd.GetSecurityDescriptorOwner(), sd.GetSecurityDescriptorGroup(), rp.conn.win32security. \
sd.GetSecurityDescriptorDacl(), sd.GetSecurityDescriptorSacl()) SetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags,
sd.GetSecurityDescriptorOwner(),sd.GetSecurityDescriptorGroup(),
sd.GetSecurityDescriptorDacl(),
(ACL.flags & SACL_SECURITY_INFORMATION) and
sd.GetSecurityDescriptorSacl() or None)
except (OSError, IOError, pywintypes.error), exc:
log.Log("Warning: unable to set ACL on %s after clearing: %s"
% (repr(rp.path), exc), 4)
def write_to_rp(self, rp): def write_to_rp(self, rp):
if self.__acl: if not self.__acl:
sd = rp.conn.win32security.ConvertStringSecurityDescriptorToSecurityDescriptor(self.__acl, return
SDDL_REVISION_1)
try:
# Enable the next block of code for dirs after we have a mechanism in sd = rp.conn.win32security. \
# backup.py (and similar) to do a first pass to see if a directory ConvertStringSecurityDescriptorToSecurityDescriptor(
# has SE_DACL_PROTECTED. In that case, we will need to self.__acl, SDDL_REVISION_1)
# 1) dest_rorp.write_win_acl(source_rorp.get_win_acl()) except (OSError, IOError, pywintypes.error), exc:
# --> And clear the existing dest_rorp one while doing so log.Log("Warning: unable to convert string %s to ACL: %s"
# 2) Check if backup user has Admin privs to write to dest_rorp % (repr(self.__acl), exc), 4)
# 3) If not, add Admin write privs to dest_rorp and add dir
# to dir_perms_list-equivalent # Enable next block of code for dirs after we have a mechanism in
# 4) THEN, allow the pre_process() function to finish and the # backup.py (and similar) to do a first pass to see if a directory
# files be copied over. Those files which wish to # has SE_DACL_PROTECTED. In that case, we will need to
# will now inherit the correct ACE objects. # 1) dest_rorp.write_win_acl(source_rorp.get_win_acl())
# 5) If dir was on dir_perms_list-equivalent, drop the write # --> And clear existing dest_rorp one while doing so
# write permission we added. # 2) Check if backup user has Admin privs to write dest_rorp
# 6) When copy_attribs is called in end_process, make sure # --> May need to use Win32 AccessCheck() API
# that the write_win_acl() call isn't made this time # 3) If not, add Admin write privs to dest_rorp and add dir
# The reason we will need to do this is because otherwise, the files # to dir_perms_list-equivalent
# which are created during step 4 will reference the ACE entries # 4) THEN, allow the pre_process() function to finish and the
# which we clear during step 6. We need to clear them *before* the # files be copied over. Those files which wish to
# children files/subdirs are created and generate the appropriate # will now inherit the correct ACE objects.
# DACL so the inheritance magic can happen during step 4. # 5) If dir was on dir_perms_list-equivalent, drop the write
(flags, revision) = sd.GetSecurityDescriptorControl() # write permission we added.
if (not rp.isdir() and flags & SE_DACL_PROTECTED): # 6) When copy_attribs is called in end_process, make sure
self.clear_rp(rp) # that the write_win_acl() call isn't made this time
# The reason we will need to do this is because otherwise, the files
rp.conn.win32security.SetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags, # which are created during step 4 will reference the ACE entries
sd.GetSecurityDescriptorOwner(), sd.GetSecurityDescriptorGroup(), # which we clear during step 6. We need to clear them *before* the
sd.GetSecurityDescriptorDacl(), sd.GetSecurityDescriptorSacl()) # children files/subdirs are created and generate the appropriate
# DACL so the inheritance magic can happen during step 4.
(flags, revision) = sd.GetSecurityDescriptorControl()
if (not rp.isdir() and flags & SE_DACL_PROTECTED):
self.clear_rp(rp)
try:
rp.conn.win32security. \
SetNamedSecurityInfo(rp.path, SE_FILE_OBJECT, ACL.flags,
sd.GetSecurityDescriptorOwner(),sd.GetSecurityDescriptorGroup(),
sd.GetSecurityDescriptorDacl(),
(ACL.flags & SACL_SECURITY_INFORMATION) and
sd.GetSecurityDescriptorSacl() or None)
except (OSError, IOError, pywintypes.error), exc:
log.Log("Warning: unable to set ACL on %s: %s"
% (repr(rp.path), exc), 4)
def __str__(self): def __str__(self):
return '# file: %s\n%s\n' % \ return '# file: %s\n%s\n' % \
...@@ -208,20 +247,24 @@ def init_acls(): ...@@ -208,20 +247,24 @@ def init_acls():
import win32api import win32api
try: try:
hnd = OpenProcessToken(win32api.GetCurrentProcess(), hnd = OpenProcessToken(win32api.GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES| TOKEN_QUERY) TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY)
except win32api.error: except win32api.error, exc:
log.Log("Warning: unable to open Windows process token: %s"
% exc, 5)
return return
try: try:
try: try:
lpv = lambda priv: LookupPrivilegeValue(None, priv) lpv = lambda priv: LookupPrivilegeValue(None, priv)
# enable the SE_*_NAME priveleges # enable the SE_*_NAME privileges
SecurityName = lpv(SE_SECURITY_NAME) SecurityName = lpv(SE_SECURITY_NAME)
AdjustTokenPrivileges(hnd, False, [ AdjustTokenPrivileges(hnd, False, [
(SecurityName, SE_PRIVILEGE_ENABLED), (SecurityName, SE_PRIVILEGE_ENABLED),
(lpv(SE_BACKUP_NAME), SE_PRIVILEGE_ENABLED), (lpv(SE_BACKUP_NAME), SE_PRIVILEGE_ENABLED),
(lpv(SE_RESTORE_NAME), SE_PRIVILEGE_ENABLED) (lpv(SE_RESTORE_NAME), SE_PRIVILEGE_ENABLED)
]) ])
except win32api.error: except win32api.error, exc:
log.Log("Warning: unable to enable SE_*_NAME privileges: %s"
% exc, 5)
return return
for name, enabled in GetTokenInformation(hnd, TokenPrivileges): for name, enabled in GetTokenInformation(hnd, TokenPrivileges):
if name == SecurityName and enabled: if name == SecurityName and enabled:
...@@ -230,4 +273,3 @@ def init_acls(): ...@@ -230,4 +273,3 @@ def init_acls():
break break
finally: finally:
win32api.CloseHandle(hnd) win32api.CloseHandle(hnd)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment