SetGID and SetUID Shell Scripts October 22, 2007

Under most UNIX implementations, shell scripts cannot be made to setuid. This is both a security precaution and hindrance. For example, let’s say that you have the need to allow your users to RSYNC content from one server (a development server), that they have an account on, to a production server that they don’t have direct access to. So here is a script called rolldata.sh that accomplishes just that:

#!/usr/bin/sh
/opt/local/bin/rsync -avP -e "/bin/ssh -i /sshdir/rolluser-id_rsa" \
"/localdir/" "rolluser@securehost.domain.com:/remotedir/"

Now, this works, but it leaves a few glaring security problems:

1) The user needs to be able to READ the /sshdir/rolluser-id_rsa file in order for the ssh command to work without a password. If they can read this file, then they could just SSH right to the box and do lots of other stuff if they know how, which is not very hard.

2) Since ssh will only allow identity files to be -r——– in permission, every user who needs to be able to execute the above script must have their own rolluser-id_rsa file somewhere (and their own personal script) that can read by them and nobody else.

So, the obvious solution I thought would be to setgid the shell script, make it owned by root and make it only executable like:

---s--s---   1 root     cit          229 Oct 22 19:32 rolldata.sh
-r--------   1 root     cit          883 Oct 22 18:59 rolluser-id_rsa

Therefore, only root would be able read the contents of the rolldata.sh script and the rolluser-id_rsa file, but any user in group cit could run it. The only problem is that most UNIX kernels (including Solaris) don’t allow you to setuid/gid a shell script!

However, this restriction obviously does not apply to compiled code, so this is what I came up with to get around this restriction. Create a C file called rolldata.c that looks like:

#include <unistd.h>;
main () {
setuid(0);
int ret;
ret = execl ("/mydir/rolldata.sh", "/mydir/rolldata.sh", "-1", (char *)0);
}

This program simply runs a pre-defined script as the user root, by way of the setuid(0); call. After compiling this (gcc ./rolldata.c), call this file rolldata and set the permissions with setgid like:

---s--s---   1 root     cit         6656 Oct 22 20:05 rolldata
---x------   1 root     cit          229 Oct 22 19:32 rolldata.sh
-r--------   1 root     cit          883 Oct 22 18:59 rolluser-id_rsa

Now, any user in group cit can now execute the rolldata.sh script by way of running rolldata, but cannot read the script or the ssh identity key!

[user@computer mydir]$ ./rolldata
 
building file list ... 
109 files to consider
 
wrote 2998 bytes  read 20 bytes  6036.00 bytes/sec
total size is 2532169264  speedup is 839022.29
 
[user@computer mydir]$ cat ./rolluser-id_rsa 
cat: cannot open ./rolluser-id_rsa
Leave a Reply