Oracle Database Vault: introdução a Rule, Ruleset e Command Rule

Vimos anteriormente como configurar o Oracle Database Vault (ODV), procedimentos para habilitar e desabilitar, além do cuidado extra que precisamos ter com a senha de seus users (a perda destas senhas pode ser trágica). Falamos também de algumas atividades do dia a dia de um DBA que sofreram alterações[1].

O objetivo agora é mostrar que nem tudo é um pesadelo e também quais são os pontos positivos desta “mudança”. Vamos continuar a utilizar um DB 12.1.0.2 com ODV habilitado com as configurações default e assim podemos explorar algumas outras features focando no ganho de segurança que elas nos proporcionam.

O ODV como um todo pode ser gerenciado a partir do Cloud Control, mas aqui nos restringiremos ao SQL*Plus e na rica API disponibilizada para gestão dos diversos componentes.

Nosso primeiro passo será passar por alguns exemplos tentando entender quais são as configurações default. Vale lembrar que grande parte das propriedades do Vault só podem ser consultadas por usuários devidamente privilegiados, como nosso C##DONO que possui privilégios da role DV_OWNER. Ao mesmo tempo, veremos também como aumentar a proteção através das Rulesets e Comand Rules que criaremos.

Há uma infinidade de combinações e aqui escolhemos usar o seguinte exemplo para nos guiar nos conceitos que discutiremos. Vamos impor que, com exceção do SCOTT, SYS e SYSTEM, qualquer user que possua o privilégio de UNLIMITED TABLESPACE terá de se conectar exclusivamente via SQL*Plus. Tenha em mente que, fazendo isso, alguns processos automáticos podem ser afetados, é sempre indicado testar em ambientes de teste/lab.

 

CENÁRIO PARA O EXEMPLO

Vamos preparar nosso cenário de testes concedendo privilégios ao SCOTT e criando dois novos users, um com privilégio de UNLIMITED TABLESPACE (USER_UNLIM_TS) e outro sem (USER_LIM_TS). Chamamos a atenção que aqui temos um DB cujo ODV foi habilitado e (como falamos no artigo anterior) temos um user C##CONTAS que gerencia contas e profiles.

SYS@cdb1> SELECT VALUE FROM V$OPTION WHERE PARAMETER = ‘Oracle Database Vault’;

VALUE

———

TRUE

C##CONTAS@pdb1> create user USER_UNLIM_TS identified by oracle;

User created.

C##CONTAS@pdb1> create user USER_LIM_TS identified by oracle;

User created.

SYS@pdb1> grant create session, unlimited tablespace to USER_UNLIM_TS;

Grant succeeded.

SYS@pdb1> grant create session to USER_LIM_TS;

Grant succeeded.

SYS@pdb1> grant unlimited tablespace to SCOTT;

Grant succeeded.

E agora vamos explorar um pouco mais o Database Vault.

 

FACTOR

É uma forma de o Oracle obter os atributos de uma conexão com o DB, seja do lado Server ou Client, como IP do host onde roda o database ou IP do cliente. Podemos utilizá-lo para restringir acesso ao DB ou aos dados, a partir de determinado host ou alguma outra propriedade específica como ferramentas utilizadas e métodos de autenticação [2]. Também é possível combiná-los com Rules nas RuleSets aumentando ainda mais a especificidade das políticas de segurança.

O conjunto de Factors já existentes pode ser consultado através da view dvsys.dba_dv_factor:

C##DONO@pdb1> SELECT name, get_expr FROM   dvsys.dba_dv_factor;

Lang                           UPPER(SYS_CONTEXT(‘USERENV’,’LANG’))

Machine                        DVSYS.DBMS_MACADM.GET_SESSION_INFO(‘MACHINE’)

Authentication_Method          UPPER(SYS_CONTEXT(‘USERENV’,’AUTHENTICATION_METHOD’))

Client_IP                      UPPER(SYS_CONTEXT(‘USERENV’,’IP_ADDRESS’))

Database_IP                    UPPER(SYS_CONTEXT(‘USERENV’,’SERVER_HOST_IP’))

Database_Hostname              UPPER(DVSYS.DBMS_MACADM.GET_INSTANCE_INFO(‘HOST_NAME’))

 

Podemos testar os Factors usando uma função do DVSYS chamada GET_FACTOR. Ela faz a avaliação do Factor solicitado que, obviamente, pode variar de sessão para sessão. Vejamos dois exemplos acessando o mesmo DB a partir de origens diferentes:

oracle-factors

C##DONO@pdb1> select DVSYS.GET_FACTOR(‘Machine’) from dual;

DVSYS.GET_FACTOR(‘MACHINE’)

———————-

prod.localdomain

 

Agora que conhecemos os Factor defaults, iremos construir uma forma de apontar o uso do SQL*Plus (ou qualquer outra ferramenta), que será necessário como um dos steps do nosso exemplo. Para enriquecer um pouco mais, também criaremos um novo tipo de Factor (factor_type) e o chamaremos de App (por default existe um Factor Type chamado Application e muitos outros, que podem ser consultados em dvsys.dba_dv_factor_type). Vamos lá, com um user que possua privilégio DV_OWNER:

C##DONO@pdb1>

BEGIN

dbms_macadm.create_factor_type(name        => ‘App’,

description => ‘Factor_type relacionado a aplicacao utilizada para conexao ao DB.’);

END;

COMMIT;

/

PL/SQL procedure successfully completed.

 

Agora que temos um novo tipo, vamos criar o Factor que nos devolverá o nome da ferramenta em uso. Depois basta validarmos se é ou não o que desejamos.

C##DONO@pdb1>

BEGIN

DBMS_MACADM.CREATE_FACTOR(factor_name      => ‘App_Name’,

factor_type_name => ‘App’,

description      => ‘Factor que devolvera o nome da app.’,

rule_set_name    => NULL,

get_expr         => ‘UPPER(SYS_CONTEXT(”USERENV”,”MODULE”))’,

validate_expr    => NULL,

identify_by      => DBMS_MACUTL.G_IDENTIFY_BY_METHOD,

labeled_by       => DBMS_MACUTL.G_LABELED_BY_SELF,

eval_options     => DBMS_MACUTL.G_EVAL_ON_SESSION,

audit_options    => DBMS_MACUTL.G_AUDIT_ON_GET_ERROR,

fail_options     => DBMS_MACUTL.G_FAIL_WITH_MESSAGE);

COMMIT;

END;

/

PL/SQL procedure successfully completed.

 

Detalhes das opções válidas para cada parâmetro você pode encontrar em [3], não vamos entrar em tantos detalhes de Factor aqui. Agora testamos o resultado do nosso novo factor.

C##DONO@pdb1> select DVSYS.GET_FACTOR(‘App_Name’) from dual;

DVSYS.GET_FACTOR(‘APP_NAME’)

——————————————

SQL*PLUS

 

Para ilustrar a diferença, vamos fazer a mesma query através do SQL Developer:

sql-developer

Demos o primeiro passo para proteção do cenário que propomos como exemplo. Vamos continuar essa caminhada.

 

RULE

Próximo passo é obter uma forma de fazer nossas asserções a respeito de propriedades/características do DB, como permissões de um determinado user, e para isso usamos as Rules (regras). Elas nada mais são que objetos no DB que possuem uma expressão lógica associada e que devem, portanto, retornar TRUE ou FALSE, levando em conta os parâmetros informados, quando for o caso. Você pode usar até 90 caracteres para atribuir um nome e a expressão que será avaliada deve possuir até 1024 caracteres.

Você pode, basicamente, fazer o mesmo que faria na cláusula WHERE de um SQL, inclusive utilizar funções. Neste caso será necessário conceder privilégio de EXECUTE para o DVSYS e utilizar o “Full Qualified Name” (schema.nome_da_funcao). Caso opte por functions/procedures, você provavelmente deverá usar “definer’s rights” [4]ou a Rule poderá falhar (por padrão o DVSYS não possui muitos privilégios). Há mais detalhes em [5].

As Rules defaults, encontradas em dvsys.dba_dv_rule, incluem duas bastante convenientes para o caso em que não se deseja “testar” condições, mas sim impor. Elas podem permitir ou impedir determinada ação. São elas:

NAME      RULE_EXPR

——— ———————

True      1=1

False     1=0

E, como esperado, a True sempre retornará TRUE e a False, FALSE. Outras interessantes são as que verificam se o user conectado possui determinado privilégio.

Is User Manager

DVSYS.DBMS_MACUTL.USER_HAS_ROLE_VARCHAR(‘DV_ACCTMGR’, ‘”‘||dvsys.dv_login_user||'”‘) = ‘Y’

Is Database Administrator

DVSYS.DBMS_MACUTL.USER_HAS_ROLE_VARCHAR(‘DBA’,'”‘||dvsys.dv_login_user||'”‘) = ‘Y’

“DVSYS.DV_LOGIN_USER” é uma function dentro do schema DVSYS que retorna o user que está logado e a DVSYS.DBMS_MACUTL.USER_HAS_ROLE_VARCHAR retornará “Y” caso o user possua a Role informada, mesmo que indiretamente através de outra.

Existem algumas funções que a API disponibiliza no schema DVSYS e podem ser utilizadas para a criação de regras mais especializadas [6,7], como a DVSYS.DV_SYSEVENT que retorna o evento que disparou a avaliação das regras, DVSYS.DV_INSTANCE_NUM que retorna o número da instância e DVSYS. DBMS_MACUTL. USER_HAS_SYSTEM_PRIV_VARCHAR que verifica se o user possui determinado privilégio de sistema.

Prosseguindo com nosso exemplo, vamos criar uma Rule que utilizaremos para verificar se o user que tentará se conectar é um dos que permitiremos, outra para verificar se o user possui ou não o privilégio de sistema UNLIMITED TABLESPACE e, finalmente, uma que analisa o Factor criado anteriormente para identificar a aplicação utilizada.

Tenha em mente que o resultado TRUE na avaliação da regra permitirá prosseguir, e se deseja proibir alguma ação, ela deve retornar FALSE. Uma forma de facilitar a compreensão das Rules é utilizar uma pergunta como nome, algo como “É o usuário X?” ou “Não possui privilégio Y?”, e a resposta será o retorno. Perguntas negativas nem sempre são tão intuitivas quando o retorno é falso, mas nada que alguns testes de mesa não resolvam.

A primeira para verificar se o user é o SCOTT, SYS ou SYSTEM:

C##DONO@pdb1>

BEGIN

DBMS_MACADM.CREATE_RULE(rule_name => ‘Eh scott, sys ou system’,

rule_expr => ‘USER IN (”SCOTT”,”SYS”,”SYSTEM”)’);

END;

/

PL/SQL procedure successfully completed.

 

Precisaremos também criar uma Rule que verifique se o usuário “não” possui o privilégio de UNLIMITED TABLESPACE.

C##DONO@pdb1>

BEGIN

DBMS_MACADM.CREATE_RULE(rule_name => ‘Nao possui UNLIMITED TS’,

rule_expr => ‘(DVSYS.DBMS_MACUTL.USER_HAS_SYSTEM_PRIV_VARCHAR(”UNLIMITED TABLESPACE”,”””||dvsys.dv_login_user||”””) = ”N”)’);

END;

/

PL/SQL procedure successfully completed.

O Factor que criamos acima pode também ser transformado em uma Rule. Ele, na verdade, virou uma função (podemos encontrá-la na dba_objects) e a utilizamos como base para a Rule:

SYS@pdb1>

select owner, object_name, object_type

from   dba_objects

where  object_name like ‘%APP_NAME%’;

OWNER        OBJECT_NAME  OBJECT_TYPE

———— ———— ————

DVF          F$APP_NAME   FUNCTION

C##DONO@pdb1>

BEGIN

DBMS_MACADM.CREATE_RULE(rule_name => ‘Eh SQL*Plus’,

rule_expr => ‘UPPER(DVF.F$APP_NAME) like ”%SQL%PLUS%”’);

END;

/

PL/SQL procedure successfully completed.

 

RULESET

Falaremos agora de um componente que nos permite agrupar Rules em uma única verificação. Rulesets são um conjunto de Rules (uma ou mais) que são avaliadas em tempo de execução e vão retornar TRUE ou FALSE baseada nas regras que a compõe. O ponto interessante é que elas atuam como acessório para outros componentes do ODV. Assim, Command Rules ou Realms (que falaremos em um próximo artigo), por exemplo, podem utilizá-las para permitir ou não a execução de determinada instrução.

Dentre as opções de avaliação de uma Ruleset, temos:

– ALL TRUE: Todas as regras deverão ser avaliadas como TRUE para que a Ruleset retorne TRUE.

– ANY TRUE: Pelo menos uma das regras deve retornar TRUE para que a Ruleset retorne TRUE.

A avaliação pode ocorrer apenas uma vez e ter o resultado reaproveitado para todas as chamadas subsequentes da mesma sessão (Static) ou ocorrer a cada nova chamada (non-Static).

Também há outras opções, como executar um PL/SQL em caso de sucesso ou falha baseado na forma escolhida para “Hander_Options” [8]. Pode ser utilizado uma procedure de envio de e-mail, uma que apenas registre em alguma tabela específica ou qualquer outra que o DV_OWNER tenha privilégio de execução. Para enriquecer um pouco nosso exemplo, vamos criar uma tabela no schema SYSTEM para registro das falhas de conexão provocadas pela Ruleset e uma procedure para populá-la (lembre-se que este é apenas um exemplo sem muito critério para ilustrar features do ODV, você deve usar uma procedure que atenda a seus requisitos e em um schema apropriado).

SYSTEM@pdb1> CREATE TABLE ruleset_fail

2  (username     VARCHAR2(128),

3  data_hora    DATE);

Table created.

 

SYSTEM@pdb1> CREATE OR REPLACE PROCEDURE prc_ruseset_fail

2  AS

3  PRAGMA AUTONOMOUS_TRANSACTION ;

4  BEGIN

5    INSERT INTO ruleset_fail

6     (username,

7      data_hora)

8    VALUES

9     (USER,

10      SYSDATE);

11     COMMIT;

12  EXCEPTION

13    WHEN OTHERS THEN

14      dbms_output.put_line(‘Algo errado:’||Sqlerrm||dbms_utility.format_error_backtrace);

15  END;

16  /

Procedure created.

 

SYSTEM@pdb1> grant execute on prc_ruseset_fail to dvsys;

Grant succeeded.

 

Agora temos pronto o que precisamos para seguir em diante:

– Verificar se o user é SCOTT, SYS ou SYSTEM;

– Se o user NÃO possui privilégio de UNLIMITED TABLESPACE;

– Se a ferramenta utilizada ser SQL*Plus;

Os passos que seguiremos agora serão: a criação da Ruleset e em seguida associá-la com as Rules criadas anteriormente.

C##DONO@pdb1> BEGIN

DBMS_MACADM.CREATE_RULE_SET(rule_set_name   => ‘RS_SCOTT_SYS_SYSTEM_SQLPLUS_UNLIMTS’,

description     => ‘Ruleset que verifica se o user eh o scott_sys_system, se possui UNLIM TS or se a conexao eh via SQLPLUS’,

enabled         => DBMS_MACUTL.G_YES,

eval_options    => DBMS_MACUTL.G_RULESET_EVAL_ANY,

audit_options   => DBMS_MACUTL.G_RULESET_AUDIT_FAIL,

fail_options    => DBMS_MACUTL.G_RULESET_FAIL_SHOW,

fail_message    => ‘Conexoes com UNLIM TS devem ser feitas pelo SQL*PLUS’,

fail_code       => -20001,

handler_options => DBMS_MACUTL.g_ruleset_handler_fail,

handler         => ‘system.prc_ruseset_fail’,

is_static       => FALSE);

END;

/

PL/SQL procedure successfully completed.

 

C##DONO@pdb1> BEGIN

DBMS_MACADM.ADD_RULE_TO_RULE_SET(rule_set_name => ‘RS_SCOTT_SYS_SYSTEM_SQLPLUS_UNLIMTS’,

rule_name     => ‘Eh SQL*Plus’,

rule_order    => 1,

enabled       => DBMS_MACUTL.G_YES);

END;

/

PL/SQL procedure successfully completed.

 

C##DONO@pdb1> BEGIN

DBMS_MACADM.ADD_RULE_TO_RULE_SET(rule_set_name => ‘RS_SCOTT_SYS_SYSTEM_SQLPLUS_UNLIMTS’,

rule_name     => ‘Eh scott, sys ou system’,

rule_order    => 2,

enabled       => DBMS_MACUTL.G_YES);

END;

/

PL/SQL procedure successfully completed.

 

C##DONO@pdb1> BEGIN

DBMS_MACADM.ADD_RULE_TO_RULE_SET(rule_set_name => ‘RS_SCOTT_SYS_SYSTEM_SQLPLUS_UNLIMTS’,

rule_name     => ‘Nao possui UNLIMITED TS’,

rule_order    => 3,

enabled       => DBMS_MACUTL.G_YES);

END;

/

PL/SQL procedure successfully completed.

 

De posse da nossa Ruleset, precisamos agora associá-la a uma Command Rule. Vejamos então o que é este outro componente.

 

COMMAND RULE

Basicamente é uma forma de se proteger de determinada instrução impondo uma variedade de condições. As condições que podem ser impostas são muitas, como IP de origem, horário, programa utilizado, username, usuário do SO e tantas outras. O único ponto importante é criar Rules/Ruleset que faça todas as verificações que deseja.

Há também uma grande coleção de instruções que podem ser protegidas, como por exemplo UPDATE, CREATE VIEW, ALTER FUNCTION, ALTER SEQUENCE. A lista completa pode ser encontrada em [9].

Assim, apenas quando as condições forem satisfeitas a instrução será executada com sucesso. São sempre verificadas em tempo de execução e afetam todas as instruções que ela “protege”. Por exemplo, no caso de alterar a senha de um usuário, como discutimos em [1], mesmo que o SYS tenha privilégios de ALTER USER, há uma Command Rule que protege esta instrução.

C##DONO@cdb1>

SELECT command,

rule_set_name,

object_owner,

object_name

FROM   dvsys.dba_dv_command_rule

WHERE  command = ‘ALTER USER’;

COMMAND       RULE_SET_NAME                OBJECT_OWNER   OBJECT_NAME

————- —————————- ————– ————

ALTER USER    Can Maintain Own Account     %              %

 

As Command Rules podem ter efeito em todo o sistema, algum schema específico ou mesmo apenas um determinado objeto. Desta forma, é possível que um objeto esteja protegido por mais de uma Command Rule. Você poderia ter uma protegendo um schema e outra em um objeto específico. Todas elas serão validadas, para cada uma será verificada a condição imposta pela Ruleset.

Command Rules são “mais fortes” que privilégios de sistema e, desta forma, é possível evitar que DBAs possam realizar determinada operação.

Vamos ver um exemplo de como isso funciona? Nossa primeira Command Rule impedirá que tabelas sejam dropadas do schema SCOTT.

Vamos criar, dropar e recriar uma tabela TB_EXEMPLO no schema SCOTT para comparar com o resultado que esperamos.

SCOTT@pdb1> create table TB_EXEMPLO as select * from all_tables;

Table created.

 

SCOTT@pdb1> drop table TB_EXEMPLO;

Table dropped.

 

SCOTT@pdb1> create table TB_EXEMPLO as select * from all_tables;

Table created.

 

Agora, com um user que possua DV_OWNER ou DV_ADMIN, basta criar a Command Rule. Usaremos a RuleSet “Disabled” para impedir todas as requisições. É uma daquelas Ruleset predefinida (coringa) e bastante útil sempre que precisamos avaliar como “FALSE” todas as instruções.

C##DONO@pdb1>

BEGIN

DBMS_MACADM.CREATE_COMMAND_RULE(command       => ‘DROP TABLE’,

rule_set_name => ‘Disabled’,

object_owner  => ‘SCOTT’,

object_name   => ‘%’,

enabled       => DBMS_MACUTL.G_YES);

END;

/

PL/SQL procedure successfully completed.

 

Em seguida, vamos apenas verificar se a criação foi realizada com sucesso.

C##DONO@pdb1>

SELECT command,

rule_set_name,

object_owner,

object_name,

enabled

FROM   dvsys.dba_dv_command_rule

WHERE  command = ‘DROP TABLE’;

COMMAND      RULE_SET_NAME   OBJECT_OWNER    OBJECT_NAME     ENABLED

———— ————– ————— ————— ———-

DROP TABLE   Disabled       SCOTT           %               Y

 

Agora basta testar nossa Command Rule tentando dropar a tabela do SCOTT. E lá está:

SCOTT@pdb1> drop table TB_EXEMPLO;

drop table TB_EXEMPLO

*

ERROR at line 1:

ORA-47400: Command Rule violation for DROP TABLE on SCOTT.TB_EXEMPLO

 

Note que apenas o schema SCOTT foi afetado, continuamos dropando tabelas do HR:

HR@pdb1> create table TB_EXEMPLO as select * from all_tables;

Table created.

HR@pdb1> drop table TB_EXEMPLO;

Table dropped.

 

Agora que vimos como funciona uma Command Rule simples, vamos aproveitar para explorar um pouco a API e apagá-la (sempre com um user apropriado):

C##DONO@pdb1> BEGIN

DBMS_MACADM.DELETE_COMMAND_RULE(command      => ‘DROP TABLE’,

object_owner => ‘SCOTT’,

object_name  => ‘%’);

END;

/

PL/SQL procedure successfully completed.

 

C##DONO@pdb1>

SELECT command,

rule_set_name,

object_owner,

object_name,

enabled

FROM   dvsys.dba_dv_command_rule

WHERE  command = ‘DROP TABLE’;

no rows selected

 

E agora é possível dropar a tabela novamente:

SCOTT@pdb1> drop table TB_EXEMPLO;

Table dropped.

Ilustrado o uso de Command Rules, iremos agora finalizar nosso exemplo inicial, pois já dispomos de toda ferramenta necessária. Então, mãos à obra.

Vamos criar a Command Rule associada a Ruleset RS_SCOTT_SYS_SYSTEM_SQLPLUS_UNLIMTS impedindo conexões (comando SQL: CONNECT).

C##DONO@pdb1> BEGIN

DBMS_MACADM.CREATE_COMMAND_RULE(command       => ‘CONNECT’,

rule_set_name => ‘RS_SCOTT_SYS_SYSTEM_SQLPLUS_UNLIMTS’,

object_owner  => ‘%’,

object_name   => ‘%’,

enabled       => DBMS_MACUTL.G_YES);

END;

/

PL/SQL procedure successfully completed.

 

Agora basta testar (as falhas ficarão registradas na tabela system.ruleset_fail):

1 – Scott, Sys e System, devem se conectar em qualquer aplicação.

2 – USER_UNLIM_TS, deve se conectar apenas no SQL*Plus, já que possui UNLIMITED TABLESPACE.

3- USER_LIM_TS, deve se conectar em qualquer aplicação, visto que não possui UNLIMITED TABLESPACE.

Testando no SQL Developer

sql-developer2

sql-developer3

sql-developer4

Conferindo os registros na nossa tabela:

SYSTEM@pdb1> select * from ruleset_fail;

 

USERNAME        DATA_HORA

————— ——————-

USER_UNLIM_TS   22/05/2016 20:26:28

 

E finalizando, vamos checar se os users USER_LIM_TS e USER_UNLIM_TS conseguem se conectar via SQL*Plus:

SYSTEM@pdb1> connect USER_UNLIM_TS/oracle@pdb1

Connected.

USER_UNLIM_TS@pdb1> connect USER_LIM_TS/oracle@pdb1

Connected.

USER_LIM_TS@pdb1> conn scott/tiger@pdb1

Connected.

 

CONCLUSÃO

Chegamos ao final do nosso exemplo passando por Factor, Rule, Ruleset e Command Rule. Vimos que as Command Rule são peças poderosas que visa impedir que um comando seja executado com sucesso. Elas se baseiam nas Ruleset que verificam as diversas Rules impostas e podem nos alertar ou registrar sucessos ou falhas.

Assim, conseguimos conhecer mais algumas características do ODV e no próximo artigo exploraremos um pouco dos Realms, visando impedir que super users consigam consultar/alterar determinados schemas/objetos.

 

REFERÊNCIA

[1] http://www.oracle.com/technetwork/pt/articles/idm/seguranca-oracle-database-vault-2999070-ptb.html

[2] https://docs.oracle.com/database/121/DVADM/cfgfact.htm

[3] https://docs.oracle.com/database/121/DVADM/apis_factors.htm#DVADM70570

[4] http://docs.oracle.com/database/121/DBSEG/dr_ir.htm#DBSEG99925

[5] https://docs.oracle.com/database/121/DVADM/cfrulset.htm

[6] https://docs.oracle.com/database/121/DVADM/apis_rule_sets.htm#CHDDDEII

[7] https://docs.oracle.com/database/121/DVADM/apis_dbms_macutl.htm#DVADM71518

[8] https://docs.oracle.com/database/121/DVADM/cfrulset.htm#DVADM70172

[9] https://docs.oracle.com/database/121/DVADM/cfcmdaut.htm#DVADM70208