mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3513 lines
95 KiB
3513 lines
95 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
userp.c
|
|
|
|
Abstract:
|
|
|
|
Internal routines for supporting the NetUser API functions
|
|
|
|
Author:
|
|
|
|
Cliff Van Dyke (cliffv) 26-Mar-1991
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
17-Apr-1991 (cliffv)
|
|
Incorporated review comments.
|
|
|
|
17-Jan-1992 (madana)
|
|
Added a new entry in the UserpUasSamTable to support account
|
|
rename.
|
|
|
|
20-Jan-1992 (madana)
|
|
Sundry API changes.
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h
|
|
#include <ntsam.h>
|
|
#include <ntsamp.h>
|
|
#include <ntlsa.h>
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <lmcons.h>
|
|
|
|
#include <access.h>
|
|
#include <accessp.h>
|
|
#include <align.h>
|
|
#include <limits.h>
|
|
#include <lmapibuf.h>
|
|
#include <lmaccess.h>
|
|
#include <lmerr.h>
|
|
#include <netdebug.h>
|
|
#include <netlib.h>
|
|
#include <netlibnt.h>
|
|
#include <secobj.h>
|
|
#include <stddef.h>
|
|
#include <uasp.h>
|
|
|
|
/*lint -e614 */ /* Auto aggregate initializers need not be constant */
|
|
|
|
// Lint complains about casts of one structure type to another.
|
|
// That is done frequently in the code below.
|
|
/*lint -e740 */ /* don't complain about unusual cast */
|
|
|
|
|
|
|
|
ULONG
|
|
UserpSizeOfLogonHours(
|
|
IN DWORD UnitsPerWeek
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calculates the size in bytes of a logon hours string
|
|
given the number of Units per Week.
|
|
|
|
Parameters:
|
|
|
|
UnitsPerWeek - The number of bits in the logon hours string.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Calculate the number of bytes in the array, rounding up to the
|
|
// nearest number of UCHARs needed to store that many bits.
|
|
//
|
|
|
|
return((UnitsPerWeek + 8 * sizeof(UCHAR) - 1) / (8 * sizeof(UCHAR)));
|
|
} // UserpSizeOfLogonHours
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
UserpGetUserPriv(
|
|
IN SAM_HANDLE BuiltinDomainHandle,
|
|
IN SAM_HANDLE UserHandle,
|
|
IN ULONG UserRelativeId,
|
|
IN PSID DomainId,
|
|
OUT LPDWORD Priv,
|
|
OUT LPDWORD AuthFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the Priv and AuthFlags for the specified user.
|
|
|
|
Arguments:
|
|
|
|
BuiltinDomainHandle - A Handle to the Builtin Domain. This handle
|
|
must grant DOMAIN_GET_ALIAS_MEMBERSHIP access.
|
|
|
|
UserHandle - A handle to the user. This handle must grant
|
|
USER_LIST_GROUPS access.
|
|
|
|
UserRelativeId - Relative ID of the user to query.
|
|
|
|
DomainId - Domain Sid of the Domain this user belongs to
|
|
|
|
Priv - Returns the Lanman 2.0 Privilege level for the specified user.
|
|
|
|
AuthFlags - Returns the Lanman 2.0 Authflags for the specified user.
|
|
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
NTSTATUS Status;
|
|
|
|
PGROUP_MEMBERSHIP GroupMembership = NULL;
|
|
ULONG GroupCount;
|
|
ULONG GroupIndex;
|
|
PSID *UserSids = NULL;
|
|
ULONG UserSidCount = 0;
|
|
ULONG AliasCount;
|
|
PULONG Aliases = NULL;
|
|
|
|
|
|
//
|
|
// Determine all the groups this user is a member of
|
|
//
|
|
|
|
Status = SamGetGroupsForUser( UserHandle,
|
|
&GroupMembership,
|
|
&GroupCount);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetUserPriv: SamGetGroupsForUser returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to point to the SIDs we're interested in
|
|
// alias membership for.
|
|
//
|
|
|
|
UserSids = (PSID *) NetpMemoryAllocate( (GroupCount+1) * sizeof(PSID) );
|
|
|
|
if ( UserSids == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the User's Sid to the Array of Sids.
|
|
//
|
|
|
|
NetStatus = NetpSamRidToSid( UserHandle,
|
|
UserRelativeId,
|
|
&UserSids[0] );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
UserSidCount ++;
|
|
|
|
|
|
|
|
//
|
|
// Add each group the user is a member of to the array of Sids.
|
|
//
|
|
|
|
for ( GroupIndex = 0; GroupIndex < GroupCount; GroupIndex ++ ) {
|
|
|
|
NetStatus = NetpSamRidToSid( UserHandle,
|
|
GroupMembership[GroupIndex].RelativeId,
|
|
&UserSids[GroupIndex+1] );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
UserSidCount ++;
|
|
}
|
|
|
|
|
|
//
|
|
// Find out which aliases in the builtin domain this user is a member of.
|
|
//
|
|
|
|
Status = SamGetAliasMembership( BuiltinDomainHandle,
|
|
UserSidCount,
|
|
UserSids,
|
|
&AliasCount,
|
|
&Aliases );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetUserPriv: SamGetAliasMembership returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Convert the alias membership to priv and auth flags
|
|
//
|
|
|
|
NetpAliasMemberToPriv(
|
|
AliasCount,
|
|
Aliases,
|
|
Priv,
|
|
AuthFlags );
|
|
|
|
NetStatus = NERR_Success;
|
|
|
|
//
|
|
// Free Locally used resources.
|
|
//
|
|
Cleanup:
|
|
if ( Aliases != NULL ) {
|
|
Status = SamFreeMemory( Aliases );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
}
|
|
|
|
if ( GroupMembership != NULL ) {
|
|
Status = SamFreeMemory( GroupMembership );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
}
|
|
|
|
if ( UserSids != NULL ) {
|
|
|
|
for ( GroupIndex = 0; GroupIndex < UserSidCount; GroupIndex ++ ) {
|
|
NetpMemoryFree( UserSids[GroupIndex] );
|
|
}
|
|
|
|
NetpMemoryFree( UserSids );
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
UserpGetDacl(
|
|
IN SAM_HANDLE UserHandle,
|
|
OUT PACL *UserDacl,
|
|
OUT LPDWORD UserDaclSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the DACL for a particular user record in SAM.
|
|
|
|
Arguments:
|
|
|
|
UserHandle - A Handle to the particular user.
|
|
|
|
UserDacl - Returns a pointer to the DACL for the user. The caller
|
|
should free this buffer using NetpMemoryFree.
|
|
Will return NULL if there is no DACL for this user.
|
|
|
|
UserDaclSize - Returns the size (in bytes) of the UserDacl.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
NTSTATUS Status;
|
|
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
BOOLEAN DaclPresent;
|
|
PACL Dacl;
|
|
BOOLEAN DaclDefaulted;
|
|
ACL_SIZE_INFORMATION AclSize;
|
|
|
|
|
|
//
|
|
// Get the Discretionary ACL (DACL) for the user
|
|
//
|
|
|
|
Status = SamQuerySecurityObject(
|
|
UserHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
&SecurityDescriptor );
|
|
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetDacl: SamQuerySecurityObject returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(
|
|
SecurityDescriptor,
|
|
&DaclPresent,
|
|
&Dacl,
|
|
&DaclDefaulted );
|
|
|
|
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetDacl: RtlGetDaclSecurityObject returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If there is no DACL, simply tell the caller
|
|
//
|
|
|
|
if ( !DaclPresent || Dacl == NULL ) {
|
|
NetStatus = NERR_Success;
|
|
*UserDacl = NULL;
|
|
if ( UserDaclSize != NULL ) {
|
|
*UserDaclSize = 0;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Determine the size of the DACL so we can copy it
|
|
//
|
|
|
|
Status = RtlQueryInformationAcl(
|
|
Dacl,
|
|
&AclSize,
|
|
sizeof(AclSize),
|
|
AclSizeInformation );
|
|
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetDacl: RtlQueryInformationAcl returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the DACL to an allocated buffer.
|
|
//
|
|
|
|
*UserDacl = NetpMemoryAllocate( AclSize.AclBytesInUse );
|
|
|
|
if ( *UserDacl == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetpMoveMemory( *UserDacl, Dacl, AclSize.AclBytesInUse );
|
|
|
|
if ( UserDaclSize != NULL ) {
|
|
*UserDaclSize = AclSize.AclBytesInUse;
|
|
}
|
|
NetStatus = NERR_Success;
|
|
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
|
|
Cleanup:
|
|
if ( SecurityDescriptor != NULL ) {
|
|
Status = SamFreeMemory( SecurityDescriptor );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
UserpSetDacl(
|
|
IN SAM_HANDLE UserHandle,
|
|
IN PACL Dacl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the specified Dacl on the specified SAM user record.
|
|
|
|
Arguments:
|
|
|
|
UserHandle - A handle to the user to modify.
|
|
|
|
Dacl - The DACL to set on the user.
|
|
|
|
Return Value:
|
|
|
|
Status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PUCHAR SecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
|
|
//
|
|
// Initialize a security descriptor to contain a pointer to the
|
|
// DACL.
|
|
//
|
|
|
|
Status = RtlCreateSecurityDescriptor(
|
|
SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION );
|
|
|
|
if (!NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetDacl: RtlCreateSecurityDescriptor rets %lX\n",
|
|
Status ));
|
|
}
|
|
return NetpNtStatusToApiStatus( Status );
|
|
}
|
|
|
|
Status = RtlSetDaclSecurityDescriptor(
|
|
(PSECURITY_DESCRIPTOR) SecurityDescriptor,
|
|
(BOOLEAN) TRUE, // Dacl is present
|
|
Dacl,
|
|
(BOOLEAN) FALSE ); // Dacl wasn't defaulted
|
|
|
|
if (!NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetDacl: RtlSetDaclSecurityDescriptor rets %lX\n",
|
|
Status ));
|
|
}
|
|
return NetpNtStatusToApiStatus( Status );
|
|
}
|
|
|
|
//
|
|
// Set this new security descriptor on the user
|
|
//
|
|
|
|
Status = SamSetSecurityObject(
|
|
UserHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor );
|
|
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "NetUserAdd: SamSetSecurityObject rets %lX\n",
|
|
Status ));
|
|
}
|
|
return NetpNtStatusToApiStatus( Status );
|
|
}
|
|
|
|
return NERR_Success;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
UserpOpenUser(
|
|
IN SAM_HANDLE DomainHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN LPCWSTR UserName,
|
|
OUT PSAM_HANDLE UserHandle OPTIONAL,
|
|
OUT PULONG RelativeId OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open a Sam User by Name
|
|
|
|
Arguments:
|
|
|
|
DomainHandle - Supplies the Domain Handle.
|
|
|
|
DesiredAccess - Supplies access mask indicating desired access to user.
|
|
|
|
UserName - User name of the user.
|
|
|
|
UserHandle - Returns a handle to the user. If NULL, user is not
|
|
actually opened (merely the relative ID is returned).
|
|
|
|
RelativeId - Returns the relative ID of the user. If NULL the relative
|
|
Id is not returned.
|
|
|
|
Return Value:
|
|
|
|
Error code for the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
//
|
|
// Variables for converting names to relative IDs
|
|
//
|
|
|
|
UNICODE_STRING NameString;
|
|
PSID_NAME_USE NameUse = NULL;
|
|
PULONG LocalRelativeId = NULL;
|
|
|
|
//
|
|
// Convert user name to relative ID.
|
|
//
|
|
|
|
RtlInitUnicodeString( &NameString, UserName );
|
|
Status = SamLookupNamesInDomain( DomainHandle,
|
|
1,
|
|
&NameString,
|
|
&LocalRelativeId,
|
|
&NameUse );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
if ( Status == STATUS_NONE_MAPPED ) {
|
|
NetStatus = NERR_UserNotFound;
|
|
} else {
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( *NameUse != SidTypeUser ) {
|
|
NetStatus = NERR_UserNotFound;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the user
|
|
//
|
|
|
|
if ( UserHandle != NULL ) {
|
|
Status = SamOpenUser( DomainHandle,
|
|
DesiredAccess,
|
|
*LocalRelativeId,
|
|
UserHandle);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the relative Id if it's wanted.
|
|
//
|
|
|
|
if ( RelativeId != NULL ) {
|
|
*RelativeId = *LocalRelativeId;
|
|
}
|
|
NetStatus = NERR_Success;
|
|
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
|
|
Cleanup:
|
|
if ( LocalRelativeId != NULL ) {
|
|
Status = SamFreeMemory( LocalRelativeId );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
}
|
|
|
|
if ( NameUse != NULL ) {
|
|
Status = SamFreeMemory( NameUse );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
}
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpOpenUser: %wZ: returns %ld\n",
|
|
&NameString, NetStatus ));
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
} // UserpOpenUser
|
|
|
|
|
|
VOID
|
|
UserpRelocationRoutine(
|
|
IN DWORD Level,
|
|
IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
|
|
IN PTRDIFF_T Offset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to relocate the pointers from the fixed portion of an enumeration
|
|
buffer to the string portion of an enumeration buffer. It is called
|
|
as a callback routine from NetpAllocateEnumBuffer when it re-allocates
|
|
such a buffer. NetpAllocateEnumBuffer copied the fixed portion and
|
|
string portion into the new buffer before calling this routine.
|
|
|
|
Arguments:
|
|
|
|
Level - Level of information in the buffer.
|
|
|
|
BufferDescriptor - Description of the new buffer.
|
|
|
|
Offset - Offset to add to each pointer in the fixed portion.
|
|
|
|
Return Value:
|
|
|
|
Returns the error code for the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD EntryCount;
|
|
DWORD EntryNumber;
|
|
DWORD FixedSize;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpRelocationRoutine: entering\n" ));
|
|
}
|
|
|
|
//
|
|
// Compute the number of fixed size entries
|
|
//
|
|
|
|
switch (Level) {
|
|
case 0:
|
|
FixedSize = sizeof(USER_INFO_0);
|
|
break;
|
|
|
|
case 1:
|
|
FixedSize = sizeof(USER_INFO_1);
|
|
break;
|
|
|
|
case 2:
|
|
FixedSize = sizeof(USER_INFO_2);
|
|
break;
|
|
|
|
case 3:
|
|
FixedSize = sizeof(USER_INFO_3);
|
|
break;
|
|
|
|
case 10:
|
|
FixedSize = sizeof(USER_INFO_10);
|
|
break;
|
|
|
|
case 11:
|
|
FixedSize = sizeof(USER_INFO_11);
|
|
break;
|
|
|
|
case 20:
|
|
FixedSize = sizeof(USER_INFO_20);
|
|
break;
|
|
|
|
default:
|
|
NetpAssert( FALSE );
|
|
return;
|
|
|
|
}
|
|
|
|
EntryCount =
|
|
(DWORD)((BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) /
|
|
FixedSize;
|
|
|
|
//
|
|
// Loop relocating each field in each fixed size structure
|
|
//
|
|
|
|
#define DO_ONE( _type, _fieldname ) \
|
|
RELOCATE_ONE( ((_type)TheStruct)->_fieldname, Offset)
|
|
|
|
for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
|
|
|
|
LPBYTE TheStruct = BufferDescriptor->Buffer + FixedSize * EntryNumber;
|
|
|
|
switch ( Level ) {
|
|
case 3:
|
|
DO_ONE( PUSER_INFO_3, usri3_profile );
|
|
DO_ONE( PUSER_INFO_3, usri3_home_dir_drive );
|
|
|
|
/* Drop through to case 2 */
|
|
|
|
case 2:
|
|
DO_ONE( PUSER_INFO_2, usri2_full_name );
|
|
DO_ONE( PUSER_INFO_2, usri2_usr_comment );
|
|
DO_ONE( PUSER_INFO_2, usri2_parms );
|
|
DO_ONE( PUSER_INFO_2, usri2_workstations );
|
|
DO_ONE( PUSER_INFO_2, usri2_logon_hours );
|
|
DO_ONE( PUSER_INFO_2, usri2_logon_server );
|
|
|
|
/* Drop through to case 1 */
|
|
|
|
case 1:
|
|
DO_ONE( PUSER_INFO_1, usri1_home_dir );
|
|
DO_ONE( PUSER_INFO_1, usri1_comment );
|
|
DO_ONE( PUSER_INFO_1, usri1_script_path );
|
|
/* Drop through to case 0 */
|
|
|
|
case 0:
|
|
DO_ONE( PUSER_INFO_0, usri0_name );
|
|
break;
|
|
|
|
case 11:
|
|
DO_ONE( PUSER_INFO_11, usri11_home_dir );
|
|
DO_ONE( PUSER_INFO_11, usri11_parms );
|
|
DO_ONE( PUSER_INFO_11, usri11_logon_server );
|
|
DO_ONE( PUSER_INFO_11, usri11_workstations );
|
|
DO_ONE( PUSER_INFO_11, usri11_home_dir );
|
|
DO_ONE( PUSER_INFO_11, usri11_logon_hours );
|
|
|
|
/* Drop through to case 10 */
|
|
|
|
case 10:
|
|
DO_ONE( PUSER_INFO_10, usri10_name );
|
|
DO_ONE( PUSER_INFO_10, usri10_comment );
|
|
DO_ONE( PUSER_INFO_10, usri10_usr_comment );
|
|
DO_ONE( PUSER_INFO_10, usri10_full_name );
|
|
break;
|
|
|
|
case 20:
|
|
DO_ONE( PUSER_INFO_20, usri20_name );
|
|
DO_ONE( PUSER_INFO_20, usri20_full_name );
|
|
DO_ONE( PUSER_INFO_20, usri20_comment );
|
|
break;
|
|
|
|
default:
|
|
NetpAssert( FALSE );
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // UserpRelocationRoutine
|
|
|
|
|
|
NET_API_STATUS
|
|
UserpGetInfo(
|
|
IN SAM_HANDLE DomainHandle,
|
|
IN PSID DomainId,
|
|
IN SAM_HANDLE BuiltinDomainHandle OPTIONAL,
|
|
IN UNICODE_STRING UserName,
|
|
IN ULONG UserRelativeId,
|
|
IN DWORD Level,
|
|
IN DWORD PrefMaxLen,
|
|
IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
|
|
IN BOOL IsGet,
|
|
IN DWORD SamFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the information on one user and fill that information into an
|
|
allocated buffer.
|
|
|
|
Arguments:
|
|
|
|
DomainHandle - Domain Handle for the Account domain.
|
|
|
|
DomainId - Domain Id corresponding to DomainHandle
|
|
|
|
BuiltinDomainHandle - Domain Handle for the builtin domain. Need only be
|
|
specified for info level 1, 2, 3, and 11.
|
|
|
|
UserName - User name of the user to query.
|
|
|
|
UserRelativeId - Relative ID of the user to query.
|
|
|
|
Level - Level of information required. level 0, 1, 2, 3, 10, 11 and 20
|
|
are valid.
|
|
|
|
PrefMaxLen - Callers prefered maximum length
|
|
|
|
BufferDescriptor - Points to a structure which describes the allocated
|
|
buffer. On the first call, pass in BufferDescriptor->Buffer set
|
|
to NULL. On subsequent calls (in the 'enum' case), pass in the
|
|
structure just as it was passed back on a previous call.
|
|
|
|
The caller must deallocate the BufferDescriptor->Buffer using
|
|
MIDL_user_free if it is non-null.
|
|
|
|
IsGet - True iff this is a 'get' call and not an 'enum' call.
|
|
|
|
Return Value:
|
|
|
|
Error code for the operation.
|
|
|
|
If this is an Enum call, the status can be ERROR_MORE_DATA implying that
|
|
the Buffer has grown to PrefMaxLen and that this much data should
|
|
be returned to the caller.
|
|
|
|
--*/
|
|
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
NTSTATUS Status;
|
|
|
|
SAM_HANDLE UserHandle = NULL;
|
|
USER_ALL_INFORMATION *UserAll = NULL;
|
|
UNICODE_STRING LogonServer;
|
|
|
|
ACCESS_MASK DesiredAccess;
|
|
ULONG RequiredFields;
|
|
PACL UserDacl = NULL;
|
|
|
|
ULONG RidToReturn = UserRelativeId;
|
|
|
|
DWORD Size; // The size of the info returned for this user
|
|
DWORD FixedSize; // The size of the info returned for this user
|
|
LPBYTE NewStruct; // Pointer to fixed portion of new structure
|
|
|
|
PSID UserSid = NULL; // sid of the user
|
|
|
|
DWORD password_expired;
|
|
|
|
//
|
|
// Variables describes membership in the special groups.
|
|
//
|
|
|
|
DWORD Priv;
|
|
DWORD AuthFlags;
|
|
|
|
|
|
|
|
//
|
|
// Validate Level parameter and remember the fixed size of each returned
|
|
// array entry.
|
|
//
|
|
RtlInitUnicodeString( &LogonServer, L"\\\\*" );
|
|
|
|
switch (Level) {
|
|
|
|
case 0:
|
|
FixedSize = sizeof(USER_INFO_0);
|
|
DesiredAccess = 0;
|
|
|
|
RequiredFields = 0;
|
|
break;
|
|
|
|
case 1:
|
|
FixedSize = sizeof(USER_INFO_1);
|
|
DesiredAccess = USER_LIST_GROUPS | USER_READ_GENERAL |
|
|
USER_READ_LOGON | USER_READ_ACCOUNT | READ_CONTROL;
|
|
|
|
RequiredFields = USER_ALL_USERNAME |
|
|
USER_ALL_PASSWORDLASTSET |
|
|
USER_ALL_HOMEDIRECTORY |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERACCOUNTCONTROL |
|
|
USER_ALL_SCRIPTPATH ;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
FixedSize = sizeof(USER_INFO_2);
|
|
DesiredAccess = USER_LIST_GROUPS | USER_READ_GENERAL |
|
|
USER_READ_LOGON | USER_READ_ACCOUNT | READ_CONTROL |
|
|
USER_READ_PREFERENCES;
|
|
|
|
RequiredFields = USER_ALL_FULLNAME |
|
|
USER_ALL_USERCOMMENT |
|
|
USER_ALL_PARAMETERS |
|
|
USER_ALL_WORKSTATIONS |
|
|
USER_ALL_LASTLOGON |
|
|
USER_ALL_LASTLOGOFF |
|
|
USER_ALL_ACCOUNTEXPIRES |
|
|
USER_ALL_LOGONHOURS |
|
|
USER_ALL_BADPASSWORDCOUNT |
|
|
USER_ALL_LOGONCOUNT |
|
|
USER_ALL_COUNTRYCODE |
|
|
USER_ALL_CODEPAGE |
|
|
USER_ALL_USERNAME |
|
|
USER_ALL_PASSWORDLASTSET |
|
|
USER_ALL_HOMEDIRECTORY |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERACCOUNTCONTROL |
|
|
USER_ALL_SCRIPTPATH ;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
FixedSize = sizeof(USER_INFO_3);
|
|
DesiredAccess = USER_LIST_GROUPS | USER_READ_GENERAL |
|
|
USER_READ_LOGON | USER_READ_ACCOUNT | READ_CONTROL |
|
|
USER_READ_PREFERENCES;
|
|
|
|
RequiredFields = USER_ALL_USERID |
|
|
USER_ALL_PRIMARYGROUPID |
|
|
USER_ALL_PROFILEPATH |
|
|
USER_ALL_HOMEDIRECTORYDRIVE |
|
|
USER_ALL_PASSWORDMUSTCHANGE |
|
|
USER_ALL_FULLNAME |
|
|
USER_ALL_USERCOMMENT |
|
|
USER_ALL_PARAMETERS |
|
|
USER_ALL_WORKSTATIONS |
|
|
USER_ALL_LASTLOGON |
|
|
USER_ALL_LASTLOGOFF |
|
|
USER_ALL_ACCOUNTEXPIRES |
|
|
USER_ALL_LOGONHOURS |
|
|
USER_ALL_BADPASSWORDCOUNT |
|
|
USER_ALL_LOGONCOUNT |
|
|
USER_ALL_COUNTRYCODE |
|
|
USER_ALL_CODEPAGE |
|
|
USER_ALL_USERNAME |
|
|
USER_ALL_PASSWORDLASTSET |
|
|
USER_ALL_HOMEDIRECTORY |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERACCOUNTCONTROL |
|
|
USER_ALL_SCRIPTPATH ;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
FixedSize = sizeof(USER_INFO_4);
|
|
DesiredAccess = USER_LIST_GROUPS | USER_READ_GENERAL |
|
|
USER_READ_LOGON | USER_READ_ACCOUNT | READ_CONTROL |
|
|
USER_READ_PREFERENCES;
|
|
|
|
RequiredFields = USER_ALL_USERID |
|
|
USER_ALL_PRIMARYGROUPID |
|
|
USER_ALL_PROFILEPATH |
|
|
USER_ALL_HOMEDIRECTORYDRIVE |
|
|
USER_ALL_PASSWORDMUSTCHANGE |
|
|
USER_ALL_FULLNAME |
|
|
USER_ALL_USERCOMMENT |
|
|
USER_ALL_PARAMETERS |
|
|
USER_ALL_WORKSTATIONS |
|
|
USER_ALL_LASTLOGON |
|
|
USER_ALL_LASTLOGOFF |
|
|
USER_ALL_ACCOUNTEXPIRES |
|
|
USER_ALL_LOGONHOURS |
|
|
USER_ALL_BADPASSWORDCOUNT |
|
|
USER_ALL_LOGONCOUNT |
|
|
USER_ALL_COUNTRYCODE |
|
|
USER_ALL_CODEPAGE |
|
|
USER_ALL_USERNAME |
|
|
USER_ALL_PASSWORDLASTSET |
|
|
USER_ALL_HOMEDIRECTORY |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERACCOUNTCONTROL |
|
|
USER_ALL_SCRIPTPATH ;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
FixedSize = sizeof(USER_INFO_10);
|
|
DesiredAccess = USER_READ_GENERAL;
|
|
|
|
RequiredFields = USER_ALL_USERNAME |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERCOMMENT |
|
|
USER_ALL_FULLNAME ;
|
|
break;
|
|
|
|
case 11:
|
|
FixedSize = sizeof(USER_INFO_11);
|
|
DesiredAccess = USER_LIST_GROUPS | USER_READ_GENERAL | USER_READ_LOGON |
|
|
USER_READ_ACCOUNT | USER_READ_PREFERENCES;
|
|
|
|
RequiredFields = USER_ALL_USERNAME |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERCOMMENT |
|
|
USER_ALL_FULLNAME |
|
|
USER_ALL_PASSWORDLASTSET |
|
|
USER_ALL_HOMEDIRECTORY |
|
|
USER_ALL_PARAMETERS |
|
|
USER_ALL_LASTLOGON |
|
|
USER_ALL_LASTLOGOFF |
|
|
USER_ALL_BADPASSWORDCOUNT |
|
|
USER_ALL_LOGONCOUNT |
|
|
USER_ALL_COUNTRYCODE |
|
|
USER_ALL_WORKSTATIONS |
|
|
USER_ALL_LOGONHOURS |
|
|
USER_ALL_CODEPAGE ;
|
|
break;
|
|
|
|
case 20:
|
|
FixedSize = sizeof(USER_INFO_20);
|
|
DesiredAccess = USER_READ_GENERAL | USER_READ_ACCOUNT | READ_CONTROL;
|
|
|
|
RequiredFields = USER_ALL_USERNAME |
|
|
USER_ALL_FULLNAME |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERACCOUNTCONTROL;
|
|
break;
|
|
|
|
case 23:
|
|
FixedSize = sizeof(USER_INFO_23);
|
|
DesiredAccess = USER_READ_GENERAL | USER_READ_ACCOUNT | READ_CONTROL;
|
|
|
|
RequiredFields = USER_ALL_USERNAME |
|
|
USER_ALL_FULLNAME |
|
|
USER_ALL_ADMINCOMMENT |
|
|
USER_ALL_USERACCOUNTCONTROL;
|
|
break;
|
|
|
|
default:
|
|
NetStatus = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate that the level is supported
|
|
//
|
|
if ( Level == 3 || Level == 20 ) {
|
|
|
|
ULONG Mode;
|
|
Status = SamGetCompatibilityMode(DomainHandle, &Mode);
|
|
if (!NT_SUCCESS(Status)) {
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
switch (Mode) {
|
|
case SAM_SID_COMPATIBILITY_STRICT:
|
|
NetStatus = ERROR_NOT_SUPPORTED;
|
|
goto Cleanup;
|
|
case SAM_SID_COMPATIBILITY_LAX:
|
|
RidToReturn = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we need to filter this account then query
|
|
// USER_ALL_USERACCOUNTCONTROL also.
|
|
//
|
|
|
|
if( SamFilter ) {
|
|
|
|
DesiredAccess |= USER_READ_ACCOUNT;
|
|
RequiredFields |= USER_ALL_USERACCOUNTCONTROL;
|
|
}
|
|
|
|
//
|
|
// Open the User account if need be
|
|
//
|
|
|
|
if ( DesiredAccess != 0 ) {
|
|
|
|
Status = SamOpenUser( DomainHandle,
|
|
DesiredAccess,
|
|
UserRelativeId,
|
|
&UserHandle);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpGetInfo: SamOpenUser returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Get all the information we need about the user
|
|
//
|
|
|
|
if ( RequiredFields != 0 ) {
|
|
|
|
Status = SamQueryInformationUser( UserHandle,
|
|
UserAllInformation,
|
|
(PVOID *)&UserAll );
|
|
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetInfo: SamQueryInformationUser returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( (UserAll->WhichFields & RequiredFields) != RequiredFields ) {
|
|
#if DBG
|
|
NetpKdPrint(( "UserpGetInfo: WhichFields: %lX RequireFields: %lX\n",
|
|
UserAll->WhichFields,
|
|
RequiredFields ));
|
|
#endif // DBG
|
|
NetStatus = ERROR_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// check the account type to filter this account.
|
|
//
|
|
|
|
if( (SamFilter != 0) &&
|
|
((UserAll->UserAccountControl & SamFilter) == 0)) {
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpGetInfo: %wZ is skipped \n", &UserName ));
|
|
}
|
|
|
|
NetStatus = NERR_Success ;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Level 1, 2 and 3 use the User's DACL to figure out the usriX_flags field.
|
|
//
|
|
|
|
if ((Level == 1) ||
|
|
(Level == 2) ||
|
|
(Level == 3) ||
|
|
(Level == 4) ||
|
|
(Level == 20) ||
|
|
(Level == 23) ) {
|
|
|
|
|
|
//
|
|
// Get the DACL for this user.
|
|
//
|
|
|
|
NetStatus = UserpGetDacl( UserHandle, &UserDacl, NULL );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetInfo: UserpGetDacl returns %ld\n",
|
|
NetStatus ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Determine the Priv and AuthFlags
|
|
//
|
|
|
|
if (Level == 1 || Level == 2 || Level == 3 || Level == 4 || Level == 11 ) {
|
|
|
|
//
|
|
//
|
|
|
|
NetStatus = UserpGetUserPriv(
|
|
BuiltinDomainHandle,
|
|
UserHandle,
|
|
UserRelativeId,
|
|
DomainId,
|
|
&Priv,
|
|
&AuthFlags );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Construct the user's SID if necessary
|
|
//
|
|
if ( (Level == 4)
|
|
|| (Level == 23) ) {
|
|
|
|
NetStatus = NetpSamRidToSid(UserHandle,
|
|
UserRelativeId,
|
|
&UserSid);
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine if the account has expired
|
|
//
|
|
if ( (Level == 3)
|
|
|| (Level == 4) ) {
|
|
|
|
//
|
|
// If the password is currently expired,
|
|
// indicate so.
|
|
//
|
|
LARGE_INTEGER CurrentTime;
|
|
(VOID) NtQuerySystemTime( &CurrentTime );
|
|
|
|
if ( CurrentTime.QuadPart
|
|
>= UserAll->PasswordMustChange.QuadPart ) {
|
|
password_expired = TRUE;
|
|
} else {
|
|
password_expired = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine the total size of the return information.
|
|
//
|
|
|
|
Size = FixedSize;
|
|
switch (Level) {
|
|
case 0:
|
|
Size += UserName.Length + sizeof(WCHAR);
|
|
break;
|
|
|
|
case 4:
|
|
NetpAssert( NULL != UserSid );
|
|
Size += RtlLengthSid(UserSid);
|
|
|
|
/* Drop through to case 3 */
|
|
|
|
case 3:
|
|
Size += UserAll->ProfilePath.Length + sizeof(WCHAR) +
|
|
UserAll->HomeDirectoryDrive.Length + sizeof(WCHAR);
|
|
|
|
/* Drop through to case 2 */
|
|
|
|
case 2:
|
|
Size += UserAll->FullName.Length + sizeof(WCHAR) +
|
|
UserAll->UserComment.Length + sizeof(WCHAR) +
|
|
UserAll->Parameters.Length + sizeof(WCHAR) +
|
|
UserAll->WorkStations.Length + sizeof(WCHAR) +
|
|
LogonServer.Length + sizeof(WCHAR) +
|
|
UserpSizeOfLogonHours( UserAll->LogonHours.UnitsPerWeek );
|
|
|
|
/* Drop through to case 1 */
|
|
|
|
case 1:
|
|
Size += UserAll->UserName.Length + sizeof(WCHAR) +
|
|
UserAll->HomeDirectory.Length + sizeof(WCHAR) +
|
|
UserAll->AdminComment.Length + sizeof(WCHAR) +
|
|
UserAll->ScriptPath.Length + sizeof(WCHAR);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
Size += UserAll->UserName.Length + sizeof(WCHAR) +
|
|
UserAll->AdminComment.Length + sizeof(WCHAR) +
|
|
UserAll->UserComment.Length + sizeof(WCHAR) +
|
|
UserAll->FullName.Length + sizeof(WCHAR);
|
|
|
|
break;
|
|
|
|
case 11:
|
|
Size += UserAll->UserName.Length + sizeof(WCHAR) +
|
|
UserAll->AdminComment.Length + sizeof(WCHAR) +
|
|
UserAll->UserComment.Length + sizeof(WCHAR) +
|
|
UserAll->FullName.Length + sizeof(WCHAR) +
|
|
UserAll->HomeDirectory.Length + sizeof(WCHAR) +
|
|
UserAll->Parameters.Length + sizeof(WCHAR) +
|
|
UserAll->WorkStations.Length + sizeof(WCHAR) +
|
|
LogonServer.Length + sizeof(WCHAR) +
|
|
UserpSizeOfLogonHours( UserAll->LogonHours.UnitsPerWeek );
|
|
|
|
break;
|
|
|
|
case 23:
|
|
|
|
NetpAssert( NULL != UserSid );
|
|
Size += RtlLengthSid(UserSid);
|
|
|
|
/* Drop through to case 20 */
|
|
|
|
|
|
case 20:
|
|
Size += UserAll->UserName.Length + sizeof(WCHAR) +
|
|
UserAll->FullName.Length + sizeof(WCHAR) +
|
|
UserAll->AdminComment.Length + sizeof(WCHAR);
|
|
|
|
break;
|
|
|
|
default:
|
|
NetStatus = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Ensure there is buffer space for this information.
|
|
//
|
|
|
|
Size = ROUND_UP_COUNT( Size, ALIGN_DWORD );
|
|
|
|
NetStatus = NetpAllocateEnumBuffer(
|
|
BufferDescriptor,
|
|
IsGet,
|
|
PrefMaxLen,
|
|
Size,
|
|
UserpRelocationRoutine,
|
|
Level );
|
|
|
|
if (NetStatus != NERR_Success) {
|
|
|
|
//
|
|
// NetpAllocateEnumBuffer returns ERROR_MORE_DATA if this
|
|
// entry doesn't fit into the buffer.
|
|
//
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpGetInfo: NetpAllocateEnumBuffer returns %ld\n",
|
|
NetStatus ));
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Define macros to make copying bytes and zero terminated strings less
|
|
// repetitive.
|
|
//
|
|
|
|
#define COPY_BYTES( _type, _fieldname, _inptr, _length, _align ) \
|
|
if ( !NetpCopyDataToBuffer( \
|
|
(_inptr), \
|
|
(_length), \
|
|
BufferDescriptor->FixedDataEnd, \
|
|
&BufferDescriptor->EndOfVariableData, \
|
|
(LPBYTE*)&((_type)NewStruct)->_fieldname, \
|
|
_align ) ){ \
|
|
\
|
|
NetStatus = NERR_InternalError; \
|
|
IF_DEBUG( UAS_DEBUG_USER ) { \
|
|
NetpKdPrint(( "UserpGetInfo: NetpCopyData returns %ld\n", \
|
|
NetStatus )); \
|
|
} \
|
|
goto Cleanup; \
|
|
}
|
|
|
|
|
|
#define COPY_STRING( _type, _fieldname, _string ) \
|
|
if ( !NetpCopyStringToBuffer( \
|
|
(_string).Buffer, \
|
|
(_string).Length/sizeof(WCHAR), \
|
|
BufferDescriptor->FixedDataEnd, \
|
|
(LPWSTR *)&BufferDescriptor->EndOfVariableData, \
|
|
&((_type)NewStruct)->_fieldname) ) { \
|
|
\
|
|
NetStatus = NERR_InternalError; \
|
|
IF_DEBUG( UAS_DEBUG_USER ) { \
|
|
NetpKdPrint(( "UserpGetInfo: NetpCopyString returns %ld\n", \
|
|
NetStatus )); \
|
|
} \
|
|
goto Cleanup; \
|
|
}
|
|
|
|
//
|
|
// Place this entry into the return buffer.
|
|
//
|
|
// Fill in the information. The array of fixed entries is
|
|
// placed at the beginning of the allocated buffer. The strings
|
|
// pointed to by these fixed entries are allocated starting at
|
|
// the end of the allocate buffer.
|
|
//
|
|
|
|
NewStruct = BufferDescriptor->FixedDataEnd;
|
|
BufferDescriptor->FixedDataEnd += FixedSize;
|
|
|
|
switch ( Level ) {
|
|
case 4:
|
|
{
|
|
//
|
|
// USER_INFO_2, below, is a subset of USER_INFO_4, so fill in our
|
|
// structures here and then fall through
|
|
//
|
|
PUSER_INFO_4 usri4 = (PUSER_INFO_4) NewStruct;
|
|
|
|
NetpAssert( NULL != UserSid );
|
|
COPY_BYTES( PUSER_INFO_4,
|
|
usri4_user_sid,
|
|
UserSid,
|
|
RtlLengthSid(UserSid),
|
|
ALIGN_DWORD );
|
|
|
|
usri4->usri4_primary_group_id = UserAll->PrimaryGroupId;
|
|
|
|
COPY_STRING( PUSER_INFO_4, usri4_profile, UserAll->ProfilePath );
|
|
|
|
COPY_STRING( PUSER_INFO_4,
|
|
usri4_home_dir_drive,
|
|
UserAll->HomeDirectoryDrive );
|
|
|
|
NetpAssert( (password_expired == TRUE)
|
|
|| (password_expired == FALSE));
|
|
|
|
usri4->usri4_password_expired = password_expired;
|
|
|
|
//
|
|
// Fall through the level 3
|
|
//
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
//
|
|
// since _USER_INFO_2 structure is subset of _USER_INFO_3,
|
|
// full-up the _USER_INFO_3 only fields first and then fall
|
|
// through for the common fields.
|
|
//
|
|
if ( Level == 3 ) {
|
|
|
|
PUSER_INFO_3 usri3 = (PUSER_INFO_3) NewStruct;
|
|
|
|
NetpAssert( UserRelativeId == UserAll->UserId );
|
|
usri3->usri3_user_id = RidToReturn;
|
|
|
|
usri3->usri3_primary_group_id = UserAll->PrimaryGroupId;
|
|
|
|
COPY_STRING( PUSER_INFO_3, usri3_profile, UserAll->ProfilePath );
|
|
|
|
COPY_STRING( PUSER_INFO_3,
|
|
usri3_home_dir_drive,
|
|
UserAll->HomeDirectoryDrive );
|
|
|
|
NetpAssert( (password_expired == TRUE)
|
|
|| (password_expired == FALSE));
|
|
|
|
usri3->usri3_password_expired = password_expired;
|
|
}
|
|
}
|
|
|
|
//
|
|
// FALL THROUGH FOR OTHER _USER_INFO_3 FIELDS
|
|
//
|
|
|
|
|
|
case 2:
|
|
{
|
|
|
|
PUSER_INFO_2 usri2 = (PUSER_INFO_2) NewStruct;
|
|
|
|
usri2->usri2_auth_flags = AuthFlags;
|
|
|
|
COPY_STRING( PUSER_INFO_2,
|
|
usri2_full_name,
|
|
UserAll->FullName );
|
|
|
|
COPY_STRING( PUSER_INFO_2,
|
|
usri2_usr_comment,
|
|
UserAll->UserComment);
|
|
|
|
COPY_STRING( PUSER_INFO_2,
|
|
usri2_parms,
|
|
UserAll->Parameters );
|
|
|
|
COPY_STRING( PUSER_INFO_2,
|
|
usri2_workstations,
|
|
UserAll->WorkStations);
|
|
|
|
if ( !RtlTimeToSecondsSince1970( &UserAll->LastLogon,
|
|
&usri2->usri2_last_logon) ) {
|
|
usri2->usri2_last_logon = 0;
|
|
}
|
|
|
|
if ( !RtlTimeToSecondsSince1970( &UserAll->LastLogoff,
|
|
&usri2->usri2_last_logoff) ) {
|
|
usri2->usri2_last_logoff = 0;
|
|
}
|
|
|
|
if ( !RtlTimeToSecondsSince1970( &UserAll->AccountExpires,
|
|
&usri2->usri2_acct_expires) ) {
|
|
usri2->usri2_acct_expires = TIMEQ_FOREVER;
|
|
}
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpGetInfo: Account Expries %lx %lx %lx\n",
|
|
UserAll->AccountExpires.HighPart,
|
|
UserAll->AccountExpires.LowPart,
|
|
usri2->usri2_acct_expires));
|
|
}
|
|
|
|
|
|
usri2->usri2_max_storage = USER_MAXSTORAGE_UNLIMITED;
|
|
|
|
usri2->usri2_units_per_week = UserAll->LogonHours.UnitsPerWeek;
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
DWORD k;
|
|
NetpDbgDisplayDword( "UserpGetInfo: units_per_week",
|
|
usri2->usri2_units_per_week );
|
|
NetpKdPrint(( "UserpGetInfo: LogonHours %lx\n",
|
|
UserAll->LogonHours.LogonHours));
|
|
|
|
|
|
for ( k=0;
|
|
k<UserpSizeOfLogonHours(
|
|
UserAll->LogonHours.UnitsPerWeek);
|
|
k++ ) {
|
|
NetpKdPrint(( "%d ",
|
|
UserAll->LogonHours.LogonHours[k] ));
|
|
}
|
|
NetpKdPrint(( "\n" ));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
COPY_BYTES( PUSER_INFO_2,
|
|
usri2_logon_hours,
|
|
UserAll->LogonHours.LogonHours,
|
|
UserpSizeOfLogonHours(
|
|
UserAll->LogonHours.UnitsPerWeek ),
|
|
sizeof(UCHAR) );
|
|
BufferDescriptor->EndOfVariableData =
|
|
ROUND_DOWN_POINTER( BufferDescriptor->EndOfVariableData,
|
|
ALIGN_WCHAR );
|
|
|
|
usri2->usri2_bad_pw_count = UserAll->BadPasswordCount;
|
|
usri2->usri2_num_logons = UserAll->LogonCount;
|
|
|
|
COPY_STRING( PUSER_INFO_2,
|
|
usri2_logon_server,
|
|
LogonServer );
|
|
|
|
usri2->usri2_country_code = UserAll->CountryCode;
|
|
usri2->usri2_code_page = UserAll->CodePage;
|
|
|
|
/* Drop through to case 1 */
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
PUSER_INFO_1 usri1 = (PUSER_INFO_1) NewStruct;
|
|
|
|
COPY_STRING( PUSER_INFO_1, usri1_name, UserAll->UserName );
|
|
usri1->usri1_password = NULL;
|
|
|
|
usri1->usri1_password_age =
|
|
NetpGetElapsedSeconds( &UserAll->PasswordLastSet );
|
|
|
|
usri1->usri1_priv = Priv;
|
|
|
|
COPY_STRING( PUSER_INFO_1, usri1_home_dir, UserAll->HomeDirectory );
|
|
COPY_STRING( PUSER_INFO_1, usri1_comment, UserAll->AdminComment);
|
|
|
|
|
|
usri1->usri1_flags = NetpAccountControlToFlags(
|
|
UserAll->UserAccountControl,
|
|
UserDacl );
|
|
|
|
COPY_STRING( PUSER_INFO_1, usri1_script_path, UserAll->ScriptPath);
|
|
|
|
break;
|
|
}
|
|
|
|
case 0:
|
|
|
|
COPY_STRING( PUSER_INFO_0, usri0_name, UserName );
|
|
break;
|
|
|
|
case 10:
|
|
|
|
COPY_STRING( PUSER_INFO_10, usri10_name, UserAll->UserName );
|
|
COPY_STRING( PUSER_INFO_10, usri10_comment, UserAll->AdminComment );
|
|
COPY_STRING( PUSER_INFO_10, usri10_usr_comment, UserAll->UserComment );
|
|
COPY_STRING( PUSER_INFO_10, usri10_full_name, UserAll->FullName );
|
|
|
|
break;
|
|
|
|
case 11:
|
|
{
|
|
PUSER_INFO_11 usri11 = (PUSER_INFO_11) NewStruct;
|
|
|
|
COPY_STRING( PUSER_INFO_11, usri11_name, UserAll->UserName );
|
|
COPY_STRING( PUSER_INFO_11, usri11_comment, UserAll->AdminComment );
|
|
COPY_STRING(PUSER_INFO_11, usri11_usr_comment,UserAll->UserComment);
|
|
COPY_STRING( PUSER_INFO_11, usri11_full_name, UserAll->FullName );
|
|
|
|
usri11->usri11_priv = Priv;
|
|
usri11->usri11_auth_flags = AuthFlags;
|
|
|
|
usri11->usri11_password_age =
|
|
NetpGetElapsedSeconds( &UserAll->PasswordLastSet );
|
|
|
|
|
|
COPY_STRING(PUSER_INFO_11, usri11_home_dir, UserAll->HomeDirectory);
|
|
COPY_STRING( PUSER_INFO_11, usri11_parms, UserAll->Parameters );
|
|
|
|
if ( !RtlTimeToSecondsSince1970( &UserAll->LastLogon,
|
|
&usri11->usri11_last_logon) ) {
|
|
usri11->usri11_last_logon = 0;
|
|
}
|
|
|
|
if ( !RtlTimeToSecondsSince1970( &UserAll->LastLogoff,
|
|
&usri11->usri11_last_logoff) ) {
|
|
usri11->usri11_last_logoff = 0;
|
|
}
|
|
|
|
usri11->usri11_bad_pw_count = UserAll->BadPasswordCount;
|
|
usri11->usri11_num_logons = UserAll->LogonCount;
|
|
|
|
COPY_STRING( PUSER_INFO_11, usri11_logon_server, LogonServer );
|
|
|
|
usri11->usri11_country_code = UserAll->CountryCode;
|
|
|
|
COPY_STRING( PUSER_INFO_11,
|
|
usri11_workstations,
|
|
UserAll->WorkStations );
|
|
|
|
usri11->usri11_max_storage = USER_MAXSTORAGE_UNLIMITED;
|
|
usri11->usri11_units_per_week = UserAll->LogonHours.UnitsPerWeek;
|
|
|
|
COPY_BYTES( PUSER_INFO_11,
|
|
usri11_logon_hours,
|
|
UserAll->LogonHours.LogonHours,
|
|
UserpSizeOfLogonHours(
|
|
UserAll->LogonHours.UnitsPerWeek ),
|
|
sizeof(UCHAR) );
|
|
BufferDescriptor->EndOfVariableData =
|
|
ROUND_DOWN_POINTER( BufferDescriptor->EndOfVariableData,
|
|
ALIGN_WCHAR );
|
|
|
|
usri11->usri11_code_page = UserAll->CodePage;
|
|
|
|
break;
|
|
}
|
|
|
|
case 23:
|
|
{
|
|
//
|
|
// Since USER_INFO_23 has the same fields as USER_INFO_20 with the
|
|
// exception of the RID and SID fields, copy in the SID here and
|
|
// then fall through for the rest of the fields
|
|
//
|
|
PUSER_INFO_23 usri23 = (PUSER_INFO_23) NewStruct;
|
|
NetpAssert( NULL != UserSid );
|
|
|
|
COPY_BYTES( PUSER_INFO_23,
|
|
usri23_user_sid,
|
|
UserSid,
|
|
RtlLengthSid(UserSid),
|
|
ALIGN_DWORD );
|
|
|
|
//
|
|
// Fall through the level 20
|
|
//
|
|
}
|
|
|
|
case 20:
|
|
{
|
|
|
|
PUSER_INFO_20 usri20 = (PUSER_INFO_20) NewStruct;
|
|
|
|
COPY_STRING( PUSER_INFO_20, usri20_name, UserAll->UserName );
|
|
|
|
COPY_STRING( PUSER_INFO_20, usri20_full_name, UserAll->FullName );
|
|
|
|
COPY_STRING( PUSER_INFO_20, usri20_comment, UserAll->AdminComment );
|
|
|
|
if ( Level == 20 ) {
|
|
usri20->usri20_user_id = RidToReturn;
|
|
}
|
|
|
|
usri20->usri20_flags = NetpAccountControlToFlags(
|
|
UserAll->UserAccountControl,
|
|
UserDacl );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
NetStatus = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
NetStatus = NERR_Success ;
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Free Sam information buffers
|
|
//
|
|
|
|
if ( UserAll != NULL ) {
|
|
Status = SamFreeMemory( UserAll );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
}
|
|
|
|
if ( UserHandle != NULL ) {
|
|
(VOID) SamCloseHandle( UserHandle );
|
|
}
|
|
|
|
if ( UserDacl != NULL ) {
|
|
NetpMemoryFree( UserDacl );
|
|
}
|
|
|
|
if ( UserSid ) {
|
|
NetpMemoryFree( UserSid );
|
|
}
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpGetInfo: returning %ld\n", NetStatus ));
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
} // UserpGetInfo
|
|
|
|
|
|
|
|
//
|
|
// Each field in the SAM USER_ALL_INFORMATION structure (and each pseudo field)
|
|
// is described here.
|
|
|
|
struct _SAM_FIELD_DESCRIPTION {
|
|
//
|
|
// Non-zero to indicate which field in the SAM USER_ALL_INFORMATION
|
|
// structure is being set.
|
|
|
|
DWORD WhichField;
|
|
|
|
//
|
|
// Define the value to return in ParmError if this field is bad.
|
|
//
|
|
|
|
DWORD UasParmNum;
|
|
|
|
//
|
|
// Describe the byte offset of the field in the SAM USER_ALL_INFORMATION
|
|
// structure.
|
|
//
|
|
|
|
DWORD SamOffset;
|
|
|
|
//
|
|
// The DesiredAccess mask includes both the access to read and the
|
|
// access to write the appropriate field in the USER_ALL_INFORMATION
|
|
//
|
|
|
|
ACCESS_MASK DesiredAccess;
|
|
|
|
} SamFieldDescription[] =
|
|
{
|
|
|
|
#define SAM_UserNameField 0
|
|
{ USER_ALL_USERNAME, USER_NAME_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, UserName),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_FullNameField 1
|
|
{ USER_ALL_FULLNAME, USER_FULL_NAME_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, FullName),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_PrimaryGroupIdField 2
|
|
{ USER_ALL_PRIMARYGROUPID, USER_PRIMARY_GROUP_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, PrimaryGroupId),
|
|
USER_LIST_GROUPS | READ_CONTROL | WRITE_DAC |
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_AdminCommentField 3
|
|
{ USER_ALL_ADMINCOMMENT, USER_COMMENT_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, AdminComment),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_UserCommentField 4
|
|
{ USER_ALL_USERCOMMENT, USER_USR_COMMENT_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, UserComment),
|
|
USER_WRITE_PREFERENCES
|
|
},
|
|
|
|
#define SAM_HomeDirectoryField 5
|
|
{ USER_ALL_HOMEDIRECTORY, USER_HOME_DIR_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, HomeDirectory),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_HomeDirectoryDriveField 6
|
|
{ USER_ALL_HOMEDIRECTORYDRIVE, USER_HOME_DIR_DRIVE_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, HomeDirectoryDrive),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_ScriptPathField 7
|
|
{ USER_ALL_SCRIPTPATH, USER_SCRIPT_PATH_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, ScriptPath),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_ProfilePathField 8
|
|
{ USER_ALL_PROFILEPATH, USER_PROFILE_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, ProfilePath),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_WorkstationsField 9
|
|
{ USER_ALL_WORKSTATIONS, USER_WORKSTATIONS_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, WorkStations),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_LogonHoursField 10
|
|
{ USER_ALL_LOGONHOURS, USER_LOGON_HOURS_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, LogonHours.LogonHours),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_UnitsPerWeekField 11
|
|
{ USER_ALL_LOGONHOURS, USER_UNITS_PER_WEEK_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, LogonHours.UnitsPerWeek),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_AccountExpiresField 12
|
|
{ USER_ALL_ACCOUNTEXPIRES, USER_ACCT_EXPIRES_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, AccountExpires),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_UserAccountControlField 13
|
|
{ USER_ALL_USERACCOUNTCONTROL, USER_FLAGS_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, UserAccountControl),
|
|
USER_WRITE_ACCOUNT | USER_READ_ACCOUNT | READ_CONTROL | WRITE_DAC
|
|
},
|
|
|
|
#define SAM_ParametersField 14
|
|
{ USER_ALL_PARAMETERS, USER_PARMS_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, Parameters),
|
|
USER_WRITE_ACCOUNT
|
|
},
|
|
|
|
#define SAM_CountryCodeField 15
|
|
{ USER_ALL_COUNTRYCODE, USER_COUNTRY_CODE_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, CountryCode),
|
|
USER_WRITE_PREFERENCES
|
|
},
|
|
|
|
#define SAM_CodePageField 16
|
|
{ USER_ALL_CODEPAGE, USER_CODE_PAGE_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, CodePage),
|
|
USER_WRITE_PREFERENCES
|
|
},
|
|
|
|
#define SAM_ClearTextPasswordField 17
|
|
{ USER_ALL_NTPASSWORDPRESENT, USER_PASSWORD_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, NtPassword),
|
|
USER_FORCE_PASSWORD_CHANGE
|
|
},
|
|
|
|
#define SAM_PasswordExpiredField 18
|
|
{ USER_ALL_PASSWORDEXPIRED, PARM_ERROR_UNKNOWN,
|
|
offsetof(USER_ALL_INFORMATION, PasswordExpired),
|
|
USER_FORCE_PASSWORD_CHANGE
|
|
},
|
|
|
|
#define SAM_OwfPasswordField 19
|
|
{ USER_ALL_LMPASSWORDPRESENT | USER_ALL_OWFPASSWORD,
|
|
USER_PASSWORD_PARMNUM,
|
|
offsetof(USER_ALL_INFORMATION, LmPassword),
|
|
USER_FORCE_PASSWORD_CHANGE
|
|
},
|
|
|
|
//
|
|
// The following levels are pseudo levels which merely define the
|
|
// access required to set a particuler UAS field.
|
|
|
|
#define SAM_AuthFlagsField 20
|
|
{ 0, PARM_ERROR_UNKNOWN,
|
|
0,
|
|
USER_LIST_GROUPS
|
|
},
|
|
|
|
#define SAM_MaxStorageField 21
|
|
{ 0, USER_MAX_STORAGE_PARMNUM,
|
|
0,
|
|
USER_READ_GENERAL
|
|
},
|
|
};
|
|
|
|
//
|
|
// Relate the NetUser API fields to the SAM API fields.
|
|
//
|
|
// This table contains as much information as possible to describe the
|
|
// relationship between fields in the NetUser API and the SAM API.
|
|
//
|
|
|
|
struct _UAS_SAM_TABLE {
|
|
|
|
//
|
|
// Describe the field types for UAS and SAM.
|
|
//
|
|
|
|
enum {
|
|
UT_STRING, // UAS is LPWSTR. SAM is UNICODE_STRING.
|
|
UT_BOOLEAN, // UAS is DWORD. SAM is BOOLEAN.
|
|
UT_USHORT, // UAS is DWORD. SAM is USHORT.
|
|
UT_ULONG, // UAS is DWORD. SAM is ULONG.
|
|
UT_TIME, // UAS is seconds since 1970. SAM is LARGE_INTEGER.
|
|
UT_PRIV, // Special case
|
|
UT_ACCOUNT_CONTROL, // Special case
|
|
UT_AUTH_FLAGS, // Special case
|
|
UT_MAX_STORAGE, // Special case
|
|
UT_OWF_PASSWORD, // Special case
|
|
UT_LOGON_HOURS, // Special case
|
|
UT_UNITS_PER_WEEK, // Special case
|
|
UT_CREATE_FULLNAME // Special case
|
|
} FieldType;
|
|
|
|
//
|
|
// The NetUser API detail level this field is in.
|
|
//
|
|
|
|
DWORD UasLevel;
|
|
|
|
//
|
|
// Index to the structure describing the Sam Field being changed.
|
|
//
|
|
|
|
DWORD SamField;
|
|
|
|
|
|
//
|
|
// Describe the byte offset of the field in the appropriate UAS
|
|
// and SAM structures.
|
|
//
|
|
|
|
DWORD UasOffset;
|
|
|
|
} UserpUasSamTable[] =
|
|
|
|
{
|
|
// Rename an account at info level 0 only.
|
|
|
|
{ UT_STRING, 0, SAM_UserNameField,
|
|
offsetof(USER_INFO_1, usri1_name) },
|
|
|
|
|
|
|
|
{ UT_STRING, 1, SAM_ClearTextPasswordField,
|
|
offsetof(USER_INFO_1, usri1_password) },
|
|
|
|
{ UT_STRING, 2, SAM_ClearTextPasswordField,
|
|
offsetof(USER_INFO_2, usri2_password) },
|
|
|
|
{ UT_STRING, 3, SAM_ClearTextPasswordField,
|
|
offsetof(USER_INFO_3, usri3_password) },
|
|
|
|
{ UT_STRING, 4, SAM_ClearTextPasswordField,
|
|
offsetof(USER_INFO_4, usri4_password) },
|
|
|
|
{ UT_STRING, 1003, SAM_ClearTextPasswordField,
|
|
offsetof(USER_INFO_1003, usri1003_password) },
|
|
|
|
|
|
|
|
{ UT_OWF_PASSWORD, 21, SAM_OwfPasswordField,
|
|
offsetof(USER_INFO_21, usri21_password[0]) },
|
|
|
|
{ UT_OWF_PASSWORD, 22, SAM_OwfPasswordField,
|
|
offsetof(USER_INFO_22, usri22_password[0]) },
|
|
|
|
|
|
|
|
{ UT_PRIV, 1, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_1, usri1_priv) },
|
|
|
|
{ UT_PRIV, 2, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_2, usri2_priv) },
|
|
|
|
{ UT_PRIV, 22, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_22, usri22_priv) },
|
|
|
|
|
|
#ifdef notdef
|
|
//
|
|
// usri3_priv is totally ignored for info level three. The field is
|
|
// supplied for compatibility with LM 2.x only and LM 2.x never uses
|
|
// info level 3.
|
|
//
|
|
{ UT_PRIV, 3, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_3, usri3_priv) },
|
|
#endif // notdef
|
|
|
|
{ UT_PRIV, 1005, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_1005, usri1005_priv) },
|
|
|
|
|
|
|
|
{ UT_STRING, 1, SAM_HomeDirectoryField,
|
|
offsetof(USER_INFO_1, usri1_home_dir) },
|
|
|
|
{ UT_STRING, 2, SAM_HomeDirectoryField,
|
|
offsetof(USER_INFO_2, usri2_home_dir) },
|
|
|
|
{ UT_STRING, 22, SAM_HomeDirectoryField,
|
|
offsetof(USER_INFO_22, usri22_home_dir) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_HomeDirectoryField,
|
|
offsetof(USER_INFO_3, usri3_home_dir) },
|
|
|
|
{ UT_STRING, 4, SAM_HomeDirectoryField,
|
|
offsetof(USER_INFO_4, usri4_home_dir) },
|
|
|
|
{ UT_STRING, 1006, SAM_HomeDirectoryField,
|
|
offsetof(USER_INFO_1006, usri1006_home_dir) },
|
|
|
|
|
|
{ UT_STRING, 1, SAM_AdminCommentField,
|
|
offsetof(USER_INFO_1, usri1_comment) },
|
|
|
|
{ UT_STRING, 2, SAM_AdminCommentField,
|
|
offsetof(USER_INFO_2, usri2_comment) },
|
|
|
|
{ UT_STRING, 22, SAM_AdminCommentField,
|
|
offsetof(USER_INFO_22, usri22_comment) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_AdminCommentField,
|
|
offsetof(USER_INFO_3, usri3_comment) },
|
|
|
|
{ UT_STRING, 4, SAM_AdminCommentField,
|
|
offsetof(USER_INFO_4, usri4_comment) },
|
|
|
|
{ UT_STRING, 1007, SAM_AdminCommentField,
|
|
offsetof(USER_INFO_1007, usri1007_comment) },
|
|
|
|
|
|
{ UT_ACCOUNT_CONTROL, 1, SAM_UserAccountControlField,
|
|
offsetof(USER_INFO_1, usri1_flags) },
|
|
|
|
{ UT_ACCOUNT_CONTROL, 2, SAM_UserAccountControlField,
|
|
offsetof(USER_INFO_2, usri2_flags) },
|
|
|
|
{ UT_ACCOUNT_CONTROL, 22, SAM_UserAccountControlField,
|
|
offsetof(USER_INFO_22, usri22_flags) },
|
|
|
|
|
|
{ UT_ACCOUNT_CONTROL, 3, SAM_UserAccountControlField,
|
|
offsetof(USER_INFO_3, usri3_flags) },
|
|
|
|
{ UT_ACCOUNT_CONTROL, 4, SAM_UserAccountControlField,
|
|
offsetof(USER_INFO_4, usri4_flags) },
|
|
|
|
{ UT_ACCOUNT_CONTROL, 1008, SAM_UserAccountControlField,
|
|
offsetof(USER_INFO_1008, usri1008_flags) },
|
|
|
|
|
|
{ UT_STRING, 1, SAM_ScriptPathField,
|
|
offsetof(USER_INFO_1, usri1_script_path) },
|
|
|
|
{ UT_STRING, 2, SAM_ScriptPathField,
|
|
offsetof(USER_INFO_2, usri2_script_path) },
|
|
|
|
{ UT_STRING, 22, SAM_ScriptPathField,
|
|
offsetof(USER_INFO_22, usri22_script_path) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_ScriptPathField,
|
|
offsetof(USER_INFO_3, usri3_script_path) },
|
|
|
|
{ UT_STRING, 4, SAM_ScriptPathField,
|
|
offsetof(USER_INFO_4, usri4_script_path) },
|
|
|
|
{ UT_STRING, 1009, SAM_ScriptPathField,
|
|
offsetof(USER_INFO_1009, usri1009_script_path) },
|
|
|
|
|
|
{ UT_AUTH_FLAGS, 2, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_2, usri2_auth_flags) },
|
|
|
|
{ UT_AUTH_FLAGS, 22, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_22, usri22_auth_flags) },
|
|
|
|
|
|
#ifdef notdef
|
|
//
|
|
// usri3_auth_flags is totally ignored for info level three. The field is
|
|
// supplied for compatibility with LM 2.x only and LM 2.x never uses
|
|
// info level 3.
|
|
//
|
|
{ UT_AUTH_FLAGS, 3, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_3, usri3_auth_flags) },
|
|
|
|
{ UT_AUTH_FLAGS, 4, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_4, usri4_auth_flags) },
|
|
#endif // notdef
|
|
|
|
{ UT_AUTH_FLAGS, 1010, SAM_AuthFlagsField,
|
|
offsetof(USER_INFO_1010, usri1010_auth_flags) },
|
|
|
|
|
|
|
|
{ UT_CREATE_FULLNAME, 1, SAM_FullNameField,
|
|
offsetof(USER_INFO_1, usri1_name) },
|
|
|
|
{ UT_STRING, 2, SAM_FullNameField,
|
|
offsetof(USER_INFO_2, usri2_full_name) },
|
|
|
|
{ UT_STRING, 22, SAM_FullNameField,
|
|
offsetof(USER_INFO_22, usri22_full_name) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_FullNameField,
|
|
offsetof(USER_INFO_3, usri3_full_name) },
|
|
|
|
{ UT_STRING, 4, SAM_FullNameField,
|
|
offsetof(USER_INFO_4, usri4_full_name) },
|
|
|
|
{ UT_STRING, 1011, SAM_FullNameField,
|
|
offsetof(USER_INFO_1011, usri1011_full_name) },
|
|
|
|
|
|
|
|
{ UT_STRING, 2, SAM_UserCommentField,
|
|
offsetof(USER_INFO_2, usri2_usr_comment) },
|
|
|
|
{ UT_STRING, 22, SAM_UserCommentField,
|
|
offsetof(USER_INFO_22, usri22_usr_comment) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_UserCommentField,
|
|
offsetof(USER_INFO_3, usri3_usr_comment) },
|
|
|
|
{ UT_STRING, 4, SAM_UserCommentField,
|
|
offsetof(USER_INFO_4, usri4_usr_comment) },
|
|
|
|
{ UT_STRING, 1012, SAM_UserCommentField,
|
|
offsetof(USER_INFO_1012, usri1012_usr_comment) },
|
|
|
|
|
|
{ UT_STRING, 2, SAM_ParametersField,
|
|
offsetof(USER_INFO_2, usri2_parms) },
|
|
|
|
{ UT_STRING, 22, SAM_ParametersField,
|
|
offsetof(USER_INFO_22, usri22_parms) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_ParametersField,
|
|
offsetof(USER_INFO_3, usri3_parms) },
|
|
|
|
{ UT_STRING, 4, SAM_ParametersField,
|
|
offsetof(USER_INFO_4, usri4_parms) },
|
|
|
|
{ UT_STRING, 1013, SAM_ParametersField,
|
|
offsetof(USER_INFO_1013, usri1013_parms) },
|
|
|
|
|
|
{ UT_STRING, 2, SAM_WorkstationsField,
|
|
offsetof(USER_INFO_2, usri2_workstations) },
|
|
|
|
{ UT_STRING, 22, SAM_WorkstationsField,
|
|
offsetof(USER_INFO_22, usri22_workstations) },
|
|
|
|
|
|
{ UT_STRING, 3, SAM_WorkstationsField,
|
|
offsetof(USER_INFO_3, usri3_workstations) },
|
|
|
|
{ UT_STRING, 4, SAM_WorkstationsField,
|
|
offsetof(USER_INFO_4, usri4_workstations) },
|
|
|
|
{ UT_STRING, 1014, SAM_WorkstationsField,
|
|
offsetof(USER_INFO_1014, usri1014_workstations) },
|
|
|
|
|
|
{ UT_TIME, 2, SAM_AccountExpiresField,
|
|
offsetof(USER_INFO_2, usri2_acct_expires) },
|
|
|
|
{ UT_TIME, 22, SAM_AccountExpiresField,
|
|
offsetof(USER_INFO_22, usri22_acct_expires) },
|
|
|
|
|
|
{ UT_TIME, 3, SAM_AccountExpiresField,
|
|
offsetof(USER_INFO_3, usri3_acct_expires) },
|
|
|
|
{ UT_TIME, 4, SAM_AccountExpiresField,
|
|
offsetof(USER_INFO_4, usri4_acct_expires) },
|
|
|
|
{ UT_TIME, 1017, SAM_AccountExpiresField,
|
|
offsetof(USER_INFO_1017, usri1017_acct_expires) },
|
|
|
|
|
|
#ifdef notdef // lm 2.1 gets this wrong when adding BDC accounts
|
|
{ UT_MAX_STORAGE, 2, SAM_MaxStorageField,
|
|
offsetof(USER_INFO_2, usri2_max_storage) },
|
|
|
|
{ UT_MAX_STORAGE, 22, SAM_MaxStorageField,
|
|
offsetof(USER_INFO_22, usri22_max_storage) },
|
|
|
|
{ UT_MAX_STORAGE, 3, SAM_MaxStorageField,
|
|
offsetof(USER_INFO_3, usri3_max_storage) },
|
|
|
|
{ UT_MAX_STORAGE, 4, SAM_MaxStorageField,
|
|
offsetof(USER_INFO_4, usri4_max_storage) },
|
|
|
|
#endif // notdef
|
|
|
|
{ UT_MAX_STORAGE, 1018, SAM_MaxStorageField,
|
|
offsetof(USER_INFO_1018, usri1018_max_storage) },
|
|
|
|
|
|
{ UT_UNITS_PER_WEEK, 2, SAM_UnitsPerWeekField,
|
|
offsetof(USER_INFO_2, usri2_units_per_week) },
|
|
|
|
{ UT_UNITS_PER_WEEK, 22, SAM_UnitsPerWeekField,
|
|
offsetof(USER_INFO_22, usri22_units_per_week) },
|
|
|
|
|
|
{ UT_UNITS_PER_WEEK, 3, SAM_UnitsPerWeekField,
|
|
offsetof(USER_INFO_3, usri3_units_per_week) },
|
|
|
|
{ UT_UNITS_PER_WEEK, 4, SAM_UnitsPerWeekField,
|
|
offsetof(USER_INFO_4, usri4_units_per_week) },
|
|
|
|
{ UT_UNITS_PER_WEEK, 1020, SAM_UnitsPerWeekField,
|
|
offsetof(USER_INFO_1020, usri1020_units_per_week) },
|
|
|
|
|
|
{ UT_LOGON_HOURS, 2, SAM_LogonHoursField,
|
|
offsetof(USER_INFO_2, usri2_logon_hours) },
|
|
|
|
{ UT_LOGON_HOURS, 22, SAM_LogonHoursField,
|
|
offsetof(USER_INFO_22, usri22_logon_hours) },
|
|
|
|
|
|
{ UT_LOGON_HOURS, 3, SAM_LogonHoursField,
|
|
offsetof(USER_INFO_3, usri3_logon_hours) },
|
|
|
|
{ UT_LOGON_HOURS, 4, SAM_LogonHoursField,
|
|
offsetof(USER_INFO_4, usri4_logon_hours) },
|
|
|
|
{ UT_LOGON_HOURS, 1020, SAM_LogonHoursField,
|
|
offsetof(USER_INFO_1020, usri1020_logon_hours) },
|
|
|
|
|
|
{ UT_USHORT, 2, SAM_CountryCodeField,
|
|
offsetof(USER_INFO_2, usri2_country_code) },
|
|
|
|
{ UT_USHORT, 22, SAM_CountryCodeField,
|
|
offsetof(USER_INFO_22, usri22_country_code) },
|
|
|
|
|
|
{ UT_USHORT, 3, SAM_CountryCodeField,
|
|
offsetof(USER_INFO_3, usri3_country_code) },
|
|
|
|
{ UT_USHORT, 4, SAM_CountryCodeField,
|
|
offsetof(USER_INFO_4, usri4_country_code) },
|
|
|
|
{ UT_USHORT, 1024, SAM_CountryCodeField,
|
|
offsetof(USER_INFO_1024, usri1024_country_code) },
|
|
|
|
|
|
{ UT_USHORT, 2, SAM_CodePageField,
|
|
offsetof(USER_INFO_2, usri2_code_page) },
|
|
|
|
{ UT_USHORT, 22, SAM_CodePageField,
|
|
offsetof(USER_INFO_22, usri22_code_page) },
|
|
|
|
|
|
{ UT_USHORT, 3, SAM_CodePageField,
|
|
offsetof(USER_INFO_3, usri3_code_page) },
|
|
|
|
{ UT_USHORT, 4, SAM_CodePageField,
|
|
offsetof(USER_INFO_4, usri4_code_page) },
|
|
|
|
{ UT_USHORT, 1025, SAM_CodePageField,
|
|
offsetof(USER_INFO_1025, usri1025_code_page) },
|
|
|
|
|
|
|
|
{ UT_ULONG, 3, SAM_PrimaryGroupIdField,
|
|
offsetof(USER_INFO_3, usri3_primary_group_id) },
|
|
|
|
{ UT_ULONG, 4, SAM_PrimaryGroupIdField,
|
|
offsetof(USER_INFO_4, usri4_primary_group_id) },
|
|
|
|
{ UT_ULONG, 1051, SAM_PrimaryGroupIdField,
|
|
offsetof(USER_INFO_1051, usri1051_primary_group_id) },
|
|
|
|
|
|
|
|
{ UT_STRING, 3, SAM_ProfilePathField,
|
|
offsetof(USER_INFO_3, usri3_profile) },
|
|
|
|
{ UT_STRING, 4, SAM_ProfilePathField,
|
|
offsetof(USER_INFO_4, usri4_profile) },
|
|
|
|
{ UT_STRING, 1052, SAM_ProfilePathField,
|
|
offsetof(USER_INFO_1052, usri1052_profile) },
|
|
|
|
{ UT_STRING, 3, SAM_HomeDirectoryDriveField,
|
|
offsetof(USER_INFO_3, usri3_home_dir_drive) },
|
|
|
|
{ UT_STRING, 4, SAM_HomeDirectoryDriveField,
|
|
offsetof(USER_INFO_4, usri4_home_dir_drive) },
|
|
|
|
{ UT_STRING, 1053, SAM_HomeDirectoryDriveField,
|
|
offsetof(USER_INFO_1053, usri1053_home_dir_drive) },
|
|
|
|
|
|
{ UT_BOOLEAN, 3, SAM_PasswordExpiredField,
|
|
offsetof(USER_INFO_3, usri3_password_expired) },
|
|
|
|
{ UT_BOOLEAN, 4, SAM_PasswordExpiredField,
|
|
offsetof(USER_INFO_4, usri4_password_expired) },
|
|
|
|
};
|
|
|
|
|
|
NET_API_STATUS
|
|
UserpSetInfo(
|
|
IN SAM_HANDLE DomainHandle,
|
|
IN PSID DomainId,
|
|
IN SAM_HANDLE UserHandle OPTIONAL,
|
|
IN SAM_HANDLE BuiltinDomainHandle OPTIONAL,
|
|
IN ULONG UserRelativeId,
|
|
IN LPCWSTR UserName,
|
|
IN DWORD Level,
|
|
IN LPBYTE Buffer,
|
|
IN ULONG WhichFieldsMask,
|
|
OUT LPDWORD ParmError OPTIONAL // Name required by NetpSetParmError
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the parameters on a user account in the user accounts database.
|
|
|
|
Arguments:
|
|
|
|
DomainHandle - Domain Handle for the domain.
|
|
|
|
PSID DomainId - Domain Sid for DomainHandle
|
|
|
|
UserHandle - User Handle of the already open group. If one is not
|
|
specified, one will be openned then closed. If one is specified,
|
|
it must be open with adequate access.
|
|
|
|
BuiltinDomainHandle - Domain Handle for the builtin domain. Need only be
|
|
specified for info level 1, 2, 3, 22, 1005 and 1010. Need not
|
|
be specified when a user is created.
|
|
|
|
UserRelativeId - Relative Id of the user.
|
|
|
|
UserName - Name of the user to set.
|
|
|
|
Level - Level of information provided.
|
|
|
|
Buffer - A pointer to the buffer containing the user information
|
|
structure.
|
|
|
|
ParmError - Optional pointer to a DWORD to return the index of the
|
|
first parameter in error when ERROR_INVALID_PARAMETER is returned.
|
|
If NULL, the parameter is not returned on error.
|
|
|
|
Return Value:
|
|
|
|
Error code for the operation.
|
|
|
|
NOTE: LogonServer field that is passed in UAS set structure is never
|
|
used or validated. It is simply ignored.
|
|
|
|
--*/
|
|
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
NTSTATUS Status;
|
|
SAM_HANDLE LocalUserHandle = NULL;
|
|
ACCESS_MASK DesiredAccess;
|
|
DWORD UasSamIndex;
|
|
|
|
|
|
USER_ALL_INFORMATION UserAll;
|
|
|
|
//
|
|
// Value of Fields from UAS structure (used for validation)
|
|
//
|
|
|
|
DWORD UasUserFlags;
|
|
DWORD NewPriv;
|
|
DWORD NewAuthFlags;
|
|
|
|
BOOL ValidatePriv = FALSE;
|
|
BOOL ValidateAuthFlags = FALSE;
|
|
|
|
USHORT UasUnitsPerWeek;
|
|
|
|
|
|
//
|
|
// Variables for changing the DACL on the user.
|
|
//
|
|
|
|
PACL OldUserDacl = NULL;
|
|
PACL NewUserDacl = NULL;
|
|
|
|
BOOL UserDaclChanged = FALSE;
|
|
BOOL HandleUserDacl = FALSE;
|
|
USHORT AceIndex;
|
|
PSID UserSid = NULL;
|
|
|
|
|
|
//
|
|
// Define several macros for accessing the various fields of the UAS
|
|
// structure. Each macro takes an index into the UserpUasSamTable
|
|
// array and returns the value.
|
|
//
|
|
|
|
#define GET_UAS_STRING_POINTER( _i ) \
|
|
(*((LPWSTR *)(Buffer + UserpUasSamTable[_i].UasOffset)))
|
|
|
|
#define GET_UAS_DWORD( _i ) \
|
|
(*((DWORD *)(Buffer + UserpUasSamTable[_i].UasOffset)))
|
|
|
|
#define GET_UAS_FIELD_ADDRESS( _i ) \
|
|
(Buffer + UserpUasSamTable[_i].UasOffset)
|
|
|
|
|
|
//
|
|
// Define a macro which returns a pointer the appropriate
|
|
// SamFieldDescription structure given an index into the UserpUasSamTable.
|
|
//
|
|
|
|
#define SAM_FIELD( _i ) \
|
|
SamFieldDescription[ UserpUasSamTable[_i].SamField ]
|
|
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: entered \n" ));
|
|
}
|
|
|
|
NetpSetParmError( PARM_ERROR_NONE );
|
|
RtlZeroMemory( &UserAll, sizeof(UserAll) );
|
|
|
|
|
|
//
|
|
// Go through the list of valid info levels determining if the info level
|
|
// is valid and computing the desired access to the user and copying the
|
|
// UAS information into the SAM structure.
|
|
//
|
|
|
|
DesiredAccess = 0;
|
|
for ( UasSamIndex=0 ;
|
|
UasSamIndex<sizeof(UserpUasSamTable)/sizeof(UserpUasSamTable[0]);
|
|
UasSamIndex++ ){
|
|
|
|
LPBYTE SamField;
|
|
|
|
|
|
//
|
|
// If this field isn't one we're changing, just skip to the next one
|
|
//
|
|
|
|
if ( Level != UserpUasSamTable[UasSamIndex].UasLevel ) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Set up a pointer to the appropriate field in SAM's structure.
|
|
//
|
|
|
|
if ( SAM_FIELD(UasSamIndex).WhichField != 0 ) {
|
|
SamField = ((LPBYTE)(&UserAll)) + SAM_FIELD(UasSamIndex).SamOffset;
|
|
} else {
|
|
SamField = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the UAS field based on the field type.
|
|
//
|
|
|
|
switch ( UserpUasSamTable[UasSamIndex].FieldType ) {
|
|
|
|
//
|
|
// Default the fullname of the account to be the user name when
|
|
// the user is created using level 1.
|
|
//
|
|
// Ignore this entry if not a "create" operation.
|
|
//
|
|
case UT_CREATE_FULLNAME:
|
|
|
|
if ( UserHandle == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
/* DROP THROUGH to the UT_STRING case */
|
|
|
|
//
|
|
// If this is a PARMNUM_ALL and the caller passed in a
|
|
// NULL pointer to a string, he doesn't want to change the string.
|
|
//
|
|
|
|
case UT_STRING:
|
|
|
|
if ( GET_UAS_STRING_POINTER( UasSamIndex ) == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
(PUNICODE_STRING) SamField,
|
|
GET_UAS_STRING_POINTER(UasSamIndex) );
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// Just save the UnitsPerWeek until UT_LOGON_HOURS can handle
|
|
// both fields at once.
|
|
//
|
|
// Sam gets confused if we pass in one field without the other.
|
|
//
|
|
|
|
case UT_UNITS_PER_WEEK:
|
|
|
|
UasUnitsPerWeek = (USHORT) GET_UAS_DWORD(UasSamIndex);
|
|
|
|
//
|
|
// If this is a create at info level 2 (e.g., dowlevel client),
|
|
// assume the caller specified UNITS_PER_WEEK.
|
|
//
|
|
// We don't special case SetInfo at level 2 because we don't
|
|
// want to corrupt the value assuming he did a query followed
|
|
// by a set.
|
|
//
|
|
|
|
if ( Level == 2 && UserHandle != NULL ) {
|
|
UasUnitsPerWeek = UNITS_PER_WEEK;
|
|
}
|
|
|
|
if ( UasUnitsPerWeek > USHRT_MAX ) {
|
|
NetpSetParmError( SAM_FIELD(UasSamIndex).UasParmNum );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Ushort too big Index:%ld Value:%ld\n",
|
|
UasSamIndex,
|
|
UasUnitsPerWeek ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ignore this field completely for now.
|
|
// Let UT_LOGON_HOURS define the desired access and Whichfields.
|
|
continue;
|
|
|
|
//
|
|
// If the caller passed in a NULL pointer to the logon hours
|
|
// he doesn't want to change the logon hours.
|
|
//
|
|
|
|
case UT_LOGON_HOURS:
|
|
|
|
if ( GET_UAS_STRING_POINTER( UasSamIndex ) == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
*((PUCHAR *)SamField) = (PUCHAR)GET_UAS_STRING_POINTER(UasSamIndex);
|
|
UserAll.LogonHours.UnitsPerWeek = UasUnitsPerWeek;
|
|
break;
|
|
|
|
|
|
//
|
|
// If the user is setting max storage, require him to set
|
|
// it to USER_MAXSTORAGE_UNLIMITED since SAM doesn't support
|
|
// max storage.
|
|
//
|
|
|
|
case UT_MAX_STORAGE:
|
|
if ( GET_UAS_DWORD(UasSamIndex) != USER_MAXSTORAGE_UNLIMITED ) {
|
|
|
|
NetpSetParmError( USER_MAX_STORAGE_PARMNUM );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: Max storage is invalid\n" ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
// 'break' to make sure the user exists.
|
|
break;
|
|
|
|
|
|
//
|
|
// Handle Account control
|
|
//
|
|
// Ensure all the required bits are on and only valid bits
|
|
// are on.
|
|
//
|
|
|
|
case UT_ACCOUNT_CONTROL: {
|
|
|
|
UasUserFlags = GET_UAS_DWORD(UasSamIndex);
|
|
|
|
if ((UasUserFlags & ~UF_SETTABLE_BITS) != 0 ) {
|
|
|
|
NetpSetParmError( USER_FLAGS_PARMNUM );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Invalid account control bits (1) \n" ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If none of the account type bit is set in the usri_flag,
|
|
// means that the caller does not want to change its account type.
|
|
// break out now, and we will set the appropriate account
|
|
// bit when we set the usri_flag.
|
|
//
|
|
|
|
if ( UasUserFlags & UF_ACCOUNT_TYPE_MASK ) {
|
|
|
|
//
|
|
// Account Types bits are exclusive, so make sure that
|
|
// precisely one Account Type bit is set.
|
|
//
|
|
|
|
if ( !JUST_ONE_BIT( UasUserFlags & UF_ACCOUNT_TYPE_MASK )) {
|
|
|
|
NetpSetParmError( USER_FLAGS_PARMNUM );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Invalid account control bits (2) \n" ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If this is a 'create' operation,
|
|
// and the user has asked for the SAM defaults.
|
|
// we have no reason to change the DACL
|
|
//
|
|
|
|
if ( UserHandle != NULL &&
|
|
(UasUserFlags & UF_PASSWD_CANT_CHANGE) == 0 ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// In all other cases, update the DACL to match the callers request.
|
|
//
|
|
|
|
HandleUserDacl = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Copy a boolean to the SAM structure.
|
|
//
|
|
|
|
case UT_BOOLEAN:
|
|
|
|
*((PBOOLEAN)SamField) = (BOOLEAN)
|
|
(GET_UAS_DWORD(UasSamIndex)) ? TRUE : FALSE;
|
|
break;
|
|
|
|
|
|
//
|
|
// Ensure unsigned shorts are really in range and
|
|
// copy it to the SAM structure.
|
|
//
|
|
|
|
case UT_USHORT:
|
|
|
|
if ( GET_UAS_DWORD(UasSamIndex) > USHRT_MAX ) {
|
|
NetpSetParmError( SAM_FIELD(UasSamIndex).UasParmNum );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Ushort too big Index:%ld Value:%ld\n",
|
|
UasSamIndex,
|
|
GET_UAS_DWORD(UasSamIndex) ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
*((PUSHORT)SamField) = (USHORT) GET_UAS_DWORD(UasSamIndex);
|
|
break;
|
|
|
|
//
|
|
// Copy the unsigned long to the SAM structure
|
|
//
|
|
|
|
case UT_ULONG:
|
|
|
|
*((PULONG)SamField) = (ULONG)GET_UAS_DWORD(UasSamIndex);
|
|
break;
|
|
|
|
//
|
|
// Convert time to its SAM counterpart
|
|
//
|
|
|
|
case UT_TIME:
|
|
|
|
//
|
|
// PREFIX: SamField can only be NULL due to a programming error
|
|
// by setting the UserpUasSamTable table incorrectly. This
|
|
// assert catches the problem.
|
|
//
|
|
NetpAssert(NULL != SamField);
|
|
if ( GET_UAS_DWORD(UasSamIndex) == TIMEQ_FOREVER ) {
|
|
|
|
((PLARGE_INTEGER) SamField)->LowPart = 0;
|
|
((PLARGE_INTEGER) SamField)->HighPart = 0;
|
|
|
|
} else {
|
|
RtlSecondsSince1970ToTime(
|
|
GET_UAS_DWORD(UasSamIndex),
|
|
(PLARGE_INTEGER) SamField );
|
|
}
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: Index: %ld Time %lx %lx %lx\n",
|
|
UasSamIndex,
|
|
((PLARGE_INTEGER) SamField)->HighPart,
|
|
((PLARGE_INTEGER) SamField)->LowPart,
|
|
GET_UAS_DWORD(UasSamIndex) ));
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// Copy the OWF password to the SAM structure.
|
|
//
|
|
case UT_OWF_PASSWORD:
|
|
|
|
((PUNICODE_STRING) SamField)->Buffer =
|
|
(LPWSTR) (GET_UAS_FIELD_ADDRESS( UasSamIndex ));
|
|
|
|
((PUNICODE_STRING) SamField)->Length =
|
|
((PUNICODE_STRING) SamField)->MaximumLength =
|
|
LM_OWF_PASSWORD_LENGTH;
|
|
|
|
//
|
|
// set that the LmPasswordField field to TRUE to indicate
|
|
// that we filled LmPassword field.
|
|
//
|
|
|
|
UserAll.LmPasswordPresent = TRUE;
|
|
UserAll.NtPasswordPresent = FALSE;
|
|
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// Ensure the specified privilege is valid.
|
|
//
|
|
|
|
case UT_PRIV:
|
|
|
|
NewPriv = GET_UAS_DWORD(UasSamIndex);
|
|
|
|
if ( (NewPriv & ~USER_PRIV_MASK) != 0 ) {
|
|
NetpSetParmError( SAM_FIELD(UasSamIndex).UasParmNum );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: Invalid priv %ld\n", NewPriv ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
ValidatePriv = TRUE;
|
|
break;
|
|
|
|
|
|
//
|
|
// Ensure the specified operator flags is valid.
|
|
//
|
|
|
|
case UT_AUTH_FLAGS:
|
|
|
|
NewAuthFlags = GET_UAS_DWORD(UasSamIndex);
|
|
if ( (NewAuthFlags & ~AF_SETTABLE_BITS) != 0 ) {
|
|
NetpSetParmError( SAM_FIELD(UasSamIndex).UasParmNum );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: Invalid auth_flag %lx\n",
|
|
NewAuthFlags ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
ValidateAuthFlags = TRUE;
|
|
break;
|
|
|
|
//
|
|
// All valid cases were explicitly checked above.
|
|
//
|
|
|
|
default:
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Invalid field type on initial scan."
|
|
" Index:%ld\n", UasSamIndex ));
|
|
}
|
|
|
|
NetStatus = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Accumulate the desired access to do all this functionality.
|
|
|
|
DesiredAccess |= SAM_FIELD(UasSamIndex).DesiredAccess;
|
|
|
|
//
|
|
// Accumalate which fields are being changed in the
|
|
// USER_ALL_INFORMATION structure.
|
|
|
|
UserAll.WhichFields |= SAM_FIELD(UasSamIndex).WhichField;
|
|
|
|
}
|
|
|
|
//
|
|
// Check to be sure the user specified a valid Level.
|
|
//
|
|
// The search of the UserpUasSamTable should have resulted in
|
|
// at least one match if the arguments are valid.
|
|
//
|
|
|
|
if ( DesiredAccess == 0 ) {
|
|
NetpSetParmError( PARM_ERROR_UNKNOWN );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: Desired Access == 0\n" ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the user asking for accumulated desired access
|
|
//
|
|
// If a UserHandle was passed in, use it.
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT( UserHandle ) ) {
|
|
LocalUserHandle = UserHandle;
|
|
} else {
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: Desired Access %lX\n", DesiredAccess ));
|
|
}
|
|
|
|
NetStatus = UserpOpenUser( DomainHandle,
|
|
DesiredAccess,
|
|
UserName,
|
|
&LocalUserHandle,
|
|
&UserRelativeId );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: UserpOpenUser returns %ld\n",
|
|
NetStatus ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If an ordinary user created this user (SamCreateUser2InDomain),
|
|
// we must mask off the fields which a user cannot set.
|
|
//
|
|
UserAll.WhichFields &= WhichFieldsMask;
|
|
|
|
|
|
//
|
|
// Handle Account control
|
|
//
|
|
// Set the individual bits. Notice that I don't change any of
|
|
// the bits which aren't defined by the UAS API.
|
|
//
|
|
|
|
if ( UserAll.WhichFields & USER_ALL_USERACCOUNTCONTROL ) {
|
|
|
|
USER_CONTROL_INFORMATION *UserControl = NULL;
|
|
|
|
//
|
|
// Use the current value of UserAccountControl as the proposed
|
|
// new value of UserAccountControl.
|
|
//
|
|
|
|
Status = SamQueryInformationUser( LocalUserHandle,
|
|
UserControlInformation,
|
|
(PVOID *)&UserControl);
|
|
|
|
if ( ! NT_SUCCESS( Status ) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpGetInfo: SamQueryInformationUser returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
UserAll.UserAccountControl = UserControl->UserAccountControl;
|
|
|
|
Status = SamFreeMemory( UserControl );
|
|
NetpAssert( NT_SUCCESS(Status) );
|
|
|
|
//
|
|
// Leave all bits not defined by the UAS API alone,
|
|
// including account type bits.
|
|
//
|
|
|
|
UserAll.UserAccountControl &= ~(USER_ACCOUNT_DISABLED |
|
|
USER_HOME_DIRECTORY_REQUIRED |
|
|
USER_PASSWORD_NOT_REQUIRED |
|
|
USER_DONT_EXPIRE_PASSWORD |
|
|
USER_ACCOUNT_AUTO_LOCKED |
|
|
USER_MNS_LOGON_ACCOUNT |
|
|
USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED |
|
|
USER_SMARTCARD_REQUIRED |
|
|
USER_TRUSTED_FOR_DELEGATION |
|
|
USER_NOT_DELEGATED |
|
|
USER_USE_DES_KEY_ONLY |
|
|
USER_DONT_REQUIRE_PREAUTH |
|
|
USER_PASSWORD_EXPIRED
|
|
);
|
|
|
|
if (UasUserFlags & UF_ACCOUNTDISABLE) {
|
|
UserAll.UserAccountControl |= USER_ACCOUNT_DISABLED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_HOMEDIR_REQUIRED) {
|
|
UserAll.UserAccountControl |= USER_HOME_DIRECTORY_REQUIRED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_PASSWD_NOTREQD) {
|
|
UserAll.UserAccountControl |= USER_PASSWORD_NOT_REQUIRED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_DONT_EXPIRE_PASSWD) {
|
|
UserAll.UserAccountControl |= USER_DONT_EXPIRE_PASSWORD;
|
|
}
|
|
|
|
if (UasUserFlags & UF_LOCKOUT) {
|
|
UserAll.UserAccountControl |= USER_ACCOUNT_AUTO_LOCKED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_MNS_LOGON_ACCOUNT) {
|
|
UserAll.UserAccountControl |= USER_MNS_LOGON_ACCOUNT;
|
|
}
|
|
|
|
if (UasUserFlags & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED) {
|
|
(UserAll.UserAccountControl) |= USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_SMARTCARD_REQUIRED) {
|
|
(UserAll.UserAccountControl) |= USER_SMARTCARD_REQUIRED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_TRUSTED_FOR_DELEGATION) {
|
|
(UserAll.UserAccountControl) |= USER_TRUSTED_FOR_DELEGATION;
|
|
}
|
|
|
|
if (UasUserFlags & UF_NOT_DELEGATED) {
|
|
(UserAll.UserAccountControl) |= USER_NOT_DELEGATED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_USE_DES_KEY_ONLY) {
|
|
(UserAll.UserAccountControl) |= USER_USE_DES_KEY_ONLY;
|
|
}
|
|
|
|
if (UasUserFlags & UF_DONT_REQUIRE_PREAUTH) {
|
|
(UserAll.UserAccountControl) |= USER_DONT_REQUIRE_PREAUTH;
|
|
}
|
|
|
|
if (UasUserFlags & UF_PASSWORD_EXPIRED) {
|
|
(UserAll.UserAccountControl) |= USER_PASSWORD_EXPIRED;
|
|
}
|
|
|
|
if (UasUserFlags & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) {
|
|
(UserAll.UserAccountControl) |= USER_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
|
|
}
|
|
|
|
|
|
//
|
|
// Set the account type bit.
|
|
//
|
|
// If no account type bit is set in user specified flag,
|
|
// then leave this bit as it is.
|
|
//
|
|
|
|
if( UasUserFlags & UF_ACCOUNT_TYPE_MASK ) {
|
|
ULONG NewSamAccountType;
|
|
ULONG OldSamAccountType;
|
|
|
|
OldSamAccountType =
|
|
(UserAll.UserAccountControl) & USER_ACCOUNT_TYPE_MASK;
|
|
|
|
|
|
//
|
|
// Determine what the new account type should be.
|
|
//
|
|
|
|
if ( UasUserFlags & UF_TEMP_DUPLICATE_ACCOUNT ) {
|
|
NewSamAccountType = USER_TEMP_DUPLICATE_ACCOUNT;
|
|
|
|
} else if ( UasUserFlags & UF_NORMAL_ACCOUNT ) {
|
|
NewSamAccountType = USER_NORMAL_ACCOUNT;
|
|
|
|
} else if (UasUserFlags & UF_INTERDOMAIN_TRUST_ACCOUNT){
|
|
NewSamAccountType = USER_INTERDOMAIN_TRUST_ACCOUNT;
|
|
|
|
} else if (UasUserFlags & UF_WORKSTATION_TRUST_ACCOUNT){
|
|
NewSamAccountType = USER_WORKSTATION_TRUST_ACCOUNT;
|
|
|
|
} else if ( UasUserFlags & UF_SERVER_TRUST_ACCOUNT ) {
|
|
NewSamAccountType = USER_SERVER_TRUST_ACCOUNT;
|
|
|
|
} else {
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Invalid account type (3)\n"));
|
|
}
|
|
|
|
NetStatus = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#ifdef notdef
|
|
//
|
|
// If we are not creating this user,
|
|
// and either the old or the new account type is a machine account,
|
|
// don't allow the account type to change.
|
|
//
|
|
// Allow changes between 'normal' and 'temp_duplicate'
|
|
//
|
|
if ( UserHandle == NULL &&
|
|
NewSamAccountType != OldSamAccountType &&
|
|
((OldSamAccountType & USER_MACHINE_ACCOUNT_MASK) ||
|
|
(NewSamAccountType & USER_MACHINE_ACCOUNT_MASK))) {
|
|
|
|
NetpSetParmError( USER_FLAGS_PARMNUM );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Attempt to change account "
|
|
" type Old: %lx New: %lx\n",
|
|
OldSamAccountType,
|
|
NewSamAccountType ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
#endif // notdef
|
|
|
|
//
|
|
// Use the new Account Type.
|
|
//
|
|
|
|
UserAll.UserAccountControl &= ~USER_ACCOUNT_TYPE_MASK;
|
|
UserAll.UserAccountControl |= NewSamAccountType;
|
|
|
|
//
|
|
// If SAM has none of its bits set,
|
|
// set USER_NORMAL_ACCOUNT.
|
|
//
|
|
} else if ((UserAll.UserAccountControl & USER_ACCOUNT_TYPE_MASK) == 0 ){
|
|
UserAll.UserAccountControl |= USER_NORMAL_ACCOUNT;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Validate the usriX_priv and usrix_auth_flags fields
|
|
//
|
|
|
|
if ( ValidatePriv || ValidateAuthFlags ) {
|
|
|
|
DWORD OldPriv, OldAuthFlags;
|
|
|
|
|
|
//
|
|
// If this is a 'create' operation, just mandate that
|
|
// the values be reasonable. These reasonable values
|
|
// are what UserpGetUserPriv probably would return, unless
|
|
// of course someone puts the 'user' group in one of the
|
|
// aliases.
|
|
//
|
|
|
|
if ( UserHandle != NULL ) {
|
|
OldPriv = USER_PRIV_USER;
|
|
OldAuthFlags = 0;
|
|
|
|
//
|
|
// On a 'set' operation, just get the previous values.
|
|
//
|
|
|
|
} else {
|
|
|
|
NetStatus = UserpGetUserPriv(
|
|
BuiltinDomainHandle,
|
|
LocalUserHandle,
|
|
UserRelativeId,
|
|
DomainId,
|
|
&OldPriv,
|
|
&OldAuthFlags
|
|
);
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Ensure AUTH_FLAGS isn't being changed.
|
|
//
|
|
|
|
if ( ValidateAuthFlags ) {
|
|
if ( NewAuthFlags != OldAuthFlags ) {
|
|
NetpSetParmError( USER_AUTH_FLAGS_PARMNUM );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Old AuthFlag %ld New AuthFlag %ld\n",
|
|
OldAuthFlags,
|
|
NewAuthFlags ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Ensure PRIV isn't being changed.
|
|
//
|
|
|
|
if ( ValidatePriv ) {
|
|
if ( NewPriv != OldPriv ) {
|
|
NetpSetParmError( USER_PRIV_PARMNUM );
|
|
NetStatus = ERROR_INVALID_PARAMETER;
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: Old Priv %ld New Priv %ld\n",
|
|
OldPriv,
|
|
NewPriv ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Handle changes to the User Dacl
|
|
//
|
|
|
|
if ( HandleUserDacl ) {
|
|
DWORD DaclSize;
|
|
PACCESS_ALLOWED_ACE Ace;
|
|
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
|
|
//
|
|
// Build the sid for the user
|
|
//
|
|
|
|
NetStatus = NetpSamRidToSid(
|
|
LocalUserHandle,
|
|
UserRelativeId,
|
|
&UserSid
|
|
);
|
|
|
|
if (NetStatus != NERR_Success) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the DACL for the user record.
|
|
//
|
|
|
|
NetStatus = UserpGetDacl( LocalUserHandle,
|
|
&OldUserDacl,
|
|
&DaclSize );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If there is no DACL, just ignore that fact.
|
|
//
|
|
|
|
if ( OldUserDacl != NULL ) {
|
|
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
DWORD WorldSid[sizeof(SID)/sizeof(DWORD) + SID_MAX_SUB_AUTHORITIES ];
|
|
|
|
//
|
|
// Build a copy of the world SID for later comparison.
|
|
//
|
|
|
|
RtlInitializeSid( (PSID) WorldSid, &WorldSidAuthority, 1 );
|
|
*(RtlSubAuthoritySid( (PSID)WorldSid, 0 )) = SECURITY_WORLD_RID;
|
|
|
|
|
|
//
|
|
// Make a copy of the DACL that reflect the new UAS field.
|
|
//
|
|
NewUserDacl = NetpMemoryAllocate( DaclSize );
|
|
|
|
if ( NewUserDacl == NULL ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: no DACL memory %ld\n",
|
|
DaclSize ));
|
|
}
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetpMoveMemory( NewUserDacl, OldUserDacl, DaclSize );
|
|
|
|
//
|
|
// The UF_PASSWD_CANT_CHANGE bit is implemented by the
|
|
// ACL on the user object in SAM. When
|
|
// UF_PASSWD_CANT_CHANGE is on, the ACL doesn't allow
|
|
// World or the user himself USER_CHANGE_PASSWORD access.
|
|
// We set/clear the USER_CHANGE_PASSWORD access
|
|
// bit in the ACEs for the user and for World. This leaves
|
|
// Administrators and Account Operators with
|
|
// USER_ALL_ACCESS access.
|
|
//
|
|
// If the DACL for the user has been set by anyone
|
|
// other than the NetUser APIs, this action may
|
|
// not accurately reflect whether the password can
|
|
// be changed. We silently ignore ACLs we don't
|
|
// recognize.
|
|
//
|
|
|
|
|
|
//
|
|
// Point Ace to the first ACE.
|
|
//
|
|
|
|
for ( AceIndex = 0;
|
|
AceIndex < NewUserDacl->AceCount;
|
|
AceIndex++ ) {
|
|
|
|
Status = RtlGetAce(
|
|
NewUserDacl,
|
|
AceIndex,
|
|
(PVOID) &Ace
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the sid in the ACE matches either the world SID
|
|
// or the User's SID, modify the access mask.
|
|
//
|
|
|
|
if ( RtlEqualSid(
|
|
&Ace->SidStart,
|
|
(PSID)WorldSid) ||
|
|
RtlEqualSid(
|
|
&Ace->SidStart,
|
|
UserSid) ) {
|
|
|
|
//
|
|
// Twiddle the USER_CHANGE_PASSWORD access bit.
|
|
//
|
|
|
|
if ( Ace->Mask & USER_CHANGE_PASSWORD ) {
|
|
if ( UasUserFlags & UF_PASSWD_CANT_CHANGE ) {
|
|
Ace->Mask &= ~USER_CHANGE_PASSWORD;
|
|
UserDaclChanged = TRUE;
|
|
}
|
|
} else {
|
|
if ( (UasUserFlags & UF_PASSWD_CANT_CHANGE) == 0 ) {
|
|
Ace->Mask |= USER_CHANGE_PASSWORD;
|
|
UserDaclChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Set the DACL if it needs to be.
|
|
//
|
|
|
|
if ( UserDaclChanged ) {
|
|
|
|
NetStatus = UserpSetDacl( LocalUserHandle, NewUserDacl );
|
|
if ( NetStatus != NERR_Success ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// If there is anything changed in the 'UserAll' structure,
|
|
// tell SAM about the changes.
|
|
//
|
|
|
|
//
|
|
// N.B. Because some of the NET fields are not treated as SAM fields
|
|
// (UT_PRIV and UT_MAX_STORAGE), there may be nothing to change. However,
|
|
// for app compat, continue to call SamSetInformationUser
|
|
//
|
|
|
|
Status = SamSetInformationUser(
|
|
LocalUserHandle,
|
|
UserAllInformation,
|
|
&UserAll );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint((
|
|
"UserpSetInfo: SamSetInformationUser returns %lX\n",
|
|
Status ));
|
|
}
|
|
NetpSetParmError( PARM_ERROR_UNKNOWN );
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
NetStatus = NERR_Success;
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
|
|
//
|
|
// If we've changed the DACL on the user and we've not been able
|
|
// to change everything, put the DACL back as we found it.
|
|
//
|
|
|
|
if ( NetStatus != NERR_Success && UserDaclChanged ) {
|
|
NET_API_STATUS NetStatus2;
|
|
|
|
NetStatus2 = UserpSetDacl( LocalUserHandle, OldUserDacl );
|
|
ASSERT( NetStatus2 == NERR_Success );
|
|
|
|
}
|
|
|
|
//
|
|
// If a handle to the user was opened by this routine,
|
|
// close it.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT( UserHandle ) && LocalUserHandle != NULL) {
|
|
(VOID) SamCloseHandle( LocalUserHandle );
|
|
}
|
|
|
|
|
|
//
|
|
// Free any locally used recources.
|
|
//
|
|
|
|
if ( NewUserDacl != NULL ) {
|
|
NetpMemoryFree( NewUserDacl );
|
|
}
|
|
|
|
if ( OldUserDacl != NULL ) {
|
|
NetpMemoryFree( OldUserDacl );
|
|
}
|
|
|
|
if ( UserSid != NULL ) {
|
|
NetpMemoryFree( UserSid );
|
|
|
|
}
|
|
|
|
IF_DEBUG( UAS_DEBUG_USER ) {
|
|
NetpKdPrint(( "UserpSetInfo: returning %ld\n", NetStatus ));
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
} // UserpSetInfo
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
NetpSamRidToSid(
|
|
IN SAM_HANDLE SamHandle,
|
|
IN ULONG RelativeId,
|
|
OUT PSID *Sid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a Rid returned from a Sam Handle, return the SID for that account.
|
|
|
|
Arguments:
|
|
|
|
SamHandle - a valid SAM handle
|
|
|
|
RelativeId - a RID obtained from a SAM call that used SamHandle
|
|
|
|
Sid - Returns a pointer to an allocated buffer containing the resultant
|
|
Sid. Free this buffer using NetpMemoryFree.
|
|
|
|
Return Value:
|
|
|
|
0 - if successful
|
|
|
|
NERR_UserNotFound if the Rid could not be mapped to a SID
|
|
|
|
a resource error, otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PSID SamSid;
|
|
DWORD err = 0;
|
|
|
|
NtStatus = SamRidToSid(SamHandle,
|
|
RelativeId,
|
|
&SamSid);
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
ULONG Length = RtlLengthSid(SamSid);
|
|
(*Sid) = NetpMemoryAllocate(Length);
|
|
if ((*Sid)) {
|
|
RtlCopySid(Length, (*Sid), SamSid);
|
|
} else {
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
SamFreeMemory(SamSid);
|
|
} else if ( STATUS_NOT_FOUND == NtStatus ) {
|
|
// This is unexpected -- the user RID could not be
|
|
// found
|
|
err = NERR_UserNotFound;
|
|
} else {
|
|
// a resource error occurred
|
|
err = RtlNtStatusToDosError(NtStatus);
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
/*lint +e614 */
|
|
/*lint +e740 */
|