|
|
/********************************************************************/ /** Copyright(c) 1989 Microsoft Corporation. **/ /********************************************************************/
//***
//
// Filename: dir.c
//
// Description: This module contains support routines for the diretory
// category API's for the AFP server service. These routines
// are called by the RPC runtime.
//
// History:
// June 11,1992. NarenG Created original version.
//
#include <nt.h>
#include <ntrtl.h>
#include <ntlsa.h>
#include <nturtl.h> // needed for winbase.h
#include "afpsvcp.h"
//**
//
// Call: AfpDirConvertSidsToNames
//
// Returns: NO_ERROR
// error return codes from LsaOpenPolicy and LsaLookupSids
//
// Description: Will convert the directory structure returned by the FSD
// which contains pointers to owner and groups SIDS to their
// respective names. The caller is responsible for freeing up
// the memory allocated to hold the converted dir structure.
//
DWORD AfpDirConvertSidsToNames( IN PAFP_DIRECTORY_INFO pAfpDirInfo, OUT PAFP_DIRECTORY_INFO* ppAfpConvertedDirInfo ) { LSA_HANDLE hLsa = NULL; NTSTATUS ntStatus; PLSA_REFERENCED_DOMAIN_LIST pDomainList = NULL; PLSA_TRANSLATED_NAME pNames = NULL; PSID pSidArray[2]; SECURITY_QUALITY_OF_SERVICE QOS; OBJECT_ATTRIBUTES ObjectAttributes; DWORD dwRetCode = NO_ERROR; PAFP_DIRECTORY_INFO pOutputBuf = NULL; DWORD cbOutputBuf; LPBYTE pbVariableData; DWORD dwIndex; WCHAR * pWchar; BOOL fUseUnknownAccount = FALSE; DWORD dwUse, dwCount = 0; SID AfpBuiltInSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID };
// First open the LSA and obtain a handle to it.
//
QOS.Length = sizeof( QOS ); QOS.ImpersonationLevel = SecurityImpersonation; QOS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; QOS.EffectiveOnly = FALSE;
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
ObjectAttributes.SecurityQualityOfService = &QOS;
ntStatus = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_LOOKUP_NAMES, &hLsa );
if ( !NT_SUCCESS( ntStatus )) return( RtlNtStatusToDosError( ntStatus ) );
// This is not a loop
//
do {
// Set up the owner and group sid into the array.
//
if ((PSID)(pAfpDirInfo->afpdir_owner) != NULL) { pSidArray[dwCount++] = (PSID)(pAfpDirInfo->afpdir_owner); } if ((PSID)(pAfpDirInfo->afpdir_group) != NULL) { pSidArray[dwCount++] = (PSID)(pAfpDirInfo->afpdir_group); } // Try to get the names of the owner and primary group.
//
if (dwCount > 0) { ntStatus = LsaLookupSids( hLsa, dwCount, pSidArray, &pDomainList, &pNames ); if ( !NT_SUCCESS( ntStatus ) ) { if ( ntStatus == STATUS_NONE_MAPPED ) { fUseUnknownAccount = TRUE; dwRetCode = NO_ERROR; } else { dwRetCode = RtlNtStatusToDosError( ntStatus ); AFP_PRINT(( "SFMSVC: AfpDirConvertSidsToNames, LsaLookupSids failed with error (%ld)\n", dwRetCode));
break; } } }
// We need to calculate the length of the buffer we need to allocate.
//
for( dwIndex = 0, dwRetCode = NO_ERROR, cbOutputBuf = sizeof( AFP_DIRECTORY_INFO );
dwIndex < dwCount;
dwIndex++ ) {
if ( fUseUnknownAccount ) dwUse = SidTypeUnknown; else dwUse = pNames[dwIndex].Use;
switch( dwUse ) {
case SidTypeInvalid: cbOutputBuf += ((wcslen((LPWSTR)(AfpGlobals.wchInvalid))+1) * sizeof(WCHAR)); break;
case SidTypeDeletedAccount: cbOutputBuf += ((wcslen((LPWSTR)(AfpGlobals.wchDeleted))+1) * sizeof(WCHAR)); break;
case SidTypeUnknown: cbOutputBuf += ((wcslen((LPWSTR)(AfpGlobals.wchUnknown))+1) * sizeof(WCHAR)); break;
case SidTypeWellKnownGroup: cbOutputBuf += (pNames[dwIndex].Name.Length+sizeof(WCHAR)); break;
case SidTypeDomain: cbOutputBuf += ((pDomainList->Domains[pNames[dwIndex].DomainIndex]).Name.Length + sizeof(WCHAR) ); break;
default: if ( ( pNames[dwIndex].DomainIndex != -1 ) && ( pNames[dwIndex].Name.Buffer != NULL ) ) {
PSID pDomainSid; PUNICODE_STRING pDomain;
pDomain = &((pDomainList->Domains[pNames[dwIndex].DomainIndex]).Name);
pDomainSid = (pDomainList->Domains[pNames[dwIndex].DomainIndex]).Sid;
if ( !RtlEqualSid( &AfpBuiltInSid, pDomainSid )) cbOutputBuf += ( pDomain->Length + sizeof( TEXT('\\')));
cbOutputBuf += (pNames[dwIndex].Name.Length+sizeof(WCHAR)); } else dwRetCode = ERROR_NONE_MAPPED; break; } }
pOutputBuf = (PAFP_DIRECTORY_INFO)MIDL_user_allocate( cbOutputBuf );
if ( pOutputBuf == NULL ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; AFP_PRINT(( "SFMSVC: AfpDirConvertSidsToNames, MIDL_user_allocate 1 failed with error (%ld)\n", dwRetCode)); break; }
ZeroMemory( (LPBYTE)pOutputBuf, cbOutputBuf );
// Copy the fixed part of the structure.
//
CopyMemory( (LPBYTE)pOutputBuf, (LPBYTE)pAfpDirInfo, sizeof(AFP_DIRECTORY_INFO) );
// Now we need to copy the names
//
for( dwIndex = 0, pbVariableData = (LPBYTE)((ULONG_PTR)pOutputBuf + cbOutputBuf);
dwIndex < dwCount;
dwIndex++ ) {
if ( fUseUnknownAccount ) dwUse = SidTypeUnknown; else dwUse = pNames[dwIndex].Use;
switch( dwUse ) {
case SidTypeInvalid: pbVariableData -= ((wcslen(AfpGlobals.wchInvalid)+1) * sizeof(WCHAR)); wcscpy( (LPWSTR)pbVariableData, AfpGlobals.wchInvalid ); break;
case SidTypeDeletedAccount: pbVariableData -= ((wcslen(AfpGlobals.wchDeleted)+1) * sizeof(WCHAR)); wcscpy( (LPWSTR)pbVariableData, AfpGlobals.wchDeleted ); break;
case SidTypeUnknown: pbVariableData -= ((wcslen(AfpGlobals.wchUnknown)+1) * sizeof(WCHAR)); wcscpy( (LPWSTR)pbVariableData, AfpGlobals.wchUnknown ); break;
case SidTypeWellKnownGroup: pbVariableData -= (pNames[dwIndex].Name.Length+sizeof(WCHAR)); CopyMemory( pbVariableData, pNames[dwIndex].Name.Buffer, pNames[dwIndex].Name.Length ); break;
case SidTypeDomain: cbOutputBuf += ((pDomainList->Domains[pNames[dwIndex].DomainIndex]).Name.Length); CopyMemory( pbVariableData, ((pDomainList->Domains[pNames[dwIndex].DomainIndex]).Name.Buffer), ((pDomainList->Domains[pNames[dwIndex].DomainIndex]).Name.Length)); break;
default:
{ PSID pDomainSid;
PUNICODE_STRING pDomain;
pDomain = &((pDomainList->Domains[pNames[dwIndex].DomainIndex]).Name);
pDomainSid = (pDomainList->Domains[pNames[dwIndex].DomainIndex]).Sid;
pbVariableData -= ((pNames[dwIndex].Name.Length+sizeof(WCHAR)));
pWchar = (WCHAR*)pbVariableData;
// Copy the domain name if it is not BUILTIN
//
if ( !RtlEqualSid( &AfpBuiltInSid, pDomainSid ) ) {
pbVariableData -= ( pDomain->Length + sizeof( TEXT('\\')));
CopyMemory(pbVariableData,pDomain->Buffer,pDomain->Length);
wcscat((LPWSTR)pbVariableData, (LPWSTR)TEXT("\\"));
pWchar = (WCHAR*)pbVariableData;
pWchar += wcslen( (LPWSTR)pbVariableData );
}
CopyMemory( pWchar, pNames[dwIndex].Name.Buffer, pNames[dwIndex].Name.Length ); }
}
// If this is the first time this loop executes then set the
// owner.
//
if ( (dwIndex == 0) && (pAfpDirInfo->afpdir_owner != NULL) ) pOutputBuf->afpdir_owner = (LPWSTR)pbVariableData; else pOutputBuf->afpdir_group = (LPWSTR)pbVariableData; }
} while( FALSE );
if ( pNames != NULL ) LsaFreeMemory( pNames );
if ( pDomainList != NULL ) LsaFreeMemory( pDomainList );
if ( hLsa != NULL ) LsaClose( hLsa );
if ( dwRetCode != NO_ERROR ) {
AFP_PRINT(( "SFMSVC: AfpDirConvertSidsToNames, failed, error = (%ld)\n" , dwRetCode));
if ( pOutputBuf != NULL ) MIDL_user_free( pOutputBuf ); } else { *ppAfpConvertedDirInfo = pOutputBuf; }
return( dwRetCode ); }
//**
//
// Call: AfpGetDirInfo
//
// Returns: NO_ERROR - success
// ERROR_NOT_ENOUGH_MEMORY
// Non-zero returns from NtOpenFile, NtQuerySecurityObject,
// NtQueryInformationFile.
//
// Description: Read the security descriptor for this directory and obtain the
// SIDs for Owner and Primary group. Finally obtain Owner, Group
// and World permissions.
DWORD AfpGetDirInfo( LPWSTR lpwsDirPath, PAFP_DIRECTORY_INFO * lppDirInfo ) { NTSTATUS ntStatus; DWORD dwSizeNeeded; PBYTE pBuffer = NULL; PBYTE pAbsBuffer = NULL; PISECURITY_DESCRIPTOR pSecDesc; PBYTE pAbsSecDesc = NULL; // Used in conversion of
// sec descriptor to
// absolute format
BOOL fSawOwnerAce = FALSE; BOOL fSawGroupAce = FALSE; BYTE bOwnerRights = 0; BYTE bGroupRights = 0; BYTE bWorldRights = 0; FILE_BASIC_INFORMATION FileBasicInfo; IO_STATUS_BLOCK IOStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DirectoryName; HANDLE hDirectory; PAFP_DIRECTORY_INFO pAfpDir; DWORD dwAlignedSizeAfpDirInfo = sizeof (AFP_DIRECTORY_INFO); LPWSTR pDirPath; SID AfpSidNull = { 1, 1, SECURITY_NULL_SID_AUTHORITY, SECURITY_NULL_RID }; SID AfpSidWorld = { 1, 1, SECURITY_WORLD_SID_AUTHORITY, SECURITY_WORLD_RID };
pDirPath = (LPWSTR)LocalAlloc( LPTR, ( STRLEN(lpwsDirPath) + STRLEN(TEXT("\\DOSDEVICES\\"))+1) * sizeof( WCHAR ) ); if ( pDirPath == NULL ) return( ERROR_NOT_ENOUGH_MEMORY );
STRCPY( pDirPath, TEXT("\\DOSDEVICES\\") ); STRCAT( pDirPath, lpwsDirPath );
RtlInitUnicodeString( &DirectoryName, pDirPath );
InitializeObjectAttributes( &ObjectAttributes, &DirectoryName, OBJ_CASE_INSENSITIVE, NULL, NULL );
ntStatus = NtOpenFile( &hDirectory, GENERIC_READ | READ_CONTROL | SYNCHRONIZE, &ObjectAttributes, &IOStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
LocalFree( pDirPath );
if ( !NT_SUCCESS( ntStatus ) ) return( RtlNtStatusToDosError( ntStatus ) ); // Read the security descriptor for this directory. First get the owner
// and group security descriptors. We want to optimize on how much memory
// we need to read this in. Its a pain to make a call just to get that.
// So just make a guess. If that turns out to be short then do the exact
// allocation.
//
dwSizeNeeded = 2048;
do {
if ( pBuffer != NULL ) MIDL_user_free( pBuffer );
if ((pBuffer = MIDL_user_allocate( dwSizeNeeded + dwAlignedSizeAfpDirInfo ))==NULL) return( ERROR_NOT_ENOUGH_MEMORY );
ZeroMemory( pBuffer, dwSizeNeeded + dwAlignedSizeAfpDirInfo );
pSecDesc = (PSECURITY_DESCRIPTOR)(pBuffer + dwAlignedSizeAfpDirInfo);
ntStatus = NtQuerySecurityObject( hDirectory, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSecDesc, dwSizeNeeded, &dwSizeNeeded);
} while ((ntStatus != STATUS_SUCCESS) && ((ntStatus == STATUS_BUFFER_OVERFLOW) || (ntStatus == STATUS_BUFFER_TOO_SMALL) || (ntStatus == STATUS_MORE_ENTRIES)));
if (!NT_SUCCESS(ntStatus)) { NtClose( hDirectory ); MIDL_user_free( pBuffer ); return( RtlNtStatusToDosError( ntStatus ) ); }
pSecDesc = (PISECURITY_DESCRIPTOR)((PBYTE)pSecDesc);
// If the security descriptor is in self-relative form, convert to absolute
//
if (pSecDesc->Control & SE_SELF_RELATIVE) { NTSTATUS Status;
DWORD dwAbsoluteSizeNeeded;
AFP_PRINT (("AfpGetDirInfo: SE_SELF_RELATIVE security desc\n"));
// An absolute SD is not necessarily the same size as a relative
// SD, so an in-place conversion may not be possible.
dwAbsoluteSizeNeeded = dwSizeNeeded; Status = RtlSelfRelativeToAbsoluteSD2(pSecDesc, &dwAbsoluteSizeNeeded); // Buffer will be small only for 64-bit
if (Status == STATUS_BUFFER_TOO_SMALL) { // Allocate a new buffer in which to store the absolute
// security descriptor, copy the contents of the relative
// descriptor in and try again
if ((pAbsBuffer = MIDL_user_allocate( dwAbsoluteSizeNeeded + dwAlignedSizeAfpDirInfo ))==NULL) { Status = STATUS_NO_MEMORY; AFP_PRINT (("AfpGetDirInfo: MIDL_user_allocate failed for pAbsBuffer\n")); } else {
ZeroMemory( pAbsBuffer, dwAbsoluteSizeNeeded + dwAlignedSizeAfpDirInfo );
memcpy (pAbsBuffer, pBuffer, sizeof(AFP_DIRECTORY_INFO));
pAbsSecDesc = (PSECURITY_DESCRIPTOR)(pAbsBuffer + dwAlignedSizeAfpDirInfo);
RtlCopyMemory((VOID *)pAbsSecDesc, (VOID *)pSecDesc, dwSizeNeeded); // All operations hereon will be performed on
// pAbsBuffer. Free earlier memory
MIDL_user_free(pBuffer); pBuffer = NULL; pBuffer = pAbsBuffer;
Status = RtlSelfRelativeToAbsoluteSD2 (pAbsSecDesc, &dwAbsoluteSizeNeeded); if (NT_SUCCESS(Status)) { // We don't need relative form anymore,
// we will work with the Absolute form
pSecDesc = (PISECURITY_DESCRIPTOR)pAbsSecDesc; } else { AFP_PRINT (("AfpGetDirInfo: RtlSelfRelativeToAbsoluteSD2 2 failed with error %ld\n", Status)); } } } else { AFP_PRINT (("AfpGetDirInfo: RtlSelfRelativeToAbsoluteSD2 failed with error %ld\n", Status)); }
if (!NT_SUCCESS(Status)) { AFP_PRINT (("AfpGetDirInfo: RtlSelfRelativeToAbsoluteSD2: returned error %lx\n", Status)); if (pBuffer != NULL) { MIDL_user_free( pBuffer ); pBuffer = NULL; } NtClose( hDirectory ); return( RtlNtStatusToDosError( ntStatus )); } }
pAfpDir = (PAFP_DIRECTORY_INFO)pBuffer;
// Walk through the ACL list and determine Owner/Group and World
// permissions. For Owner and Group, if the specific ace's are not
// present then they inherit the world permissions.
//
// A NULL Acl => All rights to everyone. An empty Acl on the other
// hand => no access for anyone.
//
// Should we be checking for creater owner/creater group well-defined
// sids or the Owner and Group fields in the security descriptor ?
//
bWorldRights = DIR_ACCESS_ALL; if (pSecDesc->Control & SE_DACL_PRESENT) bWorldRights = 0;
if (pSecDesc->Dacl != NULL ) {
DWORD dwCount; PSID pSid; PACL pAcl; PACCESS_ALLOWED_ACE pAce; bWorldRights = 0; pAcl = pSecDesc->Dacl; pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pAcl + sizeof(ACL));
for ( dwCount = 0; dwCount < pSecDesc->Dacl->AceCount; dwCount++) {
pSid = (PSID)(&pAce->SidStart);
if ( (pSecDesc->Owner != NULL) && RtlEqualSid(pSid, pSecDesc->Owner ) ){
AfpAccessMaskToAfpPermissions( bOwnerRights, pAce->Mask, pAce->Header.AceType);
fSawOwnerAce = TRUE; }
if ( ( pSecDesc->Group != NULL ) && RtlEqualSid(pSid, pSecDesc->Group)){
AfpAccessMaskToAfpPermissions( bGroupRights, pAce->Mask, pAce->Header.AceType); fSawGroupAce = TRUE; }
if (RtlEqualSid(pSid, (PSID)&AfpSidWorld)) {
AfpAccessMaskToAfpPermissions( bWorldRights, pAce->Mask, pAce->Header.AceType); }
pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pAce + pAce->Header.AceSize); }
} if (!fSawOwnerAce) bOwnerRights = bWorldRights;
if (!fSawGroupAce) bGroupRights = bWorldRights;
if (RtlEqualSid(pSecDesc->Group, &AfpSidNull) || ((AfpGlobals.NtProductType != NtProductLanManNt) && RtlEqualSid(pSecDesc->Group, AfpGlobals.pSidNone))) { bGroupRights = 0; pSecDesc->Group = NULL; }
ntStatus = NtQueryInformationFile( hDirectory, &IOStatusBlock, &FileBasicInfo, sizeof( FileBasicInfo ), FileBasicInformation );
NtClose( hDirectory );
if ( !NT_SUCCESS( ntStatus ) ) { MIDL_user_free( pBuffer ); return( RtlNtStatusToDosError( ntStatus ) ); }
pAfpDir->afpdir_perms = (bOwnerRights << OWNER_RIGHTS_SHIFT) + (bGroupRights << GROUP_RIGHTS_SHIFT) + (bWorldRights << WORLD_RIGHTS_SHIFT);
if ( FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_READONLY ) pAfpDir->afpdir_perms |= AFP_PERM_INHIBIT_MOVE_DELETE;
pAfpDir->afpdir_owner = pSecDesc->Owner; pAfpDir->afpdir_group = pSecDesc->Group;
*lppDirInfo = pAfpDir;
return( NO_ERROR ); }
//**
//
// Call: AfpValidatePartition
//
// Returns: NO_ERROR
// non-zero returns from GetVolumeInformation.
// AFPERR_UnsupportedFS
//
//
// Description: Will check to see if the directory is in an NTFS/CDFS
// partition not.
//
DWORD AfpValidatePartition( IN LPWSTR lpwsPath ) { WCHAR wchDrive[5]; DWORD dwMaxCompSize; DWORD dwFlags; WCHAR wchFileSystem[10];
// Get the drive letter, : and backslash
//
ZeroMemory( wchDrive, sizeof( wchDrive ) );
STRNCPY( wchDrive, lpwsPath, 3 );
if ( !( GetVolumeInformation( (LPWSTR)wchDrive, NULL, 0, NULL, &dwMaxCompSize, &dwFlags, (LPWSTR)wchFileSystem, sizeof( wchFileSystem ) ) ) ){ return GetLastError(); }
if ( STRICMP( wchFileSystem, TEXT("CDFS") ) == 0 ) return( (DWORD)AFPERR_SecurityNotSupported );
if ( STRICMP( wchFileSystem, TEXT("NTFS") ) == 0 ) return( NO_ERROR ); else return( (DWORD)AFPERR_UnsupportedFS ); }
//**
//
// Call: AfpAdminrDirectoryGetInfo
//
// Returns: NO_ERROR
// ERROR_ACCESS_DENIED
// non-zero retunrs from I_DirectoryGetInfo
//
// Description: This routine communicates with the AFP FSD to implement
// the AfpAdminDirectoryGetInfo function. The real work is done
// by I_DirectoryGetInfo
//
DWORD AfpAdminrDirectoryGetInfo( IN AFP_SERVER_HANDLE hServer, IN LPWSTR lpwsPath, OUT PAFP_DIRECTORY_INFO* ppAfpDirectoryInfo ) { DWORD dwRetCode=0; DWORD dwAccessStatus=0;
// Check if caller has access
//
if ( dwRetCode = AfpSecObjAccessCheck( AFPSVC_ALL_ACCESS, &dwAccessStatus)) { AFP_PRINT(( "SFMSVC: AfpAdminrDirectoryGetInfo, AfpSecObjAccessCheck failed %ld\n",dwRetCode)); AfpLogEvent( AFPLOG_CANT_CHECK_ACCESS, 0, NULL, dwRetCode, EVENTLOG_ERROR_TYPE ); return( ERROR_ACCESS_DENIED ); }
if ( dwAccessStatus ) { AFP_PRINT(( "SFMSVC: AfpAdminrDirectoryGetInfo, AfpSecObjAccessCheck returned error (%ld)\n",dwAccessStatus)); return( ERROR_ACCESS_DENIED ); }
dwRetCode = I_DirectoryGetInfo( lpwsPath, ppAfpDirectoryInfo );
return( dwRetCode ); }
//**
//
// Call: I_DirectoryGetInfo
//
// Returns: NO_ERROR
//
// Description: This does the real work to get the directory information.
// The reason for this worker routine is so that it may be
// called without the RPC handle and access checking by
// AfpAdminVolumeAdd API.
//
DWORD I_DirectoryGetInfo( IN LPWSTR lpwsPath, OUT PAFP_DIRECTORY_INFO * ppAfpDirectoryInfo ) { DWORD dwRetCode; AFP_REQUEST_PACKET AfpSrp; AFP_DIRECTORY_INFO AfpDirInfo; PAFP_DIRECTORY_INFO pAfpDirInfoSR; PAFP_DIRECTORY_INFO pAfpDirInfo; DWORD cbAfpDirInfoSRSize;
// The FSD expects AFP_VOLUME_INFO structure with only the dir path field
// filled in.
//
AfpDirInfo.afpdir_path = lpwsPath; AfpDirInfo.afpdir_owner = NULL; AfpDirInfo.afpdir_group = NULL;
// Make buffer self relative.
//
if ( dwRetCode = AfpBufMakeFSDRequest( (LPBYTE)&AfpDirInfo, 0, AFP_DIRECTORY_STRUCT, (LPBYTE*)&pAfpDirInfoSR, &cbAfpDirInfoSRSize ) ) return( dwRetCode );
// Make IOCTL to get info
//
AfpSrp.dwRequestCode = OP_DIRECTORY_GET_INFO; AfpSrp.dwApiType = AFP_API_TYPE_GETINFO; AfpSrp.Type.GetInfo.pInputBuf = pAfpDirInfoSR; AfpSrp.Type.GetInfo.cbInputBufSize = cbAfpDirInfoSRSize;
dwRetCode = AfpServerIOCtrlGetInfo( &AfpSrp );
LocalFree( pAfpDirInfoSR );
if ( ( dwRetCode != ERROR_MORE_DATA ) && ( dwRetCode != NO_ERROR ) && ( dwRetCode != AFPERR_DirectoryNotInVolume ) ) return( dwRetCode );
// If the directory is not part of a volume, then there server does not
// return any information back. So we have to do the work here.
//
if ( dwRetCode == AFPERR_DirectoryNotInVolume ) {
// First check to see if the directory is in an NTFS/CDFS partition
//
if ( ( dwRetCode = AfpValidatePartition( AfpDirInfo.afpdir_path )) != NO_ERROR ) return( dwRetCode );
if ( ( dwRetCode = AfpGetDirInfo( AfpDirInfo.afpdir_path, &pAfpDirInfo ) ) != NO_ERROR ) return( dwRetCode );
pAfpDirInfo->afpdir_in_volume = FALSE; } else {
pAfpDirInfo = AfpSrp.Type.GetInfo.pOutputBuf;
// Convert all offsets to pointers
//
AfpBufOffsetToPointer( (LPBYTE)pAfpDirInfo, 1, AFP_DIRECTORY_STRUCT );
pAfpDirInfo->afpdir_in_volume = TRUE; }
// Now convert the owner and group SIDs to names
//
dwRetCode = AfpDirConvertSidsToNames( pAfpDirInfo, ppAfpDirectoryInfo ); MIDL_user_free( pAfpDirInfo );
return( dwRetCode ); }
//**
//
// Call: AfpDirMakeFSDRequest
//
// Returns: NO_ERROR
// non-zero returnd from LsaLookupNames
// ERROR_NOT_ENOUGH_MEMORY
//
// Description: Given a AFP_DIRECTORY_INFO structure, will create a
// self-relative buffer that is used to IOCTL the directory
// information down to the FSD. If there are any SIDs names
// (owner or group) they will be converted to their
// SIDs.
//
DWORD AfpDirMakeFSDRequest( IN PAFP_DIRECTORY_INFO pAfpDirectoryInfo, IN DWORD dwParmNum, IN OUT PAFP_DIRECTORY_INFO * ppAfpDirInfoSR, OUT LPDWORD pcbAfpDirInfoSRSize ) { UNICODE_STRING Names[2]; DWORD dwIndex = 0; DWORD dwCount = 0; PLSA_REFERENCED_DOMAIN_LIST pDomainList = NULL; PLSA_TRANSLATED_SID pSids = NULL; LPBYTE pbVariableData; NTSTATUS ntStatus; LSA_HANDLE hLsa = NULL; SECURITY_QUALITY_OF_SERVICE QOS; OBJECT_ATTRIBUTES ObjectAttributes; PSID pDomainSid; DWORD AuthCount; PAFP_DIRECTORY_INFO pAfpDirInfo;
*pcbAfpDirInfoSRSize = (DWORD)(sizeof( SETINFOREQPKT ) + sizeof( AFP_DIRECTORY_INFO ) + (( wcslen( pAfpDirectoryInfo->afpdir_path ) + 1 ) * sizeof(WCHAR)));
// If the client wants to set the owner or the group
// then we need to translate the names to sids
//
if ( ( dwParmNum & AFP_DIR_PARMNUM_OWNER ) || ( dwParmNum & AFP_DIR_PARMNUM_GROUP ) ) {
// First open the LSA and obtain a handle to it.
//
QOS.Length = sizeof( QOS ); QOS.ImpersonationLevel = SecurityImpersonation; QOS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; QOS.EffectiveOnly = FALSE;
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
ObjectAttributes.SecurityQualityOfService = &QOS;
ntStatus = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_LOOKUP_NAMES, &hLsa );
if ( !NT_SUCCESS( ntStatus )) { return( RtlNtStatusToDosError( ntStatus ) ); }
//
// Translate the owner
//
if ( dwParmNum & AFP_DIR_PARMNUM_OWNER ) { RtlInitUnicodeString( &(Names[dwCount++]), pAfpDirectoryInfo->afpdir_owner ); }
//
// Translate the group
//
if ( dwParmNum & AFP_DIR_PARMNUM_GROUP ) { RtlInitUnicodeString( &(Names[dwCount++]), pAfpDirectoryInfo->afpdir_group ); }
ntStatus = LsaLookupNames(hLsa, dwCount, Names, &pDomainList, &pSids);
if ( !NT_SUCCESS( ntStatus ) ) { LsaClose( hLsa );
if ( ntStatus == STATUS_NONE_MAPPED ) { return( (DWORD)AFPERR_NoSuchUserGroup ); } else { return( RtlNtStatusToDosError( ntStatus ) ); } }
for ( dwIndex = 0; dwIndex < dwCount; dwIndex++ ) {
if ( ( pSids[dwIndex].Use == SidTypeInvalid ) || ( pSids[dwIndex].Use == SidTypeUnknown ) || ( pSids[dwIndex].Use == SidTypeDomain ) || ( pSids[dwIndex].DomainIndex == -1 ) ) {
LsaFreeMemory( pDomainList ); LsaClose( hLsa );
if ( ( pSids[dwIndex].Use == SidTypeUnknown ) || ( pSids[dwIndex].Use == SidTypeInvalid ) ) {
LsaFreeMemory( pSids );
if ((dwParmNum & AFP_DIR_PARMNUM_OWNER)&&(dwIndex == 0 )) { return( (DWORD)AFPERR_NoSuchUser ); } else { return( (DWORD)AFPERR_NoSuchGroup ); } } else {
LsaFreeMemory( pSids ); return( (DWORD)AFPERR_NoSuchUserGroup ); } }
pDomainSid = pDomainList->Domains[pSids[dwIndex].DomainIndex].Sid;
AuthCount = *RtlSubAuthorityCountSid( pDomainSid ) + 1;
*pcbAfpDirInfoSRSize += RtlLengthRequiredSid(AuthCount); } }
*ppAfpDirInfoSR=(PAFP_DIRECTORY_INFO)LocalAlloc(LPTR,*pcbAfpDirInfoSRSize);
if ( *ppAfpDirInfoSR == NULL ) { LsaFreeMemory( pDomainList ); LsaFreeMemory( pSids ); LsaClose( hLsa ); return( ERROR_NOT_ENOUGH_MEMORY ); }
pbVariableData = (LPBYTE)((ULONG_PTR)(*ppAfpDirInfoSR) + *pcbAfpDirInfoSRSize);
pAfpDirInfo = (PAFP_DIRECTORY_INFO)((ULONG_PTR)( *ppAfpDirInfoSR) + sizeof( SETINFOREQPKT )); // First copy the fixed part
//
CopyMemory( pAfpDirInfo, pAfpDirectoryInfo, sizeof(AFP_DIRECTORY_INFO) );
// Now copy the path
//
pbVariableData-=((wcslen(pAfpDirectoryInfo->afpdir_path)+1)*sizeof(WCHAR));
wcscpy( (LPWSTR)pbVariableData, pAfpDirectoryInfo->afpdir_path );
pAfpDirInfo->afpdir_path = (LPWSTR)pbVariableData;
POINTER_TO_OFFSET( pAfpDirInfo->afpdir_path, pAfpDirInfo );
// Now copy the SIDs if there are any to be copied
//
dwCount = 0;
if ( dwParmNum & AFP_DIR_PARMNUM_OWNER ) {
pDomainSid = pDomainList->Domains[pSids[dwCount].DomainIndex].Sid;
AuthCount = *RtlSubAuthorityCountSid( pDomainSid ) + 1;
pbVariableData -= RtlLengthRequiredSid(AuthCount); // Copy the Domain Sid.
//
RtlCopySid( RtlLengthRequiredSid(AuthCount), (PSID)pbVariableData, pDomainSid );
// Append the Relative Id.
//
*RtlSubAuthorityCountSid( (PSID)pbVariableData ) += 1; *RtlSubAuthoritySid( (PSID)(pbVariableData), AuthCount - 1) = pSids[dwCount].RelativeId;
pAfpDirInfo->afpdir_owner = (LPWSTR)pbVariableData;
POINTER_TO_OFFSET( pAfpDirInfo->afpdir_owner, pAfpDirInfo );
dwCount++; }
if ( dwParmNum & AFP_DIR_PARMNUM_GROUP ) { pDomainSid = pDomainList->Domains[pSids[dwCount].DomainIndex].Sid;
AuthCount = *RtlSubAuthorityCountSid( pDomainSid ) + 1;
pbVariableData -= RtlLengthRequiredSid(AuthCount);
// Copy the Domain Sid.
//
RtlCopySid( RtlLengthRequiredSid(AuthCount), (PSID)pbVariableData, pDomainSid );
// Append the Relative Id.
//
*RtlSubAuthorityCountSid( (PSID)pbVariableData ) += 1; *RtlSubAuthoritySid( (PSID)(pbVariableData), AuthCount - 1) = pSids[dwCount].RelativeId;
pAfpDirInfo->afpdir_group = (LPWSTR)pbVariableData;
POINTER_TO_OFFSET( pAfpDirInfo->afpdir_group, pAfpDirInfo ); }
LsaFreeMemory( pDomainList ); LsaFreeMemory( pSids ); LsaClose( hLsa );
return( NO_ERROR ); }
//**
//
// Call: AfpSetDirPermission
//
// Returns: NO_ERROR
// non-zero returns from AfpserverIOCtrl.
//
// Description: Given a directory path, will try to set permissions on it
//
DWORD AfpSetDirPermission( IN LPWSTR lpwsDirPath, IN PAFP_DIRECTORY_INFO pAfpDirInfo, IN DWORD dwParmNum ) { AFP_REQUEST_PACKET AfpSrp; PAFP_DIRECTORY_INFO pAfpDirInfoSR; DWORD cbAfpDirInfoSRSize; DWORD dwRetCode;
pAfpDirInfo->afpdir_path = lpwsDirPath;
// Make a self relative buffer and translate any names to SIDs
//
if ( dwRetCode = AfpDirMakeFSDRequest( pAfpDirInfo, dwParmNum, &pAfpDirInfoSR, &cbAfpDirInfoSRSize ) ) return( dwRetCode );
// Make IOCTL to set info
//
AfpSrp.dwRequestCode = OP_DIRECTORY_SET_INFO; AfpSrp.dwApiType = AFP_API_TYPE_SETINFO; AfpSrp.Type.SetInfo.pInputBuf = pAfpDirInfoSR; AfpSrp.Type.SetInfo.cbInputBufSize = cbAfpDirInfoSRSize; AfpSrp.Type.SetInfo.dwParmNum = dwParmNum;
dwRetCode = AfpServerIOCtrl( &AfpSrp );
LocalFree( pAfpDirInfoSR );
return( dwRetCode );
}
//**
//
// Call: AfpRecursePermissions
//
// Returns: NO_ERROR
// non-zero returns from FindFirstFile and FindNextFile.
// non-zero returns from AfpSetDirPermissions
// ERROR_NOT_ENOUGH_MEMORY.
//
// Description: Will recursively set permissions on a given directory.
//
DWORD AfpRecursePermissions( IN HANDLE hFile, IN LPWSTR lpwsDirPath, IN PAFP_DIRECTORY_INFO pAfpDirInfo, IN DWORD dwParmNum ) { WIN32_FIND_DATA FileInfo; DWORD dwRetCode = NO_ERROR; LPWSTR lpwsPath; WCHAR * pwchPath; DWORD dwRetryCount;
do {
lpwsPath = LocalAlloc(LPTR, (STRLEN(lpwsDirPath)+MAX_PATH)*sizeof(WCHAR));
if ( lpwsPath == NULL ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; }
STRCPY( lpwsPath, lpwsDirPath );
if ( hFile != INVALID_HANDLE_VALUE ) {
// Search for the next sub-directory
//
do {
if ( !FindNextFile( hFile, &FileInfo ) ) { dwRetCode = GetLastError(); AFP_PRINT( ( "AFPSVC_dir: Closing handle %x\n", hFile ) ); FindClose( hFile ); break; }
if ( ( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && (!( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM )) && (!( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )) && ( STRCMP( FileInfo.cFileName, TEXT(".") ) != 0 ) && ( STRCMP( FileInfo.cFileName, TEXT("..") ) != 0 ) ) break; } while( TRUE );
if ( dwRetCode != NO_ERROR ) break;
pwchPath = wcsrchr( lpwsPath, TEXT('\\') );
STRCPY( pwchPath+1, FileInfo.cFileName );
}else{
STRCAT( lpwsPath, TEXT("\\*") );
hFile = FindFirstFile( lpwsPath, &FileInfo );
// If there are no more files, we return to the previous
// level in the recursion.
//
if ( hFile == INVALID_HANDLE_VALUE ){
dwRetCode = GetLastError(); break; }
// Search for the first sub-directory
//
do {
if ( ( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && (!( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM )) && (!( FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )) && ( STRCMP( FileInfo.cFileName, TEXT(".") ) != 0 ) && ( STRCMP( FileInfo.cFileName, TEXT("..") ) != 0 ) ) break;
if ( !FindNextFile( hFile, &FileInfo ) ) { dwRetCode = GetLastError();
AFP_PRINT( ( "AFPSVC_dir: Closing handle %x\n", hFile ) ); FindClose( hFile );
break; }
} while( TRUE );
if ( dwRetCode != NO_ERROR ) break;
pwchPath = lpwsPath + STRLEN(lpwsDirPath) + 1;
STRCPY( pwchPath, FileInfo.cFileName ); }
// Don't send the \\?\ down to the server
pwchPath = lpwsPath + 4;
// Set the information
//
dwRetryCount = 0;
do { dwRetCode = AfpSetDirPermission( pwchPath, pAfpDirInfo, dwParmNum );
if ( dwRetCode != ERROR_PATH_NOT_FOUND ) break;
Sleep( 1000 );
} while( ++dwRetryCount < 4 );
if ( dwRetCode != NO_ERROR ) break;
// Recurse on the directory
//
dwRetCode = AfpRecursePermissions( hFile, lpwsPath, pAfpDirInfo, dwParmNum );
if ( dwRetCode != NO_ERROR ) break;
// Recurse on the sub-directory
//
dwRetCode = AfpRecursePermissions( INVALID_HANDLE_VALUE, lpwsPath, pAfpDirInfo, dwParmNum ); break;
if ( dwRetCode != NO_ERROR ) break;
} while( FALSE );
if ( lpwsPath != (LPWSTR)NULL ) { LocalFree( lpwsPath ); }
if ( dwRetCode == ERROR_NO_MORE_FILES ) { dwRetCode = NO_ERROR; }
return( dwRetCode ); }
//**
//
// Call: AfpAdminrDirectorySetInfo
//
// Returns: NO_ERROR
// ERROR_ACCESS_DENIED
// non-zero retunrs from I_DirectorySetInfo.
//
// Description: This routine communicates with the AFP FSD to implement
// the AfpAdminDirectorySetInfo function. The real work is done
// by I_DirectorySetInfo
//
DWORD AfpAdminrDirectorySetInfo( IN AFP_SERVER_HANDLE hServer, IN PAFP_DIRECTORY_INFO pAfpDirectoryInfo, IN DWORD dwParmNum ) { DWORD dwRetCode=0; DWORD dwAccessStatus=0;
// Check if caller has access
//
if ( dwRetCode = AfpSecObjAccessCheck( AFPSVC_ALL_ACCESS, &dwAccessStatus)) { AFP_PRINT(( "SFMSVC: AfpAdminrDirectorySetInfo, AfpSecObjAccessCheck failed %ld\n",dwRetCode)); AfpLogEvent( AFPLOG_CANT_CHECK_ACCESS, 0, NULL, dwRetCode, EVENTLOG_ERROR_TYPE ); return( ERROR_ACCESS_DENIED ); }
if ( dwAccessStatus ) { AFP_PRINT(( "SFMSVC: AfpAdminrDirectorySetInfo, AfpSecObjAccessCheck returned %ld\n",dwAccessStatus)); return( ERROR_ACCESS_DENIED ); }
dwRetCode = I_DirectorySetInfo( pAfpDirectoryInfo, dwParmNum );
return( dwRetCode ); }
//**
//
// Call: I_DirectorySetInfo
//
// Returns: NO_ERROR
//
//
// Description: This routine does the real work. The existance of this
// worker is so that it may be called from the AfpAfdminVolmeAdd
// API without the RPC handle and access checking.
//
DWORD I_DirectorySetInfo( IN PAFP_DIRECTORY_INFO pAfpDirectoryInfo, IN DWORD dwParmNum ) { DWORD dwRetCode;
if (pAfpDirectoryInfo->afpdir_path == NULL) { AFP_PRINT(( "SFMSVC: I_DirectorySetInfo, pAfpDirectoryInfo->afpdir_path == NULL\n")); return ERROR_INVALID_DATA; }
// Set the permissions on the directory
//
if ( ( dwRetCode = AfpSetDirPermission( pAfpDirectoryInfo->afpdir_path, pAfpDirectoryInfo, dwParmNum ) ) != NO_ERROR ) return( dwRetCode );
// If the user wants to set these permissions recursively
//
if ( pAfpDirectoryInfo->afpdir_perms & AFP_PERM_SET_SUBDIRS ) { LPWSTR NTDirName;
// We must use the \\?\ notation for the path in order to bypass
// the Win32 path length limitation of 260 chars
NTDirName = LocalAlloc( LPTR, (STRLEN(pAfpDirectoryInfo->afpdir_path) + 4 + 1) * sizeof(WCHAR));
if (NTDirName == NULL) return( ERROR_NOT_ENOUGH_MEMORY );
STRCPY( NTDirName, TEXT("\\\\?\\")); STRCAT( NTDirName, pAfpDirectoryInfo->afpdir_path);
dwRetCode = AfpRecursePermissions( INVALID_HANDLE_VALUE, NTDirName, pAfpDirectoryInfo, dwParmNum ); LocalFree( NTDirName );
}
return( dwRetCode ); }
|