2025-04-27 07:49:33 -04:00

5890 lines
178 KiB
C

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
davsrv.c
Abstract:
This has all of our server side entry points for the DAV Mini-Redir
RPC service interface. It also contains the helper functions for the
service interface.
Author:
Rohan Kumar [RohanK] 01-Dec-1999
Environment:
User Mode - Win32
Revision History:
--*/
#include "pch.h"
#pragma hdrstop
#include <ntumrefl.h>
#include <usrmddav.h>
#include <lmuseflg.h>
#include "global.h"
#include <time.h>
#include "nodefac.h"
#include "UniUtf.h"
DWORD
DavCheckLocalName (
LPWSTR LocalName,
PWCHAR OutputLocalDeviceBuffer
);
DWORD
DavCheckRemoteName(
IN LPWSTR LocalName OPTIONAL,
IN LPWSTR RemoteName,
OUT LPWSTR *OutputBuffer,
OUT LPDWORD OutputBufferLength OPTIONAL
);
DWORD
DavCreateTreeConnectName(
IN LPWSTR UncName,
IN LPWSTR LocalName OPTIONAL,
IN ULONG SessionId,
OUT PUNICODE_STRING TreeConnectStr
);
DWORD
DavOpenCreateConnection(
IN PUNICODE_STRING TreeConnectionName,
IN LPWSTR UserName OPTIONAL,
IN LPWSTR Password OPTIONAL,
IN LPWSTR UncName,
IN ACCESS_MASK DesiredAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN ULONG ConnectionType,
OUT PHANDLE TreeConnectionHandle,
OUT PULONG_PTR Information OPTIONAL
);
DWORD
DavImpersonateClient(
VOID
);
ULONG
DavImpersonateAndGetSessionId(
PULONG pSessionId
);
ULONG
DavImpersonateAndGetLogonId(
PLUID pLogonId
);
DWORD
DavCreateSymbolicLink(
IN LPWSTR Local,
IN LPWSTR TreeConnectStr,
IN OUT LPWSTR *Session
);
DWORD
DavDeleteSymbolicLink(
IN LPWSTR LocalDeviceName,
IN LPWSTR TreeConnectStr,
IN LPWSTR SessionDeviceName
);
ULONG
DavImpersonateAndGetUserId(
LPWSTR UserName,
LPDWORD UserNameMaxLen
);
DWORD
DavRevertToSelf(
VOID
);
NET_API_STATUS
DavGetUserEntry(
IN PDAV_USERS_OBJECT DavUsers,
IN PLUID LogonId,
OUT PULONG Index,
IN BOOL IsAdd
);
NET_API_STATUS
DavGrowTable(
IN PDAV_USERS_OBJECT DavUsers
);
LPWSTR
DavReturnSessionPath(
IN LPWSTR LocalDeviceName
);
NET_API_STATUS
DavAddUse(
IN PLUID LogonId,
IN LPWSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPWSTR AuthUserName OPTIONAL,
IN LPWSTR UncName,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN HANDLE DavCreateFileHandle
);
VOID
DavFindInsertLocation(
IN PDAV_USE_ENTRY UseList,
IN LPWSTR UncName,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *InsertPointer
);
NET_API_STATUS
DavCreateNewEntry(
OUT PDAV_USE_ENTRY *NewUse,
IN LPWSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPWSTR AuthUserName OPTIONAL,
IN LPWSTR UncName OPTIONAL,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN HANDLE DavCreateFileHandle
);
NET_API_STATUS
DavFindUse(
IN PLUID LogonId,
IN PDAV_USE_ENTRY UseList,
IN LPWSTR UseName,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *BackPointer OPTIONAL
);
VOID
DavFindLocal(
IN PDAV_USE_ENTRY UseList,
IN LPTSTR Local,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *BackPointer
);
VOID
DavFindRemote(
IN PDAV_USE_ENTRY UseList,
IN LPTSTR RemoteName,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *BackPointer
);
NET_API_STATUS
DavDeleteUse(
IN PLUID LogonId,
IN DWORD ForceLevel,
IN PDAV_USE_ENTRY MatchedPointer,
IN DWORD Index
);
VOID
DavInitializeAndInsertTheServerShareEntry(
IN OUT PDAV_SERVER_SHARE_ENTRY ServerShareEntry,
IN PWCHAR ServerName,
IN ULONG EntrySize
);
BOOL
DavIsServerInServerShareTable(
IN PWCHAR ServerName,
OUT PDAV_SERVER_SHARE_ENTRY *ServerShEntry
);
DWORD
DavGetShareListFromServer(
PDAV_SERVER_SHARE_ENTRY ServerShEntry
);
BOOL
DavCheckTheNonDAVServerList(
PWCHAR ServerName
);
//
// DAV Use Table.
//
DAV_USERS_OBJECT DavUseObject;
//
// The hash table of DAV_SERVER_SHARE_TABLE entries. This is hashed on the
// server name.
//
LIST_ENTRY ServerShareTable[SERVER_SHARE_TABLE_SIZE];
CRITICAL_SECTION ServerShareTableLock;
//
// Number of users logged on to the system. The Critical section below it
// synchronizes the acces to this variable.
//
ULONG DavNumberOfLoggedOnUsers = 0;
CRITICAL_SECTION DavLoggedOnUsersLock;
//
// Whenever we encounter a server that does not speak the DAV protocol in the
// DavrDoesServerDoDav function, we add it to the NonDAVServerList. An entry
// is kept on this list for ServerNotFoundCacheLifeTimeInSec (a global read
// from the registry during service start-up). Before going on the network
// to figure out whether a server does DAV, we look in the list to see if we
// have already seen this server (which does not do DAV) and fail the call.
//
LIST_ENTRY NonDAVServerList;
CRITICAL_SECTION NonDAVServerListLock = {0};
//
// Implementation of functions begins here.
//
DWORD
DavrCreateConnection(
IN handle_t dav_binding_h,
IN LPWSTR LocalName OPTIONAL,
IN LPWSTR RemoteName,
IN DWORD Type,
IN LPWSTR Password OPTIONAL,
IN LPWSTR UserName OPTIONAL
)
/*++
Routine Description:
This function creates a tree connection to the specified RemoteName
(UNC name) and maps it to the LocalName (local device name), if
it is specified. The password and user name are the credentials
used to create the connection, if specified; otherwise, the
interactive logged on user's credentials are used by default.
Arguments:
dav_binding_h - The explicit RPC binding handle.
LocalName - Supplies the local device name to map to the created tree
connection. Only drive letter device names are accepted. (No
LPT or COM).
RemoteName - Supplies the UNC name of the remote resource in the format
of Server\Volume\Directory. It must be a disk resource.
Type - Supplies the connection type.
Password - Supplies the password to use to make the connection to the
server. The password should be encoded with DAV_ENCODE_SEED.
UserName - Supplies the user name to use to make the connection.
Return Value:
Should return the following values under varying conditions so the
mapping on the provider side works :
ERROR_UNEXP_NET_ERR -
ERROR_INVALID_HANDLE -
ERROR_INVALID_PARAMETER -
ERROR_ALREADY_ASSIGNED - Local DOS device name is already in use.
ERROR_REM_NOT_LIST - Invalid remote resource name.
ERROR_BAD_DEVICE - Invalid local DOS device name.
ERROR_INVALID_PASSWORD - Invalid password.
--*/
{
DWORD dwErr = NO_ERROR;
ULONG SessionId;
LUID LogonId;
BOOLEAN createdLink = FALSE;
HANDLE TreeConnection = INVALID_HANDLE_VALUE;
LPWSTR Unc = NULL;
WCHAR localDrive[3]; // For L"X:\0".
PWCHAR LocalDriveName = NULL;
DWORD LocalLength = 0, UncLength = 0;
UNICODE_STRING EncodedPassword;
UNICODE_STRING TreeConnectStr;
LPWSTR Session = NULL;
LPWSTR Cookie = NULL;
LPWSTR UserNameBuffer = NULL;
DWORD UserNameLength = 0;
DavPrint((DEBUG_MISC,
"DavrCreateConnection: Entered. Local = %ws, Remote = %ws\n",
LocalName, RemoteName));
EncodedPassword.Length = 0;
TreeConnectStr.Buffer = NULL;
TreeConnectStr.Length = TreeConnectStr.MaximumLength = 0;
//
// If the RemoteName is not valid, we return right away.
//
if ( RemoteName == NULL || RemoteName[0] != L'\\' || RemoteName[1] != L'\\' ) {
dwErr = ERROR_INVALID_PARAMETER;
DavPrint((DEBUG_ERRORS, "DavrCreateConnection: RemoteName == NULL\n"));
goto EXIT_THE_FUNCTION;
}
//
// If a LocalName has been specified, its always of the format X: and so its
// 3 WCHARS long including the final '\0'.
//
if (LocalName) {
DavPrint((DEBUG_MISC,
"DavrCreateConnection: Local = %ws, Remote = %ws\n",
LocalName, RemoteName));
LocalLength = 3;
//
// Check the local name to ensure that it's "X:" syntax.
//
dwErr = DavCheckLocalName( LocalName, &localDrive[0] );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavCheckLocalName: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrCreateConnection: LocalDrive = %ws\n", &localDrive[0]));
LocalDriveName = &localDrive[0];
}
if (Type != RESOURCETYPE_ANY && Type != RESOURCETYPE_DISK) {
dwErr = ERROR_INVALID_PARAMETER;
DavPrint((DEBUG_ERRORS, "DavrCreateConnection: Invalid Type.\n"));
goto EXIT_THE_FUNCTION;
}
#if 0
//
// Decode the password.
//
if (Password != NULL) {
RtlInitUnicodeString(&EncodedPassword, Password);
RtlRunDecodeUnicodeString(DAV_ENCODE_SEED, &EncodedPassword);
}
#endif
if (UserName != NULL) {
//
// We want to use local copy of buffer name. We don't want to
// alter original UserName buffer passed else constant UserName buffer
// passed will result in exception.
//
UserNameLength = wcslen(UserName);
UserNameBuffer = LocalAlloc( (LMEM_FIXED | LMEM_ZEROINIT),
(UserNameLength + 1) * sizeof(WCHAR) );
if (!UserNameBuffer) {
dwErr = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/LocalAlloc. Error Val = %d\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
wcscpy(UserNameBuffer, UserName);
//
// Check if the UserName passed is valid. If it is, then we will use
// local buffer as it is for further use. Else we will copy modified
// version of name in local buffer.
//
if ( !IS_VALID_USERNAME_TOKEN(UserName, UserNameLength) ) {
DWORD BlackSlashOffset = 0;
BOOL BackSlashFound = FALSE;
for (BlackSlashOffset = 0; BlackSlashOffset < UserNameLength; BlackSlashOffset++) {
if (UserName[BlackSlashOffset] == L'\\') {
BackSlashFound = TRUE;
break;
}
}
if (BackSlashFound) {
//
// Replace the UserName in the form of Domain\User with the form
// of User@Domain, which can be accepted by WinInet.
//
RtlCopyMemory(UserNameBuffer,
&(UserName[BlackSlashOffset+1]),
(UserNameLength-BlackSlashOffset-1)*sizeof(WCHAR));
UserNameBuffer[UserNameLength-BlackSlashOffset-1] = L'@';
RtlCopyMemory(&(UserNameBuffer[UserNameLength-BlackSlashOffset]),
UserName,
BlackSlashOffset*sizeof(WCHAR));
if (!IS_VALID_USERNAME_TOKEN(UserNameBuffer, UserNameLength)) {
DavPrint((DEBUG_ERRORS, "DavrCreateConnection: Invalid UserName\n"));
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
} else {
DavPrint((DEBUG_ERRORS, "DavrCreateConnection: Invalid UserName\n"));
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
}
}
DavPrint((DEBUG_MISC, "DavrCreateConnection: UserName = %ws\n", UserNameBuffer));
//
// Check the remote name to ensure that has a proper syntax.
//
dwErr = DavCheckRemoteName(LocalDriveName, RemoteName, &Unc, NULL);
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavCheckRemoteName: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
UncLength = wcslen(Unc);
DavPrint((DEBUG_MISC, "DavrCreateConnection: Unc = %ws\n", Unc));
//
// Impersonate caller and get the LogonId.
//
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavImpersonateAndGetLogonId: "
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrCreateConnection: LogonId = %d %d\n",
LogonId.HighPart, LogonId.LowPart));
//
// Impersonate caller and get the SessionId.
//
dwErr = DavImpersonateAndGetSessionId( &(SessionId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavImpersonateAndGetSessionId: "
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrCreateConnection: SessionId = %d\n", SessionId));
//
// Now we create an NT-style tree connection name.
//
dwErr = DavCreateTreeConnectName(Unc, LocalDriveName, SessionId, &TreeConnectStr);
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavCreateTreeConnectName: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrCreateConnection: TreeConnectStr = %wZ.\n",
&(TreeConnectStr)));
if (LocalDriveName) {
//
// Create symbolic link for local device name. If there are multiple
// threads trying to do this, only one will succeed.
//
dwErr = DavCreateSymbolicLink(LocalDriveName,
TreeConnectStr.Buffer,
&Session);
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavCreateSymbolicLink: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
createdLink = TRUE;
}
dwErr = DavOpenCreateConnection(&TreeConnectStr,
UserNameBuffer,
Password,
Unc,
SYNCHRONIZE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_CREATE_TREE_CONNECTION,
Type,
&TreeConnection,
NULL);
if (dwErr == NO_ERROR) {
HANDLE TempTreeConnection = INVALID_HANDLE_VALUE;
//
// Check whether the directory really exists. This is our way of
// avoiding to write a lot of code in the kernel to check for a deep net use case
//
dwErr = DavOpenCreateConnection(&TreeConnectStr,
NULL,
NULL,
Unc,
SYNCHRONIZE |FILE_READ_DATA,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE,
Type,
&TempTreeConnection,
NULL);
if (dwErr == NO_ERROR) {
NtClose(TempTreeConnection);
} else {
NtClose(TreeConnection);
TreeConnection = INVALID_HANDLE_VALUE;
}
}
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavOpenCreateConnection: dwErr = %08lx\n",
dwErr));
switch(dwErr) {
case ERROR_NOT_CONNECTED:
case ERROR_FILE_NOT_FOUND:
case ERROR_INVALID_NAME:
dwErr = ERROR_BAD_NETPATH;
break;
case ERROR_CONNECTION_INVALID:
dwErr = WN_BAD_NETNAME;
break;
}
goto EXIT_THE_FUNCTION;
}
//
// Add use to the Use Table. We want to always add a user name to the
// UserEntry. So if a user name is not given explicitly, then we will use
// the UserId of the client.
//
if (UserNameBuffer == NULL) {
DWORD UserNameMaxLen = UserNameLength = (UNLEN + 1);
UserNameBuffer = LocalAlloc( (LMEM_FIXED | LMEM_ZEROINIT),
(UserNameMaxLen + 1) * sizeof(WCHAR) );
if (!UserNameBuffer) {
dwErr = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/LocalAlloc. Error Val = %d.\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
dwErr = DavImpersonateAndGetUserId(UserNameBuffer, &UserNameMaxLen);
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavImpersonateAndGetUserId: dwErr = %x\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
}
ASSERT(UserNameBuffer);
dwErr = DavAddUse( &(LogonId),
LocalDriveName,
LocalLength,
UserNameBuffer,
Unc,
UncLength,
&(TreeConnectStr),
TreeConnection );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrCreateConnection/DavAddUse: dwErr = %08lx\n", dwErr));
}
EXIT_THE_FUNCTION:
if (createdLink && dwErr != NO_ERROR) {
DWORD DeleteStatus;
DeleteStatus = DavDeleteSymbolicLink(LocalDriveName,
TreeConnectStr.Buffer,
NULL);
}
//
// If we failed after creating a handle then we need to close the handle
// before leaving. Otherwise, this handle is closed when the connection is
// deleted (in DavrCancelConnection).
//
if (TreeConnection != INVALID_HANDLE_VALUE && dwErr != NO_ERROR) {
NtClose(TreeConnection);
}
if (EncodedPassword.Length != 0) {
UCHAR Seed = DAV_ENCODE_SEED;
RtlRunEncodeUnicodeString(&Seed, &EncodedPassword);
}
if (UserNameBuffer != NULL) {
LocalFree((HLOCAL)UserNameBuffer);
UserNameBuffer = NULL;
}
if (TreeConnectStr.Buffer != NULL) {
LocalFree((HLOCAL)TreeConnectStr.Buffer);
}
if (Session != NULL) {
LocalFree( Session );
}
if (Unc != NULL) {
LocalFree( Unc );
}
return dwErr;
}
DWORD
DavrDeleteConnection(
IN handle_t dav_binding_h,
IN LPWSTR ConnectionName,
IN DWORD UseForce
)
/*++
Routine Description:
This function deletes a remote connection to the network resource and
the symbolic link which was created between the network resource and the
local device.
Arguments:
dav_binding_h - The explicit RPC binding handle.
ConnectionName - Supplies the local DOS device, or the remote resource name
if it is a UNC connection to delete.
UseForce - If TRUE, the connection should be broken even if open files
exist.
Return Value:
WN_SUCCESS - Successful. OR
Should return the following values under varying conditions so the
mapping on the provider side works :
ERROR_UNEXP_NET_ERR
ERROR_INVALID_HANDLE
ERROR_INVALID_PARAMETER
ERROR_OPEN_FILES - fForce is FALSE and there are opened files on the
connection.
ERROR_REM_NOT_LIST - Invalid remote resource name.
ERROR_BAD_DEVICE - Invalid local DOS device name.
ERROR_NOT_FOUND - Connection could not be found.
--*/
{
DWORD dwErr = NO_ERROR;
WCHAR localDrive[3]; // For L"X:\0".
PWCHAR NameToDelete = NULL;
DWORD ForceLevel;
LUID LogonId; // Logon Id of user.
ULONG Index;
PDAV_USE_ENTRY UseList = NULL, MatchedPointer = NULL, BackPointer = NULL;
BOOL ResAcquired = FALSE;
DavPrint((DEBUG_MISC, "DavrDeleteConnection: Entered. ConnectionName = "
"%ws\n", ConnectionName));
ForceLevel = (UseForce ? USE_LOTS_OF_FORCE : USE_NOFORCE);
//
// Initialize the LogonId.
//
LogonId.LowPart = 0;
LogonId.HighPart = 0;
if(ConnectionName == NULL) {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// The ConnectionName could be local or remote.
//
if ( ConnectionName[0] != L'\\' ) {
if(ConnectionName[1] != L':') {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// Check the local name to ensure that it's "X:" syntax.
//
dwErr = DavCheckLocalName( ConnectionName, &localDrive[0] );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrDeleteConnection/DavCheckLocalName: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrDeleteConnection: LocalDrive = %ws\n",
&localDrive[0]));
NameToDelete = &localDrive[0];
} else {
//
// This is a remote name.
//
NameToDelete = ConnectionName;
}
//
// Impersonate caller and get the LogonId.
//
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrDeleteConnection/DavImpersonateAndGetLogonId: "
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrDeleteConnection: LogonId = %d %d\n",
LogonId.HighPart, LogonId.LowPart));
//
// Lock the Dav Use Table while looking for entry to delete.
//
if ( !RtlAcquireResourceExclusive( &(DavUseObject.TableResource), TRUE ) ) {
DavPrint((DEBUG_ERRORS,
"DavrDeleteConnection/RtlAcquireResourceExclusive.\n"));
dwErr = NERR_InternalError;
goto EXIT_THE_FUNCTION;
}
ResAcquired = TRUE;
//
// See if the use entry is an explicit connection.
//
dwErr = DavGetUserEntry( &(DavUseObject), &(LogonId), &(Index), FALSE );
if (dwErr != NERR_Success) {
UseList = NULL;
} else {
UseList = (PDAV_USE_ENTRY) DavUseObject.Table[Index].List;
}
dwErr = DavFindUse(&LogonId,
UseList,
NameToDelete,
&MatchedPointer,
&BackPointer);
if (dwErr != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavrDeleteConnection/DavFindUse: dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
if (MatchedPointer == NULL) {
DavPrint((DEBUG_MISC,
"DavrDeleteConnection: UseName has an implicit connection.\n"));
goto EXIT_THE_FUNCTION;
}
//
// Delete tree connection and remove use entry from Dav Use Table.
//
dwErr = DavDeleteUse( &(LogonId), ForceLevel, MatchedPointer, Index );
if (dwErr != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavrDeleteConnection/DavDeleteUse: dwErr = %08lx\n", dwErr));
}
EXIT_THE_FUNCTION:
if (ResAcquired) {
RtlReleaseResource( &(DavUseObject.TableResource) );
}
return dwErr;
}
DWORD
DavrConnectionExist(
IN handle_t dav_binding_h,
IN LPWSTR ConnectionName
)
/*++
Routine Description:
This function finds if given connection exists. Given connection can be a
local DOS device name or it can be a remote UNC connection name.
Arguments:
dav_binding_h - The explicit RPC binding handle.
ConnectionName - Supplies the local DOS device, or the remote resource name.
Return Value:
WN_SUCCESS - If connection exist. OR
Should return the following values under varying conditions so the
mapping on the provider side works :
ERROR_UNEXP_NET_ERR
ERROR_INVALID_HANDLE
ERROR_INVALID_PARAMETER
ERROR_REM_NOT_LIST - Invalid remote resource name.
ERROR_BAD_DEVICE - Invalid local DOS device name.
ERROR_NOT_FOUND - Connection could not be found.
--*/
{
DWORD dwErr = NO_ERROR;
WCHAR localDrive[3]; // For L"X:\0".
PWCHAR NameToQuery = NULL;
LUID LogonId; // Logon Id of user.
ULONG Index = 0;
PDAV_USE_ENTRY UseList = NULL, MatchedPointer = NULL, BackPointer = NULL;
BOOL ResAcquired = FALSE;
DavPrint((DEBUG_MISC,
"DavrConnectionExist: Entered. ConnectionName = %ws\n",
ConnectionName));
//
// Initialize the LogonId.
//
LogonId.LowPart = 0;
LogonId.HighPart = 0;
if(ConnectionName == NULL) {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// The ConnectionName could be local or remote.
//
if ( ConnectionName[0] != L'\\' ) {
if(ConnectionName[1] != L':') {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// Check the local name to ensure that it's "X:" syntax.
//
dwErr = DavCheckLocalName( ConnectionName, &(localDrive[0]) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrConnectionExist/DavCheckLocalName: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC,
"DavrConnectionExist: LocalDrive = %ws\n",
&(localDrive[0])));
NameToQuery = &(localDrive[0]);
} else {
//
// This is a remote name.
//
NameToQuery = ConnectionName;
}
//
// Impersonate caller and get the LogonId.
//
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrConnectionExist/DavImpersonateAndGetLogonId: "
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC,
"DavrConnectionExist: LogonId = %d %d\n",
LogonId.HighPart, LogonId.LowPart));
//
// Lock the Dav Use Table while looking for entry to delete.
//
if ( !RtlAcquireResourceExclusive( &(DavUseObject.TableResource), TRUE ) ) {
DavPrint((DEBUG_ERRORS,
"DavrConnectionExist/RtlAcquireResourceExclusive.\n"));
dwErr = NERR_InternalError;
goto EXIT_THE_FUNCTION;
}
ResAcquired = TRUE;
//
// See if the use entry is an explicit connection.
//
dwErr = DavGetUserEntry( &(DavUseObject), &(LogonId), &(Index), FALSE );
if (dwErr != NERR_Success) {
UseList = NULL;
dwErr = ERROR_NOT_FOUND;
goto EXIT_THE_FUNCTION;
}
UseList = (PDAV_USE_ENTRY) DavUseObject.Table[Index].List;
dwErr = DavFindUse(&LogonId,
UseList,
NameToQuery,
&MatchedPointer,
&BackPointer);
if (dwErr != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavrConnectionExist/DavFindUse: dwErr = %08lx\n", dwErr));
dwErr = ERROR_NOT_FOUND;
goto EXIT_THE_FUNCTION;
}
ASSERT(MatchedPointer != NULL);
DavPrint((DEBUG_MISC,
"DavrConnectionExist. AuthUserName = %ws, AuthUserNameLen = %d\n",
MatchedPointer->AuthUserName,MatchedPointer->AuthUserNameLength));
dwErr = WN_SUCCESS;
goto EXIT_THE_FUNCTION;
EXIT_THE_FUNCTION:
if (ResAcquired) {
RtlReleaseResource( &(DavUseObject.TableResource) );
ResAcquired = FALSE;
}
return dwErr;
}
DWORD
DavrGetUser(
IN handle_t dav_binding_h,
IN OUT LPDWORD UserNameLength,
IN LPWSTR ConnectionName,
OUT LPWSTR UserName
)
/*++
Routine Description:
This function returns the UserName that mapped a given connection.
Arguments:
dav_binding_h - The explicit RPC binding handle.
UserNameLength - The length of the UserName buffer.
ConnectionName - Supplies the local DOS device, or the remote resource name.
UserName - The buffer where the UserName will be filled.
Return Value:
WN_SUCCESS - If connection exist. OR
Should return the following values under varying conditions so the
mapping on the provider side works :
ERROR_UNEXP_NET_ERR
ERROR_INVALID_HANDLE
ERROR_INVALID_PARAMETER
ERROR_REM_NOT_LIST - Invalid remote resource name.
ERROR_BAD_DEVICE - Invalid local DOS device name.
ERROR_NOT_FOUND - Connection could not be found.
NERR_UserNotFound - Connection is found, but User name not found in it.
--*/
{
DWORD dwErr = NO_ERROR;
WCHAR localDrive[3]; // For L"X:\0".
PWCHAR NameToQuery = NULL;
LUID LogonId; // Logon Id of user.
ULONG Index = 0;
PDAV_USE_ENTRY UseList = NULL, MatchedPointer = NULL, BackPointer = NULL;
BOOL ResAcquired = FALSE;
DavPrint((DEBUG_MISC,
"DavrGetUser: Entered. UserNameLength= %d, ConnectionName = %ws\n",
*UserNameLength, ConnectionName));
//
// Initialize the LogonId.
//
LogonId.LowPart = 0;
LogonId.HighPart = 0;
if( (ConnectionName == NULL || UserNameLength == NULL ||
(UserName == NULL && *UserNameLength != 0)) ) {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// The ConnectionName could be local or remote.
//
if ( ConnectionName[0] != L'\\' ) {
if(ConnectionName[1] != L':') {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// Check the local name to ensure that it's "X:" syntax.
//
dwErr = DavCheckLocalName( ConnectionName, &localDrive[0] );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrGetUser/DavCheckLocalName: dwErr = %08lx\n",
dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavrGetUser: LocalDrive = %ws\n",
&localDrive[0]));
NameToQuery = &localDrive[0];
} else {
//
// This is a remote name.
//
NameToQuery = ConnectionName;
}
//
// Impersonate caller and get the LogonId.
//
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrGetUser/DavImpersonateAndGetLogonId: "
"dwErr = %x\n", dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC,
"DavrGetUser: LogonId = %d %d\n",
LogonId.HighPart, LogonId.LowPart));
//
// Lock the Dav Use Table while looking for entry to delete.
//
if ( !RtlAcquireResourceExclusive( &(DavUseObject.TableResource), TRUE ) ) {
DavPrint((DEBUG_ERRORS,
"DavrGetUser/RtlAcquireResourceExclusive.\n"));
dwErr = NERR_InternalError;
goto EXIT_THE_FUNCTION;
}
ResAcquired = TRUE;
//
// See if the use entry is an explicit connection.
//
dwErr = DavGetUserEntry( &(DavUseObject), &(LogonId), &(Index), FALSE );
if (dwErr != NERR_Success) {
UseList = NULL;
dwErr = ERROR_NOT_FOUND;
goto EXIT_THE_FUNCTION;
}
UseList = (PDAV_USE_ENTRY) DavUseObject.Table[Index].List;
dwErr = DavFindUse(&LogonId,
UseList,
NameToQuery,
&MatchedPointer,
&BackPointer);
if (dwErr != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavrGetUser/DavFindUse: dwErr = %08lx\n", dwErr));
dwErr = ERROR_NOT_FOUND;
goto EXIT_THE_FUNCTION;
}
ASSERT(MatchedPointer != NULL);
DavPrint((DEBUG_MISC,
"DavrGetUser. AuthUserName=%ws, AuthUserNameLen=%d\n",
MatchedPointer->AuthUserName,MatchedPointer->AuthUserNameLength));
if ( MatchedPointer->AuthUserName == NULL ) {
//
// We are always storing a user name when adding connection. So we should always
// have a user name.
//
ASSERT(FALSE);
dwErr = NERR_UserNotFound;
DavPrint((DEBUG_ERRORS, "DavrGetUser:MatchedPointer->AuthUserName == NULL."
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
if( (*UserNameLength < MatchedPointer->AuthUserNameLength) ) {
dwErr = ERROR_INSUFFICIENT_BUFFER;
DavPrint((DEBUG_ERRORS,
"DavrGetUser: RequiredLength = %d, SuppliedName = %d. dwErr = %08lx\n",
MatchedPointer->AuthUserNameLength, *UserNameLength, dwErr));
*UserNameLength = MatchedPointer->AuthUserNameLength;
goto EXIT_THE_FUNCTION;
}
wcscpy(UserName, MatchedPointer->AuthUserName);
DavPrint((DEBUG_MISC,
"DavrGetUser: Successful. ConnectionName = "
"0x%x, %ws, UserNameLength=0x%x, *UserNameLength = %d,"
"UserName=0x%x, %ws\n", ConnectionName, ConnectionName,
UserNameLength, *UserNameLength,
UserName, UserName));
dwErr = WN_SUCCESS;
goto EXIT_THE_FUNCTION;
EXIT_THE_FUNCTION:
if (ResAcquired) {
RtlReleaseResource( &(DavUseObject.TableResource) );
ResAcquired = FALSE;
}
return dwErr;
}
DWORD
DavrDoesServerDoDav(
IN handle_t dav_binding_h,
IN LPWSTR lpServerName,
OUT PBOOLEAN DoesDav
)
/*++
Routine Description:
This routine checks whether the ServerName passed in is a valid DAV server.
Arguments:
dav_binding_h - The explicit RPC binding handle.
lpServerName - The name of the Server to query.
DoesDav - TRUE if a the server is a DAV server, FALSE otherwise.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD WStatus = NO_ERROR;
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
BOOL ReturnVal = FALSE, fIsDav = FALSE;
PWCHAR DataBuff = NULL;
DWORD DataBuffBytes = 0;
ULONG TahoeCustomHeaderLength = 0, OfficeCustomHeaderLength = 0;
WCHAR DavCustomBuffer[100];
BOOL bStatus = TRUE, revert = FALSE;
DavPrint((DEBUG_MISC, "DavrDoesServerDoDav: ServerName = %ws\n", lpServerName));
//
// Go through the NonDAVServerList to see if we have an entry for this
// server. If we do then we need not go on the network. We can fail this
// call (return server does not speak DAV) right away. Before we call
// DavCheckTheNonDAVServerList, we need to take the lock that synchronizes
// it (NonDAVServerListLock).
//
EnterCriticalSection( &(NonDAVServerListLock) );
ReturnVal = DavCheckTheNonDAVServerList(lpServerName);
LeaveCriticalSection( &(NonDAVServerListLock) );
//
// If we found an entry (implies ReturnVal == TRUE) for this ServerName in
// the list of servers that do not speak DAV, we return from here.
//
if (ReturnVal) {
WStatus = NO_ERROR;
*DoesDav = FALSE;
return WStatus;
}
WStatus = DavImpersonateClient();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/DavImpersonateClient: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
DavConnHandle = InternetConnectW(ISyncHandle,
lpServerName,
INTERNET_DEFAULT_HTTP_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0);
if (DavConnHandle == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/InternetConnectW. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
bStatus = DavHttpOpenRequestW(DavConnHandle,
L"OPTIONS",
L"/",
L"HTTP/1.1",
NULL,
NULL,
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES,
0,
L"DavrDoesServerDoDav",
&DavOpenHandle);
if(bStatus == FALSE) {
WStatus = GetLastError();
goto EXIT_THE_FUNCTION;
}
if (DavOpenHandle == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpOpenRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We need to add the header "translate:f" to tell IIS that it should
// allow the user to excecute this VERB on the specified path which it
// would not allow (in some cases) otherwise. Finally, there is a special
// flag in the metabase to allow for uploading of "dangerous" content
// (anything that can be run on the server). This is the ScriptSourceAccess
// flag in the UI or the AccessSource flag in the metabase. You will need
// to set this bit to true as well as correct NT ACLs in order to be able
// to upload .exes or anything executable.
//
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
L"translate: f\n",
-1L,
HTTP_ADDREQ_FLAG_ADD |
HTTP_ADDREQ_FLAG_REPLACE );
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpAddRequestHeadersW. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
RESEND_THE_REQUEST:
ReturnVal = HttpSendRequestExW(DavOpenHandle, NULL, NULL, HSR_SYNC, 0);
if (!ReturnVal) {
WStatus = GetLastError();
//
// If we fail with ERROR_INTERNET_NAME_NOT_RESOLVED, we make the
// following call so that WinInet picks up the correct proxy settings
// if they have changed. This is because we do call InternetOpen
// (to create a global handle from which every other handle is derived)
// when the service starts and this could be before the user logon
// happpens. In such a case the HKCU would not have been initialized
// and WinInet wouldn't get the correct proxy settings.
//
if (WStatus == ERROR_INTERNET_NAME_NOT_RESOLVED) {
InternetSetOptionW(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
}
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpSendRequestExW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
ReturnVal = HttpEndRequestW(DavOpenHandle, NULL, HSR_SYNC, 0);
if (!ReturnVal) {
WStatus = GetLastError();
//
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then WinInet
// is trying to authenticate itself with the server. If we get back
// ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION, WinInet is expecting us to
// confirm that the redirect needs to be followed. In these scenarios,
// we need to repeat the HttpSend and HttpEnd request calls.
//
if (WStatus == ERROR_INTERNET_FORCE_RETRY || WStatus == ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION) {
goto RESEND_THE_REQUEST;
}
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpEndRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
WStatus = DavQueryAndParseResponse(DavOpenHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/DavQueryAndParseResponse: WStatus = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We read the value of AcceptOfficeAndTahoeServers from the registry when
// the WebClient service starts up. If this is set to 0, it means that we
// should be rejecting OfficeWebServers, Tahoe servers and the shares on
// these servers even though they speak DAV. We do this since WebFolders
// needs to claim this name and Shell will only call into WebFolders if the
// DAV Redir fails. If this value is non-zero, we accept all servers that
// speak DAV.
//
//
if (AcceptOfficeAndTahoeServers == 0) {
//
// Figure out if this is an OFFICE Web Server. If it is then the response
// will have an entry "MicrosoftOfficeWebServer: ", in the header.
// If this is an OFFICE share then we should not claim it since the user
// actually intends to use the OFFICE specific features in Shell.
//
RtlZeroMemory(DavCustomBuffer, sizeof(DavCustomBuffer));
wcscpy(DavCustomBuffer, DavOfficeCustomHeader);
OfficeCustomHeaderLength = ( sizeof(DavCustomBuffer) / sizeof(WCHAR) );
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_CUSTOM,
(PVOID)DavCustomBuffer,
&(OfficeCustomHeaderLength),
NULL);
if ( !ReturnVal ) {
WStatus = GetLastError();
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
NTSTATUS NtStatus;
//
// First, covert wininet error to NtStatus.
//
NtStatus = DavMapErrorToNtStatus(WStatus);
//
// Now, convert NtStatus to a win32 error.
//
WStatus = RtlNtStatusToDosError(NtStatus);
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpQueryInfoW(1): Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
} else {
WStatus = ERROR_SUCCESS;
DavPrint((DEBUG_MISC, "DavrDoesServerDoDav: NOT OFFICE Web Server\n"));
}
} else {
DavPrint((DEBUG_MISC, "DavrDoesServerDoDav: OFFICE Web Server\n"));
WStatus = ERROR_SUCCESS;
*DoesDav = FALSE;
goto EXIT_THE_FUNCTION;
}
//
// Figure out if this is a TAHOE server. If it is then the response will
// have an entry "MicrosoftTahoeServer: ", in the header. If this is a
// TAHOE server then we should not claim it since the user actually
// intends to use the TAHOE specific features in Rosebud.
//
RtlZeroMemory(DavCustomBuffer, sizeof(DavCustomBuffer));
wcscpy(DavCustomBuffer, DavTahoeCustomHeader);
TahoeCustomHeaderLength = ( sizeof(DavCustomBuffer) / sizeof(WCHAR) );
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_CUSTOM,
(PVOID)DavCustomBuffer,
&(TahoeCustomHeaderLength),
NULL);
if ( !ReturnVal ) {
WStatus = GetLastError();
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
NTSTATUS NtStatus;
//
// First, covert wininet error to NtStatus.
//
NtStatus = DavMapErrorToNtStatus(WStatus);
//
// Now, convert NtStatus to a win32 error.
//
WStatus = RtlNtStatusToDosError(NtStatus);
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpQueryInfoW(2): Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
} else {
WStatus = ERROR_SUCCESS;
DavPrint((DEBUG_MISC, "DavrDoesServerDoDav: NOT TAHOE Server\n"));
}
} else {
DavPrint((DEBUG_MISC, "DavrDoesServerDoDav: TAHOE Server\n"));
WStatus = ERROR_SUCCESS;
*DoesDav = FALSE;
goto EXIT_THE_FUNCTION;
}
}
//
// This is NOT a TAHOE server nor an OFFICE Web Server. We go ahead and
// query some other stuff from the header to make sure that this is a
// DAV server.
//
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_RAW_HEADERS_CRLF,
DataBuff,
&(DataBuffBytes),
NULL);
if (!ReturnVal) {
WStatus = GetLastError();
if (WStatus != ERROR_INSUFFICIENT_BUFFER) {
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpQueryInfo(3). Error Val = "
"%d\n", WStatus));
goto EXIT_THE_FUNCTION;
} else {
DavPrint((DEBUG_MISC,
"DavrDoesServerDoDav: HttpQueryInfo: Need Buff.\n"));
}
}
//
// Allocate memory for copying the header.
//
DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, DataBuffBytes);
if (DataBuff == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/LocalAlloc. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_RAW_HEADERS_CRLF,
DataBuff,
&(DataBuffBytes),
NULL);
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/HttpQueryInfo(4). Error Val = "
"%d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Check to see whether this server is a DAV server, Http Server etc.
//
DavObtainServerProperties(DataBuff, NULL, NULL, &fIsDav);
//
// NB!!!
//
*DoesDav = (BOOLEAN)fIsDav;
WStatus = NO_ERROR;
EXIT_THE_FUNCTION:
if (DavOpenHandle) {
InternetCloseHandle(DavOpenHandle);
}
if (DavConnHandle) {
InternetCloseHandle(DavConnHandle);
}
if (revert) {
DWORD RStatus;
RStatus = DavRevertToSelf();
if (RStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrDoesServerDoDav/DavRevertToSelf: RStatus = %08lx\n",
RStatus));
}
}
if (DataBuff) {
LocalFree(DataBuff);
}
//
// If this Server is not a DAV server then we need to add it to the
// NonDAVServerList to enable -ve caching. If WStatus is ERROR_ACCESS_DENIED
// or ERROR_ LOGON_FAILURE, then we failed since the credentials were not
// correct. That doesn't mean that this server is not a DAV server and hence
// we don't add it to the NonDAVServerList.
//
if ( (WStatus != ERROR_SUCCESS && WStatus != ERROR_ACCESS_DENIED && WStatus != ERROR_LOGON_FAILURE) ||
(WStatus == ERROR_SUCCESS && *DoesDav == FALSE) ) {
PNON_DAV_SERVER_ENTRY NonDavServerEntry = NULL;
NonDavServerEntry = LocalAlloc(LPTR, sizeof(NON_DAV_SERVER_ENTRY));
if (NonDavServerEntry != NULL) {
NonDavServerEntry->ServerName = LocalAlloc( LPTR, ((1 + wcslen(lpServerName)) * sizeof(WCHAR)) );
if (NonDavServerEntry->ServerName != NULL) {
wcscpy(NonDavServerEntry->ServerName, lpServerName);
NonDavServerEntry->TimeValueInSec = time(NULL);
EnterCriticalSection( &(NonDAVServerListLock) );
InsertHeadList( &(NonDAVServerList), &(NonDavServerEntry->listEntry) );
LeaveCriticalSection( &(NonDAVServerListLock) );
} else {
LocalFree(NonDavServerEntry);
NonDavServerEntry = NULL;
}
}
}
return WStatus;
}
DWORD
DavrIsValidShare(
IN handle_t dav_binding_h,
IN PWCHAR ServerName,
IN PWCHAR ShareName,
OUT PBOOLEAN ValidShare
)
/*++
Routine Description:
This routine checks whether the ShareName is a valid share of the server
ServerName.
Arguments:
dav_binding_h - The explicit RPC binding handle.
ServerName - The name of the Server.
ShareName - The share whose validity has to be checked.
ValidShare - TRUE if a the share is valid, FALSE otherwise.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD WStatus = ERROR_SUCCESS;
BOOL CricSec = FALSE, doesServerExist = FALSE, ReturnVal = FALSE;
PDAV_SERVER_SHARE_ENTRY ServerShareEntry = NULL;
DWORD TotalLength = 0;
PLIST_ENTRY listEntry = NULL;
PDAV_FILE_ATTRIBUTES ShareEntry = NULL;
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
BOOL EnCriSec = FALSE;
PPER_USER_ENTRY PerUserEntry = NULL;
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
LUID LogonId;
BOOL bStatus = TRUE, revert = FALSE;
WCHAR DavCustomBuffer[100];
ULONG TahoeCustomHeaderLength = 0, OfficeCustomHeaderLength = 0;
DavPrint((DEBUG_MISC,
"DavrIsValidShare: ServerName = %ws, ShareName = %ws\n",
ServerName, ShareName));
//
// Check the ServerShareTable to see if we have an entry for this
// server. Need to take a lock on the table before doing the check.
//
EnterCriticalSection( &(ServerShareTableLock) );
CricSec = TRUE;
doesServerExist = DavIsServerInServerShareTable(ServerName, &(ServerShareEntry));
if (doesServerExist) {
DavPrint((DEBUG_MISC, "DavrIsValidShare: doesServerExist = TRUE\n"));
//
// If the server entry exists, but is very old, we need to discard the
// list of shares that are hanging of this entry since they could have
// changed.
//
if (ServerShareEntry->DavShareList) {
if ( ( time(NULL) - ServerShareEntry->TimeValueInSec ) > 6000 ) {
DavPrint((DEBUG_MISC, "DavrIsValidShare: ServerEntry is OLD\n"));
//
// We need to go to the server again.
//
DavFinalizeFileAttributesList(ServerShareEntry->DavShareList, TRUE);
ServerShareEntry->DavShareList = NULL;
}
}
}
//
// If we have an entry for this server and the share list is not NULL
// then we will check thru this shares list. If we don't find the share,
// then we will do a PROPFIND against this share name and figure out if it
// exists on the server. This is because the server may NOT support the
// enumeration of shares at the root level OR a share might not exist in
// this list since it has been recently created on the server.
//
if (ServerShareEntry != NULL && ServerShareEntry->DavShareList != NULL ) {
DavPrint((DEBUG_MISC, "DavrIsValidShare: Loop the share list\n"));
//
// We need to hold onto the critical section while looping through the
// list of shares.
//
listEntry = &(ServerShareEntry->DavShareList->NextEntry);
do {
ShareEntry = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
DavPrint((DEBUG_MISC,
"DavrIsValidShare: FileName = %ws\n", ShareEntry->FileName));
//
// We are not interested in files.
//
if (ShareEntry->isCollection == FALSE) {
listEntry = listEntry->Flink;
continue;
}
//
// This is a share. See if it is the same as the share passed in.
// The comparison should be case insensitive.
//
if ( _wcsicmp(ShareEntry->FileName, ShareName) == 0 ) {
DavPrint((DEBUG_MISC,
"DavrIsValidShare: ShareName = %ws. Found!!\n", ShareName));
*ValidShare = TRUE;
WStatus = NO_ERROR;
goto EXIT_THE_FUNCTION;
}
listEntry = listEntry->Flink;
} while ( listEntry != &(ServerShareEntry->DavShareList->NextEntry) );
//
// If we come here, it implies that we are done looking at all the shares
// and a match was not found.
//
*ValidShare = FALSE;
WStatus = NO_ERROR;
} else {
*ValidShare = FALSE;
WStatus = NO_ERROR;
}
ASSERT(*ValidShare == FALSE);
ASSERT(WStatus == NO_ERROR);
//
// If we don't have an entry for this server or is the ShareList is NULL or
// if we don't find this share in the shareList of this server, then we do
// a PROPFIND against this share name and figure out if it exists on the
// server.
//
//
// We need to leave the CriticalSection now since we are going to go
// over the network to figure out if a share by this name exists on the
// server. Since we are finding this out, we can allow other threads
// to come and look at the ServerShareTable.
//
LeaveCriticalSection( &(ServerShareTableLock) );
CricSec = FALSE;
if ( *ValidShare == FALSE && WStatus == NO_ERROR ) {
DavPrint((DEBUG_MISC, "DavrIsValidShare: Send PROPFIND\n"));
//
// Impersonate caller and get the LogonId. We need this to figure out
// whether we need to attach a cookie later on.
//
WStatus = DavImpersonateAndGetLogonId( &(LogonId) );
if (WStatus != NOERROR) {
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/DavImpersonateAndGetLogonId: "
"WStatus = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We need to impersonate the client since we are calling into WinInet.
//
WStatus = DavImpersonateClient();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/DavImpersonateClient: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
DavConnHandle = InternetConnectW(ISyncHandle,
ServerName,
INTERNET_DEFAULT_HTTP_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0);
if (DavConnHandle == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/InternetConnectW. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// To find out if this is a valid share, we issue a PROPFIND against
// this ShareName.
//
bStatus = DavHttpOpenRequestW(DavConnHandle,
L"PROPFIND",
ShareName,
L"HTTP/1.1",
NULL,
NULL,
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES,
0,
L"DavIsValidShare",
&DavOpenHandle);
if(bStatus == FALSE) {
WStatus = GetLastError();
goto EXIT_THE_FUNCTION;
}
if (DavOpenHandle == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpOpenRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We need to figure out if we need to add any cookies for the MSN
// scenario.
//
EnterCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = TRUE;
ReturnVal = DavIsThisServerInTheTable(ServerName, &(ServerHashEntry));
if (ReturnVal) {
ASSERT(ServerHashEntry != NULL);
ReturnVal = DavDoesUserEntryExist(ServerName,
ServerHashEntry->ServerID,
&(LogonId),
&(PerUserEntry),
&(ServerHashEntry));
if (ReturnVal) {
ASSERT(PerUserEntry != NULL);
if (PerUserEntry->Cookie != NULL) {
DavPrint((DEBUG_MISC, "DavrIsValidShare: Adding Cookie\n"));
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
PerUserEntry->Cookie,
-1L,
HTTP_ADDREQ_FLAG_ADD |
HTTP_ADDREQ_FLAG_REPLACE );
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpAddRequestHeadersW: "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
}
}
}
//
// We don't need to hold this critical section any longer.
//
if (EnCriSec) {
LeaveCriticalSection(&(HashServerEntryTableLock) );
EnCriSec = FALSE;
}
//
// Since all we need to do is figure out if this share exists, we set
// the depth header to 0.
//
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
L"Depth: 0\n",
-1L,
HTTP_ADDREQ_FLAG_ADD |
HTTP_ADDREQ_FLAG_REPLACE );
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpAddRequestHeadersW. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We need to add the header "translate:f" to tell IIS that it should
// allow the user to excecute this VERB on the specified path which it
// would not allow (in some cases) otherwise. Finally, there is a special
// flag in the metabase to allow for uploading of "dangerous" content
// (anything that can be run on the server). This is the ScriptSourceAccess
// flag in the UI or the AccessSource flag in the metabase. You will need
// to set this bit to true as well as correct NT ACLs in order to be able
// to upload .exes or anything executable.
//
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
L"translate: f\n",
-1L,
HTTP_ADDREQ_FLAG_ADD |
HTTP_ADDREQ_FLAG_REPLACE );
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpAddRequestHeadersW. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
RESEND_THE_REQUEST:
ReturnVal = HttpSendRequestExW(DavOpenHandle, NULL, NULL, HSR_SYNC, 0);
if (!ReturnVal) {
WStatus = GetLastError();
//
// If we fail with ERROR_INTERNET_NAME_NOT_RESOLVED, we make the
// following call so that WinInet picks up the correct proxy settings
// if they have changed. This is because we do call InternetOpen
// (to create a global handle from which every other handle is derived)
// when the service starts and this could be before the user logon
// happpens. In such a case the HKCU would not have been initialized
// and WinInet wouldn't get the correct proxy settings.
//
if (WStatus == ERROR_INTERNET_NAME_NOT_RESOLVED) {
InternetSetOptionW(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
}
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpSendRequestExW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
ReturnVal = HttpEndRequestW(DavOpenHandle, NULL, HSR_SYNC, 0);
if (!ReturnVal) {
WStatus = GetLastError();
//
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then WinInet
// is trying to authenticate itself with the server. If we get back
// ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION, WinInet is expecting us to
// confirm that the redirect needs to be followed. In these scenarios,
// we need to repeat the HttpSend and HttpEnd request calls.
//
if (WStatus == ERROR_INTERNET_FORCE_RETRY || WStatus == ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION) {
goto RESEND_THE_REQUEST;
}
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpEndRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Query the response the server sent to see if this share exists. We
// don't need to parse the XML response that gets returned for this
// PROPFIND request since all that we are interested in is to find out
// if this share exists, not what its properties are.
//
WStatus = DavQueryAndParseResponse(DavOpenHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpEndRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We read the value of AcceptOfficeAndTahoeServers from the registry when
// the WebClient service starts up. If this is set to 0, it means that we
// should be rejecting OfficeWebServers, Tahoe servers and the shares on
// these servers even though they speak DAV. We do this since WebFolders
// needs to claim this name and Shell will only call into WebFolders if the
// DAV Redir fails. If this value is non-zero, we accept all servers that
// speak DAV.
//
//
if (AcceptOfficeAndTahoeServers == 0) {
//
// Figure out if this is an OFFICE Web Share. If it is then the response
// will have an entry "MicrosoftOfficeWebServer: ", in the header.
// If this is an OFFICE share then we should not claim it since the user
// actually intends to use the OFFICE specific features in Shell.
//
RtlZeroMemory(DavCustomBuffer, sizeof(DavCustomBuffer));
wcscpy(DavCustomBuffer, DavOfficeCustomHeader);
OfficeCustomHeaderLength = ( sizeof(DavCustomBuffer) / sizeof(WCHAR) );
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_CUSTOM,
(PVOID)DavCustomBuffer,
&(OfficeCustomHeaderLength),
NULL);
if ( !ReturnVal ) {
WStatus = GetLastError();
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
NTSTATUS NtStatus;
//
// First, covert wininet error to NtStatus.
//
NtStatus = DavMapErrorToNtStatus(WStatus);
//
// Now, convert NtStatus to a win32 error.
//
WStatus = RtlNtStatusToDosError(NtStatus);
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpQueryInfoW(1): Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
} else {
WStatus = ERROR_SUCCESS;
DavPrint((DEBUG_MISC, "DavrIsValidShare: NOT OFFICE Web Share\n"));
}
} else {
DavPrint((DEBUG_MISC, "DavrIsValidShare: OFFICE Web Share\n"));
WStatus = ERROR_BAD_NET_NAME;
goto EXIT_THE_FUNCTION;
}
//
// Figure out if this is a TAHOE share. If it is then the response will
// have an entry "MicrosoftTahoeServer: ", in the header. If this is a
// TAHOE server then we should not claim it since the user actually
// intends to use the TAHOE specific features in Rosebud.
//
RtlZeroMemory(DavCustomBuffer, sizeof(DavCustomBuffer));
wcscpy(DavCustomBuffer, DavTahoeCustomHeader);
TahoeCustomHeaderLength = ( sizeof(DavCustomBuffer) / sizeof(WCHAR) );
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_CUSTOM,
(PVOID)DavCustomBuffer,
&(TahoeCustomHeaderLength),
NULL);
if ( !ReturnVal ) {
WStatus = GetLastError();
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
NTSTATUS NtStatus;
//
// First, covert wininet error to NtStatus.
//
NtStatus = DavMapErrorToNtStatus(WStatus);
//
// Now, convert NtStatus to a win32 error.
//
WStatus = RtlNtStatusToDosError(NtStatus);
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/HttpQueryInfoW(2): Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
} else {
WStatus = ERROR_SUCCESS;
DavPrint((DEBUG_MISC, "DavrIsValidShare: NOT TAHOE Share\n"));
}
} else {
DavPrint((DEBUG_MISC, "DavrIsValidShare: TAHOE Share\n"));
WStatus = ERROR_BAD_NET_NAME;
goto EXIT_THE_FUNCTION;
}
}
//
// If we've come here, it implies that this share exists on the server.
//
DavPrint((DEBUG_MISC, "DavrIsValidShare: ShareName = %ws. Exists!!\n", ShareName));
*ValidShare = TRUE;
WStatus = NO_ERROR;
}
EXIT_THE_FUNCTION:
//
// If we are returning failure, we set ValidShare to FALSE.
//
if (WStatus != NO_ERROR) {
*ValidShare = FALSE;
}
if (CricSec) {
LeaveCriticalSection( &(ServerShareTableLock) );
CricSec = FALSE;
}
if (EnCriSec) {
LeaveCriticalSection(&(HashServerEntryTableLock) );
EnCriSec = FALSE;
}
if (DavOpenHandle) {
InternetCloseHandle(DavOpenHandle);
DavOpenHandle = NULL;
}
if (DavConnHandle) {
InternetCloseHandle(DavConnHandle);
DavConnHandle = NULL;
}
if (revert) {
DWORD RStatus;
RStatus = DavRevertToSelf();
if (RStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrIsValidShare/DavRevertToSelf: RStatus = %08lx\n",
RStatus));
}
}
return WStatus;
}
DWORD
DavrGetConnection(
IN handle_t dav_binding_h,
IN LPWSTR lpLocalName,
OUT LPWSTR lpRemoteName,
IN OUT LPDWORD lpBufferSize,
OUT PBOOLEAN Connected
)
/*++
Routine Description:
This routine checks whether the LocalName passed in is mapped to a network
drive. If it is, then the RemoteName buffer is filled in with the remote
name the Local drive is mapped to.
Arguments:
dav_binding_h - The explicit RPC binding handle.
lpLocalName - The Local device name to be checked.
lpRemoteName - Buffer to fill in the remote name if the local name is
connected.
lpBufferSize - Size of the RemoteName buffer.
Connected - TRUE if a connection exists, FALSE otherwise.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD dwErr = NO_ERROR;
BOOLEAN ResAcquired = FALSE;
DWORD index = 0;
PDAV_USE_ENTRY DavUseEntry = NULL;
LUID LogonId; // Logon Id of user.
DavPrint((DEBUG_MISC, "DavrGetConnection: lpLocalName = %ws\n", lpLocalName));
//
// Impersonate caller and get the LogonId.
//
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrGetConnection/DavImpersonateAndGetLogonId: "
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC,
"DavrGetConnection: LogonId = %d %d\n",
LogonId.HighPart, LogonId.LowPart));
//
// Lock the Dav Use Table while looking for entry to query.
//
if ( !RtlAcquireResourceExclusive( &(DavUseObject.TableResource), TRUE ) ) {
DavPrint((DEBUG_ERRORS,
"DavrGetConnection/RtlAcquireResourceExclusive.\n"));
dwErr = NERR_InternalError;
goto EXIT_THE_FUNCTION;
}
ResAcquired = TRUE;
//
// See if the use entry is an explicit connection.
//
dwErr = DavGetUserEntry( &(DavUseObject), &(LogonId), &(index), FALSE );
if (dwErr != NERR_Success) {
dwErr = ERROR_NOT_FOUND;
goto EXIT_THE_FUNCTION;
}
DavUseEntry = (PDAV_USE_ENTRY) DavUseObject.Table[index].List;
while (DavUseEntry != NULL) {
//
// If this connection has no local name, then we just continue.
//
if (DavUseEntry->Local == NULL) {
DavUseEntry = DavUseEntry->Next;
continue;
}
if ( _wcsicmp(DavUseEntry->Local, lpLocalName) == 0 ) {
//
// We found a remote name that is associated with a LocalName
// on this machine.
//
if(Connected != NULL) {
*Connected = TRUE;
}
if(lpBufferSize == NULL) {
dwErr = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
//
// We now need to see if the Buffer supplied is large enough to
// hold the Remote name. If it is not, then we return
// WN_MORE_DATA to the calling app.
//
if ( (lpRemoteName == NULL ) ||
((DavUseEntry->Remote->UncNameLength + 1) > *lpBufferSize) ) {
DavPrint((DEBUG_ERRORS, "DavrGetConnection: WN_MORE_DATA returned\n"));
*lpBufferSize = (DavUseEntry->Remote->UncNameLength + 1);
dwErr = WN_MORE_DATA;
goto EXIT_THE_FUNCTION;
} else {
//
// The Buffer is large enough to copy the RemoteName. Copy
// it and return SUCCESS to the caller.
//
wcscpy(lpRemoteName, (LPWSTR)DavUseEntry->Remote->UncName);
lpRemoteName[DavUseEntry->Remote->UncNameLength] = L'\0';
DavPrint((DEBUG_MISC,
"DavrGetConnection: FOUND!!! LocalName = %ws,"
" RemoteName = %ws\n", lpLocalName, lpRemoteName));
dwErr = NO_ERROR;
goto EXIT_THE_FUNCTION;
}
}
DavUseEntry = DavUseEntry->Next;
} // end while
//
// The use is not found after while loop.
//
dwErr = ERROR_NOT_FOUND;
goto EXIT_THE_FUNCTION;
EXIT_THE_FUNCTION:
if (ResAcquired) {
RtlReleaseResource( &(DavUseObject.TableResource) );
}
return dwErr;
}
DWORD
DavrEnumServers(
IN handle_t dav_binding_h,
IN LPDWORD EntryIndex,
IN OUT LPDWORD ServerNameMaxLen,
OUT PWCHAR ServerName,
OUT PBOOLEAN Done
)
/*++
Routine Description:
This routine fills in the Server names on accessed on this machine. This is called by
NPEnumResource on the client when it is enumerating all the DAV servers. One server
is returned per call.
Arguments:
dav_binding_h - The explicit RPC binding handle.
EntryIndex - The index of the entry from where we start. On return this
contains the index of the entry from where one begins
enumerating next time if necessary.
Note: Do not alter value of this entry outside this function - else it may result in
unexpected behavior of enumeration.
ServerName - The server being returned.
Done - Are we done with all the entries.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD WStatus = NO_ERROR;
BOOL CricSec = FALSE, fPresent = FALSE, fDone = FALSE;
PHASH_SERVER_ENTRY SHEntry = NULL;
DWORD IndexCount = 0;
PLIST_ENTRY listEntry = NULL;
PWCHAR EntryServerName = NULL;
DWORD EntryServerNameLen = 0;
DWORD LookFromServerHashId = 0;
DWORD IndexOfEntryInHashList = 0;
DWORD EntryCount = 0;
DavPrint((DEBUG_MISC,
"DavrEnumServers: *EntryIndex = %u, *ServerNameMaxLen = %d,"
" *Done = %d\n", *EntryIndex, *ServerNameMaxLen, *Done));
if ( (EntryIndex == NULL || ServerNameMaxLen == NULL ||
Done == NULL || ( ServerName == NULL && *ServerNameMaxLen != 0)) ) {
WStatus = ERROR_INVALID_PARAMETER;
DavPrint((DEBUG_ERRORS, "DavrEnumServers : Invalid parameters\n"));
goto EXIT_THE_FUNCTION;
}
//
// To enumerate the servers - we have to go thru whole hash table.
// This function can be called many time in one single enumeration operation,
// each time with different EntryIndex to say: return next entry.
//
// EntryIndex is used to indicate entry number to be returned from the whole list
// of enumeration. It can start from 0: 0,1,2,...
// In such case, to return any next server entry, we have to go thru all entries in
// hash table until the desired entry number is reached.
//
// To optimize here, we are using special coding of variable EntryIndex. In
// this case it won't follow standard sequence 0,1,2,...
// Rather:
// EntryIndex = SERVER_TABLE_SIZE * LookFromServerHashId +
// IndexOfEntryInHashList
// => Start looking from HashList of ServerHashId (LookFromServerHashId ), and return
// entry number (IndexOfEntryInHashList) in this list. If this list don't
// contain any entry of this number, then go to next HashList and so on...
//
// For first time, EntryIndex is 0 => Start from HashId = 0 in table,
// from first entry of it.
//
// Now IndexOfEntryInHashList = [0,...,MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID]
//
// So:
// LookFromServerHashId = EntryIndex / MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID
// IndexOfEntryInHashList =
// EntryIndex % MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID
//
// We are defining:
// MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID = Max(DWORD)/ SERVER_TABLE_SIZE
// Where Max(DWORD) = Maximum value of DWORD => (DWORD)(-1)
//
fPresent = FALSE;
fDone = FALSE;
*Done = FALSE;
EntryCount = 0;
if (ServerIDCount == 0) {
*Done = TRUE;
WStatus = NO_ERROR;
DavPrint((DEBUG_MISC, "DavrEnumServers : No server entry in hash table\n"));
goto EXIT_THE_FUNCTION;
}
LookFromServerHashId = (*EntryIndex) / MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID;
IndexOfEntryInHashList = (*EntryIndex) % MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID;
if (LookFromServerHashId >= SERVER_TABLE_SIZE) {
WStatus = NO_ERROR;
*Done = TRUE;
DavPrint((DEBUG_MISC,
"DavrEnumServers : *EntryIndex OUTSIDE HashTable = %u\n",
*EntryIndex));
goto EXIT_THE_FUNCTION;
}
EnterCriticalSection( &(HashServerEntryTableLock) );
CricSec = TRUE;
while (fDone == FALSE) {
//
// Get Server entry of index = IndexOfEntryInHashList in the
// current Servers HashList.
//
IndexCount = 0;
listEntry = ServerHashTable[LookFromServerHashId].Flink;
while( listEntry != NULL &&
listEntry != &(ServerHashTable[LookFromServerHashId]) &&
IndexCount != IndexOfEntryInHashList ) {
listEntry = listEntry->Flink;
IndexCount++;
EntryCount++;
}
if ( listEntry == NULL || listEntry == &(ServerHashTable[LookFromServerHashId]) ) {
LookFromServerHashId ++;
IndexOfEntryInHashList = 0;
if (EntryCount >= ServerIDCount || LookFromServerHashId >= SERVER_TABLE_SIZE) {
fDone = TRUE;
}
} else {
SHEntry = CONTAINING_RECORD(listEntry, HASH_SERVER_ENTRY, ServerListEntry);
//
// Check that the entry found is of DAV server.
// If Yes, then return else move on to next entries.
//
if (SHEntry->isDavServer == TRUE) {
fDone = TRUE;
fPresent = TRUE;
} else {
LookFromServerHashId ++;
IndexOfEntryInHashList = 0;
if (LookFromServerHashId >= SERVER_TABLE_SIZE) {
fDone = TRUE;
}
}
}
}
//
// There could be no DAV servers accessed yet.
//
if (fPresent == FALSE) {
DavPrint((DEBUG_MISC, "DavrEnumServers : No Servers found\n"));
*Done = TRUE;
WStatus = NO_ERROR;
goto EXIT_THE_FUNCTION;
}
//
// Control comes here if a server of required *EntryIndex is found.
// i.e. fPresent = TRUE.
//
EntryServerName = SHEntry->ServerName;
EntryServerNameLen = 2 + wcslen(EntryServerName) + 1; // 2 for L"\\" before name.
ASSERT(EntryServerName != NULL);
if ( EntryServerNameLen > *ServerNameMaxLen) {
DavPrint((DEBUG_ERRORS,
"DavrEnumServers: ServerName = %ws, RequiredLen=%d, SuppliedLen=%d\n",
EntryServerName, EntryServerNameLen, *ServerNameMaxLen));
WStatus = ERROR_INSUFFICIENT_BUFFER;
*ServerNameMaxLen = EntryServerNameLen;
goto EXIT_THE_FUNCTION;
}
//
// We have to return UNC-server name.
//
wcscpy(ServerName, L"\\\\");
wcscat(ServerName, EntryServerName);
//
// We return one entry at a time.
//
*EntryIndex = ((LookFromServerHashId * MAX_NUMBER_OF_SERVER_ENTRIES_PER_HASH_ID) +
IndexOfEntryInHashList +
1); // for next entry.
DavPrint((DEBUG_MISC,
"DavrEnumServers: NextIndex = %u, ServerName = %ws\n",
*EntryIndex, ServerName));
WStatus = NO_ERROR;
goto EXIT_THE_FUNCTION;
EXIT_THE_FUNCTION:
if (CricSec) {
LeaveCriticalSection( &(HashServerEntryTableLock) );
CricSec = FALSE;
}
return WStatus;
}
DWORD
DavrEnumShares(
IN handle_t dav_binding_h,
IN LPDWORD EntryIndex,
IN PWCHAR ServerName,
OUT PWCHAR ShareName,
OUT PBOOLEAN Done
)
/*++
Routine Description:
This routine fills in the Share names on this server. This is called by
NPEnumResource on the client when it is enumerating all the DAV shares
on the server. One share is returned per call.
Arguments:
dav_binding_h - The explicit RPC binding handle.
EntryIndex - The index of the entry from where we start. On return this
contains the index of the entry from where one begins
enumerating next time if necessary.
LocalName - The Server whose shares are being enumerated.
ShareName - The share being returned.
Done - Are we done with all the entries.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD WStatus = NO_ERROR;
BOOL CricSec = FALSE, doesServerExist = FALSE;
PDAV_SERVER_SHARE_ENTRY ServerShareEntry = NULL;
DWORD TotalLength, IndexCount = 0;
PLIST_ENTRY listEntry = NULL;
PDAV_FILE_ATTRIBUTES ShareEntry;
DavPrint((DEBUG_MISC,
"DavrEnumShares: Index = %d, ServerName = %ws\n",
*EntryIndex, ServerName));
//
// Check the ServerShareTable to see if we have an entry for this
// server. Need to take a lock on the table before doing the check.
//
EnterCriticalSection( &(ServerShareTableLock) );
CricSec = TRUE;
//
// If we are starting the enumeration, we need to figure out the following:
// 1. The server exists.
// 2. If it does, whether the DavShareList is still valid.
//
if ( *EntryIndex == 0 ) {
doesServerExist = DavIsServerInServerShareTable(ServerName, &(ServerShareEntry));
if ( !doesServerExist ) {
//
// Create a new ServerShare entry for this server.
//
TotalLength = ( sizeof(DAV_SERVER_SHARE_ENTRY) +
( ( wcslen(ServerName) + 1 ) * sizeof(WCHAR) ) );
ServerShareEntry = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, TotalLength);
if (ServerShareEntry == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavrEnumShares/LocalAlloc. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Initialize the ServerShare entry and insert it into the global
// ServerShareEntry table.
//
DavInitializeAndInsertTheServerShareEntry(ServerShareEntry,
ServerName,
TotalLength);
} else {
//
// If the server entry exists, but is very old, we need to update it
// by going to the server again.
//
if ( ( time(NULL) - ServerShareEntry->TimeValueInSec ) > 6000 ) {
//
// We need to go to the server again.
//
DavFinalizeFileAttributesList(ServerShareEntry->DavShareList, TRUE);
ServerShareEntry->DavShareList = NULL;
}
}
} else {
doesServerExist = DavIsServerInServerShareTable(ServerName, &(ServerShareEntry));
if (!doesServerExist) {
ASSERT(FALSE);
WStatus = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
}
//
// If we don't have a list of shares, then we need to go to the server and
// get them.
//
if ( ServerShareEntry->DavShareList == NULL ) {
ASSERT(*EntryIndex == 0);
WStatus = DavGetShareListFromServer(ServerShareEntry);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavrEnumShares/DavGetShareListFromServer. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// There could be no shares exposed on the DAV server.
//
if (ServerShareEntry->NumOfShares == 0) {
DavPrint((DEBUG_MISC,
"DavrEnumShares: ServerName = %ws. No Shares\n", ServerName));
*Done = TRUE;
WStatus = NO_ERROR;
goto EXIT_THE_FUNCTION;
}
}
listEntry = &(ServerShareEntry->DavShareList->NextEntry);
do {
ShareEntry = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
//
// We are not interested in files and the share /.
//
if (ShareEntry->isCollection == FALSE ||
_wcsicmp(ShareEntry->FileName, L"/") == 0) {
listEntry = listEntry->Flink;
continue;
}
//
// We might have already returned a few share names.
//
if ( IndexCount < *EntryIndex ) {
IndexCount++;
listEntry = listEntry->Flink;
continue;
}
//
// We haven't sent this one. Copy the share name and return.
//
wcscpy(ShareName, ShareEntry->FileName);
//
// We return one entry at a time.
//
WStatus = NO_ERROR;
goto EXIT_THE_FUNCTION;
} while ( listEntry != &(ServerShareEntry->DavShareList->NextEntry) );
//
// If we come here, it implies that we are done returning all the entries.
//
*Done = TRUE;
WStatus = NO_ERROR;
EXIT_THE_FUNCTION:
if (CricSec) {
LeaveCriticalSection( &(ServerShareTableLock) );
}
return WStatus;
}
DWORD
DavrEnumNetUses(
IN handle_t dav_binding_h,
IN LPDWORD EntryIndex,
OUT PWCHAR LocalName,
OUT PWCHAR RemoteName,
OUT PBOOLEAN Done
)
/*++
Routine Description:
This routine fills in the Local and Remote names of a net use. This is
called by NPEnumResource on the client when it is enumerating all the net
uses on the machine.
Arguments:
dav_binding_h - The explicit RPC binding handle.
EntryIndex - The index of the entry from where we start. On return this
contains the index of the entry from where one begins
enumerating next time if necessary.
LocalName - The LocalName of a net use.
RemoteName - The RemoteName of a net use.
Done - Are we done with all the entries.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD dwErr = NO_ERROR;
DWORD BeginIndex, IndexCount = 0, index;
BOOLEAN ResAcquired = FALSE;
PDAV_USE_ENTRY DavUseEntry = NULL;
LUID LogonId;
BeginIndex = *EntryIndex;
DavPrint((DEBUG_MISC, "DavrEnumNetUses: BeginIndex = %d\n", BeginIndex));
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavrEnumNetUses/DavImpersonateAndGetLogonId: "
"dwErr = %08lx\n", dwErr));
goto EXIT_THE_FUNCTION;
}
if ( !RtlAcquireResourceExclusive(&DavUseObject.TableResource, TRUE) ) {
dwErr = NERR_InternalError;
DavPrint((DEBUG_ERRORS,
"DavrEnumNetUses/RtlAcquireResourceExclusive: Internal Error.\n"));
goto EXIT_THE_FUNCTION;
}
ResAcquired = TRUE;
//
// Go through the DAV_USE_ENTRY list of all the users.
//
for (index = 0; index < DavUseObject.TableSize; index++) {
if ( !DavUseObject.Table[index].List ) {
continue;
}
if (!RtlEqualLuid(&(DavUseObject.Table[index].LogonId), &LogonId)) {
continue;
}
DavUseEntry = (PDAV_USE_ENTRY)DavUseObject.Table[index].List;
while (DavUseEntry) {
//
// We could have returned a few entries already. BeginIndex gives
// us the number of entries we have already returned. In this case,
// we should start from the first entry which was not returned.
//
if (IndexCount < BeginIndex) {
IndexCount++;
DavUseEntry = DavUseEntry->Next;
continue;
}
//
// The LocalName may or may not exist. The LocalNamelength already
// includes the extra 1 for the '\0' in the end.
//
if (DavUseEntry->Local) {
wcscpy(LocalName, DavUseEntry->Local);
DavPrint((DEBUG_MISC, "DavrEnumNetUses: Local= %ws\n", LocalName));
}
//
// Copy the RemoteName.
//
wcscpy(RemoteName, (LPWSTR)DavUseEntry->Remote->UncName);
DavPrint((DEBUG_MISC, "DavrEnumNetUses: Remote = %ws\n", RemoteName));
//
// We return one pair of Local and Remote names at a time.
//
dwErr = NO_ERROR;
goto EXIT_THE_FUNCTION;
} // end while
} // end for
//
// If we come here, it means that we are done sending all the net uses.
//
dwErr = NO_ERROR;
*Done = TRUE;
EXIT_THE_FUNCTION:
if (ResAcquired) {
RtlReleaseResource( &(DavUseObject.TableResource) );
}
return dwErr;
}
DWORD
DavCheckLocalName(
LPWSTR LocalName,
PWCHAR OutputLocalDeviceBuffer
)
/*++
Routine Description:
This only handles NULL, empty string, and L"X:" formats.
Arguments:
LocalName - Supplies the local device name to map to the created tree
connection. Only drive letter device names are accepted. (No
LPT or COM).
OutputLocalDeviceBuffer - The drive letter is copied into this and returned.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD LocalNameLength;
LocalNameLength = ( LocalName == NULL ) ? 0 : wcslen( LocalName );
if (LocalNameLength == 0) {
*OutputLocalDeviceBuffer = L'\0';
return ERROR_SUCCESS;
}
if (LocalNameLength != 2 || !iswalpha(*LocalName) || LocalName[1] != L':') {
return ERROR_BAD_DEVICE;
}
lstrcpyW( OutputLocalDeviceBuffer, LocalName );
_wcsupr( OutputLocalDeviceBuffer );
return ERROR_SUCCESS;
}
DWORD
DavCheckRemoteName(
IN LPWSTR LocalName OPTIONAL,
IN LPWSTR RemoteName,
OUT LPWSTR *OutputBuffer,
OUT LPDWORD OutputBufferLength OPTIONAL
)
/*++
Routine Description:
This routine validates and canonicalizes the supplied
UNC name. It can be of any length in the form of:
\\Server\Volume\Directory\Subdirectory
Arguments:
LocalName - Supplies the local device name. If it is NULL or empty, then
\\Server is an acceptable format for the UNC name.
RemoteName - Supplies the UNC name.
OutputBuffer - Receives a pointer to the canonicalized RemoteName.
OutputBufferLength - Receives the length of the canonicalized name in
number of characters, if specified.
Return Value:
NO_ERROR - RemoteName is valid.
WN_BAD_NETNAME - RemoteName is invalid.
--*/
{
DWORD RemoteNameLength;
DWORD i;
DWORD TokenLength;
LPWSTR TokenPtr = NULL;
BOOL fFirstToken = TRUE;
BOOL fLocalNamePresent = FALSE;
//
// If a LocalName was specified, we set fLocalNamePresent to TRUE.
//
if (LocalName) {
fLocalNamePresent = TRUE;
}
DavPrint((DEBUG_MISC, "DavCheckRemoteName: fLocalNamePresent = %d\n",
fLocalNamePresent));
//
// The remote name cannot be a NULL or an empty string.
//
if (RemoteName == NULL || *RemoteName == 0) {
return WN_BAD_NETNAME;
}
RemoteNameLength = wcslen(RemoteName);
//
// Must be at least \\x\y if local device name is specified. Otherwise it
// must be at least \\x.
//
if ( (RemoteNameLength < 5 && fLocalNamePresent) || (RemoteNameLength < 3) ) {
return WN_BAD_NETNAME;
}
//
// First two characters must be "\\"
//
if (*RemoteName != L'\\' || RemoteName[1] != L'\\') {
return WN_BAD_NETNAME;
}
if (!fLocalNamePresent && (IS_VALID_SERVER_TOKEN(&RemoteName[2], RemoteNameLength - 2))) {
//
// Return success for \\Server case.
//
*OutputBuffer = (PVOID) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
(RemoteNameLength + 1) * sizeof(WCHAR));
if (*OutputBuffer == NULL) {
KdPrint(("DAV: DAVCanonRemoteName LocalAlloc failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(*OutputBuffer, RemoteName);
return NO_ERROR;
}
//
// Must have at least one more backslash after the third character.
//
if (wcschr(&RemoteName[3], L'\\') == NULL) {
return WN_BAD_NETNAME;
}
//
// Last character cannot a backward slash.
//
if (RemoteName[RemoteNameLength - 1] == L'\\') {
return WN_BAD_NETNAME;
}
//
// Allocate output buffer. Should be the size of the RemoteName
// and space for an extra character to simplify parsing code below.
//
*OutputBuffer = (PVOID) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
(RemoteNameLength + 2) * sizeof(WCHAR));
if (*OutputBuffer == NULL) {
KdPrint(("DAV: DAVCanonRemoteName LocalAlloc failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(*OutputBuffer, RemoteName);
//
// Convert all backslashes to NULL terminator, skipping first 2 chars.
//
for (i = 2; i < RemoteNameLength; i++) {
if ((*OutputBuffer)[i] == L'\\') {
(*OutputBuffer)[i] = 0;
//
// Two consecutive forward or backslashes is bad.
//
if ( ( (i + 1) < RemoteNameLength )
&& ( (*OutputBuffer)[i + 1] == L'\\' ) ) {
LocalFree((HLOCAL) *OutputBuffer);
*OutputBuffer = NULL;
return WN_BAD_NETNAME;
}
}
}
//
// Validate each token of the RemoteName, separated by NULL terminator.
//
TokenPtr = *OutputBuffer + 2; // Skip first 2 chars
while (*TokenPtr != 0) {
TokenLength = wcslen(TokenPtr);
if ( ( fFirstToken && !IS_VALID_SERVER_TOKEN(TokenPtr, TokenLength)) ||
( !fFirstToken && !IS_VALID_TOKEN(TokenPtr, TokenLength)) ) {
(void) LocalFree((HLOCAL) *OutputBuffer);
*OutputBuffer = NULL;
return WN_BAD_NETNAME;
}
fFirstToken = FALSE;
TokenPtr += TokenLength + 1;
}
//
// Convert NULL separators back to backslashes.
//
for (i = 0; i < RemoteNameLength; i++) {
if ((*OutputBuffer)[i] == 0) {
(*OutputBuffer)[i] = L'\\';
}
}
if (ARGUMENT_PRESENT(OutputBufferLength)) {
*OutputBufferLength = RemoteNameLength;
}
return NO_ERROR;
}
DWORD
DavCreateTreeConnectName(
IN LPWSTR UncName,
IN LPWSTR LocalName OPTIONAL,
IN ULONG SessionId,
OUT PUNICODE_STRING TreeConnectStr
)
/*++
Routine Description:
This function replaces \\ with \Device\DavRdr\LocalName:\ in the
UncName to form the NT-style tree connection name. LocalName:\ is part
of the tree connection name only if LocalName is specified. A buffer
is allocated by this function and returned as the output string.
Arguments:
UncName - Supplies the UNC name of the shared resource.
LocalName - Supplies the local device name for the redirection.
SessionId - Id that uniquely identifies a Hydra session. This value is
always 0 for non-hydra NT and console hydra session.
TreeConnectStr - Returns a string with a newly allocated buffer that
contains the NT-style tree connection name.
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
--*/
{
DWORD UncNameLength = wcslen(UncName);
BOOL fLocalNamePresent = FALSE;
WCHAR IdBuffer[18];
UNICODE_STRING IdString;
if ( g_LUIDDeviceMapsEnabled == TRUE ) {
LUID LogonId;
DWORD dwErr;
dwErr = DavImpersonateAndGetLogonId( &(LogonId) );
if (dwErr != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavCreateTreeConnectName/DavImpersonateAndGetLogonId: "
"dwErr = %08lx\n", dwErr));
return (dwErr);
}
_snwprintf( IdBuffer,
sizeof(IdBuffer)/sizeof(WCHAR),
L"%08x%08x",
LogonId.HighPart,
LogonId.LowPart );
RtlInitUnicodeString( &IdString, IdBuffer );
}
else {
IdString.Length = 0;
IdString.MaximumLength = sizeof(IdBuffer);
IdString.Buffer = IdBuffer;
RtlIntegerToUnicodeString(SessionId, 10, &IdString);
}
//
// If a LocalName was specified, we set fLocalNamePresent to TRUE.
//
if (LocalName) {
fLocalNamePresent = TRUE;
}
//
// Initialize tree connect string maximum length to hold
// "\Device\DavRdr\;LocalName:ID\Server\Volume\Path".
// The ";Localname:ID" is done because that is what RDBSS expects.
// Its one of those legacy things!!!
// If LUID DosDevice enabled, then ID = SessionId, else ID = LogonId
//
TreeConnectStr->MaximumLength = wcslen(DD_DAV_DEVICE_NAME_U) * sizeof(WCHAR);
TreeConnectStr->MaximumLength += sizeof(WCHAR); // For '\'
TreeConnectStr->MaximumLength +=
(ARGUMENT_PRESENT(LocalName) ? (wcslen(LocalName) * sizeof(WCHAR)) : 0);
//
// Includes '\' and the term char.
//
TreeConnectStr->MaximumLength += (USHORT) ( UncNameLength * sizeof(WCHAR) );
//
// For ; in ";LocalName:ID".
//
TreeConnectStr->MaximumLength += sizeof(WCHAR);
//
// For the ID in ";LocalName:ID".
//
TreeConnectStr->MaximumLength += IdString.Length;
TreeConnectStr->Buffer = (PWSTR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
TreeConnectStr->MaximumLength);
if (TreeConnectStr->Buffer == NULL) {
DavPrint((DEBUG_ERRORS,
"DavCreateTreeConnectName/LocalAlloc: WStatus = %08lx\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Copy \Device\DavRdr.
//
RtlCopyMemory(TreeConnectStr->Buffer,
DD_DAV_DEVICE_NAME_U,
( wcslen(DD_DAV_DEVICE_NAME_U) * sizeof(WCHAR) ));
TreeConnectStr->Length = ( wcslen(DD_DAV_DEVICE_NAME_U) * sizeof(WCHAR) );
//
// Concatenate "\;LocalName:ID".
//
if (fLocalNamePresent) {
wcscat(TreeConnectStr->Buffer, L"\\");
TreeConnectStr->Length += sizeof(WCHAR);
wcscat(TreeConnectStr->Buffer, L";");
TreeConnectStr->Length += sizeof(WCHAR);
wcscat(TreeConnectStr->Buffer, LocalName);
TreeConnectStr->Length += (USHORT) (wcslen(LocalName) * sizeof(WCHAR));
RtlAppendUnicodeStringToString( TreeConnectStr, &IdString );
}
//
// Concatenate \Server\Volume\Path.
//
wcscat(TreeConnectStr->Buffer, &UncName[1]);
TreeConnectStr->Length += (USHORT) ((UncNameLength - 1) * sizeof(WCHAR));
return NO_ERROR;
}
DWORD
DavOpenCreateConnection(
IN PUNICODE_STRING TreeConnectionName,
IN LPWSTR UserName OPTIONAL,
IN LPWSTR Password OPTIONAL,
IN LPWSTR UncName,
IN ACCESS_MASK DesiredAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN ULONG ConnectionType,
OUT PHANDLE TreeConnectionHandle,
OUT PULONG_PTR Information OPTIONAL
)
/*++
Routine Description:
This function asks the redirector to either open an existing tree
connection (CreateDisposition == FILE_OPEN), or create a new tree
connection if one does not exist (CreateDisposition == FILE_CREATE).
The password and user name passed to the redirector via the EA buffer
in the NtCreateFile call. The EA buffer is NULL if neither password
or user name is specified.
The redirector expects the EA descriptor strings to be in ANSI
but the password and username themselves are in Unicode.
We also add webdav signature string, which tells our redir that we
are calling it
Arguments:
TreeConnectionName - Supplies the name of the tree connection in NT-style
file name format:
\Device\WebDavRedirector\Server\Volume\Directory
UserName - Supplies the user name to create the tree connection with.
Password - Supplies the password to create the tree connection with.
DesiredAccess - Supplies the access need on the connection handle.
CreateDisposition - Supplies the create disposition value to either
open or create the tree connection.
CreateOptions - Supplies the options used when creating or opening
the tree connection.
ConnectionType - Supplies the type of the connection (DISK, PRINT,
or ANY).
TreeConnectionHandle - Returns the handle to the tree connection
created/opened by the redirector.
Information - Returns the information field of the I/O status block.
Return Value:
NO_ERROR or reason for failure.
--*/
{
DWORD WStatus = NO_ERROR;
NTSTATUS NtStatus = STATUS_SUCCESS;
OBJECT_ATTRIBUTES UncNameAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
PFILE_FULL_EA_INFORMATION Ea = NULL;
ULONG EaBufferSize = 0;
UCHAR EaNamePasswordSize =
(UCHAR) (ROUND_UP_COUNT(strlen(EA_NAME_PASSWORD) + sizeof(CHAR),
ALIGN_WCHAR) - sizeof(CHAR));
UCHAR EaNameUserNameSize =
(UCHAR) (ROUND_UP_COUNT(strlen(EA_NAME_USERNAME) + sizeof(CHAR),
ALIGN_WCHAR) - sizeof(CHAR));
UCHAR EaNameTypeSize =
(UCHAR) (ROUND_UP_COUNT(strlen(EA_NAME_TYPE) + sizeof(CHAR),
ALIGN_DWORD) - sizeof(CHAR));
UCHAR EaNameWebDavSignatureSize =
(UCHAR) (ROUND_UP_COUNT(strlen(EA_NAME_WEBDAV_SIGNATURE) + sizeof(CHAR),
ALIGN_DWORD) - sizeof(CHAR));
USHORT PasswordSize = 0;
USHORT UserNameSize = 0;
USHORT TypeSize = sizeof(ULONG);
USHORT WebDavSignatureSize = 0;
InitializeObjectAttributes(&UncNameAttributes,
TreeConnectionName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Calculate the number of bytes needed for the EA buffer to put the
// password or user name.
//
if (ARGUMENT_PRESENT(Password)) {
PasswordSize = (USHORT) (wcslen(Password) * sizeof(WCHAR));
EaBufferSize = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNamePasswordSize + sizeof(CHAR) +
PasswordSize,
ALIGN_DWORD
);
}
if (ARGUMENT_PRESENT(UserName)) {
UserNameSize = (USHORT) (wcslen(UserName) * sizeof(WCHAR));
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameUserNameSize + sizeof(CHAR) +
UserNameSize,
ALIGN_DWORD
);
}
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameTypeSize + sizeof(CHAR) +
TypeSize,
ALIGN_DWORD
);
// round up just so we get some slop
EaBufferSize += ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameWebDavSignatureSize + sizeof(CHAR) +
WebDavSignatureSize,
ALIGN_DWORD
);
//
// Allocate the EA buffer.
//
EaBuffer = (PFILE_FULL_EA_INFORMATION) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
EaBufferSize);
if (EaBuffer == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavOpenCreateConnection/LocalAlloc: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
Ea = EaBuffer;
if (ARGUMENT_PRESENT(Password)) {
//
// Copy the EA name into EA buffer. EA name length does not
// include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_PASSWORD);
Ea->EaNameLength = EaNamePasswordSize;
//
// Copy the EA value into EA buffer. EA value length does not
// include the zero terminator.
//
wcscpy((LPWSTR) &(Ea->EaName[EaNamePasswordSize + sizeof(CHAR)]),
Password);
Ea->EaValueLength = PasswordSize;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNamePasswordSize + sizeof(CHAR) +
PasswordSize,
ALIGN_DWORD
);
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
if (ARGUMENT_PRESENT(UserName)) {
//
// Copy the EA name into EA buffer. EA name length does not
// include the zero terminator.
//
strcpy((LPSTR) Ea->EaName, EA_NAME_USERNAME);
Ea->EaNameLength = EaNameUserNameSize;
//
// Copy the EA value into EA buffer. EA value length does not
// include the zero terminator.
//
wcscpy((LPWSTR) &(Ea->EaName[EaNameUserNameSize + sizeof(CHAR)]),
UserName);
Ea->EaValueLength = UserNameSize;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameUserNameSize + sizeof(CHAR) +
UserNameSize,
ALIGN_DWORD
);
Ea->Flags = 0;
(ULONG_PTR) Ea += Ea->NextEntryOffset;
}
//
// Copy the connection type name into EA buffer. EA name length
// does not include the zero terminator.
//
strcpy( (LPSTR)Ea->EaName, EA_NAME_TYPE );
Ea->EaNameLength = EaNameTypeSize;
*((PULONG) &(Ea->EaName[EaNameTypeSize + sizeof(CHAR)])) = ConnectionType;
Ea->EaValueLength = TypeSize;
Ea->Flags = 0;
Ea->NextEntryOffset = ROUND_UP_COUNT(
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
EaNameTypeSize + sizeof(CHAR) +
TypeSize,
ALIGN_DWORD
);
(ULONG_PTR) Ea += Ea->NextEntryOffset;
strcpy( (LPSTR)Ea->EaName, EA_NAME_WEBDAV_SIGNATURE );
Ea->EaNameLength = EaNameWebDavSignatureSize;
Ea->EaValueLength = 0;
//
// Terminate the EA.
//
Ea->NextEntryOffset = 0;
Ea->Flags = 0;
WStatus = DavImpersonateClient();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavOpenCreateConnection/DavImpersonateClient: WStatus = "
"%08lx\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Create or open a tree connection.
//
NtStatus = NtCreateFile(TreeConnectionHandle,
DesiredAccess,
&UncNameAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_VALID_FLAGS,
CreateDisposition,
CreateOptions,
(PVOID) EaBuffer,
EaBufferSize);
if (NtStatus != STATUS_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavOpenCreateConnection/NtCreateFile: NtStatus = %08lx\n",
NtStatus));
WStatus = DavRevertToSelf();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavOpenCreateConnection/DavRevertToSelf: WStatus = "
"%08lx\n", WStatus));
}
WStatus = WsMapStatus(NtStatus);
goto EXIT_THE_FUNCTION;
}
WStatus = WsMapStatus(NtStatus);
if (ARGUMENT_PRESENT(Information)) {
*Information = IoStatusBlock.Information;
}
WStatus = DavRevertToSelf();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavOpenCreateConnection/DavRevertToSelf: WStatus = "
"%08lx\n", WStatus));
}
EXIT_THE_FUNCTION:
//
// Clear the password to prevent it from making it to pagefile and free the
// memory.
//
if (EaBuffer != NULL) {
RtlZeroMemory( EaBuffer, EaBufferSize );
LocalFree((HLOCAL) EaBuffer);
}
return WStatus;
}
ULONG
DavImpersonateAndGetUserId(
LPWSTR UserName,
LPDWORD UserNameMaxLen
)
/*++
Routine Description:
This function gets the user id of the current thread.
Arguments:
UserName - Returns the user id of the current process.
UserNameMaxLen - Pointer to variable containing max length of UserName passed
to this function. It will be set to the length of name filled
or required.
Return Value:
Win32 Error - ERROR_SUCCESS or reason for failure.
--*/
{
DWORD dwError = NO_ERROR;
BOOLEAN revert = FALSE;
BOOL bStatus = FALSE;
dwError = DavImpersonateClient();
if (dwError != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetUserId/DavImpersonateClient: dwError = %x\n",
dwError));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
//
// Get user name in the buffer passed to this function.
//
bStatus = GetUserName(UserName, UserNameMaxLen);
if (bStatus != TRUE) {
dwError = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetUserId/GetUserName: dwError = %x\n",
dwError));
goto EXIT_THE_FUNCTION;
}
EXIT_THE_FUNCTION:
if (revert) {
dwError = DavRevertToSelf();
if (dwError != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetUserId/DavRevertToSelf: dwError = %x\n",
dwError));
}
}
return dwError;
}
ULONG
DavImpersonateAndGetSessionId(
PULONG pSessionId
)
/*++
Routine Description:
This function gets the session id of the current thread.
Arguments:
pSessionId - Returns the session id of the current process.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NET_API_STATUS NetApiStatus;
HANDLE CurrentThreadToken;
ULONG SessionId;
ULONG ReturnLength;
BOOLEAN revert = FALSE;
DWORD dwError = NO_ERROR;
dwError = DavImpersonateClient();
if (dwError != NO_ERROR) {
NtStatus = DavMapErrorToNtStatus(dwError);
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetSessionId/DavImpersonateClient: "
"dwError = %08lx NtStatus = %08lx\n", dwError, NtStatus));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
NtStatus = NtOpenThreadToken(NtCurrentThread(),
TOKEN_QUERY,
TRUE,
&(CurrentThreadToken));
if (NtStatus != STATUS_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavpImpersonateAndGetSessionId/NtOpenThreadToken."
" NtStatus = %08lx\n", NtStatus));
goto EXIT_THE_FUNCTION;
}
//
// Get the Session Id of the current thread.
//
NtStatus = NtQueryInformationToken(CurrentThreadToken,
TokenSessionId,
&SessionId,
sizeof(ULONG),
&(ReturnLength));
if (NtStatus != STATUS_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavpImpersonateAndGetSessionId/NtQueryInformationToken."
" NtStatus = %08lx\n", NtStatus));
NtClose(CurrentThreadToken);
goto EXIT_THE_FUNCTION;
}
NtClose(CurrentThreadToken);
*pSessionId = SessionId;
EXIT_THE_FUNCTION:
if (revert) {
NtStatus = DavRevertToSelf();
if (NtStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetSessionId/DavRevertToSelf: "
"NtStatus = %08lx\n", NtStatus));
}
}
NetApiStatus = NetpNtStatusToApiStatus(NtStatus);
return NetApiStatus;
}
ULONG
DavImpersonateAndGetLogonId(
PLUID pLogonId
)
/*++
Routine Description:
This function gets the Logon Id of the current thread.
Arguments:
pLogonId - Returns the Logon Id of the current thread.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NTSTATUS NtStatus;
NET_API_STATUS NetApiStatus;
HANDLE CurrentThreadToken;
TOKEN_STATISTICS TokenStats;
ULONG ReturnLength;
BOOLEAN revert = FALSE;
NtStatus = DavImpersonateClient();
if (NtStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetLogonId/DavImpersonateClient: "
"NtStatus = %08lx\n", NtStatus));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
NtStatus = NtOpenThreadToken(NtCurrentThread(),
TOKEN_QUERY,
TRUE,
&(CurrentThreadToken));
if (NtStatus != STATUS_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetLogonId/NtOpenThreadToken."
" NtStatus = %08lx\n", NtStatus));
goto EXIT_THE_FUNCTION;
}
//
// Get the LogonId of the current thread.
//
NtStatus = NtQueryInformationToken(CurrentThreadToken,
TokenStatistics,
(PVOID) &TokenStats,
sizeof(TokenStats),
&ReturnLength);
if (NtStatus != STATUS_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetLogonId/NtQueryInformationToken."
" NtStatus = %08lx\n", NtStatus));
NtClose(CurrentThreadToken);
goto EXIT_THE_FUNCTION;
}
RtlCopyLuid( pLogonId, &(TokenStats.AuthenticationId) );
NtClose(CurrentThreadToken);
EXIT_THE_FUNCTION:
if (revert) {
NtStatus = DavRevertToSelf();
if (NtStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateAndGetLogonId/DavRevertToSelf: "
"NtStatus = %08lx\n", NtStatus));
}
}
NetApiStatus = NetpNtStatusToApiStatus(NtStatus);
return NetApiStatus;
}
DWORD
DavCreateSymbolicLink(
IN LPWSTR Local,
IN LPWSTR TreeConnectStr,
IN OUT LPWSTR *Session
)
/*++
Routine Description:
This function creates a symbolic link object for the specified local
device name which is linked to the tree connection name that has a
format of \Device\DavRdr\Device:\Server\Volume\Directory.
Arguments:
Local - Supplies the local device name.
TreeConnectStr - Supplies the tree connection name string which is the
link target of the symbolick link object.
Session - The Session Path is filled into this buffer.
Return Value:
NO_ERROR or reason for failure.
--*/
{
DWORD dwError = NO_ERROR;
WCHAR TempBuf[64];
DWORD Flags;
BOOL revert = FALSE;
//
// Multiple session support.
//
*Session = DavReturnSessionPath(Local);
if ( *Session == NULL ) {
dwError = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/DavReturnSessionPath: dwError = "
"%08lx\n", dwError));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavCreateSymbolicLink: *Session = %ws\n", *Session));
if ( g_LUIDDeviceMapsEnabled == TRUE ) {
dwError = DavImpersonateClient();
if (dwError != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/DavImpersonateClient: "
"dwError = %08lx\n", dwError));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
}
//
// Local device is some X:.
//
if ( !QueryDosDeviceW( *Session, TempBuf, 64 ) ) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
//
// Most likely failure occurred because our output
// buffer is too small. It still means someone already
// has an existing symbolic link for this device.
//
dwError = ERROR_ALREADY_ASSIGNED;
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/QueryDosDeviceW: dwError = "
"%08lx\n", dwError));
goto EXIT_THE_FUNCTION;
}
//
// ERROR_FILE_NOT_FOUND (translated from OBJECT_NAME_NOT_FOUND)
// means it does not exist and we can redirect this device.
//
} else {
//
// QueryDosDevice successfully an existing symbolic link. Somebody is
// already using this device.
//
dwError = ERROR_ALREADY_ASSIGNED;
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/QueryDosDeviceW: Device already exists.\n"));
goto EXIT_THE_FUNCTION;
}
Flags = (DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM);
//
// Create a symbolic link object to the device we are redirecting.
//
if ( !DefineDosDeviceW( Flags, *Session, TreeConnectStr ) ) {
dwError = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/DefineDosDeviceW: dwError = "
"%08lx\n", dwError));
}
EXIT_THE_FUNCTION:
if ( revert == TRUE ) {
DWORD dwErrLocal;
dwErrLocal = DavRevertToSelf();
if (dwErrLocal != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/DavRevertToSelf: "
"NtStatus = %08lx\n", dwErrLocal));
dwError = dwErrLocal;
}
}
return dwError;
}
DWORD
DavDeleteSymbolicLink(
IN LPWSTR LocalDeviceName,
IN LPWSTR TreeConnectStr,
IN LPWSTR SessionDeviceName
)
/*++
Routine Description:
This function deletes the symbolic link we had created earlier for
the device.
Arguments:
LocalDeviceName - Supplies the local device name string of which the
symbolic link object is created.
TreeConnectStr - Supplies a pointer to the Unicode string which
contains the link target string we want to match and
delete.
SessionDeviceName - Terminal Server Addition. This parameter is required
because the device created is per session.
Return Value:
Win32 Error value or NO_ERROR.
--*/
{
DWORD WStatus = NO_ERROR;
DWORD CallFlags = 0;
BOOLEAN DeleteSession = FALSE;
BOOL revert = FALSE;
CallFlags = (DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM);
//
// If the Targetpath (TreeConnectStr) is specified, we need the following
// flags.
//
if (TreeConnectStr) {
CallFlags |= ( DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE );
}
if (LocalDeviceName != NULL || SessionDeviceName != NULL) {
if (SessionDeviceName == NULL) {
SessionDeviceName = DavReturnSessionPath(LocalDeviceName);
if ( SessionDeviceName == NULL ) {
WStatus = ERROR_INVALID_PARAMETER;
DavPrint((DEBUG_ERRORS,
"DavDeleteSymbolicLink/DavReturnSessionPath: WStatus ="
" %08lx\n", WStatus));
return WStatus;
}
DeleteSession = TRUE;
}
if ( g_LUIDDeviceMapsEnabled == TRUE ) {
WStatus = DavImpersonateClient();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/DavImpersonateClient: "
"WStatus = %08lx\n", WStatus));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
}
//
// Delete the symbolic link.
//
if ( !DefineDosDeviceW(CallFlags, SessionDeviceName, TreeConnectStr) ) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavDeleteSymbolicLink/DefineDosDeviceW: WStatus ="
" %08lx\n", WStatus));
}
}
EXIT_THE_FUNCTION:
if ( revert == TRUE ) {
WStatus = DavRevertToSelf();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavCreateSymbolicLink/DavRevertToSelf: "
"WStatus = %08lx\n", WStatus));
}
}
if (SessionDeviceName && DeleteSession) {
LocalFree( SessionDeviceName );
}
return WStatus;
}
DWORD
DavImpersonateClient(
VOID
)
/*++
Routine Description:
This function calls RpcImpersonateClient to impersonate the current caller
of an API.
Arguments:
None.
Return Value:
NO_ERROR or reason for failure.
--*/
{
DWORD WStatus = NO_ERROR;
WStatus = RpcImpersonateClient(NULL);
if ( WStatus != NO_ERROR ) {
DavPrint((DEBUG_ERRORS,
"DavImpersonateClient/RpcImpersonateClient: WStatus = %08lx\n",
WStatus));
}
return WStatus;
}
DWORD
DavRevertToSelf(
VOID
)
/*++
Routine Description:
This function calls RpcRevertToSelf to undo an impersonation.
Arguments:
None.
Return Value:
NO_ERROR or reason for failure.
--*/
{
DWORD WStatus = NO_ERROR;
WStatus = RpcRevertToSelf();
if ( WStatus != NO_ERROR ) {
DavPrint((DEBUG_ERRORS,
"DavRevertToSelf/RpcRevertToSelf: WStatus = %08lx\n",
WStatus));
}
return WStatus;
}
NET_API_STATUS
DavGetUserEntry(
IN PDAV_USERS_OBJECT DavUsers,
IN PLUID LogonId,
OUT PULONG Index,
IN BOOL IsAdd
)
/*++
Routine Description:
This function searches the table of user entries for one that matches the
specified LogonId, and returns the index to the entry found. If none is
found, an error is returned if IsAdd is FALSE. If IsAdd is TRUE a new
entry in the users table is created for the user and the index to this
new entry is returned.
WARNING: This function assumes that the users table resource has been
claimed.
Arguments:
DavUsers - Supplies a pointer to the DavUseObject.
LogonId - Supplies the pointer to the current user's Logon Id.
Index - Returns the index to the users table of entry belonging to the
current user.
IsAdd - Supplies flag to indicate whether to add a new entry for the
current user if none is found.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS NetApiStatus;
DWORD i;
ULONG FreeEntryIndex = MAXULONG;
for (i = 0; i < DavUsers->TableSize; i++) {
//
// If the LogonId matches the entry in the DavUsers Table, we've found
// the correct user entry.
//
if ( RtlEqualLuid( LogonId, &(DavUsers->Table[i].LogonId) ) ) {
*Index = i;
return NERR_Success;
}
else if (FreeEntryIndex == MAXULONG && DavUsers->Table[i].List == NULL) {
//
// Save away first unused entry in table.
//
FreeEntryIndex = i;
}
}
if ( !IsAdd ) {
//
// Current user is not found in users table and we are told not to
// create a new entry
//
return NERR_UserNotFound;
}
DavPrint((DEBUG_MISC,
"DavGetUserEntry: New Entry. LogonID.Low = %d, LogonId.High = %d\n",
LogonId->LowPart, LogonId->HighPart));
//
// Could not find an empty entry in the UsersTable, need to grow
//
if (FreeEntryIndex == MAXULONG) {
NetApiStatus = DavGrowTable(DavUsers);
if (NetApiStatus != NERR_Success) {
return NetApiStatus;
}
FreeEntryIndex = i;
}
DavPrint((DEBUG_MISC, "DavGetUserEntry: FreeEntryIndex = %d\n", FreeEntryIndex));
//
// Create a new entry for current user
//
RtlCopyLuid( &(DavUsers->Table[FreeEntryIndex].LogonId), LogonId);
*Index = FreeEntryIndex;
return NERR_Success;
}
NET_API_STATUS
DavGrowTable(
IN PDAV_USERS_OBJECT DavUsers
)
/*++
Routine Description:
This function grows the users table to accomodate more users.
WARNING: This function assumes that the users table resource has been
claimed.
Arguments:
DavUsers - Supplies a pointer to the DavUsersObject.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS NetApiStatus = NERR_Success;
ULONG Size;
BOOL ReturnVal;
DavPrint((DEBUG_MISC, "DavGrowTable: Grow the DavUsers table\n"));
Size = (DavUsers->TableSize + DAV_GROW_USER_COUNT) * sizeof(DAV_PER_USER_ENTRY);
if (DavUsers->TableSize > 0) {
//
// Unlock the Use Table virtual memory so that Win32 can move it
// around to find a larger piece of contiguous virtual memory if
// necessary.
//
ReturnVal = LocalUnlock(DavUsers->TableMemory);
if ( !ReturnVal ) {
NetApiStatus = GetLastError();
if ( NetApiStatus != NO_ERROR ) {
DavPrint((DEBUG_ERRORS,
"DavGrowTable/LocalUnlock. NetApiStatus = %08lx\n",
NetApiStatus));
return NetApiStatus;
}
}
//
// Grow users table.
//
DavUsers->TableMemory = LocalReAlloc(DavUsers->TableMemory,
Size,
LMEM_ZEROINIT | LMEM_MOVEABLE);
if (DavUsers->TableMemory == NULL) {
NetApiStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGrowTable/LocalReAlloc. NetApiStatus = %08lx\n",
NetApiStatus));
return NetApiStatus;
}
} else {
//
// This is the first allocation being made for the Dav Use Table.
//
DavUsers->TableMemory = LocalAlloc(LMEM_ZEROINIT | LMEM_MOVEABLE, Size);
if (DavUsers->TableMemory == NULL) {
NetApiStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGrowTable/LocalAlloc. NetApiStatus = %08lx\n",
NetApiStatus));
return NetApiStatus;
}
}
//
// Update new size of Use Table.
//
DavUsers->TableSize += DAV_GROW_USER_COUNT;
//
// Lock Use Table virtual memory so that it cannot be moved.
//
DavUsers->Table = (PDAV_PER_USER_ENTRY) LocalLock(DavUsers->TableMemory);
if (DavUsers->Table == NULL) {
DavPrint((DEBUG_ERRORS,
"DavGrowTable/LocalAlloc. NetApiStatus = %08lx\n",
NetApiStatus));
return NetApiStatus;
}
return NetApiStatus;
}
LPWSTR
DavReturnSessionPath(
IN LPWSTR LocalDeviceName
)
/*++
Routine Description:
This function returns the per session path to access the specific dos device
for multiple session support.
Arguments:
LocalDeviceName - Supplies the local device name specified by the API
caller.
Return Value:
LPWSTR - Pointer to per session path in newly allocated memory
by LocalAlloc().
--*/
{
BOOL rc;
DWORD SessionId = 0;
CLIENT_ID ClientId;
LPWSTR SessionDeviceName = NULL;
NET_API_STATUS NetApiStatus;
NetApiStatus = DavImpersonateAndGetSessionId( &(SessionId) );
if (NetApiStatus != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavReturnSessionPath/DavImpersonateAndGetSessionId: "
"NetApiStatus = %08lx\n", NetApiStatus));
return NULL;
}
rc = DosPathToSessionPathW(SessionId, LocalDeviceName, &SessionDeviceName);
if( !rc ) {
NetApiStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavReturnSessionPath/DosPathToSessionPathW: "
"NetApiStatus = %08lx\n", NetApiStatus));
return NULL;
}
return SessionDeviceName;
}
NET_API_STATUS
DavAddUse(
IN PLUID LogonId,
IN LPWSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPWSTR AuthUserName OPTIONAL,
IN LPWSTR UncName,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN HANDLE DavCreateFileHandle
)
/*++
Routine Description:
This function adds a Dav "net use" entry into the DavUseTable 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 DavUseTable.
Arguments:
LogonId - Supplies a pointer to the user's Logon Id.
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.
DavCreateFileHandle - The file handle that was created using NtCreateFile on
the net use path. This is stored in the new user entry
structure being created and is closed when the user
entry gets deleted.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS NetApiStatus = NERR_Success;
DWORD Index; // Index to user entry in Use Table.
PDAV_USE_ENTRY MatchedPointer = NULL; // Points to matching shared resource.
PDAV_USE_ENTRY InsertPointer = NULL; // Point of insertion into use list.
PDAV_USE_ENTRY NewUse; // Pointer to the new use entry.
BOOLEAN ResAcquired = FALSE;
LPWSTR UserName = NULL;
if ( !RtlAcquireResourceExclusive(&DavUseObject.TableResource, TRUE) ) {
NetApiStatus = NERR_InternalError;
DavPrint((DEBUG_ERRORS,
"DavAddUse/RtlAcquireResourceExclusive: Internal Error.\n"));
goto EXIT_THE_FUNCTION;
}
ResAcquired = TRUE;
//
// We will always save the user-name for any new use entry created.
// This user-name is either explicitly given or if NULL, is taken as
// Logged-On user. This should be taken care OF by the caller function.
//
UserName = AuthUserName;
ASSERT(UserName != NULL);
//
// Look for the matching LogonId in the Use Table. If none matched create a
// new entry.
//
NetApiStatus = DavGetUserEntry(&DavUseObject, LogonId, &Index, TRUE);
if (NetApiStatus != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavAddUse/RtlAcquireResourceExclusive: NetApiStatus = "
"%08lx\n", NetApiStatus));
goto EXIT_THE_FUNCTION;
}
if ( DavUseObject.Table[Index].List != NULL ) {
DavPrint((DEBUG_MISC, "DavAddUse: DavUseObject.Table[Index].List != NULL\n"));
//
// Traverse use list to look for location to insert new use entry.
//
DavFindInsertLocation((PDAV_USE_ENTRY)DavUseObject.Table[Index].List,
UncName,
&(MatchedPointer),
&(InsertPointer));
}
if (MatchedPointer == NULL) {
DavPrint((DEBUG_MISC, "DavAddUse: MatchedPointer == NULL\n"));
//
// No matching UNC name found. Create a new entry with a
// corresponding remote entry.
//
NetApiStatus = DavCreateNewEntry(&NewUse,
Local,
LocalLength,
UserName,
UncName,
UncNameLength,
TreeConnectStr,
DavCreateFileHandle);
if (NetApiStatus != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavAddUse/DavCreateNewEntry: NetApiStatus = %08lx\n",
NetApiStatus));
goto EXIT_THE_FUNCTION;
}
} else {
//
// Matching UNC name found.
//
DavPrint((DEBUG_MISC, "DavAddUse: MatchedPointer != NULL\n"));
//
// 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) {
DavPrint((DEBUG_MISC, "DavAddUse: Local == NULL\n"));
if (MatchedPointer->Local == NULL) {
DavPrint((DEBUG_MISC, "DavAddUse: MatchedPointer->Local == NULL\n"));
//
// We don't increment the reference count below because this is
// the behavior SMB has. So, if a user does the following.
// 1. net use http://server/share
// 2. net use http://server/share
// 3. net use http://server/share /d
// The last delete lands up deleting the mapping even though the
// user mapped it twice.
// If this #if 0 is changed to #if 1, then the NtClose that is
// being done below should not be done. This is why its included
// in the #else clause.
//
#if 0
//
// Yes, there is a NULL local device entry already.
// Increment the use count and we are done.
//
MatchedPointer->UseCount++;
MatchedPointer->Remote->TotalUseCount++;
#else
//
// We close the file handle here, because we don't need to keep
// it since this net use has no impact. As explained above, this
// is because this user has already done a net use to the same
// path with no local name and has repeated the command. This
// should only be done if we are not taking the reference above.
// If the above #if 0 is changed to add a reference to this
// entry then we need to keep this handle somewhere.
//
NtClose(DavCreateFileHandle);
#endif
RtlReleaseResource( &(DavUseObject.TableResource) );
ResAcquired = FALSE;
return NetApiStatus;
} else {
DavPrint((DEBUG_MISC, "DavAddUse: MatchedPointer->Local != NULL\n"));
}
} else {
DavPrint((DEBUG_MISC, "DavAddUse: Local != NULL\n"));
}
DavPrint((DEBUG_MISC, "DavAddUse: New Entry!!!\n"));
//
// If we get here it 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.
//
NetApiStatus = DavCreateNewEntry(&NewUse,
Local,
LocalLength,
UserName,
NULL,
0,
TreeConnectStr,
DavCreateFileHandle);
if (NetApiStatus != NERR_Success) {
DavPrint((DEBUG_ERRORS,
"DavAddUse/DavCreateNewEntry: NetApiStatus = %08lx\n",
NetApiStatus));
goto EXIT_THE_FUNCTION;
}
NewUse->Remote = MatchedPointer->Remote;
NewUse->Remote->TotalUseCount++;
}
//
// Insert the new use entry into use list.
//
if (InsertPointer == NULL) {
//
// Inserting into the head of list
//
DavPrint((DEBUG_MISC, "DavAddUse: InsertPointer == NULL\n"));
DavUseObject.Table[Index].List = (PVOID)NewUse;
}
else {
DavPrint((DEBUG_MISC, "DavAddUse: InsertPointer != NULL\n"));
InsertPointer->Next = NewUse;
}
EXIT_THE_FUNCTION:
if (ResAcquired) {
RtlReleaseResource( &(DavUseObject.TableResource) );
}
return NetApiStatus;
}
VOID
DavFindInsertLocation(
IN PDAV_USE_ENTRY DavUseList,
IN LPWSTR UncName,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *InsertPointer
)
/*++
Routine Description:
This function searches the use list for the location to insert a new use
entry. The use entry is inserted to the end of the use list so the
pointer to the last node in the use list is returned via InsertPointer.
We also have to save a pointer to the node with the same UNC name so that
the new use entry can be set to point to the same remote node (where the
UNC name is stored). This pointer is returned as MatchedPointer.
WARNING: This function assumes that the DavUseObject.TableResource has been
claimed.
Arguments:
DavUseList - Supplies the pointer to the use list.
UncName - Supplies the pointer to the shared resource.
MatchedPointer - Returns a pointer to the node that holds the matching
UncName. If no matching UncName is found, this pointer is
set to NULL. If there is more than one node that has the
same UNC name, this pointer will point to the node with the
NULL local device name, if any; otherwise, if all nodes
with matching UNC names have non-null local device names,
the pointer to the last matching node will be returned.
InsertPointer - Returns a pointer to the last use entry, after which the
new entry is to be inserted.
Return Value:
None.
--*/
{
BOOL IsMatchWithNullDevice = FALSE;
*MatchedPointer = NULL;
while (DavUseList != NULL) {
//
// Do the string comparison only if we haven't found a matching UNC
// name with a NULL local device name.
//
if ( !IsMatchWithNullDevice &&
( _wcsicmp((LPWSTR)DavUseList->Remote->UncName, UncName) == 0 ) ) {
//
// Found matching entry.
//
*MatchedPointer = DavUseList;
IsMatchWithNullDevice = (DavUseList->Local == NULL);
}
*InsertPointer = DavUseList;
DavUseList = DavUseList->Next;
}
return;
}
NET_API_STATUS
DavCreateNewEntry(
OUT PDAV_USE_ENTRY *NewUse,
IN LPWSTR Local OPTIONAL,
IN DWORD LocalLength,
IN LPWSTR AuthUserName OPTIONAL,
IN LPWSTR UncName OPTIONAL,
IN DWORD UncNameLength,
IN PUNICODE_STRING TreeConnectStr,
IN HANDLE DavCreateFileHandle
)
/*++
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.
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.
AuthUserName - Supplies the authentication user name string to be copied into the new
use entry.
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
DavCreateFileHandle - The file handle that was created using NtCreateFile on
the net use path. This is stored in the new user entry
structure being created and is closed when the user
entry gets deleted.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS NetApiStatus = NERR_Success;
PUNC_NAME NewRemoteEntry = NULL;
SIZE_T Size = 0, AlignedSize = 0;
DWORD AuthUserNameLength = 0;
SIZE_T BuffOffset = 0;
//
// The extra 1 is for last L'\0'.
//
AuthUserNameLength = ( ARGUMENT_PRESENT(AuthUserName) ? wcslen(AuthUserName)+1 : 0 );
//
// We add TreeConnectStr only if Local is present.
//
Size = ROUND_UP_COUNT( sizeof(DAV_USE_ENTRY), ALIGN_WCHAR);
Size += ROUND_UP_COUNT(( ARGUMENT_PRESENT(Local) ?
(LocalLength) * sizeof(WCHAR) : 0), ALIGN_WCHAR);
Size += ROUND_UP_COUNT(( ARGUMENT_PRESENT(Local) && ARGUMENT_PRESENT(TreeConnectStr) ?
TreeConnectStr->MaximumLength : 0 ), ALIGN_WCHAR);
Size += ROUND_UP_COUNT(( ARGUMENT_PRESENT(AuthUserName) ?
(AuthUserNameLength) * sizeof(WCHAR) : 0 ), ALIGN_WCHAR);
AlignedSize = ROUND_UP_COUNT(Size, ALIGN_WCHAR);
*NewUse = (PDAV_USE_ENTRY) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, AlignedSize);
if ( *NewUse == NULL ) {
NetApiStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavCreateNewEntry/LocalAlloc: NetApiStatus = %08lx\n",
NetApiStatus));
return NetApiStatus;
}
//
// Put the use information into the new use node.
//
(*NewUse)->Next = NULL;
(*NewUse)->UseCount = 1;
ASSERT ( Local == NULL || LocalLength == (wcslen(Local)+1));
//
// Goto next free location in the buffer.
//
BuffOffset = ( (DWORD_PTR) *NewUse + sizeof(DAV_USE_ENTRY) );
BuffOffset = ROUND_UP_COUNT(BuffOffset, ALIGN_WCHAR);
//
// Copy local device name into use entry after the LocalLength field,
// if it is specified.
//
if (ARGUMENT_PRESENT(Local)) {
//
// Copy local device name.
//
(*NewUse)->Local = (LPWSTR)BuffOffset;
(*NewUse)->LocalLength = LocalLength;
wcscpy((*NewUse)->Local, Local);
BuffOffset += ((LocalLength) * sizeof(WCHAR));
BuffOffset = ROUND_UP_COUNT(BuffOffset, ALIGN_WCHAR);
//
// Copy TreeConnectStr.
//
if (ARGUMENT_PRESENT(TreeConnectStr)) {
(*NewUse)->TreeConnectStr = (LPWSTR)BuffOffset;
wcscpy((*NewUse)->TreeConnectStr, TreeConnectStr->Buffer);
BuffOffset += (TreeConnectStr->MaximumLength);
BuffOffset = ROUND_UP_COUNT(BuffOffset, ALIGN_WCHAR);
}
} else {
(*NewUse)->Local = NULL;
(*NewUse)->TreeConnectStr = NULL;
}
//
// Copy AuthUserName.
//
if (ARGUMENT_PRESENT(AuthUserName)) {
(*NewUse)->AuthUserName = (LPWSTR)BuffOffset;
(*NewUse)->AuthUserNameLength = AuthUserNameLength;
wcscpy((*NewUse)->AuthUserName, AuthUserName);
BuffOffset += ((AuthUserNameLength) * sizeof(WCHAR));
BuffOffset = ROUND_UP_COUNT(BuffOffset, ALIGN_WCHAR);
}
//
// If shared resource name is specified, create a new remote entry to hold
// the UNC name, and total number of uses on this shared resource.
//
if (ARGUMENT_PRESENT(UncName)) {
SIZE_T UncNameSize;
UncNameSize = ( sizeof(UNC_NAME) + (UncNameLength * sizeof(WCHAR)) );
NewRemoteEntry = (PUNC_NAME) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
UncNameSize);
if (NewRemoteEntry == NULL) {
NetApiStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavCreateNewEntry/LocalAlloc: NetApiStatus = %08lx\n",
NetApiStatus));
return NetApiStatus;
}
wcscpy((LPWSTR)NewRemoteEntry->UncName, UncName);
NewRemoteEntry->UncNameLength = UncNameLength;
NewRemoteEntry->TotalUseCount = 1;
(*NewUse)->Remote = NewRemoteEntry;
}
//
// Finally, store the handle that has been created in the new user entry
// structure.
//
(*NewUse)->DavCreateFileHandle = DavCreateFileHandle;
return NetApiStatus;
}
NET_API_STATUS
DavFindUse(
IN PLUID LogonId,
IN PDAV_USE_ENTRY UseList,
IN LPWSTR UseName,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *BackPointer OPTIONAL
)
/*++
Routine Description:
This function searches the Dav Use Table for the specified use connection.
If the UseName is found in the Use Table (explicit connection), a pointer
to the matching use entry is returned. Otherwise, MatchedPointer is set to
NULL.
WARNING: This function assumes that the DavUseObject.TableResource is
claimed.
Arguments:
LogonId - Supplies a pointer to the user's Logon Id.
UseList - Supplies the use list of the user.
UseName - Supplies the name of the tree connection, this is either a
local device name or a UNC name.
MatchedPointer - Returns the pointer to the matching use entry. This
pointer is set to NULL if the specified use is an implicit
connection.
BackPointer - Returns the pointer to the entry previous to the matching use
entry if MatchedPointer is not NULL.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
PDAV_USE_ENTRY Back = NULL;
//
// Look for use entry depending on whether the local device name or UNC name
// is specified.
//
if (UseName[0] != L'\\') {
//
// Local device name is specified.
//
DavFindLocal( UseList, UseName, MatchedPointer, &(Back) );
} else {
//
// A UNC name has been specified.
//
DavFindRemote( UseList, UseName, MatchedPointer, &(Back) );
}
if ( *MatchedPointer == NULL ) {
DavPrint((DEBUG_ERRORS, "DavFindUse: %ws NOT found\n", UseName));
return NERR_UseNotFound;
} else {
if (ARGUMENT_PRESENT(BackPointer)) {
*BackPointer = Back;
}
return NERR_Success;
}
}
VOID
DavFindLocal(
IN PDAV_USE_ENTRY UseList,
IN LPWSTR Local,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *BackPointer
)
/*++
Routine Description:
This function searches the use list for the specified local device name.
WARNING: This function assumes that the DavUseObject.TableResource has been
claimed.
Arguments:
UseList - Supplies the pointer to the use list.
Local - Supplies the local device name.
MatchedPointer - Returns a pointer to the use entry that holds the matching
local device name. If no matching local device name is
found, this pointer is set to NULL.
BackPointer - Returns a pointer to the entry previous to the found entry.
If the local device name is not found, this pointer is set to
NULL.
Return Value:
None.
--*/
{
*BackPointer = UseList;
DavPrint((DEBUG_MISC, "DavFindLocal: LocalName = %ws\n", Local));
while (UseList != NULL) {
if ( (UseList->Local != NULL) && (_wcsicmp(UseList->Local, Local) == 0) ) {
//
// Found matching entry
//
*MatchedPointer = UseList;
return;
} else {
*BackPointer = UseList;
UseList = UseList->Next;
}
}
DavPrint((DEBUG_ERRORS, "DavFindLocal: LocalName NOT found\n"));
//
// Did not find matching local device name in the entire list.
//
*MatchedPointer = NULL;
*BackPointer = NULL;
return;
}
VOID
DavFindRemote(
IN PDAV_USE_ENTRY UseList,
IN LPWSTR RemoteName,
OUT PDAV_USE_ENTRY *MatchedPointer,
OUT PDAV_USE_ENTRY *BackPointer
)
/*++
Routine Description:
This function searches the use list for the specified UNC name.
WARNING: This function assumes that the DavUseObject.TableResource has been
claimed.
Arguments:
UseList - Supplies the pointer to the use list.
RemoteName - Supplies the UNC name.
MatchedPointer - Returns a pointer to the use entry that holds the matching
UNC name. If no matching UNC name is found, this pointer
is set to NULL.
BackPointer - Returns a pointer to the entry previous to the found entry.
If the UNC name is not found, this pointer is set to NULL.
Return Value:
None.
--*/
{
*BackPointer = UseList;
DavPrint((DEBUG_MISC, "DavFindRemote: RemoteName = %ws\n", RemoteName));
while (UseList != NULL) {
//
// When we are trying to delete a UNC connection, then we should make
// sure that the one we are deleting does not have a local name
// associted with it. Only if "net use http://foo/bar" was done is
// "net use http://foo/bar /d" allowed.
//
if ( (UseList->Local == NULL) &&
(UseList->Remote->UncName != NULL) &&
(_wcsicmp((LPWSTR)UseList->Remote->UncName, RemoteName) == 0) ) {
DavPrint((DEBUG_MISC, "DavFindRemote: UncName = %ws\n",
UseList->Remote->UncName));
//
// Found matching entry
//
*MatchedPointer = UseList;
return;
} else {
*BackPointer = UseList;
UseList = UseList->Next;
}
}
DavPrint((DEBUG_ERRORS, "DavFindRemote: RemoteName NOT found\n"));
//
// Did not find matching local device name in the entire list.
//
*MatchedPointer = NULL;
*BackPointer = NULL;
return;
}
NET_API_STATUS
DavDeleteUse(
IN PLUID LogonId,
IN DWORD ForceLevel,
IN PDAV_USE_ENTRY MatchedPointer,
IN DWORD Index
)
/*++
Routine Description:
This function removes the use entry pointed by MatchedPointer and frees its
memory if, it is a UNC connection deleted with force, or if 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.
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.
Index - The index to the users table of entry belonging to the current user.
Return Value:
NET_API_STATUS.
--*/
{
NET_API_STATUS NetApiStatus = NERR_Success;
PDAV_USE_ENTRY BackPointer = NULL;
NTSTATUS CloseStatus = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
BOOL didImpersonate = FALSE;
DavPrint((DEBUG_MISC, "DavDeleteUse: ForceLevel = %d\n", ForceLevel));
//
// 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 ) ) {
DavPrint((DEBUG_MISC, "DavDeleteUse: MatchedPointer->UseCount = %d\n",
MatchedPointer->UseCount));
MatchedPointer->UseCount--;
MatchedPointer->Remote->TotalUseCount--;
ASSERT(MatchedPointer->Remote->TotalUseCount);
goto EXIT_THE_FUNCTION;
}
NetApiStatus = DavImpersonateClient();
if (NetApiStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavDeleteUse/DavImpersonateClient: NetApiStatus = %08lx\n",
NetApiStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = TRUE;
CloseStatus = NtFsControlFile(MatchedPointer->DavCreateFileHandle,
NULL,
NULL,
NULL,
&(IoStatusBlock),
FSCTL_DAV_DELETE_CONNECTION,
&(ForceLevel),
sizeof(ForceLevel),
NULL,
0);
if (NT_SUCCESS(CloseStatus)) {
CloseStatus = IoStatusBlock.Status;
} else {
NetApiStatus = RtlNtStatusToDosError(CloseStatus);
goto EXIT_THE_FUNCTION;
}
//
// We need to close the file handle that was created when the "net use" was
// done. This handle is kept so that the underlying data structures in the
// kernel (like SrvCall, VNetRoot etc.) remain valid for this connection.
// Since we are deleting this connection now, we need to close this handle.
// Before closing we impersonate the client that issues this request, just
// to be in the safe side. Hence we impersonated the client above.
//
DavPrint((DEBUG_MISC,
"DavDeleteUse: Closing DavCreateFileHandle = %08lx\n",
MatchedPointer->DavCreateFileHandle));
CloseStatus = NtClose(MatchedPointer->DavCreateFileHandle);
if (CloseStatus != STATUS_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavDeleteUse/NtClose: CloseStatus = %08lx\n",
CloseStatus));
}
NetApiStatus = DavRevertToSelf();
if (NetApiStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavDeleteUse/DavRevertToSelf: NetApiStatus = %08lx\n",
NetApiStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = FALSE;
//
// Successfully deleted connection, and refound our entry. Delete symbolic
// link, if any.
//
NetApiStatus = DavDeleteSymbolicLink(MatchedPointer->Local,
MatchedPointer->TreeConnectStr,
NULL);
if (NetApiStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavDeleteuse/DavDeleteSymbolicLink: NetApiStatus = %08lx\n",
NetApiStatus));
goto EXIT_THE_FUNCTION;
}
BackPointer = (PDAV_USE_ENTRY)DavUseObject.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
//
DavUseObject.Table[Index].List = (PVOID)MatchedPointer->Next;
}
MatchedPointer->Remote->TotalUseCount -= MatchedPointer->UseCount;
if (MatchedPointer->Remote->TotalUseCount == 0) {
LocalFree((HLOCAL)MatchedPointer->Remote);
}
//
// Since MatchedPointer was allocated as one-chunk, and AuthUserName
// was stored in that chunk - so do not free AuthUserName
// separatly.
//
MatchedPointer->AuthUserName = NULL;
MatchedPointer->AuthUserNameLength = 0;
LocalFree((HLOCAL)MatchedPointer);
EXIT_THE_FUNCTION:
if (didImpersonate) {
DavRevertToSelf();
}
return NetApiStatus;
}
VOID
DavInitializeAndInsertTheServerShareEntry(
IN OUT PDAV_SERVER_SHARE_ENTRY ServerShEntry,
IN PWCHAR ServerName,
IN ULONG EntrySize
)
/*++
Routine Description:
This routine initializes a newly created ServerShare entry strucutre and
inserts it into the global ServerShareEntry table. Note that the caller
should take a lock on the ServerShareEntry Table before calling this routine.
Arguments:
ServerShEntry - Pointer to the ServerShare entry structure to be
initialized and inserted.
ServerName - Name of the server.
EntrySize - Size of the server entry including the server name.
Return Value:
none.
--*/
{
ULONG ServerHashID;
//
// IMPORTANT!!!! The caller should take a lock on the global ServerShareTable
// before calling this routine.
//
ASSERT(ServerName != NULL);
DavPrint((DEBUG_MISC,
"DavInitializeAndInsertTheServerShareEntry: ServerName: %ws\n",
ServerName));
ServerShEntry->ServerName = &ServerShEntry->StrBuffer[0];
wcscpy(ServerShEntry->ServerName, ServerName);
ServerShEntry->TimeValueInSec = time(NULL);
ServerShEntry->DavShareList = NULL;
ServerShEntry->NumOfShares = 0;
ServerHashID = DavHashTheServerName(ServerName);
//
// Insert the entry into the global ServerShareEntry table.
//
InsertHeadList( &(ServerShareTable[ServerHashID]),
&(ServerShEntry->ServerShareEntry) );
return;
}
BOOL
DavIsServerInServerShareTable(
IN PWCHAR ServerName,
OUT PDAV_SERVER_SHARE_ENTRY *ServerShEntry
)
/*++
Routine Description:
This routine checks to see if an entry for the ServerName supplied by the
caller exists in the global ServerShare table. If it does, the address of
the entry is returned in the caller supplied buffer. Note that the caller
should take a lock on the ServerShareTable before calling this routine.
Arguments:
ServerName - Name of the server.
ServerShEntry - Pointer to the ServerShare entry structure.
Return Value:
TRUE - Server entry exists in the ServerShare table.
FALSE - It does not. Duh.
--*/
{
BOOL isPresent = FALSE;
ULONG ServerHashID;
PLIST_ENTRY listEntry;
PDAV_SERVER_SHARE_ENTRY SSEntry;
//
// IMPORTANT!!!! The caller should take a lock on the global ServerShareTable
// before calling this routine.
//
ASSERT(ServerName != NULL);
DavPrint((DEBUG_MISC,
"DavIsServerInServerShareTable: ServerName = %ws\n", ServerName));
//
// Get the hash index of the server.
//
ServerHashID = DavHashTheServerName(ServerName);
//
// Search the ServerShare table at this index to see if an entry for this
// server exists.
//
listEntry = ServerShareTable[ServerHashID].Flink;
while ( listEntry != &ServerShareTable[ServerHashID] ) {
//
// Get the pointer to the DAV_SERVER_SHARE_ENTRY structure.
//
SSEntry = CONTAINING_RECORD(listEntry,
DAV_SERVER_SHARE_ENTRY,
ServerShareEntry);
//
// Check to see if this entry is for the server in question.
//
if ( wcscmp(ServerName, SSEntry->ServerName) == 0 ) {
isPresent = TRUE;
break;
}
listEntry = listEntry->Flink;
}
if (isPresent) {
//
// Yes, we found the entry for this server. Return its address to the
// caller in the supplied buffer.
//
*ServerShEntry = SSEntry;
return isPresent;
}
//
// We did not find an entry for this server. Duh.
//
*ServerShEntry = NULL;
return isPresent;
}
DWORD
DavGetShareListFromServer(
PDAV_SERVER_SHARE_ENTRY ServerShEntry
)
/*++
Routine Description:
This routine gets the shares of the server in the ServerShEntry structure.
Arguments:
ServerShEntry - Pointer to the ServerShare entry structure.
Return Value:
ERROR_SUCESS or the Win32 error code.
--*/
{
DWORD WStatus = ERROR_SUCCESS;
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
BOOL ReturnVal = FALSE, readDone = FALSE;
PCHAR DataBuff = NULL;
DWORD NumRead = 0, NumOfFileEntries = 0;
PVOID Ctx1 = NULL, Ctx2 = NULL;
PDAV_FILE_ATTRIBUTES DavFileAttributes = NULL;
BOOL bStatus = TRUE, revert = FALSE;
DavPrint((DEBUG_MISC,
"DavGetShareListFromServer: ServerName = %ws\n",
ServerShEntry->ServerName));
WStatus = DavImpersonateClient();
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/DavImpersonateClient: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
revert = TRUE;
DavConnHandle = InternetConnectW(ISyncHandle,
ServerShEntry->ServerName,
INTERNET_DEFAULT_HTTP_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0);
if (DavConnHandle == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/InternetConnectW. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// To get the shares, we do a PROPFIND on the root directory. The Depth
// header should be set to 1. The directory browsing should be enabled on
// the DAV server to enumerate the shares.
//
bStatus = DavHttpOpenRequestW(DavConnHandle,
L"PROPFIND",
L"/",
L"HTTP/1.1",
NULL,
NULL,
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES,
0,
L"DavGetSharesListFromServer",
&DavOpenHandle);
if(bStatus == FALSE) {
WStatus = GetLastError();
goto EXIT_THE_FUNCTION;
}
if (DavOpenHandle == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/HttpOpenRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
L"Depth: 1\n",
-1L,
HTTP_ADDREQ_FLAG_ADD |
HTTP_ADDREQ_FLAG_REPLACE );
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/HttpAddRequestHeadersW. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We need to add the header "translate:f" to tell IIS that it should
// allow the user to excecute this VERB on the specified path which it
// would not allow (in some cases) otherwise. Finally, there is a special
// flag in the metabase to allow for uploading of "dangerous" content
// (anything that can be run on the server). This is the ScriptSourceAccess
// flag in the UI or the AccessSource flag in the metabase. You will need
// to set this bit to true as well as correct NT ACLs in order to be able
// to upload .exes or anything executable.
//
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
L"translate: f\n",
-1L,
HTTP_ADDREQ_FLAG_ADD |
HTTP_ADDREQ_FLAG_REPLACE );
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/HttpAddRequestHeadersW. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
RESEND_THE_REQUEST:
ReturnVal = HttpSendRequestExW(DavOpenHandle, NULL, NULL, HSR_SYNC, 0);
if (!ReturnVal) {
WStatus = GetLastError();
//
// If we fail with ERROR_INTERNET_NAME_NOT_RESOLVED, we make the
// following call so that WinInet picks up the correct proxy settings
// if they have changed. This is because we do call InternetOpen
// (to create a global handle from which every other handle is derived)
// when the service starts and this could be before the user logon
// happpens. In such a case the HKCU would not have been initialized
// and WinInet wouldn't get the correct proxy settings.
//
if (WStatus == ERROR_INTERNET_NAME_NOT_RESOLVED) {
InternetSetOptionW(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
}
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/HttpSendRequestExW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
ReturnVal = HttpEndRequestW(DavOpenHandle, NULL, HSR_SYNC, 0);
if (!ReturnVal) {
WStatus = GetLastError();
//
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then
// WinInet is trying to authenticate itself with the server. We need to
// repeat the HttpSend and HttpEnd request calls.
//
if (WStatus == ERROR_INTERNET_FORCE_RETRY) {
goto RESEND_THE_REQUEST;
}
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/HttpEndRequestW. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Check for return status. Server may have returned error. Like no-access or
// others.
//
WStatus = DavQueryAndParseResponse(DavOpenHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/DavQueryAndParseResponse: WStatus = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, NUM_OF_BYTES_TO_READ);
if (DataBuff == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/LocalAlloc: WStatus = %08lx\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Read the response and parse it.
//
do {
ReturnVal = InternetReadFile(DavOpenHandle,
(LPVOID)DataBuff,
NUM_OF_BYTES_TO_READ,
&(NumRead));
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/InternetReadFile: WStatus = "
"%08lx\n", WStatus));
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavGetShareListFromServer: NumRead = %d\n", NumRead));
readDone = (NumRead == 0) ? TRUE : FALSE;
WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, NumRead, readDone);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/DavPushData."
" Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
if (readDone) {
break;
}
} while ( TRUE );
//
// We now need to parse the data.
//
DavFileAttributes = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
sizeof(DAV_FILE_ATTRIBUTES) );
if (DavFileAttributes == NULL) {
WStatus = GetLastError();
DavCloseContext(Ctx1, Ctx2);
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/LocalAlloc. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
InitializeListHead( &(DavFileAttributes->NextEntry) );
WStatus = DavParseData(DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries);
if (WStatus != ERROR_SUCCESS) {
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
DavFileAttributes = NULL;
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/DavParseData. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
ServerShEntry->NumOfShares = NumOfFileEntries;
ServerShEntry->DavShareList = DavFileAttributes;
DavCloseContext(Ctx1, Ctx2);
WStatus = ERROR_SUCCESS;
EXIT_THE_FUNCTION:
if (DavOpenHandle) {
InternetCloseHandle(DavOpenHandle);
}
if (DavConnHandle) {
InternetCloseHandle(DavConnHandle);
}
if (revert) {
DWORD RStatus;
RStatus = DavRevertToSelf();
if (RStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavGetShareListFromServer/DavRevertToSelf: RStatus = %08lx\n",
RStatus));
}
}
if (DataBuff) {
LocalFree(DataBuff);
}
return WStatus;
}
DWORD
DavrWinlogonLogonEvent(
IN handle_t dav_binding_h
)
/*++
Routine Description:
This routine implements an RPC server function. Its called by davclnt.dll
everytime a user logs on to the system. It increments the global variable
DavNumberOfLoggedOnUsers.
Arguments:
dav_binding_h - The explicit RPC binding handle.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD WStatus = ERROR_SUCCESS;
EnterCriticalSection( &(DavLoggedOnUsersLock) );
//
// Increment the Number of logged on users.
//
DavNumberOfLoggedOnUsers += 1;
LeaveCriticalSection( &(DavLoggedOnUsersLock) );
return WStatus;
}
DWORD
DavrWinlogonLogoffEvent(
IN handle_t dav_binding_h
)
/*++
Routine Description:
This routine implements an RPC server function. Its called by davclnt.dll
everytime a user logs off from the system. It decrements the global variable
DavNumberOfLoggedOnUsers.
Arguments:
dav_binding_h - The explicit RPC binding handle.
Return Value:
ERROR_SUCCESS or the appropriate Win32 error code.
--*/
{
DWORD WStatus = ERROR_SUCCESS;
EnterCriticalSection( &(DavLoggedOnUsersLock) );
//
// Decrement the Number of logged on users.
//
DavNumberOfLoggedOnUsers -= 1;
LeaveCriticalSection( &(DavLoggedOnUsersLock) );
return WStatus;
}
BOOL
DavCheckTheNonDAVServerList(
PWCHAR ServerName
)
/*++
Routine Description:
This routine checks to see if the ServerName passed is in the list of
servers that do not speak DAV (NonDAVServerList). Before we check, we go
through the list once and free up all the old entries.
IMPORTANT!!!
The caller of this routine must acquire the NonDAVServerListLock which is
used to synchronize access to this global list.
Arguments:
ServerName - The ServerName that needs to be checked.
Returns:
TRUE - ServerName is a part of the NonDAVServerList.
FALSE - ServerName is NOT a part of the NonDAVServerList.
--*/
{
PLIST_ENTRY thisListEntry = NULL;
PNON_DAV_SERVER_ENTRY NonDavServerEntry = NULL;
time_t CurrentTimeInSec;
BOOL foundEntry = FALSE;
ULONGLONG TimeDiff;
DavPrint((DEBUG_DEBUG,
"DavCheckTheNonDAVServerList: ServerName = %ws\n",
ServerName));
//
// First go through the entire list and clean up the old entries.
//
thisListEntry = NonDAVServerList.Flink;
while ( thisListEntry != &(NonDAVServerList) ) {
NonDavServerEntry = CONTAINING_RECORD(thisListEntry,
NON_DAV_SERVER_ENTRY,
listEntry);
thisListEntry = thisListEntry->Flink;
CurrentTimeInSec = time(NULL);
TimeDiff = (CurrentTimeInSec - NonDavServerEntry->TimeValueInSec);
if (TimeDiff >= ServerNotFoundCacheLifeTimeInSec) {
RemoveEntryList( &(NonDavServerEntry->listEntry) );
LocalFree(NonDavServerEntry->ServerName);
NonDavServerEntry->ServerName = NULL;
LocalFree(NonDavServerEntry);
NonDavServerEntry = NULL;
}
}
//
// Now that we have cleaned up all the old entries, go through the list
// and find out the entry that matches the ServerName.
//
thisListEntry = NonDAVServerList.Flink;
while ( thisListEntry != &(NonDAVServerList) ) {
NonDavServerEntry = CONTAINING_RECORD(thisListEntry,
NON_DAV_SERVER_ENTRY,
listEntry);
thisListEntry = thisListEntry->Flink;
if ( !_wcsicmp(ServerName, NonDavServerEntry->ServerName) ) {
foundEntry = TRUE;
break;
}
}
return foundEntry;
}