1665 lines
48 KiB
C
1665 lines
48 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
user.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the worker routines for the NetWkstaUser
|
||
APIs implemented in the Workstation service.
|
||
|
||
Author:
|
||
|
||
Rita Wong (ritaw) 20-Feb-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "wsutil.h"
|
||
#include "wsdevice.h"
|
||
#include "wssec.h"
|
||
#include "wsconfig.h"
|
||
#include "wslsa.h"
|
||
#include "wswksta.h"
|
||
|
||
#include <strarray.h>
|
||
#include <config.h> // NT config file helpers in netlib
|
||
#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
|
||
#include <confname.h> // Section and keyword equates.
|
||
|
||
#include "wsregcfg.h" // Registry helpers
|
||
|
||
#define WS_OTH_DOMAIN_DELIMITER_STR L" "
|
||
#define WS_OTH_DOMAIN_DELIMITER_CHAR L' '
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Local function prototypes //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetUserInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
OUT PMSV1_0_GETUSERINFO_RESPONSE *UserInfoResponse,
|
||
OUT PDGRECEIVE_NAMES *DgrNames,
|
||
OUT LPDWORD DgrNamesCount,
|
||
IN OUT LPDWORD TotalBytesNeeded OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetActiveDgrNames(
|
||
IN PLUID LogonId,
|
||
OUT PDGRECEIVE_NAMES *DgrNames,
|
||
OUT LPDWORD DgrNamesCount,
|
||
IN OUT LPDWORD TotalBytesNeeded OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsSetOtherDomains(
|
||
IN DWORD Level,
|
||
IN LPBYTE Buffer
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsEnumUserInfo(
|
||
IN DWORD Level,
|
||
IN DWORD PreferedMaximumLength,
|
||
IN PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse,
|
||
OUT LPBYTE *OutputBuffer,
|
||
OUT LPDWORD EntriesRead,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsPackageUserInfo(
|
||
IN DWORD Level,
|
||
IN DWORD UserInfoFixedLength,
|
||
IN PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse,
|
||
IN PDGRECEIVE_NAMES DgrNames,
|
||
IN DWORD DgrNamesCount,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN OUT LPDWORD EntriesRead OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
BOOL
|
||
WsFillUserInfoBuffer(
|
||
IN DWORD Level,
|
||
IN PMSV1_0_GETUSERINFO_RESPONSE UserInfo,
|
||
IN PDGRECEIVE_NAMES DgrNames,
|
||
IN DWORD DgrNamesCount,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN DWORD UserInfoFixedLength
|
||
);
|
||
|
||
STATIC
|
||
VOID
|
||
WsWriteOtherDomains(
|
||
IN PDGRECEIVE_NAMES DgrNames,
|
||
IN DWORD DgrNamesCount,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN DWORD UserInfoFixedLength,
|
||
OUT LPWSTR *OtherDomainsPointer
|
||
);
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Global variables //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaUserGetInfo(
|
||
IN LPTSTR Reserved,
|
||
IN DWORD Level,
|
||
OUT LPWKSTA_USER_INFO UserInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaUserGetInfo entry point in the Workstation
|
||
service. It calls the LSA subsystem and the MSV1_0 authentication
|
||
package to get per user information.
|
||
|
||
Arguments:
|
||
|
||
Reserved - Must be 0.
|
||
|
||
Level - Supplies the requested level of information.
|
||
|
||
UserInfo - Returns, in this structure, a pointer to a buffer which
|
||
contains the requested user information.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
LUID LogonId;
|
||
|
||
PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse = NULL;
|
||
LPBYTE FixedPortion;
|
||
LPTSTR EndOfVariableData;
|
||
|
||
DWORD UserInfoFixedLength = USER_FIXED_LENGTH(Level);
|
||
LPBYTE OutputBuffer;
|
||
|
||
DWORD TotalBytesNeeded = 0;
|
||
|
||
PDGRECEIVE_NAMES DgrNames = NULL;
|
||
DWORD DgrNamesCount;
|
||
|
||
|
||
if (Reserved != NULL) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Levels 0, 1, and 1101 are valid
|
||
//
|
||
if (Level > 1 && Level != 1101) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
//
|
||
// Impersonate caller and get the logon id
|
||
//
|
||
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
if ((status = WsGetUserInfo(
|
||
&LogonId,
|
||
Level,
|
||
&UserInfoResponse,
|
||
&DgrNames,
|
||
&DgrNamesCount,
|
||
&TotalBytesNeeded
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
if ((OutputBuffer = MIDL_user_allocate(TotalBytesNeeded)) == NULL) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto FreeBuffers;
|
||
}
|
||
RtlZeroMemory((PVOID) OutputBuffer, TotalBytesNeeded);
|
||
|
||
SET_USER_INFO_POINTER(UserInfo, OutputBuffer);
|
||
|
||
|
||
//
|
||
// Write the user information into output buffer.
|
||
//
|
||
FixedPortion = OutputBuffer;
|
||
EndOfVariableData = (LPTSTR) ((DWORD_PTR) FixedPortion + TotalBytesNeeded);
|
||
|
||
status = WsPackageUserInfo(
|
||
Level,
|
||
UserInfoFixedLength,
|
||
UserInfoResponse,
|
||
DgrNames,
|
||
DgrNamesCount,
|
||
&FixedPortion,
|
||
&EndOfVariableData,
|
||
NULL
|
||
);
|
||
|
||
NetpAssert(status == NERR_Success);
|
||
|
||
FreeBuffers:
|
||
if (UserInfoResponse != NULL) {
|
||
(void) LsaFreeReturnBuffer((PVOID) UserInfoResponse);
|
||
}
|
||
|
||
if (DgrNames != NULL) {
|
||
MIDL_user_free((PVOID) DgrNames);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaUserSetInfo(
|
||
IN LPTSTR Reserved,
|
||
IN DWORD Level,
|
||
IN LPWKSTA_USER_INFO UserInfo,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaUserSetInfo entry point in the Workstation
|
||
service. It sets the other domains for the current user.
|
||
|
||
Arguments:
|
||
|
||
Reserved - Must be NULL.
|
||
|
||
Level - Supplies the level of information.
|
||
|
||
UserInfo - Supplies a pointer to union structure of pointers to
|
||
buffer of fields to set. The level denotes the fields supplied in
|
||
this buffer.
|
||
|
||
ErrorParameter - Returns the identifier to the invalid parameter if
|
||
this function returns ERROR_INVALID_PARAMETER.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status = NERR_Success;
|
||
|
||
|
||
if (Reserved != NULL) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Only admins can set redirector configurable fields. Validate access.
|
||
//
|
||
if (NetpAccessCheckAndAudit(
|
||
WORKSTATION_DISPLAY_NAME, // Subsystem name
|
||
(LPTSTR) CONFIG_INFO_OBJECT, // Object type name
|
||
ConfigurationInfoSd, // Security descriptor
|
||
WKSTA_CONFIG_INFO_SET, // Desired access
|
||
&WsConfigInfoMapping // Generic mapping
|
||
) != NERR_Success) {
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Check for NULL input buffer
|
||
//
|
||
if (UserInfo->UserInfo1 == NULL) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
|
||
}
|
||
|
||
//
|
||
// Serialize write access
|
||
//
|
||
if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
switch (Level) {
|
||
|
||
//
|
||
// Other domains is the only settable field in the entire
|
||
// system-wide info structure
|
||
//
|
||
case 1:
|
||
case 1101:
|
||
|
||
if ((status = WsSetOtherDomains(
|
||
Level,
|
||
(LPBYTE) UserInfo->UserInfo1
|
||
)) == ERROR_INVALID_PARAMETER) {
|
||
if (ARGUMENT_PRESENT(ErrorParameter)) {
|
||
*ErrorParameter = WKSTA_OTH_DOMAINS_PARMNUM;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
status = ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
RtlReleaseResource(&WsInfo.ConfigResource);
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaUserEnum(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN OUT LPWKSTA_USER_ENUM_STRUCT UserInfo,
|
||
IN DWORD PreferedMaximumLength,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaUserEnum entry point in the Workstation
|
||
service.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
UserInfo - This structure supplies the level of information requested,
|
||
returns a pointer to the buffer allocated by the Workstation service
|
||
which contains a sequence of information structure of the specified
|
||
information level, and returns the number of entries read. The buffer
|
||
pointer is set to NULL if return code is not NERR_Success or
|
||
ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
|
||
value is only valid if the return code is NERR_Success or
|
||
ERROR_MORE_DATA.
|
||
|
||
PreferedMaximumLength - Supplies the number of bytes of information
|
||
to return in the buffer. If this value is MAXULONG, all available
|
||
information will be returned.
|
||
|
||
TotalEntries - Returns the total number of entries available. This value
|
||
is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
|
||
|
||
ResumeHandle - Supplies a handle to resume the enumeration from where it
|
||
left off the last time through. Returns the resume handle if return
|
||
code is ERROR_MORE_DATA.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse = NULL;
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Only levels 0 and 1
|
||
//
|
||
if (UserInfo->Level > 1) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
if( UserInfo->WkstaUserInfo.Level1 == NULL ) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (WsLsaRestrictAnonymous > 0) {
|
||
//
|
||
// Perform access validation on the caller.
|
||
//
|
||
if (NetpAccessCheckAndAudit(
|
||
WORKSTATION_DISPLAY_NAME, // Subsystem name
|
||
(LPTSTR) CONFIG_INFO_OBJECT, // Object type name
|
||
ConfigurationInfoSd, // Security descriptor
|
||
WKSTA_CONFIG_ADMIN_INFO_GET, // Desired access
|
||
&WsConfigInfoMapping // Generic mapping
|
||
) != NERR_Success) {
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Ask authentication package to enumerate users who are physically
|
||
// logged to the local machine.
|
||
//
|
||
if ((status = WsLsaEnumUsers(
|
||
(LPBYTE *) &EnumUsersResponse
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
if (EnumUsersResponse == NULL) {
|
||
return ERROR_GEN_FAILURE;
|
||
}
|
||
|
||
//
|
||
// If no users are logged on, set appropriate fields and return success.
|
||
//
|
||
if (EnumUsersResponse->NumberOfLoggedOnUsers == 0) {
|
||
UserInfo->WkstaUserInfo.Level1->Buffer = NULL;
|
||
UserInfo->WkstaUserInfo.Level1->EntriesRead = 0;
|
||
*TotalEntries = 0;
|
||
status = NERR_Success;
|
||
} else {
|
||
status = WsEnumUserInfo(
|
||
UserInfo->Level,
|
||
PreferedMaximumLength,
|
||
EnumUsersResponse,
|
||
(LPBYTE *) &(UserInfo->WkstaUserInfo.Level1->Buffer),
|
||
(LPDWORD) &(UserInfo->WkstaUserInfo.Level1->EntriesRead),
|
||
TotalEntries,
|
||
ResumeHandle
|
||
);
|
||
}
|
||
|
||
(void) LsaFreeReturnBuffer((PVOID) EnumUsersResponse);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsEnumUserInfo(
|
||
IN DWORD Level,
|
||
IN DWORD PreferedMaximumLength,
|
||
IN PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse,
|
||
OUT LPBYTE *OutputBuffer,
|
||
OUT LPDWORD EntriesRead,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function takes the logon IDs returned by MS V1.0 Authentication
|
||
Package to call it again to get information about each user.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
PreferedMaximumLength - Supplies the number of bytes of information
|
||
to return in the buffer. If this value is MAXULONG, all available
|
||
information will be returned.
|
||
|
||
EnumUsersResponse - Supplies the structure returned from calling the MS
|
||
V1.0 Authentication Package to enumerate logged on users.
|
||
|
||
OutputBuffer - Returns a pointer to the enumerated user information.
|
||
|
||
TotalEntries - Returns the total number of entries available. This value
|
||
is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
|
||
|
||
EntriesRead - Supplies a running total of the number of entries read
|
||
into the output buffer. This value is incremented every time a
|
||
user entry is successfully written into the output buffer.
|
||
|
||
ResumeHandle - Returns the handle to continue with the enumeration if
|
||
this function returns ERROR_MORE_DATA.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status = NERR_Success;
|
||
|
||
//
|
||
// Array of per user info entries, each entry contains a pointer
|
||
// LSA info, and a pointer to active datagram receiver names.
|
||
//
|
||
PWSPER_USER_INFO UserInfoArray;
|
||
|
||
DWORD UserInfoFixedLength = USER_FIXED_LENGTH(Level);
|
||
DWORD i;
|
||
DWORD OutputBufferLength = 0;
|
||
DWORD UserEntriesRead = 0;
|
||
DWORD OtherDomainsSize = 0;
|
||
|
||
LPDWORD PointerToOutputBufferLength = &OutputBufferLength;
|
||
|
||
LPBYTE FixedPortion;
|
||
LPTSTR EndOfVariableData;
|
||
|
||
DWORD StartEnumeration = 0;
|
||
|
||
|
||
if (PreferedMaximumLength != MAXULONG) {
|
||
|
||
//
|
||
// We will return as much as possible that fits into this specified
|
||
// buffer size.
|
||
//
|
||
OutputBufferLength =
|
||
ROUND_UP_COUNT(PreferedMaximumLength, ALIGN_WCHAR);
|
||
|
||
if (OutputBufferLength < UserInfoFixedLength) {
|
||
*OutputBuffer = NULL;
|
||
*EntriesRead = 0;
|
||
*TotalEntries = EnumUsersResponse->NumberOfLoggedOnUsers;
|
||
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
//
|
||
// This indicates that we should not bother calculating the
|
||
// total output buffer size needed.
|
||
//
|
||
PointerToOutputBufferLength = NULL;
|
||
}
|
||
|
||
//
|
||
// Allocate a temporary array to save pointers to user information
|
||
// we retrieve from the LSA and datagram receiver. This is because we
|
||
// need to go through the list of users twice: the first time to add
|
||
// up the number of bytes to allocate for the output buffer; the second
|
||
// time to write the user information into the output buffer.
|
||
//
|
||
if ((UserInfoArray = (PWSPER_USER_INFO) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
EnumUsersResponse->NumberOfLoggedOnUsers *
|
||
sizeof(WSPER_USER_INFO)
|
||
)) == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// Get the info for each user and calculate the amount of memory to
|
||
// allocate for the output buffer if PointerToOutputBufferLength is
|
||
// not set to NULL. If it was set to NULL, we will allocate the
|
||
// output buffer size as specified by the caller.
|
||
//
|
||
for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers; i++) {
|
||
|
||
if ((status = WsGetUserInfo(
|
||
&(EnumUsersResponse->LogonIds[i]),
|
||
Level,
|
||
(PMSV1_0_GETUSERINFO_RESPONSE *) &(UserInfoArray[i].LsaUserInfo),
|
||
(PDGRECEIVE_NAMES *) &UserInfoArray[i].DgrNames,
|
||
(LPDWORD) &UserInfoArray[i].DgrNamesCount,
|
||
PointerToOutputBufferLength
|
||
)) != NERR_Success) {
|
||
goto FreeBuffers;
|
||
}
|
||
|
||
}
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("[Wksta] NetrWkstaUserEnum: OutputBufferLength=%lu\n",
|
||
OutputBufferLength));
|
||
}
|
||
|
||
//
|
||
// Allocate the output buffer
|
||
//
|
||
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto FreeBuffers;
|
||
}
|
||
|
||
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
|
||
|
||
FixedPortion = *OutputBuffer;
|
||
EndOfVariableData = (LPTSTR) ((DWORD_PTR) FixedPortion + OutputBufferLength);
|
||
|
||
//
|
||
// Get the enumeration starting point.
|
||
//
|
||
if (ARGUMENT_PRESENT(ResumeHandle)) {
|
||
StartEnumeration = *ResumeHandle;
|
||
}
|
||
|
||
//
|
||
// Enumerate the user information
|
||
//
|
||
for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers &&
|
||
status == NERR_Success; i++) {
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("LsaList->ResumeKey=%lu\n",
|
||
EnumUsersResponse->EnumHandles[i]));
|
||
}
|
||
|
||
if (StartEnumeration <= EnumUsersResponse->EnumHandles[i]) {
|
||
|
||
status = WsPackageUserInfo(
|
||
Level,
|
||
UserInfoFixedLength,
|
||
UserInfoArray[i].LsaUserInfo,
|
||
UserInfoArray[i].DgrNames,
|
||
UserInfoArray[i].DgrNamesCount,
|
||
&FixedPortion,
|
||
&EndOfVariableData,
|
||
&UserEntriesRead
|
||
);
|
||
|
||
if (status == ERROR_MORE_DATA) {
|
||
*TotalEntries = (EnumUsersResponse->NumberOfLoggedOnUsers
|
||
- i) + UserEntriesRead;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Return entries read and total entries. We can only get NERR_Success
|
||
// or ERROR_MORE_DATA from WsPackageUserInfo.
|
||
//
|
||
*EntriesRead = UserEntriesRead;
|
||
|
||
if (status == NERR_Success) {
|
||
*TotalEntries = UserEntriesRead;
|
||
}
|
||
|
||
if (status == ERROR_MORE_DATA && ARGUMENT_PRESENT(ResumeHandle)) {
|
||
*ResumeHandle = EnumUsersResponse->EnumHandles[i - 1];
|
||
}
|
||
|
||
if (*EntriesRead == 0) {
|
||
MIDL_user_free(*OutputBuffer);
|
||
*OutputBuffer = NULL;
|
||
}
|
||
|
||
FreeBuffers:
|
||
for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers; i++) {
|
||
|
||
if (UserInfoArray[i].DgrNames != NULL) {
|
||
MIDL_user_free((PVOID) UserInfoArray[i].DgrNames);
|
||
}
|
||
|
||
if (UserInfoArray[i].LsaUserInfo != NULL) {
|
||
(void) LsaFreeReturnBuffer((PVOID) UserInfoArray[i].LsaUserInfo);
|
||
}
|
||
}
|
||
|
||
(void) LocalFree((HLOCAL) UserInfoArray);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetUserInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
OUT PMSV1_0_GETUSERINFO_RESPONSE *UserInfoResponse,
|
||
OUT PDGRECEIVE_NAMES *DgrNames,
|
||
OUT LPDWORD DgrNamesCount,
|
||
IN OUT LPDWORD TotalBytesNeeded OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function gets the other domains for the current user from
|
||
the datagram receiver.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
UserInfoResponse - Returns a pointer to the user information from the
|
||
authentication package.
|
||
|
||
DgrNames - Returns a pointer an array of active datagram receiver
|
||
names.
|
||
|
||
DgrNamesCount - Returns the number of entries in DgrNames.
|
||
|
||
TotalBytesNeeded - Returns the number of bytes required to in the
|
||
output buffer for writing the other domains to.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
|
||
ULONG UserInfoResponseLength;
|
||
|
||
DWORD OtherDomainsSize = 0;
|
||
|
||
|
||
//
|
||
// Ask the datagram receiver for the other domains
|
||
//
|
||
if (Level == 1 || Level == 1101) {
|
||
if ((status = WsGetActiveDgrNames(
|
||
LogonId,
|
||
DgrNames,
|
||
DgrNamesCount,
|
||
TotalBytesNeeded
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Don't get user info from authentication package if level
|
||
// is 1101 since only other domains are returned in this level.
|
||
//
|
||
if (Level != 1101) {
|
||
|
||
//
|
||
// Ask authentication package for user information.
|
||
//
|
||
if ((status = WsLsaGetUserInfo(
|
||
LogonId,
|
||
(LPBYTE *) UserInfoResponse,
|
||
&UserInfoResponseLength
|
||
)) != NERR_Success) {
|
||
|
||
MIDL_user_free((PVOID) *DgrNames);
|
||
*DgrNames = NULL;
|
||
*DgrNamesCount = 0;
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Calculate the amount of memory needed to hold the user information
|
||
// and allocate the return buffer of that size.
|
||
//
|
||
if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
|
||
(*TotalBytesNeeded) +=
|
||
FIXED_PLUS_LSA_SIZE(
|
||
Level,
|
||
(*UserInfoResponse)->UserName.Length +
|
||
sizeof(TCHAR),
|
||
(*UserInfoResponse)->LogonDomainName.Length +
|
||
sizeof(TCHAR),
|
||
(*UserInfoResponse)->LogonServer.Length +
|
||
sizeof(TCHAR)
|
||
);
|
||
}
|
||
}
|
||
else {
|
||
*TotalBytesNeeded += USER_FIXED_LENGTH(Level);
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetActiveDgrNames(
|
||
IN PLUID LogonId,
|
||
OUT PDGRECEIVE_NAMES *DgrNames,
|
||
OUT LPDWORD DgrNamesCount,
|
||
IN OUT LPDWORD TotalBytesNeeded OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function gets the other domains for the current user from
|
||
the datagram receiver.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
DgrNames - Returns a pointer an array of active datagram receiver
|
||
names.
|
||
|
||
DgrNamesCount - Returns the number of entries in DgrNames.
|
||
|
||
TotalBytesNeeded - Returns the number of bytes required to in the
|
||
output buffer for writing the other domains to.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
|
||
LMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
|
||
DWORD EnumDgNamesHintSize = 0;
|
||
DWORD i;
|
||
|
||
Drp.Version = LMDR_REQUEST_PACKET_VERSION;
|
||
Drp.Type = EnumerateNames;
|
||
RtlCopyLuid(&Drp.LogonId, LogonId);
|
||
Drp.Parameters.EnumerateNames.ResumeHandle = 0;
|
||
Drp.Parameters.EnumerateNames.TotalBytesNeeded = 0;
|
||
|
||
//
|
||
// Get the other domains from the datagram receiver.
|
||
//
|
||
if ((status = WsDeviceControlGetInfo(
|
||
DatagramReceiver,
|
||
WsDgReceiverDeviceHandle,
|
||
IOCTL_LMDR_ENUMERATE_NAMES,
|
||
(PVOID) &Drp,
|
||
sizeof(LMDR_REQUEST_PACKET),
|
||
(LPBYTE *) DgrNames,
|
||
MAXULONG,
|
||
EnumDgNamesHintSize,
|
||
NULL
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Include room for NULL character, in case there are no
|
||
// other domains
|
||
//
|
||
if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
|
||
(*TotalBytesNeeded) += sizeof(TCHAR);
|
||
}
|
||
|
||
|
||
*DgrNamesCount = Drp.Parameters.EnumerateNames.EntriesRead;
|
||
|
||
if (*DgrNamesCount == 0 && *DgrNames != NULL) {
|
||
MIDL_user_free((PVOID) *DgrNames);
|
||
*DgrNames = NULL;
|
||
}
|
||
|
||
//
|
||
// Calculate the amount of memory to allocate for the output buffer
|
||
//
|
||
if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
|
||
for (i = 0; i < *DgrNamesCount; i++) {
|
||
|
||
//
|
||
// Add up the lengths of all the other domain names
|
||
//
|
||
if ((*DgrNames)[i].Type == OtherDomain) {
|
||
(*TotalBytesNeeded) += (*DgrNames)[i].DGReceiverName.Length +
|
||
sizeof(TCHAR);
|
||
}
|
||
}
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsSetOtherDomains(
|
||
IN DWORD Level,
|
||
IN LPBYTE Buffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets the other domains for the current user in
|
||
the datagram receiver.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information.
|
||
|
||
Buffer - Supplies a buffer which contains the information structure
|
||
if Parameter is WkstaSetAllParm. Otherwise Buffer contains the
|
||
individual field to set.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status = NERR_Success;
|
||
|
||
PCHAR DrpBuffer[sizeof(LMDR_REQUEST_PACKET) +
|
||
DNLEN * sizeof(TCHAR)];
|
||
PLMDR_REQUEST_PACKET Drp = (PLMDR_REQUEST_PACKET) DrpBuffer;
|
||
DWORD DrpSize = sizeof(LMDR_REQUEST_PACKET) +
|
||
DNLEN * sizeof(TCHAR);
|
||
|
||
PDGRECEIVE_NAMES DgrNames;
|
||
DWORD DgrNamesCount;
|
||
DWORD EnumDgNamesHintSize = 0;
|
||
|
||
DWORD NumberOfOtherDomains = 0;
|
||
DWORD i, j, k;
|
||
DWORD IndexToList = 0;
|
||
|
||
PWSNAME_RECORD OtherDomainsInfo = NULL;
|
||
LPTSTR OtherDomainsPointer;
|
||
LPTSTR OtherDomains;
|
||
|
||
LPTSTR CanonBuffer;
|
||
DWORD CanonBufferSize;
|
||
|
||
|
||
if (Level == 1101) {
|
||
OtherDomains = ((PWKSTA_USER_INFO_1101) Buffer)->wkui1101_oth_domains;
|
||
}
|
||
else {
|
||
OtherDomains = ((PWKSTA_USER_INFO_1) Buffer)->wkui1_oth_domains;
|
||
}
|
||
|
||
//
|
||
// NULL pointer means leave the other domains unmodified
|
||
//
|
||
if (OtherDomains == NULL) {
|
||
return NERR_Success;
|
||
}
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("WsSetOtherDomains: Input other domain is %ws\n", OtherDomains));
|
||
}
|
||
|
||
//
|
||
// Before canonicalizing the input buffer, we have to find out how
|
||
// many other domain entries are there so that we can supply a
|
||
// buffer of the right size to the canonicalize routine.
|
||
//
|
||
OtherDomainsPointer = OtherDomains;
|
||
while (*OtherDomainsPointer != TCHAR_EOS) {
|
||
if (*(OtherDomainsPointer + 1) == WS_OTH_DOMAIN_DELIMITER_CHAR ||
|
||
*(OtherDomainsPointer + 1) == TCHAR_EOS) {
|
||
NumberOfOtherDomains++;
|
||
}
|
||
|
||
OtherDomainsPointer++;
|
||
}
|
||
|
||
//
|
||
// Allocate the buffer to put the canonicalized other domain names
|
||
//
|
||
CanonBufferSize = NumberOfOtherDomains * (DNLEN + 1) * sizeof(TCHAR) +
|
||
sizeof(TCHAR); // One more char for the NULL terminator
|
||
|
||
if ((CanonBuffer = (LPTSTR) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
(UINT) CanonBufferSize
|
||
)) == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// Canonicalize the input other domains separated by NULLs and put
|
||
// into CanonBuffer, each other domain name separated by NULL, and
|
||
// the buffer itself terminated by another NULL.
|
||
//
|
||
status = I_NetListCanonicalize(
|
||
NULL,
|
||
OtherDomains,
|
||
WS_OTH_DOMAIN_DELIMITER_STR,
|
||
CanonBuffer,
|
||
CanonBufferSize,
|
||
&NumberOfOtherDomains,
|
||
NULL,
|
||
0,
|
||
OUTLIST_TYPE_NULL_NULL |
|
||
NAMETYPE_DOMAIN |
|
||
INLC_FLAGS_CANONICALIZE
|
||
);
|
||
|
||
if (status != NERR_Success) {
|
||
NetpKdPrint(("[Wksta] Error in canonicalizing other domains %lu",
|
||
status));
|
||
status = ERROR_INVALID_PARAMETER;
|
||
goto FreeCanonBuffer;
|
||
}
|
||
|
||
//
|
||
// Initialize datagram receiver packet to add or delete
|
||
// other domains.
|
||
//
|
||
Drp->Version = LMDR_REQUEST_PACKET_VERSION;
|
||
Drp->Type = EnumerateNames;
|
||
Drp->Parameters.AddDelName.Type = OtherDomain;
|
||
|
||
if ((status = WsImpersonateAndGetLogonId(
|
||
&Drp->LogonId
|
||
)) != NERR_Success) {
|
||
goto FreeCanonBuffer;
|
||
}
|
||
|
||
//
|
||
// Get all datagram receiver names from the datagram receiver.
|
||
//
|
||
if ((status = WsDeviceControlGetInfo(
|
||
DatagramReceiver,
|
||
WsDgReceiverDeviceHandle,
|
||
IOCTL_LMDR_ENUMERATE_NAMES,
|
||
(PVOID) Drp,
|
||
DrpSize,
|
||
(LPBYTE *) &DgrNames,
|
||
MAXULONG,
|
||
EnumDgNamesHintSize,
|
||
NULL
|
||
)) != NERR_Success) {
|
||
goto FreeCanonBuffer;
|
||
}
|
||
|
||
DgrNamesCount = Drp->Parameters.EnumerateNames.EntriesRead;
|
||
|
||
//
|
||
// The other domains the user wants to set has to be merged with the
|
||
// one that is maintained by the datagram receiver. We will attempt
|
||
// to add all the other domains first. If it already exists, we ignore
|
||
// the error. If it was added successfully, we mark it as such so that
|
||
// if we run into an error other than the already exist error, we can
|
||
// back out the ones we've already added.
|
||
//
|
||
// This requires that we allocate a structure to keep track of the ones
|
||
// we've added.
|
||
//
|
||
if (NumberOfOtherDomains != 0) {
|
||
if ((OtherDomainsInfo = (PWSNAME_RECORD) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
(UINT) (NumberOfOtherDomains *
|
||
sizeof(WSNAME_RECORD))
|
||
)) == NULL) {
|
||
status = GetLastError();
|
||
goto FreeDgrNames;
|
||
}
|
||
|
||
|
||
//
|
||
// Add all other domains specified. If any already exist, we ignore
|
||
// the error from the datagram receiver.
|
||
//
|
||
OtherDomains = CanonBuffer;
|
||
while ((OtherDomainsPointer = I_NetListTraverse(
|
||
NULL,
|
||
&OtherDomains,
|
||
0
|
||
)) != NULL) {
|
||
|
||
OtherDomainsInfo[IndexToList].Name = OtherDomainsPointer;
|
||
OtherDomainsInfo[IndexToList].Size =
|
||
STRLEN(OtherDomainsPointer) * sizeof(TCHAR);
|
||
|
||
for (j = 0; j < DgrNamesCount; j++) {
|
||
|
||
if (DgrNames[j].Type == OtherDomain) {
|
||
|
||
if (WsCompareStringU(
|
||
DgrNames[j].DGReceiverName.Buffer,
|
||
DgrNames[j].DGReceiverName.Length / sizeof(TCHAR),
|
||
OtherDomainsPointer,
|
||
OtherDomainsInfo[IndexToList].Size / sizeof(TCHAR)
|
||
) == 0) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// User-specified domain does not already exist, so add it.
|
||
//
|
||
if (j == DgrNamesCount) {
|
||
|
||
Drp->Parameters.AddDelName.Type = OtherDomain;
|
||
status = WsAddDomainName(
|
||
Drp,
|
||
DrpSize,
|
||
OtherDomainsPointer,
|
||
OtherDomainsInfo[IndexToList].Size
|
||
);
|
||
|
||
if (status == NERR_Success) {
|
||
|
||
OtherDomainsInfo[IndexToList].IsAdded = TRUE;
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint((
|
||
"[Wksta] Successfully added other domain %ws\n",
|
||
OtherDomainsPointer
|
||
));
|
||
}
|
||
|
||
}
|
||
else {
|
||
|
||
//
|
||
// We're ran into trouble and have to delete those
|
||
// we've just added.
|
||
//
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint((
|
||
"[Wksta] Trouble with adding other domain %ws %lu\n",
|
||
OtherDomainsPointer, status
|
||
));
|
||
}
|
||
|
||
for (i = 0; i < IndexToList; i++) {
|
||
if (OtherDomainsInfo[i].IsAdded) {
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("[Wksta] About to remove %ws\n",
|
||
OtherDomainsInfo[i].Name));
|
||
}
|
||
|
||
Drp->Parameters.AddDelName.Type = OtherDomain;
|
||
(void) WsDeleteDomainName(
|
||
Drp,
|
||
DrpSize,
|
||
OtherDomainsInfo[i].Name,
|
||
OtherDomainsInfo[i].Size
|
||
);
|
||
}
|
||
}
|
||
goto FreeDomainInfo;
|
||
|
||
} // back out added domains
|
||
|
||
} // attempted to add a non-existing domain name
|
||
|
||
IndexToList++;
|
||
|
||
} // while there is a user-specified domain name
|
||
|
||
} // if NumberOfOtherDomains != 0
|
||
|
||
//
|
||
// Now we need to delete an active domain name from the Datagram
|
||
// Receiver if it is not in the input other domain list.
|
||
//
|
||
for (i = 0; i < DgrNamesCount; i++) {
|
||
|
||
if (DgrNames[i].Type == OtherDomain) {
|
||
|
||
for (j = 0; j < NumberOfOtherDomains; j++) {
|
||
|
||
if (WsCompareStringU(
|
||
DgrNames[i].DGReceiverName.Buffer,
|
||
DgrNames[i].DGReceiverName.Length / sizeof(TCHAR),
|
||
OtherDomainsInfo[j].Name,
|
||
OtherDomainsInfo[j].Size / sizeof(TCHAR)
|
||
) == 0) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Did not find the active other domain name in the
|
||
// input list. We have to delete it.
|
||
//
|
||
if (j == NumberOfOtherDomains) {
|
||
|
||
Drp->Parameters.AddDelName.Type = OtherDomain;
|
||
status = WsDeleteDomainName(
|
||
Drp,
|
||
DrpSize,
|
||
DgrNames[i].DGReceiverName.Buffer,
|
||
DgrNames[i].DGReceiverName.Length
|
||
);
|
||
|
||
//
|
||
// Save the delete status of the other domain name away
|
||
// because we might run into a problem and have to back
|
||
// out the deletion later. What a mess!
|
||
//
|
||
if (status == NERR_Success) {
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint((
|
||
"[Wksta] Successfully deleted other domain\n"));
|
||
//"[Wksta] Successfully deleted other domain %wZ\n",
|
||
//DgrNames[i].DGReceiverName);
|
||
}
|
||
|
||
DgrNames[i].Type = DGR_NAME_DELETED;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Could not delete the name. Back all successful
|
||
// changes so far--this includes adding the names
|
||
// that were deleted, and removing the names that
|
||
// were added.
|
||
//
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint((
|
||
"[Wksta] Trouble with deleting other domain %lu\n",
|
||
status));
|
||
//"[Wksta] Trouble with deleting other domain %wZ %lu\n",
|
||
//DgrNames[i].DGReceiverName, status);
|
||
}
|
||
|
||
//
|
||
// Add back all deleted names
|
||
//
|
||
for (k = 0; k < i; k++) {
|
||
if (DgrNames[k].Type == DGR_NAME_DELETED) {
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("[Wksta] About to add back %wZ\n",
|
||
DgrNames[k].DGReceiverName));
|
||
}
|
||
|
||
Drp->Parameters.AddDelName.Type = OtherDomain;
|
||
(void) WsAddDomainName(
|
||
Drp,
|
||
DrpSize,
|
||
DgrNames[k].DGReceiverName.Buffer,
|
||
DgrNames[k].DGReceiverName.Length
|
||
);
|
||
|
||
}
|
||
|
||
} // back out deletions
|
||
|
||
//
|
||
// Remove all added names
|
||
//
|
||
for (k = 0; k < NumberOfOtherDomains; k++) {
|
||
if (OtherDomainsInfo[k].IsAdded) {
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("[Wksta] About to remove %ws\n",
|
||
OtherDomainsInfo[k].Name));
|
||
}
|
||
|
||
Drp->Parameters.AddDelName.Type = OtherDomain;
|
||
(void) WsDeleteDomainName(
|
||
Drp,
|
||
DrpSize,
|
||
OtherDomainsInfo[k].Name,
|
||
OtherDomainsInfo[k].Size
|
||
);
|
||
}
|
||
|
||
} // back out additions
|
||
|
||
goto FreeDomainInfo;
|
||
|
||
} // back out all changes so far
|
||
|
||
} // delete the active other domain
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Make other domains persistent by writing to the registry
|
||
//
|
||
if (status == NERR_Success) {
|
||
|
||
LPNET_CONFIG_HANDLE SectionHandle = NULL;
|
||
|
||
|
||
if (NetpOpenConfigData(
|
||
&SectionHandle,
|
||
NULL, // no server name
|
||
SECT_NT_WKSTA,
|
||
FALSE // not read-only
|
||
) != NERR_Success) {
|
||
|
||
//
|
||
// Ignore the error if the config section couldn't be found.
|
||
//
|
||
goto FreeDomainInfo;
|
||
}
|
||
|
||
//
|
||
// Set value for OtherDomains keyword in the wksta section.
|
||
// This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ).
|
||
// Ignore error if not set properly in the registry.
|
||
//
|
||
(void) WsSetConfigTStrArray(
|
||
SectionHandle,
|
||
WKSTA_KEYWORD_OTHERDOMAINS,
|
||
CanonBuffer
|
||
);
|
||
|
||
(void) NetpCloseConfigData(SectionHandle);
|
||
}
|
||
|
||
|
||
FreeDomainInfo:
|
||
if (OtherDomainsInfo != NULL) {
|
||
(void) LocalFree((HLOCAL) OtherDomainsInfo);
|
||
}
|
||
|
||
FreeDgrNames:
|
||
MIDL_user_free((PVOID) DgrNames);
|
||
|
||
FreeCanonBuffer:
|
||
(void) LocalFree((HLOCAL) CanonBuffer);
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("WsSetOtherDomains: about to return %lu\n", status));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsPackageUserInfo(
|
||
IN DWORD Level,
|
||
IN DWORD UserInfoFixedLength,
|
||
IN PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse,
|
||
IN PDGRECEIVE_NAMES DgrNames,
|
||
IN DWORD DgrNamesCount,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN OUT LPDWORD EntriesRead OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function writes the user information from LSA and datagram
|
||
receiver into the output buffer. It increments the EntriesRead
|
||
variable when a user entry is written into the output buffer.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
UserInfoFixedLength - Supplies the length of the fixed portion of the
|
||
information structure.
|
||
|
||
UserInfoResponse - Supplies a pointer to the user information from the
|
||
authentication package.
|
||
|
||
DgrNames - Supplies an array of active datagram receiver names.
|
||
|
||
DgrNamesCount - Supplies the number of entries in DgrNames.
|
||
|
||
FixedPortion - Supplies a pointer to the output buffer where the next
|
||
entry of the fixed portion of the use information will be written.
|
||
This pointer is updated to point to the next fixed portion entry
|
||
after a user entry is written.
|
||
|
||
EndOfVariableData - Supplies a pointer just off the last available byte
|
||
in the output buffer. This is because the variable portion of the
|
||
user information is written into the output buffer starting from
|
||
the end.
|
||
|
||
This pointer is updated after any variable length information is
|
||
written to the output buffer.
|
||
|
||
EntriesRead - Supplies a running total of the number of entries read
|
||
into the output buffer. This value is incremented every time a
|
||
user entry is successfully written into the output buffer.
|
||
|
||
Return Value:
|
||
|
||
NERR_Success - The current entry fits into the output buffer.
|
||
|
||
ERROR_MORE_DATA - The current entry does not fit into the output buffer.
|
||
|
||
--*/
|
||
{
|
||
if (((DWORD_PTR) *FixedPortion + UserInfoFixedLength) >=
|
||
(DWORD_PTR) *EndOfVariableData) {
|
||
|
||
//
|
||
// Fixed length portion does not fit.
|
||
//
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
if (! WsFillUserInfoBuffer(
|
||
Level,
|
||
UserInfoResponse,
|
||
DgrNames,
|
||
DgrNamesCount,
|
||
FixedPortion,
|
||
EndOfVariableData,
|
||
UserInfoFixedLength
|
||
)) {
|
||
//
|
||
// Variable length portion does not fit.
|
||
//
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(EntriesRead)) {
|
||
(*EntriesRead)++;
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
|
||
|
||
STATIC
|
||
BOOL
|
||
WsFillUserInfoBuffer(
|
||
IN DWORD Level,
|
||
IN PMSV1_0_GETUSERINFO_RESPONSE UserInfo,
|
||
IN PDGRECEIVE_NAMES DgrNames,
|
||
IN DWORD DgrNamesCount,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN DWORD UserInfoFixedLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function fills an entry in the output buffer with the supplied user
|
||
information.
|
||
|
||
NOTE: This function assumes that the fixed size portion will fit into
|
||
the output buffer.
|
||
|
||
It also assumes that info structure level 1 is a superset of info
|
||
structure level 0, and that the offset to each common field is
|
||
exactly the same. This allows us to take advantage of a switch
|
||
statement without a break between the levels.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
UserInfo - Supplies a pointer to the user information from the
|
||
authentication package.
|
||
|
||
DgrNames - Supplies an array of active datagram receiver names.
|
||
|
||
DgrNamesCount - Supplies the number of entries in DgrNames.
|
||
|
||
FixedPortion - Supplies a pointer to the output buffer where the next
|
||
entry of the fixed portion of the use information will be written.
|
||
This pointer is updated after a user entry is written to the
|
||
output buffer.
|
||
|
||
EndOfVariableData - Supplies a pointer just off the last available byte
|
||
in the output buffer. This is because the variable portion of the use
|
||
information is written into the output buffer starting from the end.
|
||
This pointer is updated after any variable length information is
|
||
written to the output buffer.
|
||
|
||
UserInfoFixedLength - Supplies the number of bytes needed to hold the
|
||
fixed size portion.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
PWKSTA_USER_INFO_1 WkstaUserInfo = (PWKSTA_USER_INFO_1) *FixedPortion;
|
||
PWKSTA_USER_INFO_1101 UserInfo1101 = (PWKSTA_USER_INFO_1101) *FixedPortion;
|
||
|
||
|
||
*FixedPortion += UserInfoFixedLength;
|
||
|
||
switch (Level) {
|
||
|
||
case 1:
|
||
|
||
//
|
||
// Logon server from authentication package
|
||
//
|
||
|
||
if (! WsCopyStringToBuffer(
|
||
&UserInfo->LogonServer,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
(LPWSTR *) &WkstaUserInfo->wkui1_logon_server
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// Logon Domain from authentication package
|
||
//
|
||
if (! WsCopyStringToBuffer(
|
||
&UserInfo->LogonDomainName,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
(LPWSTR *) &WkstaUserInfo->wkui1_logon_domain
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
WsWriteOtherDomains(
|
||
DgrNames,
|
||
DgrNamesCount,
|
||
FixedPortion,
|
||
EndOfVariableData,
|
||
UserInfoFixedLength,
|
||
(LPWSTR *) &WkstaUserInfo->wkui1_oth_domains
|
||
);
|
||
|
||
//
|
||
// Fall through because level 1 is a superset of level 0
|
||
//
|
||
|
||
case 0:
|
||
|
||
//
|
||
// User name from authentication package
|
||
//
|
||
|
||
if (! WsCopyStringToBuffer(
|
||
&UserInfo->UserName,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
(LPWSTR *) &WkstaUserInfo->wkui1_username
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
|
||
break;
|
||
|
||
case 1101:
|
||
|
||
WsWriteOtherDomains(
|
||
DgrNames,
|
||
DgrNamesCount,
|
||
FixedPortion,
|
||
EndOfVariableData,
|
||
UserInfoFixedLength,
|
||
(LPWSTR *) &UserInfo1101->wkui1101_oth_domains
|
||
);
|
||
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// This should never happen.
|
||
//
|
||
NetpKdPrint(("WsFillUserInfoBuffer: Invalid level %u.\n", Level));
|
||
NetpAssert(FALSE);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
STATIC
|
||
VOID
|
||
WsWriteOtherDomains(
|
||
IN PDGRECEIVE_NAMES DgrNames,
|
||
IN DWORD DgrNamesCount,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN DWORD UserInfoFixedLength,
|
||
OUT LPWSTR *OtherDomainsPointer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function writes to the output buffer the other domains field.
|
||
|
||
Arguments:
|
||
|
||
DgrNames - Supplies an array of active datagram receiver names.
|
||
|
||
DgrNamesCount - Supplies the number of entries in DgrNames.
|
||
|
||
FixedPortion - Supplies a pointer to the output buffer where the next
|
||
entry of the fixed portion of the use information will be written.
|
||
This pointer is updated after a user entry is written to the
|
||
output buffer.
|
||
|
||
EndOfVariableData - Supplies a pointer just off the last available byte
|
||
in the output buffer. This is because the variable portion of the use
|
||
information is written into the output buffer starting from the end.
|
||
This pointer is updated after any variable length information is
|
||
written to the output buffer.
|
||
|
||
UserInfoFixedLength - Supplies the number of bytes needed to hold the
|
||
fixed size portion.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
DWORD i;
|
||
DWORD OtherDomainsCount = 0;
|
||
|
||
|
||
//
|
||
// Other domain names form a NULL terminated string each
|
||
// separated by a space.
|
||
//
|
||
for (i = 0; i < DgrNamesCount; i++) {
|
||
|
||
if (DgrNames[i].Type == OtherDomain) {
|
||
|
||
WsCopyStringToBuffer(
|
||
&DgrNames[i].DGReceiverName,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
OtherDomainsPointer
|
||
);
|
||
|
||
OtherDomainsCount++;
|
||
|
||
if (OtherDomainsCount > 1) {
|
||
(*OtherDomainsPointer)[
|
||
STRLEN(*OtherDomainsPointer)
|
||
] = WS_OTH_DOMAIN_DELIMITER_CHAR;
|
||
}
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint(("[Wksta] Other domains is %ws\n",
|
||
*OtherDomainsPointer));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (OtherDomainsCount == 0) {
|
||
NetpCopyStringToBuffer(
|
||
NULL,
|
||
0,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
OtherDomainsPointer
|
||
);
|
||
}
|
||
}
|
||
|