Browse Source

Windows: SACL

Antti Aalto 8 years ago
parent
commit
8c5b58d57e
3 changed files with 142 additions and 67 deletions
  1. 1 1
      src/borg/constants.py
  2. 1 0
      src/borg/item.py
  3. 140 66
      src/borg/platform/windows.pyx

+ 1 - 1
src/borg/constants.py

@@ -2,7 +2,7 @@
 ITEM_KEYS = frozenset(['path', 'source', 'rdev', 'chunks', 'chunks_healthy', 'hardlink_master',
 ITEM_KEYS = frozenset(['path', 'source', 'rdev', 'chunks', 'chunks_healthy', 'hardlink_master',
                        'mode', 'user', 'group', 'uid', 'gid', 'mtime', 'atime', 'ctime',
                        'mode', 'user', 'group', 'uid', 'gid', 'mtime', 'atime', 'ctime',
                        'xattrs', 'bsdflags', 'acl_nfs4', 'acl_access', 'acl_default', 'acl_extended', 'win_dacl',
                        'xattrs', 'bsdflags', 'acl_nfs4', 'acl_access', 'acl_default', 'acl_extended', 'win_dacl',
-                       'user_sid', ])
+                       'win_sacl', 'user_sid', ])
 
 
 # this is the set of keys that are always present in items:
 # this is the set of keys that are always present in items:
 REQUIRED_ITEM_KEYS = frozenset(['path', 'mtime', ])
 REQUIRED_ITEM_KEYS = frozenset(['path', 'mtime', ])

+ 1 - 0
src/borg/item.py

@@ -135,6 +135,7 @@ class Item(PropDict):
     acl_extended = PropDict._make_property('acl_extended', bytes)
     acl_extended = PropDict._make_property('acl_extended', bytes)
     acl_nfs4 = PropDict._make_property('acl_nfs4', bytes)
     acl_nfs4 = PropDict._make_property('acl_nfs4', bytes)
     win_dacl = PropDict._make_property('win_dacl', str, 'surrogate-escaped str', encode=safe_encode, decode=safe_decode)
     win_dacl = PropDict._make_property('win_dacl', str, 'surrogate-escaped str', encode=safe_encode, decode=safe_decode)
+    win_sacl = PropDict._make_property('win_sacl', str, 'surrogate-escaped str', encode=safe_encode, decode=safe_decode)
 
 
     mode = PropDict._make_property('mode', int)
     mode = PropDict._make_property('mode', int)
     uid = PropDict._make_property('uid', int)
     uid = PropDict._make_property('uid', int)

+ 140 - 66
src/borg/platform/windows.pyx

@@ -170,7 +170,7 @@ cdef extern from 'Aclapi.h':
     DWORD GetNamedSecurityInfoW(LPCTSTR, SE_OBJECT_TYPE, DWORD, PSID*, PSID*, PACL*, PACL*, _ACL**)
     DWORD GetNamedSecurityInfoW(LPCTSTR, SE_OBJECT_TYPE, DWORD, PSID*, PSID*, PACL*, PACL*, _ACL**)
     DWORD SetNamedSecurityInfoW(LPCTSTR, int, int, PSID, PSID, PACL, PACL)
     DWORD SetNamedSecurityInfoW(LPCTSTR, int, int, PSID, PSID, PACL, PACL)
     DWORD SetEntriesInAclW(unsigned int, _EXPLICIT_ACCESS_W*, PACL, _ACL**)
     DWORD SetEntriesInAclW(unsigned int, _EXPLICIT_ACCESS_W*, PACL, _ACL**)
-    DWORD LookupSecurityDescriptorPartsW(_TRUSTEE_W**, _TRUSTEE_W**, uint32_t*,_EXPLICIT_ACCESS_W**, uint32_t*,_EXPLICIT_ACCESS_W**, PSID)
+    DWORD LookupSecurityDescriptorPartsW(_TRUSTEE_W**, _TRUSTEE_W**, uint32_t*, _EXPLICIT_ACCESS_W**, uint32_t*, _EXPLICIT_ACCESS_W**, PSID)
 
 
 
 
 def raise_error(api, path=''):
 def raise_error(api, path=''):
@@ -194,8 +194,11 @@ permissions_granted = False # Did we get them
 
 
 
 
 cdef enable_permissions():
 cdef enable_permissions():
+    global permissions_enabled
+    global permissions_granted
     if permissions_enabled:
     if permissions_enabled:
         return
         return
+    permissions_enabled = True
     cdef HANDLE hToken
     cdef HANDLE hToken
     OpenProcessToken(GetCurrentProcess() , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)
     OpenProcessToken(GetCurrentProcess() , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)
 
 
@@ -207,7 +210,7 @@ cdef enable_permissions():
     cdef wchar_t* privilege = PyUnicode_AsWideCharString("SeSecurityPrivilege", NULL)
     cdef wchar_t* privilege = PyUnicode_AsWideCharString("SeSecurityPrivilege", NULL)
     if not LookupPrivilegeValueW( NULL, privilege, &luid ):
     if not LookupPrivilegeValueW( NULL, privilege, &luid ):
         permissions_granted = False
         permissions_granted = False
-        print("Warning: permissions to read SACL denied. Try running as admin.")
+        print("Warning: permissions to read auditing settings (SACL) denied. Try running as admin.")
         return
         return
 
 
     tp.PrivilegeCount           = 1
     tp.PrivilegeCount           = 1
@@ -217,7 +220,7 @@ cdef enable_permissions():
     AdjustTokenPrivileges(hToken, 0, &tp, sizeof(_TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious)
     AdjustTokenPrivileges(hToken, 0, &tp, sizeof(_TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious)
     if GetLastError() != ERROR_SUCCESS:
     if GetLastError() != ERROR_SUCCESS:
         permissions_granted = False
         permissions_granted = False
-        print("Warning: permissions to read SACL denied. Try running as admin.")
+        print("Warning: permissions to read auditing settings (SACL) denied. Try running as admin.")
         return
         return
 
 
     tpPrevious.PrivilegeCount           = 1
     tpPrevious.PrivilegeCount           = 1
@@ -228,7 +231,7 @@ cdef enable_permissions():
 
 
     if GetLastError() != ERROR_SUCCESS:
     if GetLastError() != ERROR_SUCCESS:
         permissions_granted = False
         permissions_granted = False
-        print("Warning: permissions to read SACL denied. Try running as admin.")
+        print("Warning: permissions to read auditing settings (SACL) denied. Try running as admin.")
         return
         return
 
 
 
 
@@ -342,109 +345,180 @@ def set_owner(path, owner, sidstring = None):
 
 
 
 
 def acl_get(path, item, st, numeric_owner=False, depth = 0):
 def acl_get(path, item, st, numeric_owner=False, depth = 0):
+    if not permissions_enabled:
+        enable_permissions()
     pyDACL = []
     pyDACL = []
+    pySACL = []
     if not os.path.samefile(os.path.abspath(path), os.path.abspath(os.path.join(path, ".."))):
     if not os.path.samefile(os.path.abspath(path), os.path.abspath(os.path.join(path, ".."))):
-        pyDACL = acl_get(os.path.abspath(os.path.join(path, "..")), item, st, numeric_owner, depth + 1)
-
-
+        pyDACL, pySACL = acl_get(os.path.abspath(os.path.join(path, "..")), item, st, numeric_owner, depth + 1)
 
 
     cdef int request = DACL_SECURITY_INFORMATION
     cdef int request = DACL_SECURITY_INFORMATION
+    if permissions_granted:
+        request = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION
     cdef BYTE* SD = _get_file_security(path, request)
     cdef BYTE* SD = _get_file_security(path, request)
     if SD == NULL:
     if SD == NULL:
         return
         return
 
 
-    cdef BOOL daclFound
-    cdef _ACL* DACL
-    cdef BOOL DACLDefaulted
-
-    cdef uint32_t length
-    cdef _EXPLICIT_ACCESS_W* ACEs
-
-    # LookupSecurityDescriptorPartsW(&owner, &group, &daclLength, &DACL, &saclLength, &sacl, SD)
-    LookupSecurityDescriptorPartsW(NULL, NULL, &length, &ACEs, NULL, NULL, SD)
-    """GetSecurityDescriptorDacl(SD, &daclFound, &DACL, &DACLDefaulted)
-    print("AceCount", DACL.AceCount)
+    cdef uint32_t dacllength
+    cdef _EXPLICIT_ACCESS_W* DACL
+    cdef uint32_t sacllength
+    cdef _EXPLICIT_ACCESS_W* SACL
 
 
-    cdef uint32_t length
-    cdef _EXPLICIT_ACCESS_W* ACEs
-
-    GetExplicitEntriesFromAclW(DACL, &length, &ACEs)"""
+    # LookupSecurityDescriptorPartsW(&owner, &group, &dacllength, &DACL, &sacllength, &sacl, SD)
+    LookupSecurityDescriptorPartsW(NULL, NULL, &dacllength, &DACL, &sacllength, &SACL, SD)
 
 
     cdef PSID newsid
     cdef PSID newsid
     cdef uint32_t domainlength
     cdef uint32_t domainlength
+    cdef uint32_t sidlength
     cdef _SID_NAME_USE sid_type
     cdef _SID_NAME_USE sid_type
-    for i in range(length):
+
+    # DACL
+    for i in range(dacllength):
         permissions = None
         permissions = None
         name = ""
         name = ""
         sidstr = ""
         sidstr = ""
-        if ACEs[i].Trustee.TrusteeForm == TRUSTEE_IS_SID:
-            name, domain, type = _look_up_account_sid(<BYTE*>(ACEs[i].Trustee.ptstrName))
-            sidstr = sid2string(<PSID>(ACEs[i].Trustee.ptstrName))
+        if DACL[i].Trustee.TrusteeForm == TRUSTEE_IS_SID:
+            name, domain, type = _look_up_account_sid(<BYTE*>(DACL[i].Trustee.ptstrName))
+            sidstr = sid2string(<PSID>(DACL[i].Trustee.ptstrName))
 
 
-        elif ACEs[i].Trustee.TrusteeForm == TRUSTEE_IS_NAME:
+        elif DACL[i].Trustee.TrusteeForm == TRUSTEE_IS_NAME:
             sid_type = SidTypeInvalid
             sid_type = SidTypeInvalid
             domainlength = 0
             domainlength = 0
-            LookupAccountNameW(NULL, ACEs[i].Trustee.ptstrName, NULL, &(length), NULL, &domainlength, &sid_type)
+            LookupAccountNameW(NULL, DACL[i].Trustee.ptstrName, NULL, &sidlength, NULL, &domainlength, &sid_type)
 
 
-            newsid = <PSID>malloc((length) * sizeof(BYTE))
+            newsid = <PSID>malloc((sidlength) * sizeof(BYTE))
             domainlength = 0
             domainlength = 0
-            LookupAccountNameW(NULL, ACEs[i].Trustee.ptstrName, newsid, &length, NULL, &domainlength, &sid_type)
+            LookupAccountNameW(NULL, DACL[i].Trustee.ptstrName, newsid, &sidlength, NULL, &domainlength, &sid_type)
             trusteeName, domain, type = _look_up_account_sid(newsid)
             trusteeName, domain, type = _look_up_account_sid(newsid)
 
 
             name = trusteeName
             name = trusteeName
             sidstr = sid2string(newsid)
             sidstr = sid2string(newsid)
             free(newsid)
             free(newsid)
 
 
-        elif ACEs[i].Trustee.TrusteeForm == TRUSTEE_BAD_FORM:
+        elif DACL[i].Trustee.TrusteeForm == TRUSTEE_BAD_FORM:
             continue
             continue
-        if ((depth == 0 and ACEs[i].grfInheritance & INHERIT_ONLY != 0)
-            or (ACEs[i].grfInheritance & INHERIT_NO_PROPAGATE and depth == 1)
-            or (ACEs[i].grfInheritance != NO_INHERITANCE and ACEs[i].grfInheritance & INHERIT_NO_PROPAGATE == 0)):
-            permissions = {'user': {'name': name, 'sid': sidstr}, 'permissions': (ACEs[i].grfAccessPermissions, ACEs[i].grfAccessMode, NO_INHERITANCE)}
+        if ((depth == 0 and DACL[i].grfInheritance & INHERIT_ONLY != 0)
+            or (DACL[i].grfInheritance & INHERIT_NO_PROPAGATE and depth == 1)
+            or (DACL[i].grfInheritance != NO_INHERITANCE and DACL[i].grfInheritance & INHERIT_NO_PROPAGATE == 0)):
+            permissions = {'user': {'name': name, 'sid': sidstr}, 'permissions': (DACL[i].grfAccessPermissions, DACL[i].grfAccessMode, NO_INHERITANCE)}
             pyDACL.append(permissions)
             pyDACL.append(permissions)
+
+    if permissions_granted:
+        for i in range(sacllength):
+            permissions = None
+            name = ""
+            sidstr = ""
+            if DACL[i].Trustee.TrusteeForm == TRUSTEE_IS_SID:
+                name, domain, type = _look_up_account_sid(<BYTE*>(SACL[i].Trustee.ptstrName))
+                sidstr = sid2string(<PSID>(SACL[i].Trustee.ptstrName))
+
+            elif SACL[i].Trustee.TrusteeForm == TRUSTEE_IS_NAME:
+                sid_type = SidTypeInvalid
+                domainlength = 0
+                LookupAccountNameW(NULL, SACL[i].Trustee.ptstrName, NULL, &sidlength, NULL, &domainlength, &sid_type)
+
+                newsid = <PSID>malloc((sidlength) * sizeof(BYTE))
+                domainlength = 0
+                LookupAccountNameW(NULL, SACL[i].Trustee.ptstrName, newsid, &sidlength, NULL, &domainlength, &sid_type)
+                trusteeName, domain, type = _look_up_account_sid(newsid)
+
+                name = trusteeName
+                sidstr = sid2string(newsid)
+                free(newsid)
+            else:
+                continue
+            if ((depth == 0 and SACL[i].grfInheritance & INHERIT_ONLY != 0)
+                or (SACL[i].grfInheritance & INHERIT_NO_PROPAGATE and depth == 1)
+                or (SACL[i].grfInheritance != NO_INHERITANCE and SACL[i].grfInheritance & INHERIT_NO_PROPAGATE == 0)):
+                permissions = {'user': {'name': name, 'sid': sidstr}, 'permissions': (SACL[i].grfAccessPermissions, SACL[i].grfAccessMode, NO_INHERITANCE)}
+                pySACL.append(permissions)
+
     if depth == 0:
     if depth == 0:
         item['win_dacl'] = json.dumps(pyDACL)
         item['win_dacl'] = json.dumps(pyDACL)
-        print(pyDACL)
+        item['win_sacl'] = json.dumps(pySACL)
 
 
     free(SD)
     free(SD)
-    LocalFree(<HLOCAL>ACEs)
-    return pyDACL
+    LocalFree(<HLOCAL>DACL)
+    LocalFree(<HLOCAL>SACL)
+    return pyDACL,pySACL
 
 
 
 
 def acl_set(path, item, numeric_owner=False):
 def acl_set(path, item, numeric_owner=False):
-    if 'win_dacl' not in item:
-        return
+    if not permissions_enabled:
+        enable_permissions()
 
 
-    pyDACL = json.loads(item.win_dacl)
-    cdef _EXPLICIT_ACCESS_W* ACEs = <_EXPLICIT_ACCESS_W*>calloc(sizeof(_EXPLICIT_ACCESS_W), len(pyDACL))
+    cdef _EXPLICIT_ACCESS_W* DACL
     cdef wchar_t* temp
     cdef wchar_t* temp
     cdef PSID newsid
     cdef PSID newsid
-    for i in range(len(pyDACL)):
-        if pyDACL[i]['user']['name'] == '' or numeric_owner:
-            ACEs[i].Trustee.TrusteeForm = TRUSTEE_IS_SID
-            temp = PyUnicode_AsWideCharString(pyDACL[i]['user']['sid'], NULL)
-            ConvertStringSidToSidW(temp, &newsid)
-            ACEs[i].Trustee.ptstrName = <LPCTSTR>newsid
-            PyMem_Free(temp)
-        else:
-            ACEs[i].Trustee.TrusteeForm = TRUSTEE_IS_NAME
-            ACEs[i].Trustee.ptstrName = PyUnicode_AsWideCharString(pyDACL[i]['user']['name'], NULL)
-        ACEs[i].grfAccessPermissions = pyDACL[i]['permissions'][0]
-        ACEs[i].grfAccessMode = pyDACL[i]['permissions'][1]
-        ACEs[i].grfInheritance = pyDACL[i]['permissions'][2]
     cdef _ACL* newDACL
     cdef _ACL* newDACL
-    SetEntriesInAclW(len(pyDACL), ACEs, NULL, &newDACL)
-    cdef wchar_t* cstrPath = PyUnicode_AsWideCharString(path, NULL)
-    SetNamedSecurityInfoW(cstrPath, SE_FILE_OBJECT, PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, newDACL, NULL)
 
 
-    for i in range(len(pyDACL)):
-        if pyDACL[i]['user']['name'] == '' or numeric_owner:
-            LocalFree(<HLOCAL>ACEs[i].Trustee.ptstrName)
-        else:
-            PyMem_Free(ACEs[i].Trustee.ptstrName)
-    free(ACEs)
-    PyMem_Free(cstrPath)
-    LocalFree(<HLOCAL>newDACL)
+    cdef wchar_t* cstrPath
+
+    if 'win_dacl' in item:
+        pyDACL = json.loads(item.win_dacl)
+        if len(pyDACL) > 0:
+            DACL = <_EXPLICIT_ACCESS_W*>calloc(sizeof(_EXPLICIT_ACCESS_W), len(pyDACL))
+
+            for i in range(len(pyDACL)):
+                if pyDACL[i]['user']['name'] == '' or numeric_owner:
+                    DACL[i].Trustee.TrusteeForm = TRUSTEE_IS_SID
+                    temp = PyUnicode_AsWideCharString(pyDACL[i]['user']['sid'], NULL)
+                    ConvertStringSidToSidW(temp, &newsid)
+                    DACL[i].Trustee.ptstrName = <LPCTSTR>newsid
+                    PyMem_Free(temp)
+                else:
+                    DACL[i].Trustee.TrusteeForm = TRUSTEE_IS_NAME
+                    DACL[i].Trustee.ptstrName = PyUnicode_AsWideCharString(pyDACL[i]['user']['name'], NULL)
+                DACL[i].grfAccessPermissions = pyDACL[i]['permissions'][0]
+                DACL[i].grfAccessMode = pyDACL[i]['permissions'][1]
+                DACL[i].grfInheritance = pyDACL[i]['permissions'][2]
+
+            SetEntriesInAclW(len(pyDACL), DACL, NULL, &newDACL)
+            cstrPath = PyUnicode_AsWideCharString(path, NULL)
+            SetNamedSecurityInfoW(cstrPath, SE_FILE_OBJECT, PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, newDACL, NULL)
+
+            for i in range(len(pyDACL)):
+                if pyDACL[i]['user']['name'] == '' or numeric_owner:
+                    LocalFree(<HLOCAL>DACL[i].Trustee.ptstrName)
+                else:
+                    PyMem_Free(DACL[i].Trustee.ptstrName)
+            free(DACL)
+            PyMem_Free(cstrPath)
+            LocalFree(<HLOCAL>newDACL)
+
+    cdef _EXPLICIT_ACCESS_W* SACL
+    cdef _ACL* newSACL
+    if permissions_granted and 'win_sacl' in item:
+        pySACL = json.loads(item.win_sacl)
+        if len(pySACL) > 0:
+            SACL = <_EXPLICIT_ACCESS_W*>calloc(sizeof(_EXPLICIT_ACCESS_W), len(pySACL))
+
+            for i in range(len(pyDACL)):
+                if pySACL[i]['user']['name'] == '' or numeric_owner:
+                    SACL[i].Trustee.TrusteeForm = TRUSTEE_IS_SID
+                    temp = PyUnicode_AsWideCharString(pySACL[i]['user']['sid'], NULL)
+                    ConvertStringSidToSidW(temp, &newsid)
+                    SACL[i].Trustee.ptstrName = <LPCTSTR>newsid
+                    PyMem_Free(temp)
+                else:
+                    SACL[i].Trustee.TrusteeForm = TRUSTEE_IS_NAME
+                    SACL[i].Trustee.ptstrName = PyUnicode_AsWideCharString(pySACL[i]['user']['name'], NULL)
+                SACL[i].grfAccessPermissions = pySACL[i]['permissions'][0]
+                SACL[i].grfAccessMode = pySACL[i]['permissions'][1]
+                SACL[i].grfInheritance = pySACL[i]['permissions'][2]
+
+            SetEntriesInAclW(len(pySACL), SACL, NULL, &newSACL)
+            cstrPath = PyUnicode_AsWideCharString(path, NULL)
+            SetNamedSecurityInfoW(cstrPath, SE_FILE_OBJECT, PROTECTED_SACL_SECURITY_INFORMATION, NULL, NULL, newSACL, NULL)
+
+            for i in range(len(pySACL)):
+                if pySACL[i]['user']['name'] == '' or numeric_owner:
+                    LocalFree(<HLOCAL>SACL[i].Trustee.ptstrName)
+                else:
+                    PyMem_Free(SACL[i].Trustee.ptstrName)
+            free(SACL)
+            PyMem_Free(cstrPath)
+            LocalFree(<HLOCAL>newSACL)
 
 
 
 
 def sync_dir(path):
 def sync_dir(path):