/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: Share.c Abstract: This module contains support for the Share catagory of APIs for the NT server service. Author: David Treadwell (davidtr) 10-Jan-1991 Revision History: --*/ #include "srvsvcp.h" #include "ssreg.h" #include #include #include #include #include #include #include #include #include #define SET_ERROR_PARAMETER(a) \ if ( ARGUMENT_PRESENT( ErrorParameter ) ) { *ErrorParameter = a; } // // Use the same directory separator as the object system uses. // #define IS_SLASH_SLASH_NAME( _x ) \ ( IS_PATH_SEPARATOR( _x[0] ) && \ IS_PATH_SEPARATOR( _x[1] ) && \ _x[2] == L'.' && \ IS_PATH_SEPARATOR( _x[3] ) ) #define IS_NTPATH_NAME( _x ) \ ( _x && \ IS_PATH_SEPARATOR( _x[0] ) && \ IS_PATH_SEPARATOR( _x[1] ) && \ _x[2] == L'?' && \ IS_PATH_SEPARATOR( _x[3] ) ) // // Local types. // PSHARE_DEL_CONTEXT SrvShareDelContextHead = NULL; CRITICAL_SECTION ShareDelContextMutex; GENERIC_MAPPING SrvShareFileGenericMapping = GENERIC_SHARE_FILE_ACCESS_MAPPING; // // Forward declarations. // PVOID CaptureShareInfo ( IN DWORD Level, IN PSHARE_INFO_2 Shi2, IN DWORD ShareType, IN LPWSTR Path, IN LPWSTR Remark, IN PSECURITY_DESCRIPTOR ConnectSecurityDescriptor, IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL, OUT PULONG CapturedBufferLength ); NET_API_STATUS DisallowSharedLanmanNetDrives( IN PUNICODE_STRING NtSharePath ); NET_API_STATUS ShareAssignSecurityDescriptor( IN PSECURITY_DESCRIPTOR PassedSecurityDescriptor, OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor ); NET_API_STATUS ShareEnumCommon ( IN DWORD Level, OUT LPBYTE *Buffer, IN DWORD PreferredMaximumLength, OUT LPDWORD EntriesRead, OUT LPDWORD TotalEntries, IN OUT LPDWORD ResumeHandle OPTIONAL, IN LPWSTR NetName OPTIONAL ); NET_API_STATUS ShareEnumSticky ( IN DWORD Level, OUT LPBYTE *Buffer, IN DWORD PreferredMaximumLength, OUT LPDWORD EntriesRead, OUT LPDWORD TotalEntries, IN OUT LPDWORD ResumeHandle OPTIONAL ); ULONG SizeShares ( IN ULONG Level, IN PSHARE_INFO_502 Shi502 ); BOOLEAN ValidSharePath( IN LPWSTR SharePath, IN BOOL IsNtPath ); NET_API_STATUS NET_API_FUNCTION NetrShareAdd ( IN LPWSTR ServerName, IN DWORD Level, IN LPSHARE_INFO Buffer, OUT LPDWORD ErrorParameter ) { return I_NetrShareAdd( ServerName, Level, Buffer, ErrorParameter, FALSE ); } NET_API_STATUS NET_API_FUNCTION I_NetrShareAdd ( IN LPWSTR ServerName, IN DWORD Level, IN LPSHARE_INFO Buffer, OUT LPDWORD ErrorParameter, IN BOOLEAN BypassSecurity ) /*++ Routine Description: This routine communicates with the server FSD to implement the NetShareAdd function. Only levels 2 and 502 are valid. Arguments: ServerName - name of the server. Level - Request level. Buffer - Contains the information about the share. If this is a level 502 request, will also contain a valid security descriptor in self-relative form. ErrorParameter - status of the FsControl call. BypassSecurity - skip security checks Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; NTSTATUS status; PSERVER_REQUEST_PACKET srp; PVOID capturedBuffer; LPWSTR path; LPWSTR netName; LPWSTR remark; ULONG bufferLength; UNICODE_STRING dosSharePath; UNICODE_STRING ntSharePath; PSRVSVC_SECURITY_OBJECT securityObject; PSECURITY_DESCRIPTOR connectSecurityDescriptor; PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL; PSECURITY_DESCRIPTOR newFileSecurityDescriptor = NULL; UINT driveType = DRIVE_FIXED; DWORD shareType; BOOL isIpc; BOOL isAdmin; BOOL isDiskAdmin; BOOL isPrintShare; BOOL isSpecial; BOOL isNtPath; BOOL isTemporary; BOOL FreeFileSecurityDescriptor; PSHARE_INFO_2 shi2; PSHARE_INFO_502 shi502; ServerName; // // Check that user input buffer is not NULL // if ( !ARGUMENT_PRESENT( Buffer ) || Buffer->ShareInfo2 == NULL) { SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN); return ERROR_INVALID_PARAMETER; } // // Set up for error cleanup. // srp = NULL; dosSharePath.Buffer = NULL; ntSharePath.Buffer = NULL; capturedBuffer = NULL; FreeFileSecurityDescriptor = FALSE; // // Extract Internal buffer information. // shi2 = Buffer->ShareInfo2; // // 502 may contain a security descriptor. // if ( Level == 502 ) { shi502 = (LPSHARE_INFO_502) Buffer->ShareInfo502; fileSecurityDescriptor = shi502->shi502_security_descriptor; // // check the reserved field. If it is zero, this was called from // inside the srvsvc. If not, it was through rpc. // if ( fileSecurityDescriptor != NULL ) { if ( !RtlValidSecurityDescriptor( fileSecurityDescriptor ) ) { SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } if ( shi502->shi502_reserved != 0 ) { error = ShareAssignSecurityDescriptor( fileSecurityDescriptor, &newFileSecurityDescriptor ); if ( error != NO_ERROR ) { SS_PRINT(( "NetrShareAdd: ShareAssignSecurityDescriptor " "error: %d\n", error )); SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } FreeFileSecurityDescriptor = TRUE; } else { newFileSecurityDescriptor = fileSecurityDescriptor; } } else { fileSecurityDescriptor = SsDefaultShareSecurityObject.SecurityDescriptor; newFileSecurityDescriptor = fileSecurityDescriptor; } } else if ( Level != 2 ) { // // The only valid levels are 2 and 502. 2 is a subset of 502. // error = ERROR_INVALID_LEVEL; goto exit; } else { // For level 2, default to the default security descriptor fileSecurityDescriptor = SsDefaultShareSecurityObject.SecurityDescriptor; newFileSecurityDescriptor = fileSecurityDescriptor; } // // A share name must be specified. // netName = shi2->shi2_netname; if ( (netName == NULL) || (*netName == '\0') ) { SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // Limit it to NNLEN // if ( wcslen(netName) > NNLEN ) { SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // If this is the IPC$ share, or the ADMIN$ share, no path // may be specified. No path is needed for the IPC$ share, while a path // is supplied internally for the ADMIN$ share. // path = shi2->shi2_path; remark = shi2->shi2_remark; shareType = (shi2->shi2_type & ~(STYPE_TEMPORARY)); // // Figure out which kind of share this is. // isIpc = (BOOL)(STRICMP( netName, IPC_SHARE_NAME ) == 0); isAdmin = (BOOL)(STRICMP( netName, ADMIN_SHARE_NAME ) == 0); isTemporary = (BOOL)(shi2->shi2_type & STYPE_TEMPORARY); isNtPath = IS_NTPATH_NAME( path ); // For NTPaths, we only allow disk-style shares if( isNtPath && (shareType != STYPE_DISKTREE) ) { SET_ERROR_PARAMETER( SHARE_TYPE_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // We have an administrative disk share if the share name is a drive letter // followed by $, and if the path name is the root of that same drive. // if( wcslen( netName ) == 2 && netName[1] == L'$' && TOUPPER( netName[0] ) >= L'A' && TOUPPER( netName[0]) <= L'Z' && path != NULL && wcslen( path ) == 3 && TOUPPER( path[0] ) == TOUPPER( netName[0] ) && path[1] == L':' && path[2] == L'\\' ) { // // The share name and path look to be an administrative disk share. // If the path refers to a fixed drive, then it really is one. // isDiskAdmin = ((SsGetDriveType( path ) == DRIVE_FIXED) || (SsGetDriveType( path ) == DRIVE_CDROM) || (SsGetDriveType( path ) == DRIVE_REMOVABLE)); } else { isDiskAdmin = FALSE; } isPrintShare = (BOOL)(shareType == STYPE_PRINTQ); isSpecial = isIpc || isAdmin || isDiskAdmin; if ( isIpc ) { if ( path != NULL ) { SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } path = NULL; // // Let the caller specify a remark if they want. If they don't, // supply a default remark. // if ( remark == NULL ) { remark = SsIPCShareRemark; } shareType = STYPE_IPC; } else if ( isAdmin ) { if ( path != NULL ) { SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // Let the caller specify a remark if they want. If they don't, // supply a default remark. // if ( remark == NULL ) { remark = SsAdminShareRemark; } shareType = STYPE_DISKTREE; // // For the ADMIN$ share, we set the path to the system root // directory. We get this from from the kernel via the // read-only shared page (USER_SHARED_DATA) // path = USER_SHARED_DATA->NtSystemRoot; } else { // // For all shares other than IPC$ and ADMIN$, a path must be // specified and must not have .. and . as directory names. // if ( (path == NULL) || (*path == '\0') || !ValidSharePath( path, isNtPath ) ) { SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); error = ERROR_INVALID_NAME; goto exit; } // // If we've got a disk admin share and they didn't supply a // comment, use the built in one // if( isDiskAdmin && remark == NULL ) { remark = SsDiskAdminShareRemark; } } // // The remark must be no longer than MAXCOMMENTSZ. // if ( (remark != NULL) && (STRLEN(remark) > MAXCOMMENTSZ) ) { SET_ERROR_PARAMETER( SHARE_REMARK_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // If the server service is fully started, make sure that the caller // is allowed to set share information in the server. We only do // this if the service is started--the default share and configured // share creations done during initialization do not need any // special access. // if ( SsData.SsInitialized && BypassSecurity == FALSE ) { if ( isSpecial ) { securityObject = &SsShareAdminSecurityObject; } else if ( isPrintShare ) { securityObject = &SsSharePrintSecurityObject; } else { securityObject = &SsShareFileSecurityObject; } error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET ); if ( error != NO_ERROR ) { SET_ERROR_PARAMETER( 0 ); goto exit; } } // // If this is a disk share, make sure that the drive is a type that // can be shared. // if ( (shareType == STYPE_DISKTREE) && !isAdmin ) { DWORD pathType; // // Check the path type. It should be an absolute directory path. // We do not check the path type for Nt Paths // if( !isNtPath ) { error = NetpPathType( NULL, path, &pathType, 0 ); if ( (error != NO_ERROR) || (pathType != ITYPE_PATH_ABSD) ) { error = ERROR_INVALID_NAME; SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); goto exit; } } driveType = SsGetDriveType( path ); if ( driveType == DRIVE_REMOVABLE ) { shareType = STYPE_REMOVABLE; } else if ( driveType == DRIVE_CDROM ) { shareType = STYPE_CDROM; } else if ( !(driveType == DRIVE_REMOTE && SsData.ServerInfo599.sv599_enablesharednetdrives) && driveType != DRIVE_FIXED && driveType != DRIVE_RAMDISK ) { if ( driveType == DRIVE_REMOTE ) { error = NERR_RedirectedPath; } else { error = NERR_UnknownDevDir; } SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); goto exit; } } // // Set up the request packet. // srp = SsAllocateSrp( ); if ( srp == NULL ) { error = ERROR_NOT_ENOUGH_MEMORY; goto exit; } srp->Level = Level; // // Get the path name in NT format and put it in the SRP. // if ( path != NULL ) { RtlInitUnicodeString( &dosSharePath, path ); if ( !RtlDosPathNameToNtPathName_U( dosSharePath.Buffer, &ntSharePath, NULL, NULL ) ) { SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // If this is a redirected drive, make sure the redir is not // LanMan. // if ( driveType == DRIVE_REMOTE ) { error = DisallowSharedLanmanNetDrives( &ntSharePath ); if ( error != NERR_Success ) { SET_ERROR_PARAMETER( SHARE_PATH_PARMNUM ); goto exit; } } // if remote drive srp->Name1 = ntSharePath; } // // Determine whether this is an admin share and use the appropriate // security descriptor. // if ( isAdmin || isDiskAdmin ) { connectSecurityDescriptor = SsShareAdmConnectSecurityObject.SecurityDescriptor; } else { connectSecurityDescriptor = SsShareConnectSecurityObject.SecurityDescriptor; } // // If this is a disk share, verify that the directory to be shared // exists and that the caller has access. (Don't do the access // check during server startup.) Don't check the admin$ share -- we // know it exists. Skip removable type disks. // if ( !isAdmin && (shareType == STYPE_DISKTREE) && (shi2->shi2_path != NULL) ) { OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK iosb; HANDLE handle = INVALID_HANDLE_VALUE; NTSTATUS status; if ( SsData.SsInitialized && BypassSecurity == FALSE && (error = RpcImpersonateClient(NULL)) != NO_ERROR ) { goto exit; } InitializeObjectAttributes( &objectAttributes, &ntSharePath, OBJ_CASE_INSENSITIVE, 0, NULL ); status = NtOpenFile( &handle, FILE_LIST_DIRECTORY, &objectAttributes, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT ); if( status == STATUS_INVALID_PARAMETER ) { status = NtOpenFile( &handle, FILE_LIST_DIRECTORY, &objectAttributes, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); } if ( SsData.SsInitialized && BypassSecurity == FALSE ) { (VOID)RpcRevertToSelf( ); } if ( !NT_SUCCESS(status) ) { if ( SsData.SsInitialized || (status != STATUS_ACCESS_DENIED) ) { // // During startup, if the directory does not // exist (renamed/deleted), log an event. // if ( !SsData.SsInitialized && ((status == STATUS_OBJECT_NAME_NOT_FOUND) || (status == STATUS_OBJECT_PATH_NOT_FOUND)) ) { LPWSTR subStrings[2]; subStrings[0] = netName; subStrings[1] = shi2->shi2_path; SsLogEvent( EVENT_SRV_CANT_RECREATE_SHARE, 2, subStrings, NO_ERROR ); } SET_ERROR_PARAMETER( 0 ); error = RtlNtStatusToDosError( status ); goto exit; } } else if( !IS_SLASH_SLASH_NAME( path ) ) { if ( SsData.SsInitialized ) { FILE_FS_ATTRIBUTE_INFORMATION fileFsAttributeInformation; RtlZeroMemory( &fileFsAttributeInformation, sizeof( fileFsAttributeInformation )); status = NtQueryVolumeInformationFile( handle, &iosb, &fileFsAttributeInformation, sizeof( fileFsAttributeInformation ), FileFsAttributeInformation ); if( (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW) && !(fileFsAttributeInformation.FileSystemAttributes & FILE_SUPPORTS_REPARSE_POINTS) ) { // // Query the name from the file system. This is because some // fs like fat uses only upper case oem names. This can cause // a problem because some oem characters do not have upper case // equivalents and thus get mapped to something funny. // // We do not do this if the filesystem supports reparse points, since // we will come up with the wrong name! // PFILE_NAME_INFORMATION fileNameInformation; ULONG fileInfoSize; ULONG fileNameLength; fileInfoSize = sizeof(FILE_NAME_INFORMATION) + SIZE_WSTR( path ); fileNameInformation = MIDL_user_allocate( fileInfoSize ); if ( fileNameInformation == NULL ) { error = ERROR_NOT_ENOUGH_MEMORY; NtClose( handle ); goto exit; } status = NtQueryInformationFile( handle, &iosb, fileNameInformation, fileInfoSize, FileNameInformation ); if ( status == STATUS_SUCCESS ) { // // The file name returned is expected to be // 3 characters shorter than the share path length. // These 3 characters are "X", ":", "\0". // // If the lengths do not match, than this could be a mounted // FAT volume on an NTFS drive, so we only copy the necessary data // fileNameLength = fileNameInformation->FileNameLength; if ((fileNameLength+3*sizeof(WCHAR)) <= SIZE_WSTR(path)) { // // Copy the path name // RtlCopyMemory( (LPBYTE) path + 2*sizeof(WCHAR) + (SIZE_WSTR(path) - (fileNameLength+3*sizeof(WCHAR))), fileNameInformation->FileName, fileNameLength ); path[fileNameLength/sizeof(WCHAR)+2+(SIZE_WSTR(path) - (fileNameLength+3*sizeof(WCHAR)))/sizeof(WCHAR)] = L'\0'; } } MIDL_user_free( fileNameInformation ); } } NtClose( handle ); } else { NtClose( handle ); } } // // Capture the share data structure passed in. // if ( isSpecial ) { shareType |= STYPE_SPECIAL; } if ( isTemporary ) { shareType |= STYPE_TEMPORARY; } capturedBuffer = CaptureShareInfo( Level, shi2, shareType, path, remark, connectSecurityDescriptor, newFileSecurityDescriptor, &bufferLength ); if ( capturedBuffer == NULL ) { SET_ERROR_PARAMETER( 0 ); error = ERROR_NOT_ENOUGH_MEMORY; goto exit; } // // Send the request on to the server. // error = SsServerFsControl( FSCTL_SRV_NET_SHARE_ADD, srp, capturedBuffer, bufferLength ); SET_ERROR_PARAMETER( srp->Parameters.Set.ErrorParameter ); // // If the request succeeded, add a value to the Shares key, thus // effecting a sticky share. We only do this if the server is fully // started -- the default share and configured share creations done // during initialization should not be added to the registry. // // Don't do this if this is an admin share being [re]created. // if ( SsData.SsInitialized && (error == NO_ERROR) && !isSpecial && !isTemporary ) { SsAddShareToRegistry( shi2, newFileSecurityDescriptor, 0 ); } // // If a print share was successfully added, increment the number // of print shares and update the exported (announced) server type. // if ( isPrintShare ) { InterlockedIncrement( &SsData.NumberOfPrintShares ); SsSetExportedServerType( NULL, FALSE, TRUE ); } exit: // // Clean up. Free the share data structure that was allocated by // CaptureShareInfo2. Free the server request packet. If there was // an NT path name allocated by RtlDosPathNameToNtPathName, free it. // If we created ADMIN$, free the system path string and the system // path information buffer. // if (FreeFileSecurityDescriptor) { (VOID) RtlDeleteSecurityObject ( &newFileSecurityDescriptor ); } if ( capturedBuffer != NULL ) { MIDL_user_free( capturedBuffer ); } if ( srp != NULL ) { SsFreeSrp( srp ); } if ( ntSharePath.Buffer != NULL ) { RtlFreeUnicodeString( &ntSharePath ); } return error; } // NetrShareAdd NET_API_STATUS NetrShareCheck ( IN LPWSTR ServerName, IN LPWSTR Device, OUT LPDWORD Type ) /*++ Routine Description: This routine implements NetShareCheck by calling NetrShareEnum. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { DWORD totalEntries; DWORD entriesRead; ULONG i; PSHARE_INFO_2 shi2; NET_API_STATUS error; LPBYTE buffer = NULL; ServerName; // // Call ShareEnumCommon to actually get the information about what // the shares are on the server. We use this routine rather than // calling NetrShareEnum because NetShareCheck requires no privilege // to execute, and we don't want the security checks in // NetrShareEnum. // error = ShareEnumCommon( 2, &buffer, (DWORD)-1, &entriesRead, &totalEntries, NULL, NULL ); if ( error != NO_ERROR ) { if( buffer ) { MIDL_user_free( buffer ); } return error; } SS_ASSERT( totalEntries == entriesRead ); // // Attempt to find the drive letter in a share's path name. // for ( shi2 = (PSHARE_INFO_2)buffer, i = 0; i < totalEntries; shi2++, i++ ) { if ( shi2->shi2_path != NULL && Device && *Device == *shi2->shi2_path ) { // // Something on the specified disk is shared--free the buffer // and return the type of share. // *Type = shi2->shi2_type & ~STYPE_SPECIAL; MIDL_user_free( buffer ); return NO_ERROR; } } // // Nothing on the specified disk is shared. Return an error. // MIDL_user_free( buffer ); return NERR_DeviceNotShared; } // NetrShareCheck NET_API_STATUS NET_API_FUNCTION NetrShareDel ( IN LPWSTR ServerName, IN LPWSTR NetName, IN DWORD Reserved ) /*++ Routine Description: This routine communicates with the server FSD to implement the NetShareDel function. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; SHARE_DEL_HANDLE handle; error = I_NetrShareDelStart( ServerName, NetName, Reserved, &handle, TRUE ); if ( error == NO_ERROR ) { error = NetrShareDelCommit( &handle ); } return error; } // NetrShareDel NET_API_STATUS NET_API_FUNCTION NetrShareDelStart ( IN LPWSTR ServerName, IN LPWSTR NetName, IN DWORD Reserved, IN PSHARE_DEL_HANDLE ContextHandle ) { return I_NetrShareDelStart( ServerName, NetName, Reserved, ContextHandle, TRUE ); } NET_API_STATUS NET_API_FUNCTION I_NetrShareDelStart ( IN LPWSTR ServerName, IN LPWSTR NetName, IN DWORD Reserved, IN PSHARE_DEL_HANDLE ContextHandle, IN BOOLEAN CheckAccess ) /*++ Routine Description: This routine implements the first phase of the share deletion function, which simply remembers that the specified share is to be deleted. The NetrShareDelCommit function actually deletes the share. This two-phase deletion is used to delete IPC$, which is the share used for named pipes, so that RPC can be used to delete the IPC$ share without receiving RPC errors. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; PSHARE_INFO_2 shareInfo = NULL; DWORD entriesRead; DWORD totalEntries; DWORD shareType; BOOL isPrintShare; BOOL isSpecial; PSRVSVC_SECURITY_OBJECT securityObject; PSHARE_DEL_CONTEXT context; ServerName, Reserved; // // A share name must be specified. // if ( (NetName == NULL) || (*NetName == '\0') ) { return ERROR_INVALID_PARAMETER; } // // First determine what kind of share is being deleted. // error = ShareEnumCommon( 2, (LPBYTE *)&shareInfo, (DWORD)-1, &entriesRead, &totalEntries, NULL, NetName ); if ( error != NO_ERROR ) { if( shareInfo ) { MIDL_user_free( shareInfo ); } return error; } if ( entriesRead == 0 ) { if( shareInfo ) { MIDL_user_free( shareInfo ); } return NERR_NetNameNotFound; } shareType = shareInfo->shi2_type & ~STYPE_SPECIAL; isSpecial = (BOOL)((shareInfo->shi2_type & STYPE_SPECIAL) != 0); isPrintShare = (BOOL)(shareType == STYPE_PRINTQ); MIDL_user_free( shareInfo ); // // Make sure that the caller is allowed to delete this share. // if( CheckAccess ) { if ( isSpecial ) { securityObject = &SsShareAdminSecurityObject; } else if ( isPrintShare ) { securityObject = &SsSharePrintSecurityObject; } else { securityObject = &SsShareFileSecurityObject; } error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET ); if ( error != NO_ERROR ) { return error; } } // // Set up context for the commit phase. // context = MIDL_user_allocate( sizeof(SHARE_DEL_CONTEXT) + wcslen(NetName)*sizeof(WCHAR) + sizeof(WCHAR) ); if ( context == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } RtlZeroMemory( context, sizeof(SHARE_DEL_CONTEXT) + wcslen(NetName)*sizeof(WCHAR) + sizeof(WCHAR) ); context->IsPrintShare = isPrintShare; context->IsSpecial = isSpecial; wcscpy( (LPWSTR)(context + 1), NetName ); RtlInitUnicodeString( &context->Srp.Name1, (LPWSTR)(context + 1) ); // Insert it into the context list EnterCriticalSection( &ShareDelContextMutex ); context->Next = SrvShareDelContextHead; SrvShareDelContextHead = context; LeaveCriticalSection( &ShareDelContextMutex ); // // Return the context pointer as an RPC context handle. // *ContextHandle = context; return NO_ERROR; } // I_NetrShareDelStart NET_API_STATUS NET_API_FUNCTION NetrShareDelCommit ( IN PSHARE_DEL_HANDLE ContextHandle ) /*++ Routine Description: This routine implements the second phase of the share deletion function, which actually deletes the share. The first phase, NetrShareDelStart simply remembers that the share is to be deleted. This two-phase deletion is used to delete IPC$, which is the share used for named pipes, so that RPC can be used to delete the IPC$ share without receiving RPC errors. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; PSHARE_DEL_CONTEXT context; PSHARE_DEL_CONTEXT pSearch; // // The context handle is a pointer to allocated storage containing // the name of the share being deleted and other useful information. // Copy the pointer, then clear the context handle. // if( (ContextHandle == NULL) || (*ContextHandle == NULL) ) { return ERROR_INVALID_PARAMETER; } context = *ContextHandle; *ContextHandle = NULL; // // Look for this context to validate that its on the list // EnterCriticalSection( &ShareDelContextMutex ); pSearch = SrvShareDelContextHead; if( pSearch == context ) { SrvShareDelContextHead = pSearch->Next; context->Next = NULL; } else { while( (pSearch != NULL) && (pSearch->Next != context) ) { pSearch = pSearch->Next; } if( (pSearch != NULL) && (pSearch->Next == context) ) { pSearch->Next = pSearch->Next->Next; context->Next = NULL; } else { pSearch = NULL; } } LeaveCriticalSection( &ShareDelContextMutex ); if( pSearch == NULL ) { return ERROR_INVALID_PARAMETER; } // // Send the request on to the server. // error = SsServerFsControl( FSCTL_SRV_NET_SHARE_DEL, &context->Srp, NULL, 0 ); // // If the request succeeded, remove the value corresponding to the // share from the Shares key, thus effecting a sticky share // deletion. // // We don't do this if this is an admin share being deleted. No // registry information is kept for these shares. // if ( (error == NO_ERROR) && !context->IsSpecial ) { SsRemoveShareFromRegistry( (LPWSTR)(context + 1) ); } // // If a print share was successfully deleted, decrement the number // of print shares and update the exported (announced) server type. // if ( context->IsPrintShare ) { InterlockedDecrement( &SsData.NumberOfPrintShares ); SsSetExportedServerType( NULL, FALSE, TRUE ); } // // Free the context. // MIDL_user_free( context ); return error; } // NetrShareDelCommit NET_API_STATUS NET_API_FUNCTION NetrShareDelSticky ( IN LPWSTR ServerName, IN LPWSTR NetName, IN DWORD Reserved ) /*++ Routine Description: This routine implements the NetShareDelSticky function. It removes the named share from the sticky share list in the registry. The primary use of this function is to delete a sticky share whose root directory has been deleted, thus preventing actual recreation of the share, but whose entry still exists in the registry. It can also be used to remove the persistence of a share without deleting the current incarnation of the share. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; PSHARE_INFO_2 shareInfo, shi2; DWORD entriesRead; DWORD totalEntries; DWORD shareType; ULONG i; BOOL isPrintShare; PSRVSVC_SECURITY_OBJECT securityObject; ServerName, Reserved; // // A share name must be specified. // if ( (NetName == NULL) || (*NetName == '\0') ) { return ERROR_INVALID_PARAMETER; } // // First determine what kind of share is being deleted. // error = ShareEnumSticky( 2, (LPBYTE *)&shareInfo, (DWORD)-1, &entriesRead, &totalEntries, NULL ); if ( error != NO_ERROR ) { return error; } else if ( entriesRead == 0 ) { return NERR_NetNameNotFound; } for ( shi2 = shareInfo, i = 0 ; i < entriesRead; i++, shi2++ ) { if ( _wcsicmp( shi2->shi2_netname, NetName ) == 0 ) { break; } } // // Does it exist? // if ( i == entriesRead ) { MIDL_user_free( shareInfo ); return NERR_NetNameNotFound; } // // Use appropriate security object based on whether it is a print // share or not. Admin shares are not sticky. // shareType = shi2->shi2_type & ~STYPE_SPECIAL; isPrintShare = (BOOL)(shareType == STYPE_PRINTQ); MIDL_user_free( shareInfo ); // // Make sure that the caller is allowed to delete this share. // if ( isPrintShare ) { securityObject = &SsSharePrintSecurityObject; } else { securityObject = &SsShareFileSecurityObject; } error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET ); if ( error != NO_ERROR ) { return error; } // // Remove the value corresponding to the share from the Shares key, // thus effecting a sticky share deletion. // error = SsRemoveShareFromRegistry( NetName ); if ( error == ERROR_FILE_NOT_FOUND ) { error = NERR_NetNameNotFound; } return error; } // NetrShareDelSticky NET_API_STATUS NET_API_FUNCTION NetrShareEnum ( SRVSVC_HANDLE ServerName, LPSHARE_ENUM_STRUCT InfoStruct, DWORD PreferredMaximumLength, LPDWORD TotalEntries, LPDWORD ResumeHandle ) /*++ Routine Description: This routine communicates with the server FSD to implement the NetShareEnum function. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; ACCESS_MASK desiredAccess; ServerName; if( !ARGUMENT_PRESENT( InfoStruct ) ) { return ERROR_INVALID_PARAMETER; } // // Determine the desired access. // switch ( InfoStruct->Level ) { case 0: case 1: case 501: desiredAccess = SRVSVC_SHARE_USER_INFO_GET; break; case 2: case 502: desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET; break; default: return ERROR_INVALID_LEVEL; } // // Make sure that the caller has the access necessary for this // operation. // error = SsCheckAccess( &SsSharePrintSecurityObject, desiredAccess ); if ( error != NO_ERROR ) { return error; } // // Use the common routine to get the information. // if( InfoStruct->ShareInfo.Level2 == NULL || InfoStruct->ShareInfo.Level2->Buffer != NULL ) { return ERROR_INVALID_PARAMETER; } return ShareEnumCommon( InfoStruct->Level, (LPBYTE *)&InfoStruct->ShareInfo.Level2->Buffer, PreferredMaximumLength, &InfoStruct->ShareInfo.Level2->EntriesRead, TotalEntries, ResumeHandle, NULL ); } // NetrShareEnum NET_API_STATUS NET_API_FUNCTION NetrShareEnumSticky ( SRVSVC_HANDLE ServerName, LPSHARE_ENUM_STRUCT InfoStruct, DWORD PreferredMaximumLength, LPDWORD TotalEntries, LPDWORD ResumeHandle OPTIONAL ) /*++ Routine Description: This routine communicates with the server FSD to implement the NetShareEnumSticky function. Arguments: ServerName - the name of the server whose shares we want to enumerate. InfoStruct - pointer to a PSHARE_ENUM_STRUCT that will contain the output buffer upon completion. PreferredMaximumLength - an advisory value that specifies the maximum number of bytes the client is expecting to be returned. If -1, the client expects the whole list to be returned. TotalEntries - Upon return, will contain the number of entries that were available. ResumeHandle - is not NULL, will contain the resume handle that can be used to continue a search. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; ACCESS_MASK desiredAccess; ServerName; if( !ARGUMENT_PRESENT( InfoStruct ) ) { return ERROR_INVALID_PARAMETER; } if( InfoStruct->ShareInfo.Level2 == NULL || InfoStruct->ShareInfo.Level2->Buffer != NULL ) { return ERROR_INVALID_PARAMETER; } // // Determine the desired access. // switch ( InfoStruct->Level ) { case 0: case 1: desiredAccess = SRVSVC_SHARE_USER_INFO_GET; break; case 2: case 502: desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET; break; default: return ERROR_INVALID_LEVEL; } // // Make sure that the caller has the access necessary for this // operation. // error = SsCheckAccess( &SsSharePrintSecurityObject, desiredAccess ); if ( error != NO_ERROR ) { return error; } // // Use the common routine to get the information. // return ShareEnumSticky( InfoStruct->Level, (LPBYTE *)&InfoStruct->ShareInfo.Level2->Buffer, PreferredMaximumLength, &InfoStruct->ShareInfo.Level2->EntriesRead, TotalEntries, ResumeHandle ); } // NetrShareEnumSticky NET_API_STATUS NET_API_FUNCTION NetrShareGetInfo ( IN LPWSTR ServerName, IN LPWSTR NetName, IN DWORD Level, OUT LPSHARE_INFO Buffer ) /*++ Routine Description: This routine communicates with the server FSD to implement the NetShareGetInfo function. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; PSHARE_INFO_2 shareInfo = NULL; ULONG entriesRead; ULONG totalEntries; ACCESS_MASK desiredAccess; ServerName; // // A share name must be specified. // if ( (NetName == NULL) || (*NetName == '\0') ) { return ERROR_INVALID_PARAMETER; } // // Determine the desired access. // switch ( Level ) { case 0: case 1: case 501: case 1005: desiredAccess = SRVSVC_SHARE_USER_INFO_GET; break; case 2: case 502: desiredAccess = SRVSVC_SHARE_ADMIN_INFO_GET; break; default: return ERROR_INVALID_LEVEL; } // // Make sure that the caller has the access necessary for this // operation. // error = SsCheckAccess( &SsSharePrintSecurityObject, desiredAccess ); if ( error != NO_ERROR ) { return error; } // // Use the common routine to get the information. // error = ShareEnumCommon( Level, (LPBYTE *)&shareInfo, (DWORD)-1, &entriesRead, &totalEntries, NULL, NetName ); if ( error != NO_ERROR ) { if( shareInfo ) { MIDL_user_free( shareInfo ); } return error; } if ( entriesRead == 0 ) { if( shareInfo ) { MIDL_user_free( shareInfo ); } return NERR_NetNameNotFound; } SS_ASSERT( entriesRead == 1 ); // // Make sure that the caller is allowed to get share information on // this share. // if ( Level == 502 ) { Buffer->ShareInfo502 = (LPSHARE_INFO_502_I)shareInfo; } else { Buffer->ShareInfo2 = (LPSHARE_INFO_2)shareInfo; } return NO_ERROR; } // NetrShareGetInfo NET_API_STATUS NET_API_FUNCTION NetrShareSetInfo ( IN LPWSTR ServerName, IN LPWSTR NetName, IN DWORD Level, IN LPSHARE_INFO Buffer, OUT LPDWORD ErrorParameter OPTIONAL ) /*++ Routine Description: This routine communicates with the server FSD to implement the NetShareSetInfo function. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; PSERVER_REQUEST_PACKET srp; DWORD entriesRead; DWORD totalEntries; DWORD shareType; BOOL isPrintShare; BOOL isSpecial; PSRVSVC_SECURITY_OBJECT securityObject; PVOID capturedBuffer = NULL; ULONG bufferLength; LPWSTR remark = NULL; ULONG maxUses = 0; PSECURITY_DESCRIPTOR fileSd = NULL; PSECURITY_DESCRIPTOR newFileSd = NULL; BOOL setRemark; BOOL setFileSd; PSHARE_INFO_2 shi2 = NULL; SHARE_INFO_2 localShi2; ServerName; // // Check that user input buffer is not NULL // if (Buffer->ShareInfo2 == NULL) { SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN); return ERROR_INVALID_PARAMETER; } // // Determine what the caller is trying to set. // switch ( Level ) { case 502: fileSd = Buffer->ShareInfo502->shi502_security_descriptor; // *** lack of break is intentional! case 2: maxUses = Buffer->ShareInfo2->shi2_max_uses; // *** lack of break is intentional! case 1: remark = Buffer->ShareInfo2->shi2_remark; break; case SHARE_REMARK_INFOLEVEL: remark = Buffer->ShareInfo1004->shi1004_remark; break; case SHARE_MAX_USES_INFOLEVEL: maxUses = Buffer->ShareInfo1006->shi1006_max_uses; break; case SHARE_FILE_SD_INFOLEVEL: fileSd = Buffer->ShareInfo1501->shi1501_security_descriptor; break; case 1005: Buffer->ShareInfo1005->shi1005_flags &= SHI1005_VALID_FLAGS_SET; break; default: SS_PRINT(( "NetrShareSetInfo: invalid level: %ld\n", Level )); SET_ERROR_PARAMETER( 0 ); return ERROR_INVALID_LEVEL; } setRemark = (BOOLEAN)( remark != NULL ); setFileSd = (BOOLEAN)( fileSd != NULL ); // // A share name must be specified. // if ( (NetName == NULL) || (*NetName == '\0') ) { return ERROR_INVALID_PARAMETER; } // // Determine what kind of share is being modified. // error = ShareEnumCommon( 2, (LPBYTE *)&shi2, (DWORD)-1, &entriesRead, &totalEntries, NULL, NetName ); if ( error != NO_ERROR ) { if( shi2 ) { MIDL_user_free( shi2 ); } return error; } if ( entriesRead == 0 ) { if( shi2 ) { MIDL_user_free( shi2 ); } return NERR_NetNameNotFound; } shareType = shi2->shi2_type & ~STYPE_SPECIAL; isSpecial = (BOOL)((shi2->shi2_type & STYPE_SPECIAL) != 0); MIDL_user_free( shi2 ); // // The share ACL cannot be changed on admin shares. // if ( isSpecial && setFileSd ) { SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM ); error = ERROR_INVALID_PARAMETER; goto exit; } // // Figure out which kind of share this is. // isPrintShare = (BOOL)(shareType == STYPE_PRINTQ); // // Only disk shares can be affected by 1005 // if( Level == 1005 && shareType != STYPE_DISKTREE ) { error = ERROR_BAD_DEV_TYPE; goto exit; } if( SsData.SsInitialized ) { // // Make sure that the caller is allowed to set share information on // this share. // if ( isSpecial ) { securityObject = &SsShareAdminSecurityObject; } else if ( isPrintShare ) { securityObject = &SsSharePrintSecurityObject; } else { securityObject = &SsShareFileSecurityObject; } error = SsCheckAccess( securityObject, SRVSVC_SHARE_INFO_SET ); if ( error != NO_ERROR ) { return error; } } // // Just return success if not trying to set anything. // if ( !setRemark && (maxUses == 0) && !setFileSd && Level != 1005 ) { return NO_ERROR; } // // The remark must be no longer than MAXCOMMENTSZ. // if ( setRemark ) { if ( STRLEN(remark) > MAXCOMMENTSZ ) { SET_ERROR_PARAMETER( SHARE_REMARK_PARMNUM ); return ERROR_INVALID_PARAMETER; } } // // Mapped the security descriptor to remove the generic permissions // if ( setFileSd ) { if ( !RtlValidSecurityDescriptor( fileSd ) ) { SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM ); return ERROR_INVALID_PARAMETER; } error = ShareAssignSecurityDescriptor( fileSd, &newFileSd ); if ( error != NO_ERROR ) { SS_PRINT(( "NetrShareSetInfo: ShareAssignSecurityDescriptor " "error: %d\n", error )); SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM ); return ERROR_INVALID_PARAMETER; } } // // Allocate a request packet. // srp = SsAllocateSrp( ); if ( srp == NULL ) { error = ERROR_NOT_ENOUGH_MEMORY; goto exit; } srp->Level = Level; // // Set up the share name. // RtlInitUnicodeString( &srp->Name1, NetName ); // // Set up the MaxUses field. If equal to 0, then it won't be changed // by the server. // srp->Parameters.Set.Api.ShareInfo.MaxUses = maxUses; // // Capture the share data structure passed in. // localShi2.shi2_netname = NetName; switch( Level ) { case 1005: bufferLength = sizeof(SHARE_INFO_1005); capturedBuffer = MIDL_user_allocate( bufferLength ); if( capturedBuffer == NULL ) { SET_ERROR_PARAMETER( 0 ); error = ERROR_NOT_ENOUGH_MEMORY; goto exit; } RtlZeroMemory( capturedBuffer, bufferLength ); *((PSHARE_INFO_1005) capturedBuffer) = *Buffer->ShareInfo1005; break; default: capturedBuffer = CaptureShareInfo( Level, &localShi2, 0, // ShareType, unused for SHARE_SET_INFO NULL, remark, NULL, newFileSd, &bufferLength ); if ( capturedBuffer == NULL ) { SET_ERROR_PARAMETER( 0 ); error = ERROR_NOT_ENOUGH_MEMORY; goto exit; } break; } // // Send the request to the server. // error = SsServerFsControl( FSCTL_SRV_NET_SHARE_SET_INFO, srp, capturedBuffer, bufferLength ); // // If the request succeeded, modify the share's value in the Shares // key, thus effecting a sticky change. // // We don't do this if this is an admin share being modified. No // registry information is kept for these shares. // if ( (error == NO_ERROR) && !isSpecial ) { DWORD entriesRead; DWORD totalEntries; NET_API_STATUS error2; shi2 = NULL; error2 = ShareEnumCommon( 2, (LPBYTE *)&shi2, (DWORD)-1, &entriesRead, &totalEntries, NULL, NetName ); if ( error2 == NO_ERROR ) { DWORD CSCFlags = 0; if( Level != 1005 ) { PSHARE_INFO_501 shi501 = NULL; if( ShareEnumCommon( 501, (LPBYTE *)&shi501, (DWORD)-1, &entriesRead, &totalEntries, NULL, NetName ) == NO_ERROR && shi501 != NULL ) { CSCFlags = shi501->shi501_flags & CSC_MASK; } if( shi501 ) { MIDL_user_free( shi501 ); } } else { CSCFlags = Buffer->ShareInfo1005->shi1005_flags & CSC_MASK; } SsAddShareToRegistry( shi2, newFileSd, CSCFlags ); } if( shi2 ) { MIDL_user_free( shi2 ); } } // // Set up the error parameter if requested and return. // SET_ERROR_PARAMETER( srp->Parameters.Set.ErrorParameter ); exit: if (srp != NULL) { SsFreeSrp( srp ); } if ( newFileSd != NULL ) { (VOID)RtlDeleteSecurityObject( &newFileSd ); } if( capturedBuffer != NULL ) { MIDL_user_free( capturedBuffer ); } return error; } // NetrShareSetInfo PVOID CaptureShareInfo ( IN DWORD Level, IN PSHARE_INFO_2 Shi2, IN DWORD ShareType OPTIONAL, IN LPWSTR Path OPTIONAL, IN LPWSTR Remark OPTIONAL, IN PSECURITY_DESCRIPTOR ConnectSecurityDescriptor OPTIONAL, IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL, OUT PULONG CapturedBufferLength ) { PSHARE_INFO_502 capturedShi502; ULONG capturedBufferLength; PCHAR variableData; ULONG pathNameLength; ULONG shareNameLength; ULONG remarkLength; ULONG connectSDLength = 0; ULONG fileSdLength = 0; // // Determine the lengths of the strings in the buffer and the total // length of the buffer. // if ( Shi2->shi2_netname == NULL ) { shareNameLength = 0; } else { shareNameLength = SIZE_WSTR( Shi2->shi2_netname ); } if ( Path == NULL ) { pathNameLength = 0; } else { pathNameLength = SIZE_WSTR( Path ); } if ( Remark == NULL ) { remarkLength = 0; } else { remarkLength = SIZE_WSTR( Remark ); } if ( ARGUMENT_PRESENT( ConnectSecurityDescriptor ) ) { // // Allocate extra space for the security descriptor since it needs // to be longword-aligned and there may be padding in front of it. // connectSDLength = RtlLengthSecurityDescriptor( ConnectSecurityDescriptor ) + sizeof(ULONG); } if ( ARGUMENT_PRESENT( FileSecurityDescriptor ) ) { // // ULONG added for alignment. // fileSdLength = RtlLengthSecurityDescriptor( FileSecurityDescriptor ) + sizeof(ULONG); } // // Allocate a buffer in which to capture the share information. // capturedBufferLength = sizeof(SHARE_INFO_502) + shareNameLength + remarkLength + pathNameLength + connectSDLength + fileSdLength; // // Allocate a buffer to hold the input information. // capturedShi502 = MIDL_user_allocate( capturedBufferLength ); if ( capturedShi502 == NULL ) { *CapturedBufferLength = 0; return NULL; } // // Copy over the share info structure. // *((PSHARE_INFO_2) capturedShi502) = *Shi2; // // Optionally override the share type. // if ( ShareType != 0 ) { capturedShi502->shi502_type = ShareType; } // // Capture the share name. // variableData = (PCHAR)( capturedShi502 + 1 ); if ( shareNameLength != 0 ) { capturedShi502->shi502_netname = (LPWSTR)variableData; RtlCopyMemory( variableData, Shi2->shi2_netname, shareNameLength ); variableData += shareNameLength; } else { capturedShi502->shi502_netname = NULL; } // // Capture the remark. // if ( remarkLength != 0 ) { capturedShi502->shi502_remark = (LPWSTR)variableData; RtlCopyMemory( variableData, Remark, remarkLength ); variableData += remarkLength; } else { capturedShi502->shi502_remark = NULL; } // // Capture the path. // if ( pathNameLength > 0 ) { capturedShi502->shi502_path = (LPWSTR)variableData; RtlCopyMemory( variableData, Path, pathNameLength ); variableData += pathNameLength; } else { capturedShi502->shi502_path = NULL; } // // Capture the security descriptor. Use the shi502_permissions field // to contain the offset to the security descriptor in the buffer. // if ( ARGUMENT_PRESENT( ConnectSecurityDescriptor ) ) { variableData = (PCHAR)( ((ULONG_PTR)variableData + 3) & ~3 ); // // Store the offset directly into shi502_permissions now. The // reason is that shi502_permissions is a 32-bit field, insufficient // to contain a pointer under Sundown. // capturedShi502->shi502_permissions = (ULONG)((ULONG_PTR)variableData - (ULONG_PTR)capturedShi502); RtlCopyMemory( variableData, ConnectSecurityDescriptor, connectSDLength - sizeof(ULONG) ); variableData += (connectSDLength - sizeof(ULONG)); } else { capturedShi502->shi502_permissions = 0; } // // Capture the self relative form of the file security descriptor. // if ( ARGUMENT_PRESENT( FileSecurityDescriptor ) ) { variableData = (PCHAR)( ((ULONG_PTR)variableData + 3) & ~3 ); capturedShi502->shi502_security_descriptor = (LPBYTE) variableData; variableData += ( fileSdLength - sizeof(ULONG)) ; RtlCopyMemory( (PVOID)capturedShi502->shi502_security_descriptor, FileSecurityDescriptor, fileSdLength - sizeof(ULONG) ); } else { capturedShi502->shi502_security_descriptor = (LPBYTE) NULL; } // // Convert all the pointers in the structure to offsets from the // beginning of the structure. // POINTER_TO_OFFSET( capturedShi502->shi502_netname, capturedShi502 ); POINTER_TO_OFFSET( capturedShi502->shi502_remark, capturedShi502 ); POINTER_TO_OFFSET( capturedShi502->shi502_path, capturedShi502 ); POINTER_TO_OFFSET( (PCHAR)capturedShi502->shi502_security_descriptor, capturedShi502 ); // // Set up the length of the captured buffer to return to the caller // and return the captures structure. // *CapturedBufferLength = capturedBufferLength; return capturedShi502; } // CaptureShareInfo NET_API_STATUS DisallowSharedLanmanNetDrives( IN PUNICODE_STRING NtSharePath ) { NET_API_STATUS error = NERR_Success; NTSTATUS status; HANDLE linkHandle; OBJECT_ATTRIBUTES objAttr; UNICODE_STRING linkTarget; ULONG returnedLength = 0; UNICODE_STRING tempNtPath; linkTarget.Buffer = NULL; linkTarget.MaximumLength = 0; linkTarget.Length = 0; tempNtPath = *NtSharePath; // // Remove the trailing '\\' // tempNtPath.Length -= 2; InitializeObjectAttributes( &objAttr, &tempNtPath, OBJ_CASE_INSENSITIVE, 0, NULL ); status = NtOpenSymbolicLinkObject( &linkHandle, SYMBOLIC_LINK_QUERY, &objAttr ); if ( !NT_SUCCESS(status) ) { return NERR_Success; } // // Get the size of the buffer needed. // status = NtQuerySymbolicLinkObject( linkHandle, &linkTarget, &returnedLength ); if ( !NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL ) { NtClose( linkHandle ); return NERR_Success; } // // Allocate our buffer // linkTarget.Length = (USHORT)returnedLength; linkTarget.MaximumLength = (USHORT)(returnedLength + sizeof(WCHAR)); linkTarget.Buffer = MIDL_user_allocate( linkTarget.MaximumLength ); if ( linkTarget.Buffer == NULL ) { NtClose( linkHandle ); return NERR_Success; } status = NtQuerySymbolicLinkObject( linkHandle, &linkTarget, &returnedLength ); NtClose( linkHandle ); if ( NT_SUCCESS(status) ) { // // See if this is a lanman drive // if (_wcsnicmp( linkTarget.Buffer, DD_NFS_DEVICE_NAME_U, wcslen(DD_NFS_DEVICE_NAME_U)) == 0) { error = NERR_RedirectedPath; } } MIDL_user_free( linkTarget.Buffer ); return(error); } // DisallowSharedLanmanNetDrives NET_API_STATUS FillStickyShareInfo( IN PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo, IN PSHARE_INFO_502 Shi502 ) /*++ Routine Description: This routine fills in the output buffer with data from the shi502 structure. Arguments: ShareEnumInfo - contains the parameters passed in through the NetShareEnumSticky api. Shi502 - pointer to a shi502 structure Return Value: status of operation. --*/ { PSHARE_INFO_502 newShi502; PCHAR endOfVariableData; ShareEnumInfo->TotalBytesNeeded += SizeShares( ShareEnumInfo->Level, Shi502 ); // // If we have more data but ran out of space, return ERROR_MORE_DATA // if ( ShareEnumInfo->TotalBytesNeeded > ShareEnumInfo->OutputBufferLength ) { return(ERROR_MORE_DATA); } // // Transfer data from the share info 502 structure to the output // buffer. // newShi502 = (PSHARE_INFO_502)ShareEnumInfo->StartOfFixedData; ShareEnumInfo->StartOfFixedData += FIXED_SIZE_OF_SHARE(ShareEnumInfo->Level); endOfVariableData = ShareEnumInfo->EndOfVariableData; // // Case on the level to fill in the fixed structure appropriately. // We fill in actual pointers in the output structure. This is // possible because we are in the server FSD, hence the server // service's process and address space. // // *** This routine assumes that the fixed structure will fit in the // buffer! // // *** Using the switch statement in this fashion relies on the fact // that the first fields on the different share structures are // identical. // switch( ShareEnumInfo->Level ) { case 502: if ( Shi502->shi502_security_descriptor != NULL ) { ULONG fileSDLength; fileSDLength = RtlLengthSecurityDescriptor( Shi502->shi502_security_descriptor ); // // DWord Align // endOfVariableData = (PCHAR) ( (ULONG_PTR) ( endOfVariableData - fileSDLength ) & ~3 ); newShi502->shi502_security_descriptor = endOfVariableData; newShi502->shi502_reserved = fileSDLength; RtlMoveMemory( newShi502->shi502_security_descriptor, Shi502->shi502_security_descriptor, fileSDLength ); } else { newShi502->shi502_security_descriptor = NULL; newShi502->shi502_reserved = 0; } case 2: // // Set level 2 specific fields in the buffer. Since this server // can only have user-level security, share permissions are // meaningless. // newShi502->shi502_permissions = 0; newShi502->shi502_max_uses = Shi502->shi502_max_uses; // // To get the current uses, we need to query the server for this // { PSHARE_INFO_2 shareInfo = NULL; NET_API_STATUS error; DWORD entriesRead; DWORD totalEntries; error = ShareEnumCommon( 2, (LPBYTE *)&shareInfo, (DWORD)-1, &entriesRead, &totalEntries, NULL, Shi502->shi502_netname ); if ( error != NO_ERROR || entriesRead == 0 ) { newShi502->shi502_current_uses = 0; } else { newShi502->shi502_current_uses = shareInfo->shi2_current_uses; } if( shareInfo ) { MIDL_user_free( shareInfo ); } } // // Copy the DOS path name to the buffer. // if ( Shi502->shi502_path != NULL ) { endOfVariableData -= SIZE_WSTR( Shi502->shi502_path ); newShi502->shi502_path = (LPTSTR) endOfVariableData; wcscpy( newShi502->shi502_path, Shi502->shi502_path ); } else { newShi502->shi502_path = NULL; } // // We don't have per-share passwords (share-level security) // so set the password pointer to NULL. // newShi502->shi502_passwd = NULL; // *** Lack of break is intentional! case 1: newShi502->shi502_type = Shi502->shi502_type; // // Copy the remark to the buffer. The routine will handle the // case where there is no remark on the share and put a pointer // to a zero terminator in the buffer. // if ( Shi502->shi502_remark != NULL ) { endOfVariableData -= SIZE_WSTR( Shi502->shi502_remark ); newShi502->shi502_remark = (LPTSTR) endOfVariableData; wcscpy( newShi502->shi502_remark, Shi502->shi502_remark ); } else { newShi502->shi502_remark = NULL; } // *** Lack of break is intentional! case 0: // // Copy the share name to the buffer. // if ( Shi502->shi502_netname != NULL ) { endOfVariableData -= SIZE_WSTR( Shi502->shi502_netname ); newShi502->shi502_netname = (LPTSTR) endOfVariableData; wcscpy( newShi502->shi502_netname, Shi502->shi502_netname ); } else { newShi502->shi502_remark = NULL; } break; } ShareEnumInfo->EndOfVariableData = endOfVariableData; return NO_ERROR; } // FillStickyShareInfo NET_API_STATUS ShareAssignSecurityDescriptor( IN PSECURITY_DESCRIPTOR PassedSecurityDescriptor, OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor ) /*++ Routine Description: This routine converts a the generic mappings in an sd to standards and specifics. Arguments: PassedSecurityDescriptor - Security descriptor passed from the client. NewSecurityDescriptor - Pointer to a buffer to receive the new sd. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NTSTATUS status; HANDLE token; PISECURITY_DESCRIPTOR_RELATIVE trustedSecurityDescriptor = NULL; NET_API_STATUS error; ULONG secLen; // // We don't necessarily trust the security descriptor passed in from the client. // And, since we are going to write to it, we better make sure it is in some // memory that we understand. // try { // We only work with Self-Relative SD's, reject any others if( !RtlpAreControlBitsSet((PISECURITY_DESCRIPTOR)PassedSecurityDescriptor, SE_SELF_RELATIVE) ) { return ERROR_INVALID_PARAMETER; } secLen = RtlLengthSecurityDescriptor( PassedSecurityDescriptor ); if( secLen < sizeof( *trustedSecurityDescriptor ) ) { RaiseException( STATUS_INVALID_PARAMETER, 0, 0, NULL ); } trustedSecurityDescriptor = (PISECURITY_DESCRIPTOR_RELATIVE)MIDL_user_allocate( secLen ); if( trustedSecurityDescriptor == NULL ) { RaiseException( STATUS_INSUFFICIENT_RESOURCES, 0, 0, NULL ); } RtlCopyMemory( trustedSecurityDescriptor, PassedSecurityDescriptor, secLen ); trustedSecurityDescriptor->Owner = 0; trustedSecurityDescriptor->Group = 0; trustedSecurityDescriptor->Sacl = 0; trustedSecurityDescriptor->Control &= (SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_SELF_RELATIVE | SE_DACL_PROTECTED); // // Impersonate client // status = RpcImpersonateClient( NULL ); if( !NT_SUCCESS( status ) ) { RaiseException( status, 0, 0, NULL ); } status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, &token ); (VOID)RpcRevertToSelf( ); if( !NT_SUCCESS( status ) ) { RaiseException( status, 0, 0, NULL ); } // // Get a new sd which has the generics mapped to specifics. // the returned sd is in self-relative form. // status = RtlNewSecurityObject( NULL, trustedSecurityDescriptor, NewSecurityDescriptor, FALSE, token, &SrvShareFileGenericMapping ); ASSERT( RtlValidSecurityDescriptor( *NewSecurityDescriptor ) ); NtClose( token ); } except ( EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode(); } if( trustedSecurityDescriptor != NULL ) { MIDL_user_free( trustedSecurityDescriptor ); } return RtlNtStatusToDosError( status ); } // ShareAssignSecurityDescriptor void SHARE_DEL_HANDLE_rundown ( SHARE_DEL_HANDLE ContextHandle ) { (VOID)NetrShareDelCommit( &ContextHandle ); return; } // SHARE_DEL_HANDLE_rundown NET_API_STATUS ShareEnumCommon ( IN DWORD Level, OUT LPBYTE *Buffer, IN DWORD PreferredMaximumLength, OUT LPDWORD EntriesRead, OUT LPDWORD TotalEntries, IN OUT LPDWORD ResumeHandle OPTIONAL, IN LPWSTR NetName OPTIONAL ) { NET_API_STATUS error; PSERVER_REQUEST_PACKET srp; if( !ARGUMENT_PRESENT( EntriesRead ) || !ARGUMENT_PRESENT( TotalEntries ) ) { return ERROR_INVALID_PARAMETER; } // // Make sure that the level is valid. Since it is an unsigned // value, it can never be less than 0. // if ( (Level > 2) && (Level != 501 ) && (Level != 502) && (Level != 1005) ) { return ERROR_INVALID_LEVEL; } // // Set up the input parameters in the request buffer. // srp = SsAllocateSrp( ); if ( srp == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } srp->Level = Level; if ( ARGUMENT_PRESENT( NetName ) ) { srp->Flags = SRP_RETURN_SINGLE_ENTRY; } if ( ARGUMENT_PRESENT( ResumeHandle ) ) { srp->Parameters.Get.ResumeHandle = *ResumeHandle; } else { srp->Parameters.Get.ResumeHandle = 0; } RtlInitUnicodeString( &srp->Name1, NetName ); // // Get the data from the server. This routine will allocate the // return buffer and handle the case where PreferredMaximumLength == // -1. // error = SsServerFsControlGetInfo( FSCTL_SRV_NET_SHARE_ENUM, srp, (PVOID *)Buffer, PreferredMaximumLength ); // // Set up return information. // *EntriesRead = srp->Parameters.Get.EntriesRead; *TotalEntries = srp->Parameters.Get.TotalEntries; if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) { *ResumeHandle = srp->Parameters.Get.ResumeHandle; } SsFreeSrp( srp ); // // We need to null out the owner, group, and sacl. // if ( Level == 502 && *Buffer != NULL ) { PSHARE_INFO_502 shi502 = (PSHARE_INFO_502) *Buffer; PSECURITY_DESCRIPTOR fileSD; ULONG i; for ( i = 0 ; i < *EntriesRead; i++, shi502++ ) { fileSD = shi502->shi502_security_descriptor; if ( fileSD != NULL ) { PISECURITY_DESCRIPTOR SD = fileSD; if (SD->Control & SE_SELF_RELATIVE) { PISECURITY_DESCRIPTOR_RELATIVE SDR = fileSD; SDR->Owner = 0; SDR->Group = 0; SDR->Sacl = 0; } else { SD->Owner = NULL; SD->Group = NULL; SD->Sacl = NULL; } SD->Control &= (SE_DACL_DEFAULTED | SE_DACL_PROTECTED | SE_DACL_PRESENT | SE_SELF_RELATIVE); ASSERT( RtlValidSecurityDescriptor( fileSD ) ); } } // for } return error; } // ShareEnumCommon NET_API_STATUS ShareEnumSticky ( IN DWORD Level, OUT LPBYTE *Buffer, IN DWORD PreferredMaximumLength, OUT LPDWORD EntriesRead, OUT LPDWORD TotalEntries, IN OUT LPDWORD ResumeHandle OPTIONAL ) /*++ Routine Description: This routine enumerates all the shares kept in the registry. Arguments: Same as NetShareEnumSticky api. Return Value: status of request. --*/ { NET_API_STATUS error; BOOLEAN getEverything; ULONG oldResumeHandle; SRVSVC_SHARE_ENUM_INFO enumInfo; // // Set up the input parameters in the request buffer. // enumInfo.Level = Level; if ( ARGUMENT_PRESENT( ResumeHandle ) ) { enumInfo.ResumeHandle = *ResumeHandle; } else { enumInfo.ResumeHandle = 0; } oldResumeHandle = enumInfo.ResumeHandle; // // If the length of the second buffer is specified as -1, then we // are supposed to get all the information, regardless of size. // Allocate space for the output buffer and try to use it. If this // fails, the SsEnumerateStickyShares will tell us just how much we // really need to allocate. // if ( PreferredMaximumLength == 0xFFFFFFFF ) { enumInfo.OutputBufferLength = INITIAL_BUFFER_SIZE; getEverything = TRUE; } else { enumInfo.OutputBufferLength = PreferredMaximumLength; getEverything = FALSE; } enumInfo.OutputBuffer = MIDL_user_allocate( enumInfo.OutputBufferLength ); if ( enumInfo.OutputBuffer == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } // // Make the request // error = SsEnumerateStickyShares( &enumInfo ); // // If the call was successful, or there was an error other than // ERROR_MORE_DATA (which indicates that the buffer wasn't large // enough), or the passed in buffer size was all we're allowed to // allocate, return to the caller. // if ( (error != ERROR_MORE_DATA && error != NERR_BufTooSmall) || !getEverything ) { // // If no entries were found, free the buffer and set the pointer // to NULL. // if ( enumInfo.EntriesRead == 0 ) { MIDL_user_free( enumInfo.OutputBuffer ); enumInfo.OutputBuffer = NULL; } goto exit; } // // The initial buffer wasn't large enough, and we're allowed to // allocate more. Free the first buffer. // MIDL_user_free( enumInfo.OutputBuffer ); // // Allocate a buffer large enough to hold all the information, plus // a fudge factor in case the amount of information has increased. // If the amount of information increased more than the fudge factor, // then we give up. This should almost never happen. // enumInfo.OutputBufferLength = enumInfo.TotalBytesNeeded + EXTRA_ALLOCATION; enumInfo.OutputBuffer = MIDL_user_allocate( enumInfo.OutputBufferLength ); if ( enumInfo.OutputBuffer == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } // // Reset the resume handle in the SRP. It was altered by the first // Enum attempt. // enumInfo.ResumeHandle = oldResumeHandle; // // Try again to get the information from the server, this time with the // larger buffer. // error = SsEnumerateStickyShares( &enumInfo ); exit: // // Set up return information. // *Buffer = enumInfo.OutputBuffer; *EntriesRead = enumInfo.EntriesRead; *TotalEntries = enumInfo.TotalEntries; if ( *EntriesRead > 0 && ARGUMENT_PRESENT( ResumeHandle ) ) { *ResumeHandle = enumInfo.ResumeHandle; } return error; } // ShareEnumSticky ULONG SizeShares ( IN ULONG Level, IN PSHARE_INFO_502 Shi502 ) /*++ Routine Description: This routine returns the size the passed-in share would take up in an API output buffer. Arguments: Level - level of request Shi502 - pointer to a shi502 structure Return Value: ULONG - The number of bytes the share would take up in the output buffer. --*/ { ULONG shareSize = 0; switch ( Level ) { case 502: if ( Shi502->shi502_security_descriptor != NULL ) { // // add 4 bytes for possible padding // shareSize = sizeof( ULONG ) + RtlLengthSecurityDescriptor( Shi502->shi502_security_descriptor ); } case 2: shareSize += SIZE_WSTR( Shi502->shi502_path ); case 501: case 1: shareSize += SIZE_WSTR( Shi502->shi502_remark ); case 0: shareSize += SIZE_WSTR( Shi502->shi502_netname ); } return ( shareSize + FIXED_SIZE_OF_SHARE( Level ) ); } // SizeShares BOOLEAN ValidSharePath( IN LPWSTR SharePath, IN BOOL IsNtPath ) /*++ Routine Description: This routine checks to see if .. and . exists on the path. If they do, then we reject this path name. Arguments: SharePath - The share path to be checked. Return Value: TRUE, if path is ok. --*/ { LPWCH source = SharePath; if( IsNtPath ) { // The NT Path is validated by the OPEN call return TRUE; } // // Walk through the pathname until we reach the zero terminator. At // the start of this loop, source points to the first charaecter // after a directory separator or the first character of the // pathname. // // // Allow the NT naming convention of slash slash . slash through here // if( IS_SLASH_SLASH_NAME( source ) ) { // // We have a path which starts with slash slash // Set the buffer ptr so we start checking the pathname after // the slash slash dot source += 3; } while ( *source != L'\0' ) { if ( *source == L'.') { source++; if ( ( IS_PATH_SEPARATOR(*source) ) || ( (*source++ == L'.') && IS_PATH_SEPARATOR(*source) ) ) { // // '.' and '..' appear as a directory names. Reject. // return(FALSE); } } // // source does not point to a dot, so continue until we see // another directory separator // while ( *source != L'\0' ) { if ( *source++ == L'\\' ) { break; } } } return TRUE; } // ValidSharePath