#include <windows.h>
#include <ntsecapi.h>
#include <stdio.h>
#pragma hdrstop



/*******************************************************************
This sample uses LSA to assign privileges to user accounts. 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 );
void print_sid( SID *ps );



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( ']' );
}



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_laar computername [-u username ...] [-p privilege ...]" );
		puts( "Assigns named privileges to accounts on the given machine." );
		return 1;
	}

	// parse command line

#define MAXPRIV 32
#define MAXACCT 32

	int i, maxAcct = 0, maxPriv = 0;
	LsaUnicodeString accountList[MAXACCT];
	LsaUnicodeString privList[MAXPRIV];
	enum { collectNone, collectUsers, collectPrivs } argState = collectNone;
	struct NameAndSid { char *origName; PSID sid; } nameAndSid[MAXACCT];

	for ( i = 2; i < argc; i ++ )
	{
		if ( *argv[i] == '-' || *argv[i] == '/' )
		{
			switch ( argv[i][1] )
			{
				case 'p': case 'P':
					argState = collectPrivs;
					break;
				case 'u': case 'U':
					argState = collectUsers;
					break;
				default:
					printf( "'%s': illegal switch.\n", argv[i] );
					return 1;
			}
		}
		else
		{
			switch ( argState )
			{
				case collectNone:
					printf( "'%s': use -u and -p to distinguish accounts from privileges.\n", argv[i] );
					return 1;
				case collectUsers:
					if ( maxAcct == MAXACCT )
					{
						printf( "Sorry, only %d account names allowed.\n", MAXACCT );
						return 1;
					}
					accountList[maxAcct] = argv[i];
					nameAndSid[maxAcct].origName = argv[i];
					nameAndSid[maxAcct].sid = NULL;
					++ maxAcct;
					break;
				case collectPrivs:
					if ( maxPriv == MAXPRIV )
					{
						printf( "Sorry, only %d privileges allowed.\n", MAXPRIV );
						return 1;
					}
					privList[maxPriv] = argv[i];
					++ maxPriv;
					break;
			}
		}
	}

	// 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_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT, &polHandle );
	err( "LOP()", nts );

	// translate the account name to a RID plus associated domain SID

	PLSA_REFERENCED_DOMAIN_LIST refDomList = NULL;
	PLSA_TRANSLATED_SID sidList = NULL;

	nts = LsaLookupNames( polHandle, maxAcct, (LSA_UNICODE_STRING *) &accountList[0], &refDomList, &sidList );
	if ( nts == 263 )
	{
		printf( "Some names were not mapped.\n" );
		nts = 0;
	}
	err( "LLN()", nts );

	// build list of complete SIDs here
	DWORD d;
	const char *acctTypeString;
	PSID s;

	for ( i = 0; i < maxAcct; i ++ )
	{
		switch ( sidList[i].Use )
		{
			case SidTypeAlias:
			case SidTypeUser:
			case SidTypeGroup:
			case SidTypeWellKnownGroup:
				s = refDomList->Domains[sidList[i].DomainIndex].Sid;
				nameAndSid[i].sid = malloc( 256 ); // max SID length (we hope)
				InitializeSid( nameAndSid[i].sid, GetSidIdentifierAuthority( s ),
					(BYTE) 1 + *GetSidSubAuthorityCount( s ) );
				for ( d = 0; d < (DWORD) *GetSidSubAuthorityCount( s ); ++ d )
				{
					*GetSidSubAuthority( nameAndSid[i].sid, d ) = *GetSidSubAuthority( s, d );
				}

				*GetSidSubAuthority( nameAndSid[i].sid, d ) = sidList[i].RelativeId;
				printf( "%-30.30s ", nameAndSid[i].origName );
				print_sid( (SID *) nameAndSid[i].sid );
				putchar( '\n' );
				break;
			case SidTypeDomain:
				if ( acctTypeString == NULL )
					acctTypeString = "domain";
				// fall-through
			case SidTypeInvalid:
				if ( acctTypeString == NULL )
					acctTypeString = "invalid";
				// fall-through
			case SidTypeUnknown:
				if ( acctTypeString == NULL )
					acctTypeString = "unknown";
				// fall-through
			case SidTypeDeletedAccount:
				if ( acctTypeString == NULL )
					acctTypeString = "deleted";
				printf( "%-30.30s %s\n", nameAndSid[i].origName, acctTypeString );
				break;
		}
	}
	putchar( '\n' );

	// now, we iterate over the accounts list and try to set those privileges

	for ( i = 0; i < maxAcct; i ++ )
	{
		printf( "%-30.30s ", nameAndSid[i].origName );
		if ( nameAndSid[i].sid != NULL )
		{
			nts = LsaAddAccountRights( polHandle, nameAndSid[i].sid,
				(LSA_UNICODE_STRING *) &privList[0], maxPriv );
			printf( "%lu [%08lxh], gle = %lu\n", nts, nts,
				LsaNtStatusToWinError( nts ) );
			err( "LAAR()", nts );
		}
		else
			printf( "no SID, skipped\n" );
	}

	LsaClose( polHandle );

	for ( i = 0; i < maxAcct; i ++ )
	{
		if ( nameAndSid[i].sid != NULL )
			free( nameAndSid[i].sid );
	}

	return 0;
}

