/*++ 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 #include #include #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h #include #include #include #include #include #include #include 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; AceIndexAceType != 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; } // // 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 = (Destination - WorkstationList->Buffer); if ( NumberOfCharacters > 0 && WorkstationList->Buffer[NumberOfCharacters-1] == ' ' ) { NumberOfCharacters--; } WorkstationList->Length = (USHORT) (NumberOfCharacters * sizeof(WCHAR)); }