#include <windows.h>
#include <ntsecapi.h>
#include <stdio.h>
#pragma hdrstop



/*******************************************************************
This sample enumerates privileges assigned to an account. 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 );
}



int main( int argc, char *argv[] )
{
	if ( argc != 3 )
	{
		puts( "Usage: lsa_lear <system name> <account name>" );
		puts( "Enumerates privileges for the named account on the named machine." );
		puts( "<system name> is the machine where the lookup will execute." );
		puts( "<account name> is the account to examine, e.g. \"FOO\\felixk\" or \"Administrators\"." );
		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, &polHandle );
	err( "LOP()", nts );

	// translate the account name to a RID plus associated domain SID

	SID *userSid;
	char refDom[MAX_PATH];
	SID_NAME_USE sidUse;
	DWORD sidSize, domSize;
	const char *acctTypeString;

	userSid = (SID *) malloc( MAX_PATH );
	sidSize = domSize = MAX_PATH;

	if ( ! LookupAccountName( argv[1], argv[2], userSid, &sidSize, refDom, &domSize, &sidUse ) )
		err( "LAN()", gle, false );

	acctTypeString = NULL;
	switch ( sidUse )
	{
		case SidTypeAlias:
		case SidTypeUser:
		case SidTypeGroup:
		case SidTypeWellKnownGroup:
			break;
		case SidTypeDomain:
			if ( acctTypeString == NULL )
				acctTypeString = " domain";
			// fall-through
		case SidTypeInvalid:
			if ( acctTypeString == NULL )
				acctTypeString = "n invalid";
			// fall-through
		case SidTypeUnknown:
			if ( acctTypeString == NULL )
				acctTypeString = "n unknown";
			// fall-through
		case SidTypeDeletedAccount:
			if ( acctTypeString == NULL )
				acctTypeString = " deleted";
			printf( "Don't know how to handle a%s account.\n", acctTypeString );
			return 1;
	}

	LsaUnicodeString *userRights;
	ULONG count;

	userRights = NULL;
	count = 0;
	nts = LsaEnumerateAccountRights( polHandle, userSid, (LSA_UNICODE_STRING **) &userRights, &count );
	err( "LEAR()", nts );

	DWORD i;
	char *p;

	printf( "%d privileges for user \"%s\" in domain \"%s\":\n", count, argv[2], refDom );
	for ( i = 0; i < count; ++ i )
	{
		p = (char *) userRights[i]; // must free() later
		printf( "priv %u: %s\n", i, p );
		free( p );
	}

	LsaClose( polHandle );
	free( userSid );

	return 0;
}

