/*++ Copyright (c) 1993 Microsoft Corporation Module Name: adtsrv.c Abstract: AdminTools Server functions. This file contains the remote interface for NetpGetFileSecurity and NetpSetFileSecurity API. Author: Dan Lafferty (danl) 25-Mar-1993 Environment: User Mode - Win32 Revision History: 27-Oct-1994 IsaacHe Make sure the share permissions allow these operations. 05-Sep-1994 Danl Free memory and NULL the pointer to the SecurityDescriptor if a failure occurs. Also free the buffer returned from NetShareGetInfo. 25-Mar-1993 danl Created --*/ // // Includes // #include "srvsvcp.h" #include #include #include DWORD AdtsvcDebugLevel = DEBUG_ERROR; // // LOCAL FUNCTIONS // NET_API_STATUS AdtCheckShareAccessAndGetFullPath( LPWSTR pShare, LPWSTR pFileName, LPWSTR *pPath, ACCESS_MASK DesiredAccess ); NET_API_STATUS NET_API_FUNCTION NetrpGetFileSecurity ( IN LPWSTR ServerName, IN LPWSTR ShareName, IN LPWSTR FileName, IN SECURITY_INFORMATION RequestedInfo, OUT PADT_SECURITY_DESCRIPTOR *pSecurityDescriptor ) /*++ Routine Description: This function returns to the caller a copy of the security descriptor protecting a file or directory. It calls GetFileSecurity. The Security Descriptor is always returned in the self-relative format. This function is called only when accessing remote files. In this case, the filename is broken into ServerName, ShareName, and FileName components. The ServerName gets the request to this routine. The ShareName must be expanded to find the local path associated with it. This is combined with the FileName to create a fully qualified pathname that is local to this machine. Arguments: ServerName - A pointer to a string containing the name of the remote server on which the function is to execute. A NULL pointer or string specifies the local machine. ShareName - A pointer to a string that identifies the share name on which the file is found. FileName - A pointer to the name fo the file or directory whose security is being retrieved. RequestedInfo - The type of security information being requested. pSecurityDescriptor - A pointer to a pointer to a structure which contains the buffer pointer for the security descriptor and a length field for the security descriptor. Return Value: NERR_Success - The operation was successful. ERROR_NOT_ENOUGH_MEMORY - Unable to allocate memory for the security descriptor. Other - This function can also return any error that GetFileSecurity, RpcImpersonateClient, or ShareEnumCommon can return. --*/ { NET_API_STATUS status; PSECURITY_DESCRIPTOR pNewSecurityDescriptor; DWORD bufSize; LPWSTR FullPath=NULL; ACCESS_MASK DesiredAccess = 0; *pSecurityDescriptor = MIDL_user_allocate(sizeof(ADT_SECURITY_DESCRIPTOR)); if (*pSecurityDescriptor == NULL) { ADT_LOG0( ERROR, "NetrpGetFileSecurity:MIDL_user_alloc failed\n" ); return ERROR_NOT_ENOUGH_MEMORY; } // // Figure out accesses needed to perform the indicated operation(s). // This code is taken from ntos\se\semethod.c // if ((RequestedInfo & OWNER_SECURITY_INFORMATION) || (RequestedInfo & GROUP_SECURITY_INFORMATION) || (RequestedInfo & DACL_SECURITY_INFORMATION)) { DesiredAccess |= READ_CONTROL; } if ((RequestedInfo & SACL_SECURITY_INFORMATION)) { DesiredAccess |= ACCESS_SYSTEM_SECURITY; } // // Check share perms and create a full path string by getting // the path for the share name, and adding the FileName string to it. // status = AdtCheckShareAccessAndGetFullPath( ShareName, FileName, &FullPath, DesiredAccess ); if( status == NO_ERROR ) { if( (status = RpcImpersonateClient(NULL)) == NO_ERROR ) { // // Get the File Security information // status = PrivateGetFileSecurity( FullPath, RequestedInfo, &pNewSecurityDescriptor, &bufSize); if ( status == NO_ERROR ) { (*pSecurityDescriptor)->Length = bufSize; (*pSecurityDescriptor)->Buffer = pNewSecurityDescriptor; } (VOID)RpcRevertToSelf(); } MIDL_user_free( FullPath ); } if ( status != NO_ERROR ) { MIDL_user_free(*pSecurityDescriptor); *pSecurityDescriptor = NULL; } return status; } NET_API_STATUS NET_API_FUNCTION NetrpSetFileSecurity ( IN LPWSTR ServerName OPTIONAL, IN LPWSTR ShareName, IN LPWSTR FileName, IN SECURITY_INFORMATION SecurityInfo, IN PADT_SECURITY_DESCRIPTOR pSecurityDescriptor ) /*++ Routine Description: This function can be used to set the security of a file or directory. It calls SetFileSecurity(). Arguments: ServerName - A pointer to a string containing the name of the remote server on which the function is to execute. A NULL pointer or string specifies the local machine. ShareName - A pointer to a string that identifies the share name on which the file or directory is found. FileName - A pointer to the name of the file or directory whose security is being changed. SecurityInfo - Information describing the contents of the Security Descriptor. pSecurityDescriptor - A pointer to a structure that contains a self-relative security descriptor and a length. Return Value: NERR_Success - The operation was successful. Other - This function can also return any error that SetFileSecurity, RpcImpersonateClient, or ShareEnumCommon can return. --*/ { NET_API_STATUS status; LPWSTR FullPath=NULL; ACCESS_MASK DesiredAccess = 0; UNREFERENCED_PARAMETER(ServerName); // Validate the parameters if( (pSecurityDescriptor->Buffer == NULL) && (pSecurityDescriptor->Length > 0) ) { return ERROR_INVALID_PARAMETER; } // // Figure out accesses needed to perform the indicated operation(s). // This code is taken from ntos\se\semethod.c // if ((SecurityInfo & OWNER_SECURITY_INFORMATION) || (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { DesiredAccess |= WRITE_OWNER; } if (SecurityInfo & DACL_SECURITY_INFORMATION) { DesiredAccess |= WRITE_DAC; } if (SecurityInfo & SACL_SECURITY_INFORMATION) { DesiredAccess |= ACCESS_SYSTEM_SECURITY; } // // Check perms and create a full path string by getting the path // for the share name, and adding the FileName string to it. // status = AdtCheckShareAccessAndGetFullPath( ShareName, FileName, &FullPath, DesiredAccess ); if ( status == NO_ERROR ) { if( (status = RpcImpersonateClient(NULL)) == NO_ERROR ) { if (RtlValidRelativeSecurityDescriptor( pSecurityDescriptor->Buffer, pSecurityDescriptor->Length, SecurityInfo)) { // // Call SetFileSecurity // status = PrivateSetFileSecurity( FullPath, SecurityInfo, pSecurityDescriptor->Buffer); } else { status = ERROR_INVALID_SECURITY_DESCR; } (VOID)RpcRevertToSelf(); } MIDL_user_free(FullPath); } return(status); } NET_API_STATUS AdtCheckShareAccessAndGetFullPath( LPWSTR pShare, LPWSTR pFileName, LPWSTR *pPath, ACCESS_MASK DesiredAccess ) /*++ Routine Description: This function ensures the DesiredAccess is allowed and finds the path associated with the share name, combines this with the file name, and creates a fully qualified path name. NOTE: This function allocates storage for the pPath string. Arguments: pShare - This is a pointer to the share name string. pFileName - This is a pointer to the file name (or path) string. pPath - This is a pointer to a location where the pointer to the complete file path string can be stored. This pointer needs to be free'd with MIDL_user_free when the caller is finished with it. DesiredAccess - what we'd like to do through the share Return Value: NO_ERROR - if The operation was completely successful. Other - Errors returned from ShareEnumCommon, and MIDL_user_allocate may be returned from this routine. Comments: The share access checking is complicated by the fact that the share ACL has had the owner and group SIDs stripped out. We need to put them back in, or the SsCheckAccess() call will fail. --*/ { NET_API_STATUS status; PSHARE_INFO_502 pshi502 = NULL; DWORD bufSize; DWORD fileNameSize; LPWSTR pLastChar; DWORD entriesRead; DWORD totalEntries; PSECURITY_DESCRIPTOR NewDescriptor = NULL; SECURITY_DESCRIPTOR ModificationDescriptor; GENERIC_MAPPING Mapping; SRVSVC_SECURITY_OBJECT SecurityObject; HANDLE token; status = ShareEnumCommon( 502, (LPBYTE *)&pshi502, (DWORD)-1, &entriesRead, &totalEntries, NULL, pShare ); if( status != NO_ERROR ) { goto getout; } else if( entriesRead == 0 || pshi502 == NULL ) { status = NERR_NetNameNotFound; } else if( pshi502->shi502_path == NULL ) { status = ERROR_BAD_DEV_TYPE; } else if( pshi502->shi502_security_descriptor != NULL ) { status = RtlCopySecurityDescriptor( pshi502->shi502_security_descriptor, &NewDescriptor ); if( status != STATUS_SUCCESS ) goto getout; RtlCreateSecurityDescriptor( &ModificationDescriptor, SECURITY_DESCRIPTOR_REVISION ); RtlSetOwnerSecurityDescriptor( &ModificationDescriptor, SsData.SsLmsvcsGlobalData->LocalSystemSid, FALSE ); RtlSetGroupSecurityDescriptor( &ModificationDescriptor, SsData.SsLmsvcsGlobalData->LocalSystemSid, FALSE ); Mapping.GenericRead = FILE_GENERIC_READ; Mapping.GenericWrite = FILE_GENERIC_WRITE; Mapping.GenericExecute = FILE_GENERIC_EXECUTE; Mapping.GenericAll = FILE_ALL_ACCESS; if( ImpersonateSelf( SecurityImpersonation ) == FALSE ) { status = GetLastError(); goto getout; } status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, &token ); RevertToSelf(); if( status != STATUS_SUCCESS ) goto getout; status = RtlSetSecurityObject ( GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, &ModificationDescriptor, &NewDescriptor, &Mapping, token ); NtClose( token ); if( status == STATUS_SUCCESS ) { SecurityObject.ObjectName = pShare; SecurityObject.Mapping = &Mapping; SecurityObject.SecurityDescriptor = NewDescriptor; // // SsCheckAccess does an RpcImpersonateClient()... // status = SsCheckAccess( &SecurityObject, DesiredAccess ); } } if( status == STATUS_SUCCESS ) { // // If the last character is a '\', then we must remove it. // pLastChar = pshi502->shi502_path + wcslen(pshi502->shi502_path); pLastChar--; if (*pLastChar == L'\\') { *pLastChar = L'\0'; } bufSize = STRSIZE(pshi502->shi502_path); fileNameSize = STRSIZE(pFileName); *pPath = MIDL_user_allocate( bufSize + fileNameSize ); if (*pPath != NULL) { wcscpy (*pPath, pshi502->shi502_path); wcscat (*pPath, pFileName); } else { status = ERROR_NOT_ENOUGH_MEMORY; } } getout: if( NewDescriptor != NULL ) RtlDeleteSecurityObject( &NewDescriptor ); if( pshi502 != NULL ) MIDL_user_free( pshi502 ); return status; }