/********************************************************************/
/**               Copyright(c) 1989 Microsoft Corporation.	   **/
/********************************************************************/

//***
//
// Filename:	security.c
//
// Description: This module contains code that will create and delete the
//		security object. It will also contain access checking calls.
//
// History:
//	June 21,1992.	NarenG		Created original version.
//
// NOTE: ??? The lpdwAccessStatus parameter for AccessCheckAndAuditAlarm
//	     returns junk. ???
//
#include "afpsvcp.h"

typedef struct _AFP_SECURITY_OBJECT {

    LPWSTR		  lpwsObjectName;
    LPWSTR		  lpwsObjectType;
    GENERIC_MAPPING	  GenericMapping;
    PSECURITY_DESCRIPTOR  pSecurityDescriptor;

} AFP_SECURITY_OBJECT, PAFP_SECURITY_OBJECT;

static AFP_SECURITY_OBJECT	AfpSecurityObject;

#define AFPSVC_SECURITY_OBJECT		TEXT("AfpSvcAdminApi");
#define AFPSVC_SECURITY_OBJECT_TYPE	TEXT("Security");



//**
//
// Call:	AfpSecObjCreate
//
// Returns:	NO_ERROR	- success
//		ERROR_NOT_ENOUGH_MEMORY
//		non-zero returns from security functions
//
// Description: This procedure will set up the security object that will
//		be used to check to see if an RPC client is an administrator
//		for the local machine.
//
DWORD
AfpSecObjCreate( 
	VOID 
)
{
PSID			 pAdminSid 	  = NULL;
PSID			 pLocalSystemSid  = NULL;
PSID 			 pServerOpSid     = NULL;
PACL			 pDacl		  = NULL;
HANDLE			 hProcessToken    = NULL;
PULONG			 pSubAuthority;
SID_IDENTIFIER_AUTHORITY SidIdentifierNtAuth = SECURITY_NT_AUTHORITY;
SECURITY_DESCRIPTOR	 SecurityDescriptor;
DWORD			 dwRetCode;
DWORD			 cbDaclSize;

    AFP_PRINT(("AFPSVC_security: Creating security object\n"));

    // Set up security object
    //
    AfpSecurityObject.lpwsObjectName = AFPSVC_SECURITY_OBJECT;
    AfpSecurityObject.lpwsObjectType = AFPSVC_SECURITY_OBJECT_TYPE;

    // Generic mapping structure for the security object
    // All generic access types are allowed API access.
    //
    AfpSecurityObject.GenericMapping.GenericRead =  STANDARD_RIGHTS_READ |
	    					    AFPSVC_ALL_ACCESS;

    AfpSecurityObject.GenericMapping.GenericWrite = STANDARD_RIGHTS_WRITE |
	    					    AFPSVC_ALL_ACCESS;

    AfpSecurityObject.GenericMapping.GenericExecute = 
						    STANDARD_RIGHTS_EXECUTE |
						    AFPSVC_ALL_ACCESS;

    AfpSecurityObject.GenericMapping.GenericAll =   AFPSVC_ALL_ACCESS;

    // The do - while(FALSE) statement is used so that the break statement
    // maybe used insted of the goto statement, to execute a clean up and
    // and exit action.
    //
    do {

	dwRetCode = NO_ERROR;

    	// Set up the SID for the admins that will be allowed to have
    	// access. This SID will have 2 sub-authorities
    	// SECURITY_BUILTIN_DOMAIN_RID and DOMAIN_ALIAS_ADMIN_RID.
    	//
    	pAdminSid = (PSID)LocalAlloc( LPTR, GetSidLengthRequired( 2 ) );

    	if ( pAdminSid == NULL ) {
	    dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
	    break;
	}

    	if ( !InitializeSid( pAdminSid, &SidIdentifierNtAuth, 2 ) ) {
	    dwRetCode = GetLastError();
	    break;
	}
    
    	// Set the sub-authorities 
    	//
    	pSubAuthority  = GetSidSubAuthority( pAdminSid, 0 );
    	*pSubAuthority = SECURITY_BUILTIN_DOMAIN_RID;

    	pSubAuthority  = GetSidSubAuthority( pAdminSid, 1 );
    	*pSubAuthority = DOMAIN_ALIAS_RID_ADMINS;
    
	// Create the server operators SID
	//
    	pServerOpSid = (PSID)LocalAlloc( LPTR, GetSidLengthRequired( 2 ) );

    	if ( pServerOpSid == NULL ) {
	    dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
	    break;
	}

    	if ( !InitializeSid( pServerOpSid, &SidIdentifierNtAuth, 2 ) ) {
	    dwRetCode = GetLastError();
	    break;
	}
    
    	// Set the sub-authorities 
    	//
    	pSubAuthority  = GetSidSubAuthority( pServerOpSid, 0 );
    	*pSubAuthority = SECURITY_BUILTIN_DOMAIN_RID;

    	pSubAuthority  = GetSidSubAuthority( pServerOpSid, 1 );
    	*pSubAuthority = DOMAIN_ALIAS_RID_SYSTEM_OPS;

    	// Create the LocalSystemSid which will be the owner and the primary 
    	// group of the security object. 
    	//
    	pLocalSystemSid = (PSID)LocalAlloc( LPTR, GetSidLengthRequired( 1 ) );

    	if ( pLocalSystemSid == NULL ) {
	    dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
	    break;
	}

    	if ( !InitializeSid( pLocalSystemSid, &SidIdentifierNtAuth, 1 ) ) {
	    dwRetCode = GetLastError();
	    break;
	}

    	// Set the sub-authorities 
    	//
    	pSubAuthority = GetSidSubAuthority( pLocalSystemSid, 0 );
    	*pSubAuthority = SECURITY_LOCAL_SYSTEM_RID;

    	// Set up the DACL that will allow admins with the above SID all access
    	// It should be large enough to hold all ACEs.
    	// 
    	cbDaclSize = sizeof(ACL) + ( sizeof(ACCESS_ALLOWED_ACE) * 2 ) +
		     GetLengthSid(pAdminSid) + GetLengthSid(pServerOpSid);
		     
    	if ( (pDacl = (PACL)LocalAlloc( LPTR, cbDaclSize ) ) == NULL ) {
	    dwRetCode = ERROR_NOT_ENOUGH_MEMORY; 
	    break;
	}
	
        if ( !InitializeAcl( pDacl,  cbDaclSize, ACL_REVISION2 ) ) {
	    dwRetCode = GetLastError();
	    break;
 	}
    
        // Add the ACE to the DACL
    	//
    	if ( !AddAccessAllowedAce( pDacl, 
			           ACL_REVISION2, 
			           AFPSVC_ALL_ACCESS, // What the admin can do
			           pAdminSid )) {
	    dwRetCode = GetLastError();
	    break;
	}

    	if ( !AddAccessAllowedAce( pDacl, 
			           ACL_REVISION2, 
			           AFPSVC_ALL_ACCESS, // What the admin can do
			           pServerOpSid )) {
	    dwRetCode = GetLastError();
	    break;
	}

        // Create the security descriptor an put the DACL in it.
    	//
    	if ( !InitializeSecurityDescriptor( &SecurityDescriptor, 1 )){
	    dwRetCode = GetLastError();
	    break;
    	}

    	if ( !SetSecurityDescriptorDacl( &SecurityDescriptor, 
					 TRUE, 
					 pDacl, 
					 FALSE ) ){
	    dwRetCode = GetLastError();
	    break;
	}
	

	// Set owner for the descriptor
   	//
    	if ( !SetSecurityDescriptorOwner( &SecurityDescriptor, 
					  pLocalSystemSid, 
					  FALSE ) ){
	    dwRetCode = GetLastError();
	    break;
	}


	// Set group for the descriptor
   	//
    	if ( !SetSecurityDescriptorGroup( &SecurityDescriptor, 
					  pLocalSystemSid, 
					  FALSE ) ){
	    dwRetCode = GetLastError();
	    break;
	}

    	// Get token for the current process
    	//
    	if ( !OpenProcessToken( GetCurrentProcess(), 
				TOKEN_QUERY, 
				&hProcessToken )){
	    dwRetCode = GetLastError();
	    break;
    	}

    	// Create a security object. This is really just a security descriptor
    	// is self-relative form. This procedure will allocate memory for this
    	// security descriptor and copy all in the information passed in. This
    	// allows us to free all dynamic memory allocated.
    	//
    	if ( !CreatePrivateObjectSecurity( 
				      NULL,
				      &SecurityDescriptor,
				      &(AfpSecurityObject.pSecurityDescriptor),
				      FALSE,
				      hProcessToken, 
    				      &(AfpSecurityObject.GenericMapping)
				     )) 
	     dwRetCode = GetLastError();

    } while( FALSE );

	
    // Free up the dynamic memory
    //
    if ( pLocalSystemSid != NULL )
    	LocalFree( pLocalSystemSid );

    if ( pAdminSid != NULL )
    	LocalFree( pAdminSid );

    if ( pServerOpSid != NULL )
    	LocalFree( pServerOpSid );

    if ( pDacl != NULL )
    	LocalFree( pDacl );

    if ( hProcessToken != NULL )
    	CloseHandle( hProcessToken );

    return( dwRetCode );

}

//**
//
// Call:	AfpSecObjDelete
//
// Returns:	NO_ERROR	- success
//		non-zero returns from security functions
//
// Description: Will destroy a valid security descriptor.
//
DWORD
AfpSecObjDelete( VOID )
{
    if ( !IsValidSecurityDescriptor( AfpSecurityObject.pSecurityDescriptor))
    	return( NO_ERROR );

    if (!DestroyPrivateObjectSecurity( &AfpSecurityObject.pSecurityDescriptor))
	return( GetLastError() );

    return( NO_ERROR );
}

//**
//
// Call:	AfpSecObjAccessCheck
//
// Returns:	NO_ERROR	- success
//		non-zero returns from security functions
//
// Description: This procedure will first impersonate the client, then
//		check to see if the client has the desired access to the
//		security object. If he/she does then the AccessStatus 
//		variable will be set to NO_ERROE otherwise it will be
//		set to ERROR_ACCESS_DENIED. It will the revert to self and
//		return.
//
DWORD
AfpSecObjAccessCheck( IN  DWORD 		DesiredAccess, 
		      OUT LPDWORD 		lpdwAccessStatus 		
)
{
DWORD		dwRetCode;
ACCESS_MASK	GrantedAccess;
BOOL		fGenerateOnClose;

    // Impersonate the client
    //
    dwRetCode = RpcImpersonateClient( NULL );

    if ( dwRetCode != RPC_S_OK )
	return( I_RpcMapWin32Status( dwRetCode ));

    dwRetCode = AccessCheckAndAuditAlarm( 
				    AFP_SERVICE_NAME,
				    NULL,
    				    AfpSecurityObject.lpwsObjectType,
				    AfpSecurityObject.lpwsObjectName,
				    AfpSecurityObject.pSecurityDescriptor,
				    DesiredAccess,
				    &(AfpSecurityObject.GenericMapping),
				    FALSE,
 			 	    &GrantedAccess,	
				    (LPBOOL)lpdwAccessStatus,
				    &fGenerateOnClose
				  );

    RpcRevertToSelf();

    if ( !dwRetCode )
	return( GetLastError() );

    // Check if desired access == granted Access
    //
    if ( DesiredAccess != GrantedAccess )
	*lpdwAccessStatus = ERROR_ACCESS_DENIED;
    else
	*lpdwAccessStatus = NO_ERROR;

    return( NO_ERROR );
}
