Allow user without dir write permission to execute a script that creates files

In our project we have several unix scripts that trigger different processes. These scripts write logs to a particular folder 'sesslogs', create output data files in a separate directory called 'datafiles' etc. Usually L1 support team re-run these scripts . We donot want L1 support team to have write access to any of the 'Sesslogs' or 'datafiles' folder as they could accidentally delete the critical files in these folders. So we would like their account 'operator' to have no write permission on these folders but just read and execute. At the same time when they run these shell scripts using the 'operator' account, the shell scripts must be able to create the new logfiles in the 'SessLogs' folder and also write output datafiles to the 'datafiles' folder.
How can this be achieved at the same time that 'operator' cannot manually delete or create a file in the SessLogs/datafiles folder.
Please advise.

thanks
waavman

Check out setuid/setgid.
A small c program / executable with setuid set could achieve your goal.

Thanks Peasant.
We have about 50 scripts like that. Does it mean I have to create a wrapper script for each of the 50 scripts which invokes those scripts using the C executable ?
Eg: for script1.ksh I should create script1_wrapper.ksh which contains inside it

eval `cexecutablename script1.ksh`

Similarly for script2.ksh I should create script2_wrapper.ksh which contains

eval `cexecutablename script2.ksh`

and so on...

Is that a correct assumption or is there another approach which does not involve creation of 50 new wrapper scripts ?

thanks

With ksh and a tradional Unix (not Linux) you can directly have script1.ksh suid, owned by the data owner, and a shebang

#!/bin/ksh -p

--
The alternative is sudo. Every script is one entry in sudoers.

Yes the #!/usr/bin/ksh -p option will not work for me because the servers run SuSE 10.4 Linux

sudoers example

ALL ALL = (operator) NOPASSWD: /path/to/script1.ksh

Run with

sudo -u operator script1.ksh

I chose the setuid option because with the sudoers option, for every new script we add, there would be dependency on the SA's adding the script entry to the sudoers file.
With the setuid option I am able to get half way there. But I am facing this issue. I just simplified the script for asking the question in this forum. The C binary is name 'invokescript'. This script basically invokes the shell script passed as argument to it using execvp

wrapperscript.ksh invokes script.ksh as follows

#!/usr/bin/ksh
/tmp/invokescript /tmp/script.ksh

script.ksh has the following lines. Note that env.sh sets up the PATH and LD_LIBRARY_PATH environment variables fort ORACLE command line interface sqlplus 

#!/usr/bin/ksh
. <path>/env.sh
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
echo "LDD OUTPUT"
ldd `which sqlplus`
echo "SQLOUTPUT"
sqlplus -s dbuser/dbpasswd@dbname <<EOF
sqlstatement;
EOF

script.ksh, wrapperscript.ksh and invokescript are owned by user 'cdds'
invokescript C binary has SETUID bit set for owner 'cdds'.

When the owner 'cdds' runs wrapperscript.ksh I get the following output:

L

D_LIBRARY_PATH=blahblahblah:/app/oracle/lib:blahblahblah
LDD_OUTPUT
blahblahblah=>blahblahblah
libsqlplus.so=>/app/oracle/lib/libsqlplus.so (0x00002b03d8075000)
libc.so.6=>/lib64/libc.so.6 (0x00002b03.....)
blahblahblah=>blahblahblah

SQLOUTPUT
<The correct sqloutput comes here>

But when I run wrapperscript.ksh as 'cddsoper' user I get an error that can be understood by the following output:

LD_LIBRARY_PATH=blahblahblah:/app/oracle/lib:blahblahblah
LDD_OUTPUT
blahblahblah=>blahblahblah
libsqlplus.so=> not found (0x00002b03d8075000)
libc.so.6=>/lib64/libc.so.6 (0x00002b03.....)
blahblahblah=>blahblahblah

sqlplus: error when loading shared libraries: libsqlplus.so: cannot open shared object file: no such file or directory

Based on the above output the question is as follows:
Even though the LD_LIBRARY_PATH contains the same value whether we run the wrapperscript.ksh as owner 'cdds' or user 'cddsoper' and /app/oracle/lib exists in both runs, when running as 'cddsoper', ldd sqlplus cannot find libsqlplus.so even though /app/oracle/lib is in LD_LIBRARY_PATH.
As a result the call to sqlplus fails when using 'cddsoper' account.
Why is that ?

Any help would be much appreciated.

thanks

Ensure that env.sh exports the defined variables!
(Otherwise they are just internal shell variables - not environment variables.)
Check with

env | grep -w LD_LIBRARY_PATH

Yes env variables are being exported. That is why when I print LD_LIBRARY_PATH has the right value. But I read online that the reason ldd cannot find the library file even though LD_LIBRARY_PATH has the path is because when setuid binary is used, for security reasons Linux and most modern unix systems ignore LD_LIBRARY_PATH variable because of the risk that some usercould point LD_LIBRARY_PATH to some nefarious library file and use the C setuid binary to run some malicious code.
In my case

scriptwrapper.ksh contains

#!/usr/bin/ksh
setuidbinary script.ksh

script.ksh contains

#!/usr/bin/ksh
. <path>/setenv.ksh
sqlplus -s ......

Since C setuidbinary is setuid to owner cdds, when user cddsoper tries to run scriptwrapper.ksh, even though LD_LIBRARY_PATH is exported the right value by setenv.ksh, when sqplus ORACLE binary is run, loader cannot find the .so libraries used by Oracle sqlplus binary because those paths are defined in LD_LIBRARY_PATH variable which is disabled.

I even tried specifying the Dynamic library path at compile time into the C binary setuidbinary using

gcc setuidbinary.c -Wl,-rpath=/app/oracle/lib -o setuidbinary

But even that doesnot work because when I run setuidwrapper.ksh as cddsoper user, I get the same "libsqlplus.so not found" error.

Does anybody know any other workaround for this that tells the linker to look exactly in /app/oracle/lib for libsqlplus.so when user cddsoper calls the setuid binary setuidbinary even if it ignores LD_LIBRARY_PATH for security reasons ?

Much appreciated.

thanks

I would be concerned that someone will run:-

setuidbinary rm -r ~cdds

It is leaving a fairly big gap in security. The better way is to use sudo as advised by MadeInGermany.

It's not hard to manage and removes the risk of coding setuidbinary as a matter or course. Making things easy often means making it easy to make a mistake.

Robin

sudoers is owned by root, that's an administrative obstacle here.
I have understood that LD_LIBRARY_PATH is set after the suid - and that should work!
Please test with the env command (not print/echo that works for internal variables, too)!
Further, your setuidbinary.c might need a
setuid(geteuid())
.

Hello MadeInGermany,

As suggested by you i used env and here is an interesting observation
Inside testremove.ksh I added the following lines

#!/usr/bin/ksh
export LD_LIBRARY_PATH=/app/asset_control_shared/DEV1_acdev1_usl20028171/ac/lib
echo "LD_LIBRARY_PATH using echo====$LD_LIBRARY_PATH"echo "Printing LD_LIBRARY_PATH value using env begins"
env|egrep '(HISTSIZE|LD_LIBRARY_PATH)'echo "Printing LD_LIBRARY_PATH value using env end"
/app/asset_control_shared/DEV1_acdev1_usl20028171/ac/bin/waitdb.ORACLE

First I remove setuid on invoke_shellscripts binary as below (owner acdev1)

-rwxr-x--x 1 acdev1 rdgac 8612 Jan  3 11:53 invoke_shellscripts
-rwxr-x--- 1 acdev1 rdgac  526 Jan  3 18:11 testremove.ksh

Then as another user acdev2 I ran from commandline

./invoke_shellscripts ./testremove.ksh

I got the following output:

LD_LIBRARY_PATH using echo====/app/asset_control_shared/DEV1_acdev1_usl20028171/ac/lib
Printing LD_LIBRARY_PATH value using env begins
HISTSIZE=1000
LD_LIBRARY_PATH=/app/asset_control_shared/DEV1_acdev1_usl20028171/ac/lib
Printing LD_LIBRARY_PATH value using env end

Now I added setuid bit to invoke_shellscripts binary as follows

-rwsr-x--x 1 acdev1 rdgac 8612 Jan  3 11:53 invoke_shellscripts
-rwxr-x--- 1 acdev1 rdgac  526 Jan  3 18:11 testremove.ksh

Now I reran this from command line

./invoke_shellscripts ./testremove.ksh

And this time I got following output suggesting that when setuid bit is set, LD_LIBRARY_PATH is ignored. As you can see echo correctly prints the value of LD_LIBRARY_PATH set in the 1st line of the script however env doesnot have it. It shows only HISTSIZE from the env|egrep command.

LD_LIBRARY_PATH using echo====/app/asset_control_shared/DEV1_acdev1_usl20028171/ac/lib
Printing LD_LIBRARY_PATH value using env begins
HISTSIZE=1000
Printing LD_LIBRARY_PATH value using env end

testremove.ksh is the one that will be calling the 3rd party binary that uses the library libuidata.so located in /app/asset_control_shared/DEV1_acdev1_usl20028171/ac/lib. How do you think this problem can be fixed using setuid( geteuid()) ?

thanks

The setuid() in the C code might need root privilege..
In fact Google only finds setuid(0).
It must be an undocumented feature in ksh to drop LD_LIBRARY_PATH environment if ruid!=euid.
My only idea is a shebang

#!/bin/ksh -p

And hope it will change this behavier.

Hi MadeInGermany,

The shebang with the -p option does not help either.
Linux just adamantly wants to ignore LD_LIBRARY_PATH variable when the calling binary is setuid regardless of how high up the call chain it is.
So i finally abandoned the setuid option and as suggested by you ealier and rbatte1, have decided to go with the SUDOERS option which works fine.
There is no such LD_LIBRARY_PATH restriction in SUDOERS which I am guessing is because SUDOERS limits access to a limited set of users that are added in the /etc/sudoers file for that script, where as setuid gives access to that binary to anybody who has a login on that unix box and so they want to mitigate the risk.
If users field is set to ALL in /etc/sudoers for a particular script or binary, then even SUDOERS poses a similar risk level as setuid.

thanks for your help.

I think sudo is successful because it is setuid-root.
Then it can do a setuid(0) to remove restrictions, maybe along with some other magic, and finally it can setuid(user) to switch to a user and run an unrestricted exec().
If you have time, study the sudo source files (sudo.c etc.)!