#include <windows.h>
#include <ntsecapi.h>
#include <stdio.h>
#pragma hdrstop



/*********************************************************************
This sample enumerates accounts that have a particular privilege
assigned to them. There are two other samples for LSA stuff in the
Win32/Platform SDK, under \mssdk\samples\win32\winnt\security\lsapriv\
and ...\lsasamp\. The latter directory also contains the help file
describing the LSA functions. There are also a few KB articles --
query for the term "LsaOpenPolicy" to get them.
**********************************************************************/



#define	gle	GetLastError()



int main( int argc, char *argv[] );
void err( const char *msg, NTSTATUS nts, bool isNtStatus = true );



class LsaUnicodeString: public _LSA_UNICODE_STRING
{
public:
	LsaUnicodeString( int maxSize = 512 );
	~LsaUnicodeString();
	void init( int maxSize = 512 );
	operator LSA_UNICODE_STRING *();
	operator char *();
	const LsaUnicodeString& operator=( const char *s );
};



LsaUnicodeString::LsaUnicodeString( int maxSize /* = 512 */ )
{
	Buffer = NULL;
	init( maxSize );
}



void LsaUnicodeString::init( int maxSize /* = 512 */ )
{
	if ( Buffer )
		free( Buffer );
	Length = 0;
	MaximumLength = maxSize;
	Buffer = ( maxSize == 0 )? NULL: (wchar_t *) malloc( maxSize );
	if ( Buffer )
		*Buffer = L'\0';
}



LsaUnicodeString::operator LSA_UNICODE_STRING *()
{
	return static_cast<LSA_UNICODE_STRING *>( this );
}



LsaUnicodeString::operator char *()
{
	char *ansiBuf;

	ansiBuf = (char *) malloc( Length / 2 + 1 );
	int n = WideCharToMultiByte( CP_ACP, 0, Buffer, Length / 2, ansiBuf, Length + 2 / 1, NULL, NULL );
	ansiBuf[n] = '\0';
	return ansiBuf;
}



LsaUnicodeString::~LsaUnicodeString()
{
	if ( Buffer )
		free( Buffer );
}



const LsaUnicodeString& LsaUnicodeString::operator=( const char *s )
{
	if ( strlen( s ) * 2 >= MaximumLength )
		init( strlen( s ) ); // get new buffer
	MultiByteToWideChar( CP_ACP, 0, s, -1, Buffer, MaximumLength * 2 );
	Length = strlen( s ) * 2;
	return *this;
}



void err( const char *msg, NTSTATUS nts, bool isNtStatus /* = true */ )
{
	DWORD e;

	if ( nts == 0 )
		return;

	if ( isNtStatus )
	{
		e = LsaNtStatusToWinError( nts );
		printf( "%s: ntstatus %lu [%lXh], gle %lu\n", msg, nts, nts, e );
	}
	else
	{
		printf( "%s: gle %lu\n", msg, nts );
	}

	exit( 1 );
}


void print_sid( SID *ps )
{
	PSID_IDENTIFIER_AUTHORITY psia;
	DWORD subAuthCount, i;

	psia = GetSidIdentifierAuthority( ps );
	subAuthCount = *GetSidSubAuthorityCount( ps );
	printf( "[S-%lu-", SID_REVISION );

	if ( ( psia->Value[0] != 0 ) || ( psia->Value[1] != 0 ) )
		printf( "0x%02hx%02hx%02hx%02hx%02hx%02hx", (USHORT) psia->Value[0], (USHORT) psia->Value[1],
			(USHORT) psia->Value[2], (USHORT) psia->Value[3], (USHORT) psia->Value[4], (USHORT) psia->Value[5] );
	else
		printf( "%lu", (ULONG) ( psia->Value[5] ) + (ULONG) ( psia->Value[4] << 8 ) +
		(ULONG) ( psia->Value[3] << 16 ) + (ULONG) ( psia->Value[2] << 24 ) );

	for ( i = 0; i < subAuthCount; ++ i )
		printf( "-%lu", *GetSidSubAuthority( ps, i ) );
	putchar( ']' );
}


void print_name( LSA_HANDLE polHandle, PSID ps )
{
	DWORD nts;
	LSA_REFERENCED_DOMAIN_LIST *refDoms = NULL;
	LSA_TRANSLATED_NAME *names = NULL;
	char *ansiDomain, *ansiName;

	nts = LsaLookupSids( polHandle, 1, &ps, &refDoms, &names );
	err( "LLS()", nts );

	ansiDomain = ansiName = NULL;
	switch ( names->Use )
	{
		case SidTypeDeletedAccount:
			ansiDomain = (char *) *( (LsaUnicodeString *) &(refDoms->Domains[names->DomainIndex].Name) );
			printf( "%s\\*SidTypeDeletedAccount*", ansiDomain );
			break;
		case SidTypeUser: // fall through
		case SidTypeGroup: // fall through
		case SidTypeAlias: // fall through
			ansiDomain = (char *) *( (LsaUnicodeString *) &(refDoms->Domains[names->DomainIndex].Name) );
			ansiName = (char *) *( (LsaUnicodeString *) &names->Name );
			printf( "%s\\%s", ansiDomain, ansiName );
			break;
		case SidTypeWellKnownGroup:
			ansiName = (char *) *( (LsaUnicodeString *) &names->Name );
			printf( "*SidTypeWellKnownGroup*\\%s", ansiName );
			break;
		case SidTypeDomain:
			ansiDomain = (char *) *( (LsaUnicodeString *) &(refDoms->Domains[names->DomainIndex].Name) );
			printf( "%s\\*SidTypeDomain*", ansiDomain );
			break;
		case SidTypeInvalid:
			printf( "*SidTypeInvalid*" );
			break;
		case SidTypeUnknown:
			printf( "*SidTypeUnknown*" );
			break;
		default:
			printf( "*bad SID_NAME_USE value*" );
			break;
	}

	if ( ansiDomain )
		free( ansiDomain );
	if ( ansiName )
		free( ansiName );

	LsaFreeMemory( refDoms );
	LsaFreeMemory( names );
}


int main( int argc, char *argv[] )
{
	if ( argc < 2 || argc > 3 )
	{
		puts( "Usage: lsa_lear <system name> [<privilege name>]" );
		puts( "Enumerates those accounts on the named machine which have the given priv." );
		puts( "<system name> is the machine where the lookup will execute." );
		puts( "<privilege name> is the privilege to look for, e.g. SeTcbPrivilege." );
		return 1;
	}


	// open the policy object on the target computer

	static SECURITY_QUALITY_OF_SERVICE sqos = { sizeof SECURITY_QUALITY_OF_SERVICE, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE };
	static LSA_OBJECT_ATTRIBUTES lsaOA = { sizeof LSA_OBJECT_ATTRIBUTES, NULL, NULL, 0, NULL, &sqos };
	NTSTATUS nts;
	LSA_HANDLE polHandle;
	LsaUnicodeString systemName;

	systemName = argv[1];

	nts = LsaOpenPolicy( systemName, &lsaOA, GENERIC_READ | GENERIC_EXECUTE |
		POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES, &polHandle );
	err( "LOP()", nts );


	LsaUnicodeString *privName;

	// format privName into a LSA_UNICODE_STRING
	if ( argc == 2 )
		privName = NULL;
	else
	{
		privName = new LsaUnicodeString;
		*privName = argv[2];
	}


	void *buf;
	SID **psidArray;
	DWORD count, i;

	// do the enumeration
	count = 0;
	nts = LsaEnumerateAccountsWithUserRight( polHandle, privName, &buf, &count );
	printf( "Got %lu items; nts == %lu.\n", count, nts );
	err( "LEAWUR()", nts );


	// present the results
	psidArray = (SID **) buf;
	for ( i = 0; i < count; ++ i )
	{
		print_name( polHandle, psidArray[i] );
		putchar( ' ' );
		print_sid( psidArray[i] );
		putchar( '\n' );
	}


	// free the returned buffer
	if ( buf )
		LsaFreeMemory( buf );
	buf = NULL;

	delete privName;

	LsaClose( polHandle );

	return 0;
}

