windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/wkssvc/server/useaddel.c
2020-09-26 16:20:57 +08:00

1568 lines
41 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1991-92 Microsoft Corporation
Module Name:
useaddel.c
Abstract:
This module contains the worker routines for the NetUseAdd and
NetUseDel APIs implemented in the Workstation service.
Author:
Rita Wong (ritaw) 4-Mar-1991
Revision History:
--*/
#include "wsutil.h"
#include "wsdevice.h"
#include "wsuse.h"
//-------------------------------------------------------------------//
// //
// Local function prototypes //
// //
//-------------------------------------------------------------------//
STATIC
NET_API_STATUS
WsAddUse(
IN PLUID LogonId,
IN HANDLE TreeConnection,
IN LPTSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPTSTR UncName,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN DWORD Flags
);
STATIC
NET_API_STATUS
WsDeleteUse(
IN PLUID LogonId,
IN DWORD ForceLevel,
IN PUSE_ENTRY MatchedPointer,
IN DWORD Index
);
STATIC
NET_API_STATUS
WsCreateNewEntry(
OUT PUSE_ENTRY *NewUse,
IN HANDLE TreeConnection,
IN LPTSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPTSTR UncName OPTIONAL,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN DWORD Flags
);
STATIC
NET_API_STATUS
WsCheckLocalAndDeviceType(
IN LPTSTR Local,
IN DWORD DeviceType,
OUT LPDWORD ErrorParameter OPTIONAL
);
STATIC
NET_API_STATUS
WsCheckEstablishedDeviceType(
IN HANDLE TreeConnection,
IN DWORD RequestedDeviceType
);
STATIC
NET_API_STATUS
WsAllocateUseWorkBuffer(
IN PUSE_INFO_2 UseInfo,
IN DWORD Level,
OUT LPTSTR *UncName,
OUT LPTSTR *Local,
OUT LPTSTR *UserName,
OUT LPTSTR *DomainName
);
#if DBG
STATIC
VOID
DumpUseList(
DWORD Index
);
#endif
//-------------------------------------------------------------------//
// //
// Global variables //
// //
//-------------------------------------------------------------------//
//
// Monotonically incrementing integer. A unique value is assigned to
// each new use entry created so that we can provide an enumeration
// resume handle.
//
STATIC DWORD GlobalResumeKey = 0;
//-------------------------------------------------------------------//
// //
// Macros //
// //
//-------------------------------------------------------------------//
#define GET_USE_INFO_POINTER(UseInfo, InfoStruct) \
UseInfo = InfoStruct->UseInfo3;
NET_API_STATUS NET_API_FUNCTION
NetrUseAdd(
IN LPTSTR ServerName OPTIONAL,
IN DWORD Level,
IN LPUSE_INFO InfoStruct,
OUT LPDWORD ErrorParameter OPTIONAL
)
/*++
Routine Description:
This function is the NetUseAdd entry point in the Workstation service.
Arguments:
Level - Supplies the level of information specified in Buffer.
Buffer - Supplies the parameters to create the new tree connection with.
ErrorParameter - Returns the identifier to the invalid parameter in Buffer
if this function returns ERROR_INVALID_PARAMETER.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
LUID LogonId;
NET_API_STATUS status;
LPTSTR UncName = NULL;
DWORD UncNameLength = 0;
PTSTR Local = NULL;
DWORD LocalLength = 0;
DWORD Flags;
LPTSTR UserName = NULL;
LPTSTR DomainName = NULL;
LPTSTR Password = NULL;
UNICODE_STRING EncodedPassword;
ULONG CreateFlags;
HANDLE TreeConnection;
UNICODE_STRING TreeConnectStr;
PUSE_INFO_3 pUseInfo;
PUSE_INFO_2 UseInfo;
DWORD SessionId;
LPWSTR Session = NULL;
UNREFERENCED_PARAMETER(ServerName);
if (Level == 0) {
return ERROR_INVALID_LEVEL;
}
#define NETR_USE_ADD_PASSWORD_SEED 0x56 // Pick a non-zero seed
RtlInitUnicodeString( &EncodedPassword, NULL );
GET_USE_INFO_POINTER(pUseInfo, InfoStruct);
if (pUseInfo == NULL) {
RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
}
//
// Cast a pointer to USE_INFO_2 to make things easy ...
//
UseInfo = &pUseInfo->ui3_ui2;
if(Level == 3)
{
CreateFlags = pUseInfo->ui3_flags;
}
else
{
CreateFlags = 0;
}
//
// UNC name can never be NULL or empty string.
//
if ((UseInfo->ui2_remote == NULL) ||
(UseInfo->ui2_remote[0] == TCHAR_EOS)) {
RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
}
//
// Allocate one large buffer for storing the UNC name, local device name,
// username, and domain name.
//
if ((status = WsAllocateUseWorkBuffer(
UseInfo,
Level,
&UncName, // Free using this pointer
&Local,
&UserName,
&DomainName
)) != NERR_Success) {
return status;
}
//
// If local device is a NULL string, it will be treated as a pointer to
// NULL.
//
if ((UseInfo->ui2_local != NULL) &&
(UseInfo->ui2_local[0] != TCHAR_EOS)) {
//
// Local device name is not NULL, canonicalize it
//
if (WsUseCheckLocal(
UseInfo->ui2_local,
Local,
&LocalLength
) != NERR_Success) {
(void) LocalFree(UncName);
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
}
}
//
// Check the format of the shared resource name.
//
if (WsUseCheckRemote(
UseInfo->ui2_remote,
UncName,
&UncNameLength
) != NERR_Success) {
(void) LocalFree(UncName);
RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
}
if ((Level >= 2) &&
(UseInfo->ui2_password != NULL) &&
(UseInfo->ui2_password[0] == TCHAR_EOS) &&
(UseInfo->ui2_username != NULL) &&
(UseInfo->ui2_username[0] == TCHAR_EOS) &&
(UseInfo->ui2_domainname != NULL) &&
(UseInfo->ui2_domainname[0] == TCHAR_EOS)) {
//
// The user explicitly specified an empty password, username, and
// domain. This means they want a null session.
//
*UserName = TCHAR_EOS;
*DomainName = TCHAR_EOS;
Password = TEXT("");
} else {
//
// Canonicalize user and domain names.
//
if (UserName != NULL) {
//
// Canonicalize username
//
if ((status = I_NetNameCanonicalize(
NULL,
UseInfo->ui2_username,
UserName,
(UNLEN + 1) * sizeof(TCHAR),
NAMETYPE_USER,
0
)) != NERR_Success) {
(void) LocalFree(UncName);
RETURN_INVALID_PARAMETER(ErrorParameter, USE_USERNAME_PARMNUM);
}
}
if ( (DomainName != NULL)
&& (UseInfo->ui2_domainname[0] != TCHAR_EOS) ) {
// Must now allow null string for domain name to support UPNs
// which contain the domain name in the username.
//
// Canonicalize domain name
// Canonicalize as a computername since a computername can be
// a valid domain (on the workstation to which you are connecting.
// This allows computernames with spaces to work.
//
if ((status = I_NetNameCanonicalize(
NULL,
UseInfo->ui2_domainname,
DomainName,
(DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR),
NAMETYPE_COMPUTER,
0
)) != NERR_Success) {
(void) LocalFree(UncName);
RETURN_INVALID_PARAMETER(ErrorParameter, USE_DOMAINNAME_PARMNUM);
}
}
//
// Make sure password length is not too long
//
if (UseInfo->ui2_password != NULL) {
Password = UseInfo->ui2_password;
if (STRLEN(Password) > PWLEN) {
(void) LocalFree(UncName);
RETURN_INVALID_PARAMETER(ErrorParameter, USE_PASSWORD_PARMNUM);
}
//
// Decode the password (the client obfuscated it.)
//
RtlInitUnicodeString( &EncodedPassword, Password );
RtlRunDecodeUnicodeString( NETR_USE_ADD_PASSWORD_SEED,
&EncodedPassword );
}
else {
Flags |= USE_DEFAULT_CREDENTIALS;
}
}
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] NetrUseAdd %ws %ws\n", Local, UncName));
}
//
// Check to see if the format of the local device name is correct based
// on the shared resource type to be accessed. This function also checks
// to see if the device is shared.
//
if ((status = WsCheckLocalAndDeviceType(
Local,
UseInfo->ui2_asg_type,
ErrorParameter
)) != NERR_Success) {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] WsCheckLocalAndDeviceType return %lu\n", status));
}
goto FreeWorkBuffer;
}
//
// Impersonate caller and get the logon id
//
if ((status = WsImpersonateAndGetSessionId(&SessionId)) != NERR_Success) {
goto FreeWorkBuffer;
}
//
// Replace \\ with \Device\LanmanRedirector in UncName, and create
// the NT-style tree connection names (without password or user name)
//
if ((status = WsCreateTreeConnectName(
UncName,
UncNameLength,
Local,
SessionId,
&TreeConnectStr
)) != NERR_Success) {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] NetrUseAdd Bad tree connect name: %lu\n",
status));
}
goto FreeWorkBuffer;
}
//
// Impersonate caller and get the logon id
//
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
goto FreeAllocatedBuffers;
}
//
// Don't redirect comm or spooled devices if redirection is paused.
//
if( Local != NULL && WsRedirectionPaused(Local) ) {
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] NetrUseAdd Redirector paused\n"));
}
status = ERROR_REDIR_PAUSED;
goto FreeAllocatedBuffers;
}
if (Local != NULL) {
PUSE_ENTRY UseList;
DWORD Index;
//
// Lock Use Table so nobody will do anything destructive to it while
// we're in the middle of all this. If multiple threads are trying
// to redirect the same drive, only one will succeed creating the
// symbolic link, and the others will fail.
//
if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
status = NERR_InternalError;
goto FreeAllocatedBuffers;
}
//
// Look for the matching LogonId in the Use Table, if none matched
// create a new entry.
//
if (WsGetUserEntry(
&Use,
&LogonId,
&Index,
FALSE
) != NERR_Success) {
UseList = NULL;
}
else {
UseList = Use.Table[Index].List;
}
//
// Create symbolic link for local device name. If there are multiple
// threads trying to do this, only one will succeed.
//
if ((status = WsCreateSymbolicLink(
Local,
UseInfo->ui2_asg_type,
TreeConnectStr.Buffer,
UseList,
&Session
)) != NERR_Success) {
if ((ARGUMENT_PRESENT(ErrorParameter)) &&
(status == ERROR_INVALID_PARAMETER)) {
*ErrorParameter = USE_LOCAL_PARMNUM;
}
}
RtlReleaseResource(&Use.TableResource);
if( status )
goto FreeAllocatedBuffers;
}
//
// Create the tree connection if none already exists; otherwise, open it.
//
status = WsOpenCreateConnection(
&TreeConnectStr,
UserName,
DomainName,
Password,
CreateFlags,
FILE_OPEN_IF,
UseInfo->ui2_asg_type,
&TreeConnection,
NULL
);
if (status != NERR_Success) {
if (status == NERR_UseNotFound) {
status = ERROR_DEV_NOT_EXIST;
}
WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
goto FreeAllocatedBuffers;
}
//
// Make sure user was correct about the shared resource type
//
if ((status = WsCheckEstablishedDeviceType(
TreeConnection,
UseInfo->ui2_asg_type
)) != NERR_Success) {
WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE);
WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
goto FreeAllocatedBuffers;
}
//
// Add use to the Use Table.
//
status = WsAddUse(
&LogonId,
TreeConnection,
Local,
LocalLength,
UncName,
UncNameLength,
&TreeConnectStr,
Flags
);
if( status ) {
WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE);
WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
}
FreeAllocatedBuffers:
//
// Free tree connection name buffer and work buffer
//
(void) LocalFree(TreeConnectStr.Buffer);
FreeWorkBuffer:
(void) LocalFree(UncName);
(void) LocalFree(Session);
//
// Put the password back the way we found it.
//
if ( EncodedPassword.Length != 0 ) {
UCHAR Seed = NETR_USE_ADD_PASSWORD_SEED;
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
}
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] NetrUseAdd: about to return status=%lu\n",
status));
}
return status;
}
NET_API_STATUS NET_API_FUNCTION
NetrUseDel (
IN LPTSTR ServerName OPTIONAL,
IN LPTSTR UseName,
IN DWORD ForceLevel
)
/*++
Routine Description:
This function is the NetUseDel entry point in the Workstation service.
Arguments:
UseName - Supplies the local device name or shared resource name of
the tree connection to be deleted.
ForceLevel - Supplies the level of force to delete the tree connection.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
LUID LogonId; // Logon Id of user
DWORD Index; // Index to user entry in Use Table
PUSE_ENTRY MatchedPointer; // Points to found use entry
PUSE_ENTRY BackPointer; // Points to node previous to
// found use entry
HANDLE TreeConnection; // Handle to connection
TCHAR *FormattedUseName;
// For canonicalizing a local device
// name
DWORD PathType = 0;
PUSE_ENTRY UseList;
UNREFERENCED_PARAMETER(ServerName);
//
// Check that ForceLevel parameter is valid
//
switch (ForceLevel) {
case USE_NOFORCE:
case USE_LOTS_OF_FORCE:
case USE_FORCE:
break;
default:
return ERROR_INVALID_PARAMETER;
}
FormattedUseName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(MAX_PATH+1)*sizeof(TCHAR));
if (FormattedUseName == NULL) {
return GetLastError();
}
//
// Check to see if UseName is valid, and canonicalize it.
//
if (I_NetPathCanonicalize(
NULL,
UseName,
FormattedUseName,
(MAX_PATH+1)*sizeof(TCHAR),
NULL,
&PathType,
0
) != NERR_Success) {
LocalFree(FormattedUseName);
return NERR_UseNotFound;
}
IF_DEBUG(USE) {
NetpKdPrint(("\n[Wksta] NetrUseDel %ws %u, formatted use name %ws\n",
UseName, ForceLevel, FormattedUseName));
}
//
// Impersonate caller and get the logon id
//
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
LocalFree(FormattedUseName);
return status;
}
//
// Lock Use Table while looking for entry to delete.
//
if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
LocalFree(FormattedUseName);
return NERR_InternalError;
}
//
// See if the use entry is an explicit connection.
//
status = WsGetUserEntry(
&Use,
&LogonId,
&Index,
FALSE
);
UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
NULL;
if ((status = WsFindUse(
&LogonId,
UseList,
FormattedUseName,
&TreeConnection,
&MatchedPointer,
&BackPointer
)) != NERR_Success) {
RtlReleaseResource(&Use.TableResource);
LocalFree(FormattedUseName);
return status;
}
LocalFree(FormattedUseName);
if (MatchedPointer == NULL) {
//
// UseName specified has an implicit connection. Don't need to hold
// on to Use Table anymore.
//
RtlReleaseResource(&Use.TableResource);
status = WsDeleteConnection(&LogonId, TreeConnection, ForceLevel);
//
// Close the connection handle if the API failed.
//
if (status != NERR_Success) {
NtClose(TreeConnection);
}
return status;
}
else if ((MatchedPointer->Local != NULL) &&
(MatchedPointer->LocalLength > 2)) {
//
// Don't allow delete on comm or spooled devices if redirection is
// paused for the current user.
//
if (WsRedirectionPaused(MatchedPointer->Local)) {
RtlReleaseResource(&Use.TableResource);
return ERROR_REDIR_PAUSED;
}
}
//
// Delete tree connection and remove use entry from Use Table. This function
// releases the TableResource
//
status = WsDeleteUse(
&LogonId,
ForceLevel,
MatchedPointer,
Index
);
IF_DEBUG(USE) {
NetpKdPrint(("[Wksta] NetrUseDel: about to return status=%lu\n", status));
}
return status;
}
STATIC
NET_API_STATUS
WsAddUse(
IN PLUID LogonId,
IN HANDLE TreeConnection,
IN LPTSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPTSTR UncName,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN DWORD Flags
)
/*++
Routine Description:
This function adds a use (tree connection) entry into the Use Table for
the user specified by the Logon Id. There is a linked list of uses for
each user. Each new use entry is inserted into the end of the linked
list so that enumeration of the list is resumable.
NOTE: This function locks the Use Table.
It also closes the tree connection in case of a error, or if a tree
connection to the same shared resource already exists.
Arguments:
LogonId - Supplies a pointer to the user's Logon Id.
TreeConnection - Supplies the handle to the tree connection created.
Local - Supplies the string of the local device name.
LocalLength - Supplies the length of the local device name.
UncName - Supplies the name of the shared resource (UNC name).
UncNameLength - Supplies the length of the shared resource.
TreeConnectStr - Supplies the string of UNC name in NT-style format
(\Device\LanmanRedirector\X:\Orville\Razzle).
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
DWORD Index; // Index to user entry in Use Table
PUSE_ENTRY MatchedPointer = NULL; // Points to matching shared resource
PUSE_ENTRY InsertPointer = NULL; // Point of insertion into use list
PUSE_ENTRY NewUse; // Pointer to the new use entry
if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
(void) NtClose(TreeConnection);
return NERR_InternalError;
}
//
// Look for the matching LogonId in the Use Table, if none matched
// create a new entry.
//
if ((status = WsGetUserEntry(
&Use,
LogonId,
&Index,
TRUE
)) != NERR_Success) {
RtlReleaseResource(&Use.TableResource);
(void) NtClose(TreeConnection);
return status;
}
if (Use.Table[Index].List != NULL) {
//
// Traverse use list to look for location to insert new use entry.
//
WsFindInsertLocation(
(PUSE_ENTRY) Use.Table[Index].List,
UncName,
&MatchedPointer,
&InsertPointer
);
}
if (MatchedPointer == NULL) {
//
// No matching UNC name found. Create a new entry with a
// corresponding remote entry.
//
if ((status = WsCreateNewEntry(
&NewUse,
TreeConnection,
Local,
LocalLength,
UncName,
UncNameLength,
TreeConnectStr,
Flags
)) != NERR_Success) {
RtlReleaseResource(&Use.TableResource);
(void) NtClose(TreeConnection);
return status;
}
}
else {
//
// Matching UNC name found.
//
//
// It may be unnecessary to create a new use entry if the use
// we are adding has a NULL local device and a NULL local device
// entry already exists.
//
if (Local == NULL) {
if (MatchedPointer->Local == NULL) {
//
// Yes, there is a NULL local device entry already.
// Increment the use count and we are done.
//
MatchedPointer->UseCount++;
MatchedPointer->Remote->TotalUseCount++;
#if DBG
DumpUseList(Index);
#endif
RtlReleaseResource(&Use.TableResource);
//
// Close newly opened handle to the same tree connection because
// one already exists.
//
(void) NtClose(TreeConnection);
return NERR_Success;
}
}
//
// If we get here means we need to create a new use entry but not
// a corresponding remote entry because a use with the same UNC
// name already exists.
//
if ((status = WsCreateNewEntry(
&NewUse,
TreeConnection,
Local,
LocalLength,
NULL,
0,
TreeConnectStr,
Flags
)) != NERR_Success) {
RtlReleaseResource(&Use.TableResource);
(void) NtClose(TreeConnection);
return status;
}
NewUse->Remote = MatchedPointer->Remote;
NewUse->Remote->TotalUseCount++;
}
//
// Insert the new use entry into use list
//
if (InsertPointer == NULL) {
//
// Inserting into the head of list
//
Use.Table[Index].List = (PVOID) NewUse;
}
else {
InsertPointer->Next = NewUse;
}
#if DBG
DumpUseList(Index);
#endif
RtlReleaseResource(&Use.TableResource);
return NERR_Success;
}
STATIC
NET_API_STATUS
WsDeleteUse(
IN PLUID LogonId,
IN DWORD ForceLevel,
IN PUSE_ENTRY MatchedPointer,
IN DWORD Index
)
/*++
Routine Description:
This function removes the use entry pointed by MatchedPointer and
free it memory if it is a UNC connection deleted with force, or
it is a UNC connection deleted with no force and the use count is
decremented to 0, or it is a connection mapped to a local device.
WARNING: This function assumes that the Use.TableResource is claimed.
And it releases it on exit.
Arguments:
LogonId - Supplies a pointer to the user's Logon Id.
ForceLevel - Supplies the level of force to delete.
MatchedPointer - Supplies the pointer to the use entry to be deleted.
Return Value:
None.
--*/
{
PUSE_ENTRY BackPointer;
NET_API_STATUS status;
//
// No need to remove entry if UNC connection is deleted with USE_NOFORCE
// level, and use count is not 0 after the deletion.
//
if ((MatchedPointer->Local == NULL) &&
(ForceLevel == USE_NOFORCE) &&
((MatchedPointer->UseCount - 1) > 0)) {
MatchedPointer->UseCount--;
MatchedPointer->Remote->TotalUseCount--;
NetpAssert(MatchedPointer->Remote->TotalUseCount);
RtlReleaseResource(&Use.TableResource);
return NERR_Success;
}
//
// Delete the tree connection and close the handle.
//
if ((status = WsDeleteConnection(
LogonId,
MatchedPointer->TreeConnection,
ForceLevel )) != NERR_Success) {
RtlReleaseResource(&Use.TableResource);
return status;
}
//
// Successfully deleted connection, and refound our entry.
//
BackPointer = (PUSE_ENTRY)Use.Table[Index].List;
if (BackPointer != MatchedPointer) {
while (BackPointer->Next != NULL) {
if (BackPointer->Next == MatchedPointer) {
break;
} else {
BackPointer = BackPointer->Next;
}
}
ASSERT(BackPointer->Next == MatchedPointer);
BackPointer->Next = MatchedPointer->Next;
} else {
//
// Use entry is the first one on the use list
//
Use.Table[Index].List = (PVOID) MatchedPointer->Next;
}
MatchedPointer->Remote->TotalUseCount -= MatchedPointer->UseCount;
if (MatchedPointer->Remote->TotalUseCount == 0) {
(void) LocalFree((HLOCAL) MatchedPointer->Remote);
}
RtlReleaseResource(&Use.TableResource);
//
// Delete symbolic link, if any.
// Must perform the deletion outside of exclusively holding the
// Use.TableResource.
// Otherwise, when the shell tries to update the current status of
// a drive letter change, the explorer.exe thread will block while
// trying to acquire the Use.TableResource
//
WsDeleteSymbolicLink(
MatchedPointer->Local,
MatchedPointer->TreeConnectStr,
NULL
);
(void) LocalFree((HLOCAL) MatchedPointer);
return status;
}
STATIC
NET_API_STATUS
WsCreateNewEntry(
OUT PUSE_ENTRY *NewUse,
IN HANDLE TreeConnection,
IN LPTSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPTSTR UncName OPTIONAL,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN DWORD Flags
)
/*++
Routine Description:
This function creates and initializes a new use entry. If the UncName
is specified, a new remote entry is created and initialized with
UncName.
Arguments:
NewUse - Returns a pointer to the newly allocated and initialized use
entry.
TreeConnection - Supplies the handle to the tree connection to set in
the new use entry.
Local - Supplies the local device name string to be copied into the new
use entry.
LocalLength - Supplies the length of the local device name string.
UncName - Supplies the UNC name string to be copied into the new use entry.
UncNameLength - Supplies the length of the UNC name string.
TreeConnectStr - Supplies the string of UNC name in NT-style format
(\Device\LanmanRedirector\X:\Orville\Razzle).
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
PUNC_NAME NewRemoteEntry; // Common extension to use entries which
// share the same UNC connection.
//
// Allocate memory for new use. String length does not include zero
// terminator so add that.
//
if ((*NewUse = (PUSE_ENTRY) LocalAlloc(
LMEM_ZEROINIT,
ROUND_UP_COUNT(
sizeof(USE_ENTRY) + (LocalLength + 1)
* sizeof(TCHAR),
ALIGN_WCHAR
) +
(ARGUMENT_PRESENT(Local) ?
TreeConnectStr->MaximumLength :
0
)
)) == NULL) {
return GetLastError();
}
//
// Put use information into the new use node
//
(*NewUse)->Next = NULL;
(*NewUse)->LocalLength = LocalLength;
(*NewUse)->UseCount = 1;
(*NewUse)->TreeConnection = TreeConnection;
(*NewUse)->ResumeKey = GlobalResumeKey++;
(*NewUse)->Flags = Flags;
//
// GlobalResumeKey wraps back to 0 if it is 0x80000000 because we use the
// high bit to indicate whether the resume handle for NetUseEnum comes
// from the workstation service or from the redirector.
//
GlobalResumeKey &= ~(REDIR_LIST);
//
// Copy local device name into use entry after the LocalLength field,
// if it is specified.
//
if (ARGUMENT_PRESENT(Local)) {
(*NewUse)->Local = (LPTSTR) ((DWORD_PTR) *NewUse + sizeof(USE_ENTRY));
STRCPY((*NewUse)->Local, Local);
(*NewUse)->TreeConnectStr = (LPWSTR) ROUND_UP_COUNT(
((DWORD_PTR) *NewUse +
sizeof(USE_ENTRY) +
(LocalLength + 1) *
sizeof(TCHAR)),
ALIGN_WCHAR
);
wcscpy((*NewUse)->TreeConnectStr, TreeConnectStr->Buffer);
}
else {
(*NewUse)->Local = NULL;
(*NewUse)->TreeConnectStr = NULL;
}
//
// If shared resource name is specified, create a new remote entry to hold
// the UNC name, the tree connection handle, and total number of uses on
// this shared resource.
//
if (ARGUMENT_PRESENT(UncName)) {
if ((NewRemoteEntry = (PUNC_NAME) LocalAlloc(
LMEM_ZEROINIT,
(UINT) (sizeof(UNC_NAME) +
UncNameLength * sizeof(TCHAR))
)) == NULL) {
(void) LocalFree((HLOCAL) *NewUse);
return GetLastError();
}
STRCPY((LPWSTR) NewRemoteEntry->UncName, UncName);
NewRemoteEntry->UncNameLength = UncNameLength;
NewRemoteEntry->TotalUseCount = 1;
// NewRemoteEntry->RedirUseInfo = NULL;
(*NewUse)->Remote = NewRemoteEntry;
}
return NERR_Success;
}
STATIC
NET_API_STATUS
WsCheckLocalAndDeviceType(
IN OUT LPTSTR Local,
IN DWORD DeviceType,
OUT LPDWORD ErrorParameter OPTIONAL
)
/*++
Routine Description:
This function checks the format of the specified local device name
based on the device type of shared resource to be accessed, and at
the same time verifies that the device type is valid.
Arguments:
Local - Supplies the local device name. Returns its canonicalized
form.
DeviceType - Supplies the shared resource device type.
ErrorParameter - Returns the identifier to the invalid parameter in Buffer
if this function returns ERROR_INVALID_PARAMETER.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
//
// Validate local device name based on the shared resource type.
//
//
// Check for wild card device type outside of the switch statement
// below because compiler complains about constant too big.
//
if (DeviceType == USE_WILDCARD || DeviceType == USE_IPC) {
//
// Local device name must be NULL for wild card or IPC connection.
//
if (Local == NULL) {
return NERR_Success;
}
else {
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
}
}
switch (DeviceType) {
case USE_DISKDEV:
if (Local == NULL) {
return NERR_Success;
}
//
// Local device name must have "<drive>:" format for disk
// device.
//
if (STRLEN(Local) != 2 || Local[1] != TCHAR_COLON) {
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
}
break;
case USE_SPOOLDEV:
if (Local == NULL) {
return NERR_Success;
}
//
// Local device name must have "LPTn:" or "PRN:" format
// for a print device.
//
if ((STRNICMP(Local, TEXT("PRN"), 3) != 0) &&
(STRNICMP(Local, TEXT("LPT"), 3) != 0)) {
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
}
break;
case USE_CHARDEV:
if (Local == NULL) {
return NERR_Success;
}
//
// Local device name must have "COMn:" or "AUX:" format
// for a comm device.
//
if ((STRNICMP(Local, TEXT("AUX"), 3) != 0) &&
(STRNICMP(Local, TEXT("COM"), 3) != 0)) {
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
}
break;
default:
IF_DEBUG(USE) {
NetpKdPrint((
"[Wksta] NetrUseAdd: Unknown shared resource type %lu\n",
DeviceType));
}
return NERR_BadAsgType;
}
return NERR_Success;
}
STATIC
NET_API_STATUS
WsCheckEstablishedDeviceType(
IN HANDLE TreeConnection,
IN DWORD RequestedDeviceType
)
/*++
Routine Description:
This function verifies that the device type of the shared resource we
have connected to is the same as the requested device type.
Arguments:
TreeConnection - Supplies handle to established tree connection.
RequestedDeviceType - Supplies the shared resource device type specified
by the user to create the tree connection.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NTSTATUS ntstatus;
FILE_FS_DEVICE_INFORMATION FileInformation;
IO_STATUS_BLOCK IoStatusBlock;
ntstatus = NtQueryVolumeInformationFile(
TreeConnection,
&IoStatusBlock,
(PVOID) &FileInformation,
sizeof(FILE_FS_DEVICE_INFORMATION),
FileFsDeviceInformation
);
if (! NT_SUCCESS(ntstatus) || ! NT_SUCCESS(IoStatusBlock.Status)) {
return NERR_InternalError;
}
//
// Check for wild card device type outside of the switch statement
// below because compiler complains about constant too big.
//
if (RequestedDeviceType == USE_WILDCARD) {
return NERR_Success;
}
switch (RequestedDeviceType) {
case USE_DISKDEV:
if (FileInformation.DeviceType != FILE_DEVICE_DISK) {
return ERROR_BAD_DEV_TYPE;
}
break;
case USE_SPOOLDEV:
if (FileInformation.DeviceType != FILE_DEVICE_PRINTER) {
return ERROR_BAD_DEV_TYPE;
}
break;
case USE_CHARDEV:
if (FileInformation.DeviceType != FILE_DEVICE_SERIAL_PORT) {
return ERROR_BAD_DEV_TYPE;
}
break;
case USE_IPC:
if (FileInformation.DeviceType != FILE_DEVICE_NAMED_PIPE) {
return ERROR_BAD_DEV_TYPE;
}
break;
default:
//
// This should have been error checked earlier.
//
NetpKdPrint((
"WsCheckEstablishedDeviceType: Unknown device type.\n"
));
NetpAssert(FALSE);
return ERROR_BAD_DEV_TYPE;
}
return NERR_Success;
}
STATIC
NET_API_STATUS
WsAllocateUseWorkBuffer(
IN PUSE_INFO_2 UseInfo,
IN DWORD Level,
OUT LPTSTR *UncName,
OUT LPTSTR *Local,
OUT LPTSTR *UserName,
OUT LPTSTR *DomainName
)
/*++
Routine Description:
This function allocates the work buffer for NetrUseAdd. The buffer
is the maximum need for canonicalizing and storing the strings
described below. If any of the strings is NULL, no memory is allocated
for it.
UncName - UNC name of remote resource. Cannot be NULL.
Local - local device name specified in the NetUseAdd. May be NULL.
UserName - username to establish connection with. May be NULL.
DomainName - domain name. Must be specified if UserName is,
otherwise if UserName is NULL this string is ignored.
Arguments:
UseInfo - Supplies the input structure for NetUseAdd.
Level - Supplies the use info level.
Output pointers are set to point into allocated work buffer if its
corresponding input string is not NULL or empty.
Return Value:
Error from LocalAlloc.
--*/
{
DWORD WorkBufferSize = (MAX_PATH + 1) * sizeof(TCHAR);
LPBYTE WorkBuffer;
if ((UseInfo->ui2_local != NULL) &&
(UseInfo->ui2_local[0] != TCHAR_EOS)) {
WorkBufferSize += (DEVLEN + 1) * sizeof(TCHAR);
}
if (Level >= 2) {
if (UseInfo->ui2_username != NULL) {
WorkBufferSize += (UNLEN + 1) * sizeof(TCHAR);
}
if (UseInfo->ui2_domainname != NULL) {
WorkBufferSize += (DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR);
}
}
if ((WorkBuffer = (LPBYTE) LocalAlloc(
LMEM_ZEROINIT,
(UINT) WorkBufferSize
)) == NULL) {
return GetLastError();
}
*UncName = (LPTSTR) WorkBuffer;
IF_DEBUG(USE) {
NetpKdPrint((" Remote x%08lx\n", *UncName));
}
WorkBuffer += (MAX_PATH + 1) * sizeof(TCHAR);
if ((UseInfo->ui2_local != NULL) &&
(UseInfo->ui2_local[0] != TCHAR_EOS)) {
*Local = (LPTSTR) WorkBuffer;
WorkBuffer += (DEVLEN + 1) * sizeof(TCHAR);
}
else {
*Local = NULL;
}
IF_DEBUG(USE) {
NetpKdPrint((" Local x%08lx\n", *Local));
}
if (Level >= 2) {
if (UseInfo->ui2_username != NULL) {
*UserName = (LPTSTR) WorkBuffer;
WorkBuffer += (UNLEN + 1) * sizeof(TCHAR);
}
else {
*UserName = NULL;
}
if (UseInfo->ui2_domainname != NULL) {
*DomainName = (LPTSTR) WorkBuffer;
}
else {
*DomainName = NULL;
}
}
IF_DEBUG(USE) {
NetpKdPrint((" UserName x%08lx, DomainName x%08lx\n",
*UserName, *DomainName));
}
return NERR_Success;
}
#if DBG
STATIC
VOID
DumpUseList(
DWORD Index
)
/*++
Routine Description:
This function dumps the user's use list for debugging purposes.
Arguments:
Index - Supplies the index to the user entry in the Use Table.
Return Value:
None.
--*/
{
PUSE_ENTRY UseList = (PUSE_ENTRY) Use.Table[Index].List;
IF_DEBUG(USE) {
NetpKdPrint(("\nDump Use List @%08lx\n", UseList));
while (UseList != NULL) {
NetpKdPrint(("%ws %ws\n", UseList->Local,
UseList->Remote->UncName));
NetpKdPrint(("usecount=%lu, totalusecount=%lu\n",
UseList->UseCount, UseList->Remote->TotalUseCount));
NetpKdPrint(("Connection handle %08lx, resume key=%lu\n",
UseList->TreeConnection, UseList->ResumeKey));
UseList = UseList->Next;
}
}
}
#endif