Oracle - spawn extproc by listener or database
We can use EXTPROC to invoke an external programs, like C.
There is an simple example about setting:
How to Dynamically Execute a SQL Script from a Stored Procedure? (Doc ID 341586.1)
Be aware that the IPC listener and extproc_connection_data is not mandatory, without IPC listener, a local extproc process will be started to communicate with server process. The C code I am using can be found:
How To Run Extproc Agent Under A Less Privileged Account (Doc ID 1204853.1)
SQL> create or replace directory libdir as '$ORACLE_HOME/lib'; Directory created. SQL> SQL> CREATE or replace LIBRARY shell_lib is 'shell.so' in libdir; 2 / Library created. SQL> CREATE OR REPLACE PACKAGE BD_UTIL as 2 FUNCTION shell_ejecucion(comando in char) return pls_integer; 3 END; 4 / Package created. SQL> SQL> CREATE OR REPLACE PACKAGE BODY BD_UTIL is 2 FUNCTION shell_ejecucion(comando in char) return pls_integer 3 as language C 4 name "sh" 5 library shell_lib 6 parameters (comando string,return int); 7 END bd_util; 8 / Package body created. SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / PL/SQL procedure successfully completed. SQL> host ls -l /tmp/test.txt -rw-r--r--. 1 oracle oinstall 29 Feb 25 22:10 /tmp/test.txt -- with IPC listener, and extproc_connection_data -- we can see there is a EXTPROC being spawned by Listener, and LOCAL=NO # ps -ef|grep extproc oracle 2732 1 0 22:41 ? 00:00:00 /opt/oracle/product/12.1/dbhome_1/bin/extprocPLSExtProc (LOCAL=NO) root 2741 26893 0 22:41 pts/2 00:00:00 grep --color=auto extproc -- after remove entry in tnsnames.ora -- a little delay, the process will be spawned after timeout, with LOCAL=YES # ps -ef|grep extproc oracle 2887 1 0 22:42 ? 00:00:00 /opt/oracle/product/12.1/dbhome_1/bin/extproc (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=BEQ)))
Usually, the EXTPROC will be folked in “oracle” which is the owner of database and listener.
For security consideration, we normally want to extproc can be spawned in the less privilege users.
So, for the situation above, there are two different way to configure this.
If the process is spawned by database, we can use credential.
SQL> begin 2 dbms_credential.create_credential( 3 credential_name => 'run_marc', 4 username => 'marc', 5 password => '123456' 6 ); 7 end; 8 / PL/SQL procedure successfully completed. SQL> CREATE or replace LIBRARY shell_lib is 'shell.so' in libdir credential run_marc; 2 / Library created. SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / 0 PL/SQL procedure successfully completed. SQL> host ls -l /tmp/test.txt -rw-rw-rw-. 1 marc users 29 Feb 25 22:52 /tmp/test.txt
We change the owner and permission of “/tmp/test.txt”, let user marc cannot write it.
# chown oracle.oinstall /tmp/test.txt # chmod 644 /tmp/test.txt SQL> set serveroutput on SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / 256 -- an error reported. PL/SQL procedure successfully completed.
Unfortunately, credential is not working if the extproc is spawned by listener.
I add extproc_connection_data in tnsnames.ora
SQL> host rm -rf /tmp/test.txt SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / 0 PL/SQL procedure successfully completed. -- the file is still generated by oracle SQL> host ls -l /tmp/test.txt -rw-r--r--. 1 oracle oinstall 29 Feb 25 23:13 /tmp/test.txt
How To Run Extproc Agent Under A Less Privileged Account (Doc ID 1204853.1)
gives a way but not working, even the process is spawned as marc
[root@testenv bin]# ps -ef|grep extproc marc 5936 1 0 23:24 ? 00:00:00 /opt/oracle/product/12.1/dbhome_1/bin/extprocPLSExtProc (LOCAL=NO) -- rerun the test code. -- the file is still generated by oracle, even the credential is to marc SQL> host ls -l /tmp/test.txt -rw-r--r--. 1 oracle oinstall 29 Feb 25 23:25 /tmp/test.txt
Due to the extproc which spawned by listener will inherent the priliveges of listener. So, we need to move IPC listener to the diffenent user.
$ whoami marc $ . oraenv ORACLE_SID = [o12102] ? o12102 The Oracle base remains unchanged with value /opt/oracle $ export TNS_ADMIN=/home/marc/admin $ lsnrctl start extproc_listener LSNRCTL for Linux: Version 12.1.0.2.0 - Production on 25-FEB-2019 23:33:02 Copyright (c) 1991, 2014, Oracle. All rights reserved. Starting /opt/oracle/product/12.1/dbhome_1/bin/tnslsnr: please wait... TNSLSNR for Linux: Version 12.1.0.2.0 - Production System parameter file is /home/marc/admin/listener.ora Log messages written to /opt/oracle/diag/tnslsnr/testenv/extproc_listener/alert/log.xml Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1))) Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1))) STATUS of the LISTENER ------------------------ Alias extproc_listener Version TNSLSNR for Linux: Version 12.1.0.2.0 - Production Start Date 25-FEB-2019 23:33:02 Uptime 0 days 0 hr. 0 min. 0 sec Trace Level off Security ON: Local OS Authentication SNMP OFF Listener Parameter File /home/marc/admin/listener.ora Listener Log File /opt/oracle/diag/tnslsnr/testenv/extproc_listener/alert/log.xml Listening Endpoints Summary... (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1))) Services Summary... Service "PLSExtProc" has 1 instance(s). Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service... The command completed successfully $ ps -ef|grep tns root 22 2 0 Feb24 ? 00:00:00 [netns] oracle 2353 1 0 22:39 ? 00:00:00 /opt/oracle/product/12.1/dbhome_1/bin/tnslsnr LISTENER -inherit marc 6582 1 0 23:33 ? 00:00:00 /opt/oracle/product/12.1/dbhome_1/bin/tnslsnr extproc_listener -inherit -- change tnsnames.ora: extproc_connection_data = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))) (CONNECT_DATA = (SID = PLSExtProc) (PRESENTATION = RO)) ) SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / PL/SQL procedure successfully completed. SQL> host ls -l /tmp/test.txt -rw-r--r--. 1 marc users 29 Feb 25 23:36 /tmp/test.txt
Be careful, from Oracle database version 12.2.0.1 and above, the “extproc” agent no longer resolves the EXTPROC_CONNECTION_DATA entry in tnsnames.ora for security reaons and the agent gets spawned by the database itself.
Configuring Listener To Spawn EXTPROC Agent Is Not Working in 12.2, EXTPROC_CONNECTION_DATA Is Not Getting Resolved. (Doc ID 2326592.1)
We have to use agent after 12.2 to use the listener to spawn an EXTPROC
$ cat listener.ora LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC2)) (ADDRESS = (PROTOCOL = TCP)(HOST = testenv)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCPS)(HOST = testenv)(PORT = 2484)) ) ) SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (SID_NAME = PLSExtProc) (ORACLE_HOME=/opt/oracle/product/12.2/dbhome_1) (PROGRAM=/opt/oracle/product/12.2/dbhome_1/bin/extproc) (ENVS="EXTPROC_DLLS=ANY", "LD_LIBRARY_PATH=/home/oracle/lib") ) ) $ cat tnsnames.ora extproc_connection_data = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC2))) (CONNECT_DATA = (SID = PLSExtProc) (PRESENTATION = RO)) )
This time, I put shell.so to /home/oracle/lib
SQL> create or replace directory libdir as '/home/oracle/lib'; Directory created. SQL> SQL> CREATE or replace LIBRARY shell_lib is 'shell.so' in libdir; 2 / Library created. SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / declare * ERROR at line 1: ORA-28595: Extproc agent : Invalid DLL Path ORA-06512: at "SYS.BD_UTIL", line 2 ORA-06512: at line 4
Due to /home/oracle/lib is not on the LD_LIBRARY_PATH in .bash_profile, so, the EXTPROC spawned by database cannot find this one in 12.2
We can follow DOC 1109144.1 to set
How to Execute Both 32-bit and 64-bit External Procedures Within The Same 11.2 and Above Database (Doc ID 1109144.1)
Be aware that the tnsname cannot be extproc_connection_data
SQL> DATABASE LINK agent_link USING 'extproc_connection_data'; Database link created. SQL> CREATE or replace LIBRARY shell_lib is 'shell.so' in libdir AGENT 'agent_link'; Library created. SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / declare * ERROR at line 1: ORA-28595: Extproc agent : Invalid DLL Path ORA-06512: at "SYS.BD_UTIL", line 2 ORA-06512: at line 4 -- rename the tnsname to extproc_connection_data2 SQL> DROP DATABASE LINK agent_link; Database link dropped. SQL> CREATE DATABASE LINK agent_link USING 'extproc_connection_data2'; Database link created. SQL> CREATE or replace LIBRARY shell_lib is 'shell.so' in libdir AGENT 'agent_link'; Library created. SQL> declare 2 retorno binary_integer; 3 begin 4 retorno := bd_util.shell_ejecucion('/bin/date > /tmp/test.txt'); 5 dbms_output.put_line(retorno); 6 end; 7 / 0 PL/SQL procedure successfully completed.
Looks like it is impossible to disable database to spawn an EXTPROC. So, in order to control the behavior of EXTPROC
- always use listener to spawn EXTPROC
- use credential to restrict the EXTPROC the database spawned in the less privileged user.
- do not store .so file in the LD_LIBRARY_PATH, using listener to locate .so
- use ENVS=”EXTPROC_DLLS=ONLY:/home/oracle/lib/shell.so” in listener to restrict the .so the user can use.
- never give create library and grant the use of library to end user directly, use procedure to encapsulate it, and only give the execute privilege on this procedure to end user.