which included commits to RCS files with non-trunk default branches. git-svn-id: svn://10.65.10.50/trunk@5403 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			660 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			660 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
| 
 | |
|   Copyright (c) 1996  Scott Field
 | |
| 
 | |
|   Module Name:
 | |
| 
 | |
|     nt.c
 | |
| 
 | |
|   Abstract:
 | |
| 
 | |
|     This module implements WinNT security descriptor operations for the
 | |
|     Win32 Info-ZIP project.  Operation such as setting file security,
 | |
|     using/querying local and remote privileges, and queuing of operations
 | |
|     is performed here.  The contents of this module are only relevant
 | |
|     when the code is running on Windows NT, and the target volume supports
 | |
|     persistent Acl storage.
 | |
| 
 | |
|     User privileges that allow accessing certain privileged aspects of the
 | |
|     security descriptor (such as the Sacl) are only used if the user specified
 | |
|     to do so.
 | |
| 
 | |
|   Author:
 | |
| 
 | |
|     Scott Field (sfield@microsoft.com)
 | |
| 
 | |
|   Last revised:  18 Jan 97
 | |
| 
 | |
|  */
 | |
| 
 | |
| #define WIN32_LEAN_AND_MEAN
 | |
| #define UNZIP_INTERNAL
 | |
| #include "unzip.h"
 | |
| #include <windows.h>
 | |
| #ifdef __RSXNT__
 | |
| #  include "win32/rsxntwin.h"
 | |
| #endif
 | |
| #include "win32/nt.h"
 | |
| 
 | |
| 
 | |
| #ifdef NTSD_EAS         /* This file is only needed for NTSD handling */
 | |
| 
 | |
| /* Borland C++ does not define FILE_SHARE_DELETE. Others also? */
 | |
| #ifndef FILE_SHARE_DELETE
 | |
| #  define FILE_SHARE_DELETE 0x00000004
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /* private prototypes */
 | |
| 
 | |
| #ifndef NO_NTSD_WITH_RSXNT  /* RSXNT windows.h does not yet support NT sec. */
 | |
| static BOOL Initialize(VOID);
 | |
| #if 0   /* currently unused */
 | |
| static BOOL Shutdown(VOID);
 | |
| #endif
 | |
| static BOOL DeferSet(char *resource, PVOLUMECAPS VolumeCaps, uch *buffer);
 | |
| static VOID GetRemotePrivilegesSet(CHAR *FileName, PDWORD dwRemotePrivileges);
 | |
| static VOID InitLocalPrivileges(VOID);
 | |
| 
 | |
| 
 | |
| BOOL bInitialized = FALSE;  /* module level stuff initialized? */
 | |
| HANDLE hInitMutex = NULL;   /* prevent multiple initialization */
 | |
| 
 | |
| BOOL g_bRestorePrivilege = FALSE;   /* for local set file security override */
 | |
| BOOL g_bSaclPrivilege = FALSE;      /* for local set sacl operations, only when
 | |
|                                        restore privilege not present */
 | |
| 
 | |
| /* our single cached volume capabilities structure that describes the last
 | |
|    volume root we encountered.  A single entry like this works well in the
 | |
|    zip/unzip scenario for a number of reasons:
 | |
|    1. typically one extraction path during unzip.
 | |
|    2. typically process one volume at a time during zip, and then move
 | |
|       on to the next.
 | |
|    3. no cleanup code required and no memory leaks.
 | |
|    4. simple code.
 | |
| 
 | |
|    This approach should be reworked to a linked list approach if we expect to
 | |
|    be called by many threads which are processing a variety of input/output
 | |
|    volumes, since lock contention and stale data may become a bottleneck. */
 | |
| 
 | |
| VOLUMECAPS g_VolumeCaps;
 | |
| CRITICAL_SECTION VolumeCapsLock;
 | |
| 
 | |
| 
 | |
| /* our diferred set structure linked list element, used for making a copy
 | |
|    of input data which is used at a later time to process the original input
 | |
|    at a time when it makes more sense. eg, applying security to newly created
 | |
|    directories, after all files have been placed in such directories. */
 | |
| 
 | |
| CRITICAL_SECTION SetDeferLock;
 | |
| 
 | |
| typedef struct _DEFERRED_SET {
 | |
|     struct _DEFERRED_SET *Next;
 | |
|     uch *buffer;                /* must point to DWORD aligned block */
 | |
|     PVOLUMECAPS VolumeCaps;
 | |
|     char *resource;
 | |
| } DEFERRED_SET, *PDEFERRED_SET, *LPDEFERRED_SET;
 | |
| 
 | |
| PDEFERRED_SET pSetHead = NULL;
 | |
| PDEFERRED_SET pSetTail;
 | |
| 
 | |
| static BOOL Initialize(VOID)
 | |
| {
 | |
|     HANDLE hMutex;
 | |
|     HANDLE hOldMutex;
 | |
| 
 | |
|     if(bInitialized) return TRUE;
 | |
| 
 | |
|     hMutex = CreateMutex(NULL, TRUE, NULL);
 | |
|     if(hMutex == NULL) return FALSE;
 | |
| 
 | |
|     hOldMutex = (HANDLE)InterlockedExchange((LPLONG)&hInitMutex, (LONG)hMutex);
 | |
| 
 | |
|     if(hOldMutex != NULL) {
 | |
|         /* somebody setup the mutex already */
 | |
|         InterlockedExchange((LPLONG)&hInitMutex, (LONG)hOldMutex);
 | |
| 
 | |
|         CloseHandle(hMutex); /* close new, un-needed mutex */
 | |
| 
 | |
|         /* wait for initialization to complete and return status */
 | |
|         WaitForSingleObject(hOldMutex, INFINITE);
 | |
|         ReleaseMutex(hOldMutex);
 | |
| 
 | |
|         return bInitialized;
 | |
|     }
 | |
| 
 | |
|     /* initialize module level resources */
 | |
| 
 | |
|     InitializeCriticalSection( &SetDeferLock );
 | |
| 
 | |
|     InitializeCriticalSection( &VolumeCapsLock );
 | |
|     memset(&g_VolumeCaps, 0, sizeof(VOLUMECAPS));
 | |
| 
 | |
|     InitLocalPrivileges();
 | |
| 
 | |
|     bInitialized = TRUE;
 | |
| 
 | |
|     ReleaseMutex(hMutex); /* release correct mutex */
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| #if 0   /* currently not used ! */
 | |
| static BOOL Shutdown(VOID)
 | |
| {
 | |
|     /* really need to free critical sections, disable enabled privilges, etc,
 | |
|        but doing so brings up possibility of race conditions if those resources
 | |
|        are about to be used.  The easiest way to handle this is let these
 | |
|        resources be freed when the process terminates... */
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| #endif /* never */
 | |
| 
 | |
| 
 | |
| static BOOL DeferSet(char *resource, PVOLUMECAPS VolumeCaps, uch *buffer)
 | |
| {
 | |
|     PDEFERRED_SET psd;
 | |
|     DWORD cbDeferSet;
 | |
|     DWORD cbResource;
 | |
|     DWORD cbBuffer;
 | |
| 
 | |
|     if(!bInitialized) if(!Initialize()) return FALSE;
 | |
| 
 | |
|     cbResource = lstrlenA(resource) + 1;
 | |
|     cbBuffer = GetSecurityDescriptorLength(buffer);
 | |
|     cbDeferSet = sizeof(DEFERRED_SET) + cbBuffer + sizeof(VOLUMECAPS) +
 | |
|       cbResource;
 | |
| 
 | |
|     psd = (PDEFERRED_SET)HeapAlloc(GetProcessHeap(), 0, cbDeferSet);
 | |
|     if(psd == NULL) return FALSE;
 | |
| 
 | |
|     psd->Next = NULL;
 | |
|     psd->buffer = (uch *)(psd+1);
 | |
|     psd->VolumeCaps = (PVOLUMECAPS)((char *)psd->buffer + cbBuffer);
 | |
|     psd->resource = (char *)((char *)psd->VolumeCaps + sizeof(VOLUMECAPS));
 | |
| 
 | |
|     memcpy(psd->buffer, buffer, cbBuffer);
 | |
|     memcpy(psd->VolumeCaps, VolumeCaps, sizeof(VOLUMECAPS));
 | |
|     psd->VolumeCaps->bProcessDefer = TRUE;
 | |
|     memcpy(psd->resource, resource, cbResource);
 | |
| 
 | |
|     /* take defer lock */
 | |
|     EnterCriticalSection( &SetDeferLock );
 | |
| 
 | |
|     /* add element at tail of list */
 | |
| 
 | |
|     if(pSetHead == NULL) {
 | |
|         pSetHead = psd;
 | |
|     } else {
 | |
|         pSetTail->Next = psd;
 | |
|     }
 | |
| 
 | |
|     pSetTail = psd;
 | |
| 
 | |
|     /* release defer lock */
 | |
|     LeaveCriticalSection( &SetDeferLock );
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| BOOL ProcessDefer(PDWORD dwDirectoryCount, PDWORD dwBytesProcessed,
 | |
|                   PDWORD dwDirectoryFail, PDWORD dwBytesFail)
 | |
| {
 | |
|     PDEFERRED_SET This;
 | |
|     PDEFERRED_SET Next;
 | |
| 
 | |
|     *dwDirectoryCount = 0;
 | |
|     *dwBytesProcessed = 0;
 | |
| 
 | |
|     *dwDirectoryFail = 0;
 | |
|     *dwBytesFail = 0;
 | |
| 
 | |
|     if(!bInitialized) return TRUE; /* nothing to do */
 | |
| 
 | |
|     EnterCriticalSection( &SetDeferLock );
 | |
| 
 | |
|     This = pSetHead;
 | |
| 
 | |
|     while(This) {
 | |
| 
 | |
|         if(SecuritySet(This->resource, This->VolumeCaps, This->buffer)) {
 | |
|             (*dwDirectoryCount)++;
 | |
|             *dwBytesProcessed += GetSecurityDescriptorLength(This->buffer);
 | |
|         } else {
 | |
|             (*dwDirectoryFail)++;
 | |
|             *dwBytesFail += GetSecurityDescriptorLength(This->buffer);
 | |
|         }
 | |
| 
 | |
|         Next = This->Next;
 | |
|         HeapFree(GetProcessHeap(), 0, This);
 | |
|         This = Next;
 | |
|     }
 | |
| 
 | |
|     pSetHead = NULL;
 | |
| 
 | |
|     LeaveCriticalSection( &SetDeferLock );
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| BOOL ValidateSecurity(uch *securitydata)
 | |
| {
 | |
|     PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)securitydata;
 | |
|     PACL pAcl;
 | |
|     PSID pSid;
 | |
|     BOOL bAclPresent;
 | |
|     BOOL bDefaulted;
 | |
| 
 | |
|     if(!IsWinNT()) return TRUE; /* don't do anything if not on WinNT */
 | |
| 
 | |
|     if(!IsValidSecurityDescriptor(sd)) return FALSE;
 | |
| 
 | |
|     /* verify Dacl integrity */
 | |
| 
 | |
|     if(!GetSecurityDescriptorDacl(sd, &bAclPresent, &pAcl, &bDefaulted))
 | |
|         return FALSE;
 | |
| 
 | |
|     if(bAclPresent) {
 | |
|         if(!IsValidAcl(pAcl)) return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* verify Sacl integrity */
 | |
| 
 | |
|     if(!GetSecurityDescriptorSacl(sd, &bAclPresent, &pAcl, &bDefaulted))
 | |
|         return FALSE;
 | |
| 
 | |
|     if(bAclPresent) {
 | |
|         if(!IsValidAcl(pAcl)) return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* verify owner integrity */
 | |
| 
 | |
|     if(!GetSecurityDescriptorOwner(sd, &pSid, &bDefaulted))
 | |
|         return FALSE;
 | |
| 
 | |
|     if(pSid != NULL) {
 | |
|         if(!IsValidSid(pSid)) return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* verify group integrity */
 | |
| 
 | |
|     if(!GetSecurityDescriptorGroup(sd, &pSid, &bDefaulted))
 | |
|         return FALSE;
 | |
| 
 | |
|     if(pSid != NULL) {
 | |
|         if(!IsValidSid(pSid)) return FALSE;
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static VOID GetRemotePrivilegesSet(char *FileName, PDWORD dwRemotePrivileges)
 | |
| {
 | |
|     HANDLE hFile;
 | |
| 
 | |
|     *dwRemotePrivileges = 0;
 | |
| 
 | |
|     /* see if we have the SeRestorePrivilege */
 | |
| 
 | |
|     hFile = CreateFileA(
 | |
|         FileName,
 | |
|         ACCESS_SYSTEM_SECURITY | WRITE_DAC | WRITE_OWNER | READ_CONTROL,
 | |
|         FILE_SHARE_READ | FILE_SHARE_DELETE, /* no sd updating allowed here */
 | |
|         NULL,
 | |
|         OPEN_EXISTING,
 | |
|         FILE_FLAG_BACKUP_SEMANTICS,
 | |
|         NULL
 | |
|         );
 | |
| 
 | |
|     if(hFile != INVALID_HANDLE_VALUE) {
 | |
|         /* no remote way to determine SeRestorePrivilege -- just try a
 | |
|            read/write to simulate it */
 | |
|         SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION |
 | |
|           SACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION |
 | |
|           GROUP_SECURITY_INFORMATION;
 | |
|         PSECURITY_DESCRIPTOR sd;
 | |
|         DWORD cbBuf = 0;
 | |
| 
 | |
|         GetKernelObjectSecurity(hFile, si, NULL, cbBuf, &cbBuf);
 | |
| 
 | |
|         if(ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
 | |
|             if((sd = HeapAlloc(GetProcessHeap(), 0, cbBuf)) != NULL) {
 | |
|                 if(GetKernelObjectSecurity(hFile, si, sd, cbBuf, &cbBuf)) {
 | |
|                     if(SetKernelObjectSecurity(hFile, si, sd))
 | |
|                         *dwRemotePrivileges |= OVERRIDE_RESTORE;
 | |
|                 }
 | |
|                 HeapFree(GetProcessHeap(), 0, sd);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         CloseHandle(hFile);
 | |
|     } else {
 | |
| 
 | |
|         /* see if we have the SeSecurityPrivilege */
 | |
|         /* note we don't need this if we have SeRestorePrivilege */
 | |
| 
 | |
|         hFile = CreateFileA(
 | |
|             FileName,
 | |
|             ACCESS_SYSTEM_SECURITY,
 | |
|             FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, /* max */
 | |
|             NULL,
 | |
|             OPEN_EXISTING,
 | |
|             0,
 | |
|             NULL
 | |
|             );
 | |
| 
 | |
|         if(hFile != INVALID_HANDLE_VALUE) {
 | |
|             CloseHandle(hFile);
 | |
|             *dwRemotePrivileges |= OVERRIDE_SACL;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| BOOL GetVolumeCaps(
 | |
|     char *rootpath,         /* filepath, or NULL */
 | |
|     char *name,             /* filename associated with rootpath */
 | |
|     PVOLUMECAPS VolumeCaps  /* result structure describing capabilities */
 | |
|     )
 | |
| {
 | |
|     char TempRootPath[MAX_PATH + 1];
 | |
|     DWORD cchTempRootPath = 0;
 | |
|     BOOL bSuccess = TRUE;   /* assume success until told otherwise */
 | |
| 
 | |
|     if(!bInitialized) if(!Initialize()) return FALSE;
 | |
| 
 | |
|     /* process the input path to produce a consistent path suitable for
 | |
|        compare operations and also suitable for certain picky Win32 API
 | |
|        that don't like forward slashes */
 | |
| 
 | |
|     if(rootpath != NULL && rootpath[0] != '\0') {
 | |
|         DWORD i;
 | |
| 
 | |
|         cchTempRootPath = lstrlen(rootpath);
 | |
|         if(cchTempRootPath > MAX_PATH) return FALSE;
 | |
| 
 | |
|         /* copy input, converting forward slashes to back slashes as we go */
 | |
| 
 | |
|         for(i = 0 ; i <= cchTempRootPath ; i++) {
 | |
|             if(rootpath[i] == '/') TempRootPath[i] = '\\';
 | |
|             else TempRootPath[i] = rootpath[i];
 | |
|         }
 | |
| 
 | |
|         /* check for UNC and Null terminate or append trailing \ as
 | |
|            appropriate */
 | |
| 
 | |
|         /* possible valid UNCs we are passed follow:
 | |
|            \\machine\foo\bar (path is \\machine\foo\)
 | |
|            \\machine\foo     (path is \\machine\foo\)
 | |
|            \\machine\foo\
 | |
|            \\.\c$\     (FIXFIX: Win32API doesn't like this - GetComputerName())
 | |
|            LATERLATER: handling mounted DFS drives in the future will require
 | |
|                        slightly different logic which isn't available today.
 | |
|                        This is required because directories can point at
 | |
|                        different servers which have differing capabilities.
 | |
|          */
 | |
| 
 | |
|         if(TempRootPath[0] == '\\' && TempRootPath[1] == '\\') {
 | |
|             DWORD slash = 0;
 | |
| 
 | |
|             for(i = 2 ; i < cchTempRootPath ; i++) {
 | |
|                 if(TempRootPath[i] == '\\') {
 | |
|                     slash++;
 | |
| 
 | |
|                     if(slash == 2) {
 | |
|                         i++;
 | |
|                         TempRootPath[i] = '\0';
 | |
|                         cchTempRootPath = i;
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             /* if there was only one slash found, just tack another onto the
 | |
|                end */
 | |
| 
 | |
|             if(slash == 1 && TempRootPath[cchTempRootPath] != '\\') {
 | |
|                 TempRootPath[cchTempRootPath] = TempRootPath[0]; /* '\' */
 | |
|                 TempRootPath[cchTempRootPath+1] = '\0';
 | |
|                 cchTempRootPath++;
 | |
|             }
 | |
| 
 | |
|         } else {
 | |
| 
 | |
|             if(TempRootPath[1] == ':') {
 | |
| 
 | |
|                 /* drive letter specified, truncate to root */
 | |
|                 TempRootPath[2] = '\\';
 | |
|                 TempRootPath[3] = '\0';
 | |
|                 cchTempRootPath = 3;
 | |
|             } else {
 | |
| 
 | |
|                 /* must be file on current drive */
 | |
|                 TempRootPath[0] = '\0';
 | |
|                 cchTempRootPath = 0;
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|     } /* if path != NULL */
 | |
| 
 | |
|     /* grab lock protecting cached entry */
 | |
|     EnterCriticalSection( &VolumeCapsLock );
 | |
| 
 | |
|     if(!g_VolumeCaps.bValid ||
 | |
|        lstrcmpi(g_VolumeCaps.RootPath, TempRootPath) != 0)
 | |
|     {
 | |
| 
 | |
|         /* no match found, build up new entry */
 | |
| 
 | |
|         DWORD dwFileSystemFlags;
 | |
|         DWORD dwRemotePrivileges = 0;
 | |
|         BOOL bRemote = FALSE;
 | |
| 
 | |
|         /* release lock during expensive operations */
 | |
|         LeaveCriticalSection( &VolumeCapsLock );
 | |
| 
 | |
|         bSuccess = GetVolumeInformation(
 | |
|             (TempRootPath[0] == '\0') ? NULL : TempRootPath,
 | |
|             NULL, 0,
 | |
|             NULL, NULL,
 | |
|             &dwFileSystemFlags,
 | |
|             NULL, 0);
 | |
| 
 | |
| 
 | |
|         /* only if target volume supports Acls, and we were told to use
 | |
|            privileges do we need to go out and test for the remote case */
 | |
| 
 | |
|         if(bSuccess && (dwFileSystemFlags & FS_PERSISTENT_ACLS) &&
 | |
|            VolumeCaps->bUsePrivileges)
 | |
|         {
 | |
|             if(GetDriveType( (TempRootPath[0] == '\0') ? NULL : TempRootPath )
 | |
|                == DRIVE_REMOTE)
 | |
|             {
 | |
|                 bRemote = TRUE;
 | |
| 
 | |
|                 /* make a determination about our remote capabilities */
 | |
| 
 | |
|                 GetRemotePrivilegesSet(name, &dwRemotePrivileges);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* always take the lock again, since we release it below */
 | |
|         EnterCriticalSection( &VolumeCapsLock );
 | |
| 
 | |
|         /* replace the existing data if successful */
 | |
|         if(bSuccess) {
 | |
| 
 | |
|             lstrcpynA(g_VolumeCaps.RootPath, TempRootPath, cchTempRootPath+1);
 | |
|             g_VolumeCaps.bProcessDefer = FALSE;
 | |
|             g_VolumeCaps.dwFileSystemFlags = dwFileSystemFlags;
 | |
|             g_VolumeCaps.bRemote = bRemote;
 | |
|             g_VolumeCaps.dwRemotePrivileges = dwRemotePrivileges;
 | |
|             g_VolumeCaps.bValid = TRUE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(bSuccess) {
 | |
|         /* copy input elements */
 | |
|         g_VolumeCaps.bUsePrivileges = VolumeCaps->bUsePrivileges;
 | |
|         g_VolumeCaps.dwFileAttributes = VolumeCaps->dwFileAttributes;
 | |
| 
 | |
|         /* give caller results */
 | |
|         memcpy(VolumeCaps, &g_VolumeCaps, sizeof(VOLUMECAPS));
 | |
|     } else {
 | |
|         g_VolumeCaps.bValid = FALSE;
 | |
|     }
 | |
| 
 | |
|     LeaveCriticalSection( &VolumeCapsLock ); /* release lock */
 | |
| 
 | |
|     return bSuccess;
 | |
| }
 | |
| 
 | |
| 
 | |
| BOOL SecuritySet(char *resource, PVOLUMECAPS VolumeCaps, uch *securitydata)
 | |
| {
 | |
|     HANDLE hFile;
 | |
|     DWORD dwDesiredAccess = 0;
 | |
|     DWORD dwFlags = 0;
 | |
|     PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)securitydata;
 | |
|     SECURITY_DESCRIPTOR_CONTROL sdc;
 | |
|     SECURITY_INFORMATION RequestedInfo = 0;
 | |
|     DWORD dwRev;
 | |
|     BOOL bRestorePrivilege = FALSE;
 | |
|     BOOL bSaclPrivilege = FALSE;
 | |
|     BOOL bSuccess;
 | |
| 
 | |
|     if(!bInitialized) if(!Initialize()) return FALSE;
 | |
| 
 | |
|     /* defer directory processing */
 | |
| 
 | |
|     if(VolumeCaps->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 | |
|         if(!VolumeCaps->bProcessDefer) {
 | |
|             return DeferSet(resource, VolumeCaps, securitydata);
 | |
|         } else {
 | |
|             /* opening a directory requires FILE_FLAG_BACKUP_SEMANTICS */
 | |
|             dwFlags |= FILE_FLAG_BACKUP_SEMANTICS;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* evaluate the input security desriptor and act accordingly */
 | |
| 
 | |
|     if(!IsValidSecurityDescriptor(sd))
 | |
|         return FALSE;
 | |
| 
 | |
|     if(!GetSecurityDescriptorControl(sd, &sdc, &dwRev))
 | |
|         return FALSE;
 | |
| 
 | |
|     /* setup privilege usage based on if told we can use privileges, and if so,
 | |
|        what privileges we have */
 | |
| 
 | |
|     if(VolumeCaps->bUsePrivileges) {
 | |
|         if(VolumeCaps->bRemote) {
 | |
|             /* use remotely determined privileges */
 | |
|             if(VolumeCaps->dwRemotePrivileges & OVERRIDE_RESTORE)
 | |
|                 bRestorePrivilege = TRUE;
 | |
| 
 | |
|             if(VolumeCaps->dwRemotePrivileges & OVERRIDE_SACL)
 | |
|                 bSaclPrivilege = TRUE;
 | |
| 
 | |
|         } else {
 | |
|             /* use local privileges */
 | |
|             bRestorePrivilege = g_bRestorePrivilege;
 | |
|             bSaclPrivilege = g_bSaclPrivilege;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /* if a Dacl is present write Dacl out */
 | |
|     /* if we have SeRestorePrivilege, write owner and group info out */
 | |
| 
 | |
|     if(sdc & SE_DACL_PRESENT) {
 | |
|         dwDesiredAccess |= WRITE_DAC;
 | |
|         RequestedInfo |= DACL_SECURITY_INFORMATION;
 | |
| 
 | |
|         if(bRestorePrivilege) {
 | |
|             dwDesiredAccess |= WRITE_OWNER;
 | |
|             RequestedInfo |= (OWNER_SECURITY_INFORMATION |
 | |
|               GROUP_SECURITY_INFORMATION);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* if a Sacl is present and we have either SeRestorePrivilege or
 | |
|        SeSystemSecurityPrivilege try to write Sacl out */
 | |
| 
 | |
|     if((sdc & SE_SACL_PRESENT) && (bRestorePrivilege || bSaclPrivilege)) {
 | |
|         dwDesiredAccess |= ACCESS_SYSTEM_SECURITY;
 | |
|         RequestedInfo |= SACL_SECURITY_INFORMATION;
 | |
|     }
 | |
| 
 | |
|     if(RequestedInfo == 0)  /* nothing to do */
 | |
|         return FALSE;
 | |
| 
 | |
|     if(bRestorePrivilege)
 | |
|         dwFlags |= FILE_FLAG_BACKUP_SEMANTICS;
 | |
| 
 | |
|     hFile = CreateFileA(
 | |
|         resource,
 | |
|         dwDesiredAccess,
 | |
|         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,/* max sharing */
 | |
|         NULL,
 | |
|         OPEN_EXISTING,
 | |
|         dwFlags,
 | |
|         NULL
 | |
|         );
 | |
| 
 | |
|     if(hFile == INVALID_HANDLE_VALUE)
 | |
|         return FALSE;
 | |
| 
 | |
|     bSuccess = SetKernelObjectSecurity(hFile, RequestedInfo, sd);
 | |
| 
 | |
|     CloseHandle(hFile);
 | |
| 
 | |
|     return bSuccess;
 | |
| }
 | |
| 
 | |
| static VOID InitLocalPrivileges(VOID)
 | |
| {
 | |
|     HANDLE hToken;
 | |
|     TOKEN_PRIVILEGES tp;
 | |
| 
 | |
|     /* try to enable some interesting privileges that give us the ability
 | |
|        to get some security information that we normally cannot.
 | |
| 
 | |
|        note that enabling privileges is only relevant on the local machine;
 | |
|        when accessing files that are on a remote machine, any privileges
 | |
|        that are present on the remote machine get enabled by default. */
 | |
| 
 | |
|     if(!OpenProcessToken(GetCurrentProcess(),
 | |
|         TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
 | |
|         return;
 | |
| 
 | |
|     tp.PrivilegeCount = 1;
 | |
|     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 | |
| 
 | |
|     if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tp.Privileges[0].Luid)) {
 | |
| 
 | |
|         /* try to enable SeRestorePrivilege; if this succeeds, we can write
 | |
|            all aspects of the security descriptor */
 | |
| 
 | |
|         if(AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
 | |
|            GetLastError() == ERROR_SUCCESS) g_bRestorePrivilege = TRUE;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /* try to enable SeSystemSecurityPrivilege, if SeRestorePrivilege not
 | |
|        present; if this succeeds, we can write the Sacl */
 | |
| 
 | |
|     if(!g_bRestorePrivilege &&
 | |
|         LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) {
 | |
| 
 | |
|         if(AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
 | |
|            GetLastError() == ERROR_SUCCESS) g_bSaclPrivilege = TRUE;
 | |
|     }
 | |
| 
 | |
|     CloseHandle(hToken);
 | |
| }
 | |
| #endif /* !NO_NTSD_WITH_RSXNT */
 | |
| #endif /* NTSD_EAS */
 |