You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3236 lines
81 KiB
3236 lines
81 KiB
/*++
|
|
|
|
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 <lmaccess.h>
|
|
#include <lmerr.h>
|
|
#include <ntddnfs.h>
|
|
#include <tstr.h>
|
|
#include <netevent.h>
|
|
#include <icanon.h>
|
|
|
|
#include <seopaque.h>
|
|
#include <sertlp.h>
|
|
#include <sddl.h>
|
|
|
|
#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] ) )
|
|
|
|
|
|
GENERIC_MAPPING SrvShareFileGenericMapping = GENERIC_SHARE_FILE_ACCESS_MAPPING;
|
|
|
|
//
|
|
// Local types.
|
|
//
|
|
|
|
PSHARE_DEL_CONTEXT SrvShareDelContextHead = NULL;
|
|
CRITICAL_SECTION ShareDelContextMutex;
|
|
|
|
//
|
|
// 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 ( shi502->shi502_reserved != 0 ) {
|
|
|
|
if ( !RtlValidRelativeSecurityDescriptor( fileSecurityDescriptor,
|
|
shi502->shi502_reserved,
|
|
0 ) ) {
|
|
SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
|
|
error = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
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 {
|
|
|
|
if ( !RtlValidSecurityDescriptor( fileSecurityDescriptor ) ) {
|
|
SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
|
|
error = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Don't allow the creation of shares named "pipe" or "mailslot"
|
|
//
|
|
if( !_wcsnicmp( netName, L"pipe", NNLEN ) || !_wcsnicmp( netName, L"mailslot", NNLEN ) )
|
|
{
|
|
SET_ERROR_PARAMETER( SHARE_NETNAME_PARMNUM );
|
|
error = ERROR_ACCESS_DENIED;
|
|
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.
|
|
//
|
|
// Note that we only autoshare fixed drives now, so any other share setup
|
|
// to a non-fixed drive should not be considered a "DiskAdmin" share.
|
|
isDiskAdmin = (SsGetDriveType( path ) == DRIVE_FIXED);
|
|
} 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 = NULL;
|
|
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;
|
|
ULONG sdLength = 0;
|
|
|
|
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;
|
|
sdLength = Buffer->ShareInfo502->shi502_reserved;
|
|
|
|
// *** 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;
|
|
sdLength = Buffer->ShareInfo1501->shi1501_reserved;
|
|
|
|
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|STYPE_TEMPORARY);
|
|
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 ( !RtlValidRelativeSecurityDescriptor( fileSd,
|
|
sdLength,
|
|
0 ) ) {
|
|
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 & SHI1005_VALID_FLAGS_SET;
|
|
}
|
|
|
|
if( shi501 ) {
|
|
MIDL_user_free( shi501 );
|
|
}
|
|
|
|
} else {
|
|
|
|
CSCFlags = Buffer->ShareInfo1005->shi1005_flags & SHI1005_VALID_FLAGS_SET;
|
|
}
|
|
|
|
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 );
|
|
|
|
/*
|
|
This code is not used because it will result in an invalid SD that cannot
|
|
be used by Apps. The Owner and Group fields are significant, and should be
|
|
included.
|
|
|
|
//
|
|
// 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
|