oracle.psm1

$script:SqlExplicitRoleMembersWithInclusions = "
SELECT
       rp.Granted_Role AS RoleName,
       rp.Grantee AS AccountName
FROM dba_role_privs rp
WHERE rp.Granted_Role IN ({0})"


$script:AllSqlExplicitGrants = "
SELECT sp.Grantee AS AccountName,
       sp.Privilege AS PermissionName
FROM dba_sys_privs sp
WHERE "


$script:SqlExplicitGrantsWithInclusions = $script:AllSqlExplicitGrants + "sp.Privilege IN ({0})"
$script:SqlExplicitGrantsWithExclusions = $script:AllSqlExplicitGrants + "sp.Privilege NOT IN ({0})"

$script:OraclePermissionsMap = @{
    "DEQUEUE ANY QUEUE" = "DEQUEUE ANY QUEUE"
    "MANAGE ANY QUEUE" = "MANAGE ANY QUEUE"
    "CREATE ANY EVALUATION CONTEXT" = "CREATE ANY EVALUATION CONTEXT"
    "CREATE ANY RULE" = "CREATE ANY RULE"
    "CREATE ANY JOB" = "CREATE ANY JOB"
    "CREATE INDEXTYPE" = "CREATE INDEXTYPE"
    "SELECT ANY TABLE" = "SELECT ANY TABLE"
    "INSERT ANY TABLE" = "INSERT ANY TABLE"
    "EXECUTE ANY EVALUATION CONTEXT" = "EXECUTE ANY EVALUATION CONTEXT"
    "CREATE RULE SET" = "CREATE RULE SET"
    "ALTER ANY RULE" = "ALTER ANY RULE"
    "EXECUTE ANY PROGRAM" = "EXECUTE ANY PROGRAM"
    "EXECUTE ANY CLASS" = "EXECUTE ANY CLASS"
    "CREATE ANY CREDENTIAL" = "CREATE ANY CREDENTIAL"
    "CREATE EVALUATION CONTEXT" = "CREATE EVALUATION CONTEXT"
    "UPDATE ANY TABLE" = "UPDATE ANY TABLE"
    "ENQUEUE ANY QUEUE" = "ENQUEUE ANY QUEUE"    
    "CREATE DIMENSION" = "CREATE DIMENSION"
    "CREATE SEQUENCE" = "CREATE SEQUENCE"
    "CREATE SESSION"   = "CREATE SESSION"
    "CREATE TABLE" = "CREATE TABLE"
    "CREATE TYPE" = "CREATE TYPE"
    "DROP ANY RULE SET" = "DROP ANY RULE SET"
    "READ ANY TABLE" = "READ ANY TABLE"
    "CREATE RULE" = "CREATE RULE"
    "CREATE CLUSTER" = "CREATE CLUSTER"
    "CREATE PROCEDURE" = "CREATE PROCEDURE"
    "CREATE VIEW" = "CREATE VIEW"
    "ALTER ANY EVALUATION CONTEXT" = "ALTER ANY EVALUATION CONTEXT"
    "CREATE OPERATOR" = "CREATE OPERATOR"
    "CREATE SYNONYM"   = "CREATE SYNONYM"
    "DROP ANY EVALUATION CONTEXT"   = "DROP ANY EVALUATION CONTEXT"
    "EXECUTE ANY RULE" = "EXECUTE ANY RULE"
    "MANAGE SCHEDULER" = "MANAGE SCHEDULER"
    "CREATE EXTERNAL JOB" = "CREATE EXTERNAL JOB"
    "DELETE ANY TABLE" = "DELETE ANY TABLE"
    "ALTER ANY RULE SET"  = "ALTER ANY RULE SET"
    "EXECUTE ANY RULE SET"   = "EXECUTE ANY RULE SET"
    "ADMINISTER KEY MANAGEMENT" = "ADMINISTER KEY MANAGEMENT"
    "CREATE TRIGGER"  = "CREATE TRIGGER"
    "CREATE JOB"   = "CREATE JOB"
    "CREATE ANY RULE SET" = "CREATE ANY RULE SET"
    "DROP ANY RULE" = "DROP ANY RULE"
    "CREATE MATERIALIZED VIEW" = "CREATE MATERIALIZED VIEW"
}
$script:OraclePermissionsString = ($script:OraclePermissionsMap | Out-String)

function Invoke-ThrowPermissionsException
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$Message
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Write-Host -ForegroundColor Yellow $script:OraclePermissionsString
    Write-Host -ForegroundColor Yellow "Example Usage:"
    Write-Host -ForegroundColor Yellow " -IncludePermissions @(`"INSERT ANY TABLE`",`"DROP ANY RULE`")"
    Write-Host -ForegroundColor Yellow " or"
    Write-Host -ForegroundColor Yellow " -ExcludePermissions @(`"INSERT ANY TABLE`",`"DROP ANY RULE`",`"EXECUTE ANY RULE SET`")"
    Write-Host -ForegroundColor Yellow " or (to turn it off)"
    Write-Host -ForegroundColor Yellow " -ExcludePermissions @()"
    throw $Message
}

<#
.SYNOPSIS
Discover privileged accounts in a Oracle database.
 
.DESCRIPTION
This cmdlet may be used to discover privileged accounts in a Oracle database. When
called without arguments, the default behavior is to find local accounts that have been
added to the built-in role (AQ_ADMINISTRATOR_ROLE,DBA) and any local accounts that have been
granted any permissions other than SELECT ANY TABLE, READ ANY TABLE. The caller can
override this behavior by specifying the exact list of roles to look for. The caller
can also specify which directly granted permissions to exclude or include.
 
When a credential is not supplied to this cmdlet, it will automatically look for an open
access request with a matching asset name or network address and use that access request
to get the password to run the discovery. If no access request is found, the cmdlet
will prompt for an account name and password to use.
 
.PARAMETER NetworkAddress
IP address or hostname of a Oracle database.
 
.PARAMETER Instance
Name of the postgres instance to connect to
 
.PARAMETER Credential
A PowerShell credential object that can be used to connect to the database server to
execute the discovery job.
 
.PARAMETER Roles
A list of roles to search for to identify privileged accounts, or set to @() to turn off role search.
See https://docs.oracle.com/cd/A97630_01/server.920/a96521/privs.htm#:~:text=A%20user%20privilege%20is%20a,together%20privileges%20or%20other%20roles.
 
.PARAMETER ExcludePermissions
A list of permissions to exclude when searching for privileged accounts, or set to @() to turn off permission search.
See https://docs.oracle.com/cd/A97630_01/server.920/a96521/privs.htm#:~:text=A%20user%20privilege%20is%20a,together%20privileges%20or%20other%20roles.
 
.PARAMETER IncludePermissions
A list of permissions to include when searching for privileged accounts.
See https://docs.oracle.com/cd/A97630_01/server.920/a96521/privs.htm#:~:text=A%20user%20privilege%20is%20a,together%20privileges%20or%20other%20roles.
 
.INPUTS
None.
 
.OUTPUTS
System.Management.Automation.PSObject.
 
.EXAMPLE
Get-SgDiscOracleAccount oracle.test.env
 
.EXAMPLE
Get-SgDiscOracleAccount oracle.test.env -Credential (Get-Credential)
 
.EXAMPLE
Get-SgDiscOracleAccount oracle.test.env -Roles DBA,CAPTURE_ADMIN,DV_SECANALYST -IncludePermissions CREATE TABLE,MANAGE ANY QUEUE,DEQUEUE ANY QUEUE
#>

function Get-SgDiscOracleAccount
{
    [CmdletBinding(DefaultParameterSetName="ExcludePerms")]
    param (
        [Parameter(Mandatory=$true,Position=0)]
        [string]$NetworkAddress,
        [Parameter(Mandatory=$true,Position=1)]
        [string]$Instance,
        [Parameter(Mandatory=$false)]
        [PSCredential]$Credential = $null,
        [Parameter(Mandatory=$false)]
        [string[]]$Roles = @("AQ_ADMINISTRATOR_ROLE","DBA"),
        [Parameter(Mandatory=$false,ParameterSetName="ExcludePerms")]
        [string[]]$ExcludePermissions = @("SELECT ANY TABLE","READ ANY TABLE"),
        [Parameter(Mandatory=$false,ParameterSetName="IncludePerms")]
        [string[]]$IncludePermissions
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    if (-not $Credential)
    {
        # doing this here allows error action and verbose parameters to propagate
        $Credential = (Get-SgDiscConnectionCredential $NetworkAddress)
    }

    $local:NetworkCredential = $Credential.GetNetworkCredential()
    $ConnectionString = "Data Source=$NetworkAddress/$Instance;User Id=$($local:NetworkCredential.UserName);Password=$($local:NetworkCredential.Password)"

    # make sure InvokeQuery is installed
    if (-not (Get-Module InvokeQuery)) 
    { 
        try 
        {
            Import-Module InvokeQuery
        }
        catch 
        {
            throw "SQL account discovery in safeguard-discovery requires InvokeQuery. Please run: Install-Module InvokeQuery."
        }
    }

    # handle explicit permissions
    if (($PSCmdlet.ParameterSetName -eq "IncludePerms" -and $IncludePermissions) -or ($PSCmdlet.ParameterSetName -eq "ExcludePerms" -and $ExcludePermissions))
    {
        if ($PSCmdlet.ParameterSetName -eq "IncludePerms" -and $IncludePermissions)
        {
            $local:PermInclusions = @()
            foreach ($local:Perm in $IncludePermissions)
            {
                $local:PermInclusions += "'$($local:Perm)'"
            }
            $local:Sql = ($script:SqlExplicitGrantsWithInclusions -f ($local:PermInclusions -join ","))
        }
        elseif ($PSCmdlet.ParameterSetName -eq "ExcludePerms" -and $ExcludePermissions)
        {
            $local:PermExclusions = @()
            foreach ($local:Perm in $ExcludePermissions)
            {
                $local:PermExclusions += "'$($local:Perm)'"
            }
            $local:Sql = ($script:SqlExplicitGrantsWithExclusions -f ($local:PermExclusions -join ","))
        }

        # query to find matching permissions (this is filtered to local accounts)
        $local:PrivilegedAccountsFromPermissions = (Invoke-OracleQuery -Sql $local:Sql -ConnectionString $ConnectionString)
    }
    else
    {
        $local:PrivilegedAccountsFromPermissions = @()
    }

    # handle Oracle roles
    if ($Roles)
    {
        $local:RoleInclusions = @()
        foreach ($local:Role in $Roles)
        {
            $local:RoleInclusions += "'$($local:Role)'"
        }
        $local:Sql = ($script:SqlExplicitRoleMembersWithInclusions -f ($local:RoleInclusions -join ","))

        # query to find matching role memberships (this is filtered to local accounts)
        $local:PrivilegedAccountsFromRoles = (Invoke-OracleQuery -Sql $local:Sql -ConnectionString $ConnectionString)
    }
    else
    {
        $local:PrivilegedAccountsFromRoles = @()
    }

    # process results
    $local:Results = @{}
    $local:PrivilegedAccountsFromPermissions | ForEach-Object {
        if ($local:Results[$_.AccountName])
        {
            $local:Results[$_.AccountName].Permissions += (New-Object PSObject -Property ([ordered]@{
                PermissionName = $_.PermissionName;
            }))
        }
        else
        {
            $local:Results[$_.AccountName] = New-Object PSObject -Property ([ordered]@{
                AccountName = $_.AccountName;
                Roles = @();
                Permissions = @(New-Object PSObject -Property ([ordered]@{
                    PermissionName = $_.PermissionName;
                }));
                Description = "safeguard-discovery";
            })
        }
    }
    $local:PrivilegedAccountsFromRoles | ForEach-Object {
        if ($local:Results[$_.AccountName])
        {
            $local:Results[$_.AccountName].Roles += ($_.RoleName)
        }
        else
        {
            $local:Results[$_.AccountName] = New-Object PSObject -Property ([ordered]@{
                AccountName = $_.AccountName;
                Roles = @($_.RoleName);
                Permissions = @();
                Description = "safeguard-discovery";
            })
        }
    }
    
    $local:Results.Values
}