windows-nt/Source/XPSP1/NT/ds/netapi/netlib/accessp.c

779 lines
16 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
accessp.c
Abstract:
Internal routines shared by NetUser API and Netlogon service. These
routines convert from SAM specific data formats to UAS specific data
formats.
Author:
Cliff Van Dyke (cliffv) 29-Aug-1991
Environment:
User mode only.
Contains NT-specific code.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
22-Oct-1991 JohnRo
Made changes suggested by PC-LINT.
04-Dec-1991 JohnRo
Trying to get around a weird MIPS compiler bug.
--*/
#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 <windef.h>
#include <lmcons.h>
#include <accessp.h>
#include <debuglib.h>
#include <lmaccess.h>
#include <netdebug.h>
#include <netsetp.h>
#if(_WIN32_WINNT >= 0x0500)
NET_API_STATUS
NET_API_FUNCTION
NetpSetDnsComputerNameAsRequired(
IN PWSTR DnsDomainName
)
/*++
Routine Description:
Determines if the machine is set to update the machine Dns computer name based on changes
to the Dns domain name. If so, the new value is set. Otherwise, no action is taken.
Arguments:
DnsDomainName - New Dns domain name of this machine
Return Value:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
HKEY SyncKey;
DWORD ValueType, Value, Length;
BOOLEAN SetName = FALSE;
PWCHAR AbsoluteSignifier = NULL;
if ( DnsDomainName == NULL ) {
return ERROR_INVALID_PARAMETER;
}
//
// See if we should be doing the name change
//
NetStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
0,
KEY_QUERY_VALUE,
&SyncKey );
if ( NetStatus == NERR_Success ) {
Length = sizeof( ULONG );
NetStatus = RegQueryValueEx( SyncKey,
L"SyncDomainWithMembership",
NULL,
&ValueType,
( LPBYTE )&Value,
&Length );
if ( NetStatus == NERR_Success) {
if ( Value == 1 ) {
SetName = TRUE;
}
} else if ( NetStatus == ERROR_FILE_NOT_FOUND ) {
NetStatus = NERR_Success;
SetName = TRUE;
}
RegCloseKey( SyncKey );
}
if ( NetStatus == NERR_Success && SetName == TRUE ) {
//
// If we've got an absolute Dns domain name, shorten it up...
//
if ( wcslen(DnsDomainName) > 0 ) {
AbsoluteSignifier = &DnsDomainName[ wcslen( DnsDomainName ) - 1 ];
if ( *AbsoluteSignifier == L'.' ) {
*AbsoluteSignifier = UNICODE_NULL;
} else {
AbsoluteSignifier = NULL;
}
}
if ( !SetComputerNameEx( ComputerNamePhysicalDnsDomain, DnsDomainName ) ) {
NetStatus = GetLastError();
}
if ( AbsoluteSignifier ) {
*AbsoluteSignifier = L'.';
}
}
return( NetStatus );
}
#endif
VOID
NetpGetAllowedAce(
IN PACL Dacl,
IN PSID Sid,
OUT PVOID *Ace
)
/*++
Routine Description:
Given a DACL, find an AccessAllowed ACE containing a particuar SID.
Arguments:
Dacl - A pointer to the ACL to search.
Sid - A pointer to the Sid to search for.
Ace - Returns a pointer to the specified ACE. Returns NULL if there
is no such ACE
Return Value:
None.
--*/
{
NTSTATUS Status;
ACL_SIZE_INFORMATION AclSize;
DWORD AceIndex;
//
// Determine the size of the DACL so we can copy it
//
Status = RtlQueryInformationAcl(
Dacl,
&AclSize,
sizeof(AclSize),
AclSizeInformation );
if ( ! NT_SUCCESS( Status ) ) {
IF_DEBUG( ACCESSP ) {
NetpKdPrint((
"NetpGetDacl: RtlQueryInformationAcl returns %lX\n",
Status ));
}
*Ace = NULL;
return;
}
//
// Loop through the ACEs looking for an ACCESS_ALLOWED ACE with the
// right SID.
//
for ( AceIndex=0; AceIndex<AclSize.AceCount; AceIndex++ ) {
Status = RtlGetAce( Dacl, AceIndex, (PVOID *)Ace );
if ( ! NT_SUCCESS( Status ) ) {
*Ace = NULL;
return;
}
if ( ((PACE_HEADER)*Ace)->AceType != ACCESS_ALLOWED_ACE_TYPE ) {
continue;
}
if ( RtlEqualSid( Sid,
(PSID)&((PACCESS_ALLOWED_ACE)(*Ace))->SidStart )
){
return;
}
}
//
// Couldn't find any such ACE.
//
*Ace = NULL;
return;
}
DWORD
NetpAccountControlToFlags(
IN DWORD UserAccountControl,
IN PACL UserDacl
)
/*++
Routine Description:
Convert a SAM UserAccountControl field and the Discretionary ACL for
the user into the NetUser API usriX_flags field.
Arguments:
UserAccountControl - The SAM UserAccountControl field for the user.
UserDacl - The Discretionary ACL for the user.
Return Value:
Returns the usriX_flags field for the user.
--*/
{
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
DWORD WorldSid[sizeof(SID)/sizeof(DWORD) + SID_MAX_SUB_AUTHORITIES ];
PACCESS_ALLOWED_ACE Ace;
DWORD Flags = UF_SCRIPT;
//
// Build a copy of the world SID for later comparison.
//
RtlInitializeSid( (PSID) WorldSid, &WorldSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)WorldSid, 0 )) = SECURITY_WORLD_RID;
//
// Determine if the UF_PASSWD_CANT_CHANGE bit should be returned
//
// Return UF_PASSWD_CANT_CHANGE unless the world can change the
// password.
//
//
// If the user has no DACL, the password can change
//
if ( UserDacl != NULL ) {
//
// Find the WORLD grant ACE
//
NetpGetAllowedAce( UserDacl, (PSID) WorldSid, (PVOID *)&Ace );
if ( Ace == NULL ) {
Flags |= UF_PASSWD_CANT_CHANGE;
} else {
if ( (Ace->Mask & USER_CHANGE_PASSWORD) == 0 ) {
Flags |= UF_PASSWD_CANT_CHANGE;
}
}
}
//
// Set all other bits as a function of the SAM UserAccountControl
//
if ( UserAccountControl & USER_ACCOUNT_DISABLED ) {
Flags |= UF_ACCOUNTDISABLE;
}
if ( UserAccountControl & USER_HOME_DIRECTORY_REQUIRED ){
Flags |= UF_HOMEDIR_REQUIRED;
}
if ( UserAccountControl & USER_PASSWORD_NOT_REQUIRED ){
Flags |= UF_PASSWD_NOTREQD;
}
if ( UserAccountControl & USER_DONT_EXPIRE_PASSWORD ){
Flags |= UF_DONT_EXPIRE_PASSWD;
}
if ( UserAccountControl & USER_ACCOUNT_AUTO_LOCKED ){
Flags |= UF_LOCKOUT;
}
if ( UserAccountControl & USER_MNS_LOGON_ACCOUNT ){
Flags |= UF_MNS_LOGON_ACCOUNT;
}
if ( UserAccountControl & USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED ){
Flags |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
}
if ( UserAccountControl & USER_SMARTCARD_REQUIRED ){
Flags |= UF_SMARTCARD_REQUIRED;
}
if ( UserAccountControl & USER_TRUSTED_FOR_DELEGATION ){
Flags |= UF_TRUSTED_FOR_DELEGATION;
}
if ( UserAccountControl & USER_NOT_DELEGATED ){
Flags |= UF_NOT_DELEGATED;
}
if ( UserAccountControl & USER_USE_DES_KEY_ONLY ){
Flags |= UF_USE_DES_KEY_ONLY;
}
if ( UserAccountControl & USER_DONT_REQUIRE_PREAUTH) {
Flags |= UF_DONT_REQUIRE_PREAUTH;
}
if ( UserAccountControl & USER_PASSWORD_EXPIRED) {
Flags |= UF_PASSWORD_EXPIRED;
}
if ( UserAccountControl & USER_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION) {
Flags |= UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
}
//
// set account type bit.
//
//
// account type bit are exculsive and precisely only one
// account type bit is set. So, as soon as an account type bit is set
// in the following if sequence we can return.
//
if( UserAccountControl & USER_TEMP_DUPLICATE_ACCOUNT ) {
Flags |= UF_TEMP_DUPLICATE_ACCOUNT;
} else if( UserAccountControl & USER_NORMAL_ACCOUNT ) {
Flags |= UF_NORMAL_ACCOUNT;
} else if( UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT ) {
Flags |= UF_INTERDOMAIN_TRUST_ACCOUNT;
} else if( UserAccountControl & USER_WORKSTATION_TRUST_ACCOUNT ) {
Flags |= UF_WORKSTATION_TRUST_ACCOUNT;
} else if( UserAccountControl & USER_SERVER_TRUST_ACCOUNT ) {
Flags |= UF_SERVER_TRUST_ACCOUNT;
} else {
//
// There is no known account type bit set in UserAccountControl.
// ?? Flags |= UF_NORMAL_ACCOUNT;
// NetpAssert( FALSE );
}
return Flags;
}
ULONG
NetpDeltaTimeToSeconds(
IN LARGE_INTEGER DeltaTime
)
/*++
Routine Description:
Convert an NT delta time specification to seconds
Arguments:
DeltaTime - Specifies the NT Delta time to convert. NT delta time is
a negative number of 100ns units.
Return Value:
Returns the number of seconds. Any invalid or too large input
returns TIMEQ_FOREVER.
--*/
{
LARGE_INTEGER LargeSeconds;
//
// These are the magic numbers needed to do our extended division by
// 10,000,000 = convert 100ns tics to one second tics
//
LARGE_INTEGER Magic10000000 = { (ULONG) 0xe57a42bd, (LONG) 0xd6bf94d5};
#define SHIFT10000000 23
//
// Special case zero.
//
if ( DeltaTime.HighPart == 0 && DeltaTime.LowPart == 0 ) {
return( 0 );
}
//
// Convert the Delta time to a Large integer seconds.
//
LargeSeconds = RtlExtendedMagicDivide(
DeltaTime,
Magic10000000,
SHIFT10000000 );
#ifdef notdef
NetpKdPrint(( "NetpDeltaTimeToSeconds: %lx %lx %lx %lx\n",
DeltaTime.HighPart,
DeltaTime.LowPart,
LargeSeconds.HighPart,
LargeSeconds.LowPart ));
#endif // notdef
//
// Return too large a number or a positive number as TIMEQ_FOREVER
//
if ( LargeSeconds.HighPart != -1 ) {
return TIMEQ_FOREVER;
}
return ( (ULONG)(- ((LONG)(LargeSeconds.LowPart))) );
} // NetpDeltaTimeToSeconds
LARGE_INTEGER
NetpSecondsToDeltaTime(
IN ULONG Seconds
)
/*++
Routine Description:
Convert a number of seconds to an NT delta time specification
Arguments:
Seconds - a positive number of seconds
Return Value:
Returns the NT Delta time. NT delta time is a negative number
of 100ns units.
--*/
{
LARGE_INTEGER DeltaTime;
LARGE_INTEGER LargeSeconds;
LARGE_INTEGER Answer;
//
// Special case TIMEQ_FOREVER (return a full scale negative)
//
if ( Seconds == TIMEQ_FOREVER ) {
DeltaTime.LowPart = 0;
DeltaTime.HighPart = (LONG) 0x80000000;
//
// Convert seconds to 100ns units simply by multiplying by 10000000.
//
// Convert to delta time by negating.
//
} else {
LargeSeconds = RtlConvertUlongToLargeInteger( Seconds );
Answer = RtlExtendedIntegerMultiply( LargeSeconds, 10000000 );
if ( Answer.QuadPart < 0 ) {
DeltaTime.LowPart = 0;
DeltaTime.HighPart = (LONG) 0x80000000;
} else {
DeltaTime.QuadPart = -Answer.QuadPart;
}
}
return DeltaTime;
} // NetpSecondsToDeltaTime
VOID
NetpAliasMemberToPriv(
IN ULONG AliasCount,
IN PULONG AliasMembership,
OUT LPDWORD Priv,
OUT LPDWORD AuthFlags
)
/*++
Routine Description:
Converts membership in Aliases to LANMAN 2.0 style Priv and AuthFlags.
Arguments:
AliasCount - Specifies the number of Aliases in the AliasMembership array.
AliasMembership - Specifies the Aliases that are to be converted to Priv
and AuthFlags. Each element in the array specifies the RID of an
alias in the BuiltIn domain.
Priv - Returns the Lanman 2.0 Privilege level for the specified aliases.
AuthFlags - Returns the Lanman 2.0 Authflags for the specified aliases.
Return Value:
None.
--*/
{
DWORD j;
BOOLEAN IsAdmin = FALSE;
BOOLEAN IsUser = FALSE;
//
// Loop through the aliases finding any special aliases.
//
// If this user is the member of multiple operator aliases,
// just "or" the appropriate bits in.
//
// If this user is the member of multiple "privilege" aliases,
// just report the one with the highest privilege.
// Report the user is a member of the Guest aliases by default.
//
*AuthFlags = 0;
for ( j=0; j < AliasCount; j++ ) {
switch ( AliasMembership[j] ) {
case DOMAIN_ALIAS_RID_ADMINS:
IsAdmin = TRUE;
break;
case DOMAIN_ALIAS_RID_USERS:
IsUser = TRUE;
break;
case DOMAIN_ALIAS_RID_ACCOUNT_OPS:
*AuthFlags |= AF_OP_ACCOUNTS;
break;
case DOMAIN_ALIAS_RID_SYSTEM_OPS:
*AuthFlags |= AF_OP_SERVER;
break;
case DOMAIN_ALIAS_RID_PRINT_OPS:
*AuthFlags |= AF_OP_PRINT;
break;
}
}
if ( IsAdmin ) {
*Priv = USER_PRIV_ADMIN;
} else if ( IsUser ) {
*Priv = USER_PRIV_USER;
} else {
*Priv = USER_PRIV_GUEST;
}
}
DWORD
NetpGetElapsedSeconds(
IN PLARGE_INTEGER Time
)
/*++
Routine Description:
Computes the elapsed time in seconds since the time specified.
Returns 0 on error.
Arguments:
Time - Time (typically in the past) to compute the elapsed time from.
Return Value:
0: on error.
Number of seconds.
--*/
{
LARGE_INTEGER CurrentTime;
DWORD Current1980Time;
DWORD Prior1980Time;
NTSTATUS Status;
//
// Compute the age of the password
//
Status = NtQuerySystemTime( &CurrentTime );
if( !NT_SUCCESS(Status) ) {
return 0;
}
if ( !RtlTimeToSecondsSince1980( &CurrentTime, &Current1980Time) ) {
return 0;
}
if ( !RtlTimeToSecondsSince1980( Time, &Prior1980Time ) ) {
return 0;
}
if ( Current1980Time <= Prior1980Time ) {
return 0;
}
return Current1980Time - Prior1980Time;
}
VOID
NetpConvertWorkstationList(
IN OUT PUNICODE_STRING WorkstationList
)
/*++
Routine Description:
Convert the list of workstations from a comma separated list to
a blank separated list. Any workstation name containing a blank is
silently removed.
Arguments:
WorkstationList - List of workstations to convert
Return Value:
None
--*/
{
LPWSTR Source;
LPWSTR Destination;
LPWSTR EndOfBuffer;
LPWSTR BeginningOfName;
BOOLEAN SkippingName;
ULONG NumberOfCharacters;
//
// Handle the trivial case.
//
if ( WorkstationList->Length == 0 ) {
return;
}
//
// Initialization.
//
Destination = Source = WorkstationList->Buffer;
EndOfBuffer = Source + WorkstationList->Length/sizeof(WCHAR);
//
// Loop handling special characters
//
SkippingName = FALSE;
BeginningOfName = Destination;
while ( Source < EndOfBuffer ) {
switch ( *Source ) {
case ',':
if ( !SkippingName ) {
*Destination = ' ';
Destination++;
}
SkippingName = FALSE;
BeginningOfName = Destination;
break;
case ' ':
SkippingName = TRUE;
Destination = BeginningOfName;
break;
default:
if ( !SkippingName ) {
*Destination = *Source;
Destination ++;
}
break;
}
Source ++;
}
//
// Remove any trailing delimiter
//
NumberOfCharacters = (ULONG)(Destination - WorkstationList->Buffer);
if ( NumberOfCharacters > 0 &&
WorkstationList->Buffer[NumberOfCharacters-1] == ' ' ) {
NumberOfCharacters--;
}
WorkstationList->Length = (USHORT) (NumberOfCharacters * sizeof(WCHAR));
}