/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: wsutil.c Abstract: This module contains miscellaneous utility routines used by the Workstation service. Author: Rita Wong (ritaw) 01-Mar-1991 Revision History: --*/ #include "wsutil.h" //-------------------------------------------------------------------// // // // Local function prototypes // // // //-------------------------------------------------------------------// STATIC NET_API_STATUS WsGrowTable( IN PUSERS_OBJECT Users ); //-------------------------------------------------------------------// // // // Global variables // // // //-------------------------------------------------------------------// // // Debug trace flag for selecting which trace statements to output // #if DBG DWORD WorkstationTrace = 0; #endif // DBG NET_API_STATUS WsInitializeUsersObject( IN PUSERS_OBJECT Users ) /*++ Routine Description: This function allocates the table of users, and initializes the resource to serialize access to this table. Arguments: Users - Supplies a pointer to the users object. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { // // Allocate the users table memory so that it can be grown (reallocated) // as more entries are needed. // if ((Users->TableMemory = (HANDLE) LocalAlloc( LMEM_ZEROINIT | LMEM_MOVEABLE, INITIAL_USER_COUNT * sizeof(PER_USER_ENTRY) )) == NULL) { return GetLastError(); } Users->TableSize = INITIAL_USER_COUNT; // // Keep the memory from moving by locking it to a specific location in // virtual memory. When it is necessary to grow this table, which may // result in the virtual memory being relocated, it will be unlocked. // if ((Users->Table = (PPER_USER_ENTRY) LocalLock(Users->TableMemory)) == NULL) { return GetLastError(); } // // Initialize the resource for the users table. // try { RtlInitializeResource(&Users->TableResource); } except(EXCEPTION_EXECUTE_HANDLER) { return RtlNtStatusToDosError(GetExceptionCode()); } return NERR_Success; } VOID WsDestroyUsersObject( IN PUSERS_OBJECT Users ) /*++ Routine Description: This function free the table allocated for logged on users, and deletes the resource used to serialize access to this table. Arguments: Users - Supplies a pointer to the users object. Return Value: None. --*/ { // // Unlock the memory holding the table to allow us to free it. // LocalUnlock(Users->TableMemory); (void) LocalFree(Users->TableMemory); RtlDeleteResource(&(Users->TableResource)); } NET_API_STATUS WsGetUserEntry( IN PUSERS_OBJECT Users, 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: Users - Supplies a pointer to the users object. 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 status; DWORD i; ULONG FreeEntryIndex = MAXULONG; for (i = 0; i < Users->TableSize; i++) { // // If the LogonId matches the entry in the UsersTable, we've found the // correct user entry. // if (RtlEqualLuid(LogonId, &Users->Table[i].LogonId)) { *Index = i; return NERR_Success; } else if (FreeEntryIndex == MAXULONG && Users->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; } // // Could not find an empty entry in the UsersTable, need to grow // if (FreeEntryIndex == MAXULONG) { if ((status = WsGrowTable(Users)) != NERR_Success) { return status; } FreeEntryIndex = i; } // // Create a new entry for current user // RtlCopyLuid(&Users->Table[FreeEntryIndex].LogonId, LogonId); *Index = FreeEntryIndex; return NERR_Success; } STATIC NET_API_STATUS WsGrowTable( IN PUSERS_OBJECT Users ) /*++ 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: Users - Supplies a pointer to the users object. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { // // Unlock the Use Table virtual memory so that Win32 can move it // around to find a larger piece of contiguous virtual memory if // necessary. // LocalUnlock(Users->TableMemory); // // Grow users table // Users->TableMemory = LocalReAlloc( Users->TableMemory, (Users->TableSize + GROW_USER_COUNT) * sizeof(PER_USER_ENTRY), LMEM_ZEROINIT | LMEM_MOVEABLE ); if (Users->TableMemory == NULL) { return GetLastError(); } // // Update new size of Use Table // Users->TableSize += GROW_USER_COUNT; // // Lock Use Table virtual memory so that it cannot be moved // if ((Users->Table = (PPER_USER_ENTRY) LocalLock(Users->TableMemory)) == NULL) { return GetLastError(); } return NERR_Success; } NET_API_STATUS WsMapStatus( IN NTSTATUS NtStatus ) /*++ Routine Description: This function takes an NT status code and maps it to the appropriate error code expected from calling a LAN Man API. Arguments: NtStatus - Supplies the NT status. Return Value: Returns the appropriate LAN Man error code for the NT status. --*/ { // // A small optimization for the most common case. // if (NtStatus == STATUS_SUCCESS) { return NERR_Success; } switch (NtStatus) { case STATUS_OBJECT_NAME_COLLISION: return ERROR_ALREADY_ASSIGNED; case STATUS_OBJECT_NAME_NOT_FOUND: return NERR_UseNotFound; case STATUS_IMAGE_ALREADY_LOADED: case STATUS_REDIRECTOR_STARTED: return ERROR_SERVICE_ALREADY_RUNNING; case STATUS_REDIRECTOR_HAS_OPEN_HANDLES: return ERROR_REDIRECTOR_HAS_OPEN_HANDLES; default: return NetpNtStatusToApiStatus(NtStatus); } } int WsCompareString( IN LPTSTR String1, IN DWORD Length1, IN LPTSTR String2, IN DWORD Length2 ) /*++ Routine Description: This function compares two strings based on their lengths. The return value indicates if the strings are equal or String1 is less than String2 or String1 is greater than String2. This function is a modified version of RtlCompareString. Arguments: String1 - Supplies the pointer to the first string. Length1 - Supplies the length of String1 in characters. String2 - Supplies the pointer to the second string. Length2 - Supplies the length of String2 in characters. Return Value: Signed value that gives the results of the comparison: 0 - String1 equals String2 < 0 - String1 less than String2 > 0 - String1 greater than String2 --*/ { TCHAR Char1, Char2; int CharDiff; while (Length1 && Length2) { Char1 = *String1++; Char2 = *String2++; if ((CharDiff = (Char1 - Char2)) != 0) { return CharDiff; } Length1--; Length2--; } return Length1 - Length2; } int WsCompareStringU( IN LPWSTR String1, IN DWORD Length1, IN LPTSTR String2, IN DWORD Length2 ) { UNICODE_STRING S1; UNICODE_STRING S2; int rValue; S1.Length = S1.MaximumLength = (USHORT) (Length1 * sizeof(WCHAR)); S1.Buffer = String1; S2.Length = S2.MaximumLength = (USHORT) (Length2 * sizeof(WCHAR)); S2.Buffer = String2; rValue = RtlCompareUnicodeString(&S1, &S2, TRUE); return(rValue); } BOOL WsCopyStringToBuffer( IN PUNICODE_STRING SourceString, IN LPBYTE FixedPortion, IN OUT LPTSTR *EndOfVariableData, OUT LPTSTR *DestinationStringPointer ) /*++ Routine Description: This function converts the unicode source string to ANSI string (if we haven't flipped the unicode switch yet) and calls NetpCopyStringToBuffer. Arguments: SourceString - Supplies a pointer to the source string to copy into the output buffer. If String is null then a pointer to a zero terminator is inserted into output buffer. FixedDataEnd - Supplies a pointer to just after the end of the last fixed structure in the buffer. EndOfVariableData - Supplies an address to a pointer to just after the last position in the output buffer that variable data can occupy. Returns a pointer to the string written in the output buffer. DestinationStringPointer - Supplies a pointer to the place in the fixed portion of the output buffer where a pointer to the variable data should be written. Return Value: Returns TRUE if string fits into output buffer, FALSE otherwise. --*/ { if (! NetpCopyStringToBuffer( SourceString->Buffer, SourceString->Length / sizeof(WCHAR), FixedPortion, EndOfVariableData, DestinationStringPointer )) { return FALSE; } return TRUE; } NET_API_STATUS WsImpersonateClient( VOID ) /*++ Routine Description: This function calls RpcImpersonateClient to impersonate the current caller of an API. Arguments: None. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; if ((status = RpcImpersonateClient(NULL)) != NERR_Success) { NetpKdPrint(("[Wksta] Fail to impersonate client 0x%x\n", status)); } return status; } NET_API_STATUS WsRevertToSelf( VOID ) /*++ Routine Description: This function calls RpcRevertToSelf to undo an impersonation. Arguments: None. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; if (( status = RpcRevertToSelf()) != NERR_Success) { NetpKdPrint(("[Wksta] Fail to revert to self 0x%x\n", status)); NetpAssert(FALSE); } return status; } NET_API_STATUS WsImpersonateAndGetLogonId( OUT PLUID LogonId ) /*++ Routine Description: This function gets the logon id of the current thread. Arguments: LogonId - Returns the logon id of the current process. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; NTSTATUS ntstatus; HANDLE CurrentThreadToken; TOKEN_STATISTICS TokenStats; ULONG ReturnLength; if ((status = WsImpersonateClient()) != NERR_Success) { return status; } ntstatus = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, // Use workstation service's security // context to open thread token &CurrentThreadToken ); status = NetpNtStatusToApiStatus(ntstatus); if (! NT_SUCCESS(ntstatus)) { NetpKdPrint(("[Wksta] Cannot open the current thread token %08lx\n", ntstatus)); goto RevertToSelf; } // // Get the logon id of the current thread // ntstatus = NtQueryInformationToken( CurrentThreadToken, TokenStatistics, (PVOID) &TokenStats, sizeof(TokenStats), &ReturnLength ); status = NetpNtStatusToApiStatus(ntstatus); if (! NT_SUCCESS(ntstatus)) { NetpKdPrint(("[Wksta] Cannot query current thread's token %08lx\n", ntstatus)); NtClose(CurrentThreadToken); goto RevertToSelf; } RtlCopyLuid(LogonId, &TokenStats.AuthenticationId); NtClose(CurrentThreadToken); RevertToSelf: WsRevertToSelf(); return status; } NET_API_STATUS WsOpenDestinationMailslot( IN LPWSTR TargetName, IN LPWSTR MailslotName, OUT PHANDLE MailslotHandle ) /*++ Routine Description: This function combines the target domain or computer name and the mailslot name to form the destination mailslot name. It then opens this destination mailslot and returns a handle to it. Arguments: TargetName - Supplies the name of a domain or computer which we want to target when sending a mailslot message. MailslotName - Supplies the name of the mailslot. MailslotHandle - Returns the handle to the destination mailslot of \\TargetName\MailslotName. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status = NERR_Success; LPWSTR DestinationMailslot; if ((DestinationMailslot = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, (UINT) (wcslen(TargetName) + wcslen(MailslotName) + 3) * sizeof(WCHAR) )) == NULL) { return GetLastError(); } wcscpy(DestinationMailslot, L"\\\\"); wcscat(DestinationMailslot, TargetName); wcscat(DestinationMailslot, MailslotName); if ((*MailslotHandle = (HANDLE) CreateFileW( DestinationMailslot, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE) { status = GetLastError(); NetpKdPrint(("[Wksta] Error opening mailslot %s %lu", DestinationMailslot, status)); } (void) LocalFree(DestinationMailslot); return status; } NET_API_STATUS WsImpersonateAndGetSessionId( OUT 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. --*/ { NET_API_STATUS status; NTSTATUS ntstatus; HANDLE CurrentThreadToken; ULONG SessionId; ULONG ReturnLength; if ((status = WsImpersonateClient()) != NERR_Success) { return status; } ntstatus = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, // Use workstation service's security // context to open thread token &CurrentThreadToken ); status = NetpNtStatusToApiStatus(ntstatus); if (! NT_SUCCESS(ntstatus)) { NetpKdPrint(("[Wksta] Cannot open the current thread token %08lx\n", ntstatus)); goto RevertToSelf; } // // Get the session id of the current thread // ntstatus = NtQueryInformationToken( CurrentThreadToken, TokenSessionId, &SessionId, sizeof(ULONG), &ReturnLength ); status = NetpNtStatusToApiStatus(ntstatus); if (! NT_SUCCESS(ntstatus)) { NetpKdPrint(("[Wksta] Cannot query current thread's token %08lx\n", ntstatus)); NtClose(CurrentThreadToken); goto RevertToSelf; } NtClose(CurrentThreadToken); *pSessionId = SessionId; RevertToSelf: WsRevertToSelf(); return status; }