windows-nt/Source/XPSP1/NT/net/sfm/afp/service/server/dir.c
2020-09-26 16:20:57 +08:00

1390 lines
45 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************************/
/** 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 );
}