Wednesday, December 9, 2009

Performing a chmod on a symbolic link

On HP-UX, symbolic links cannot have their permissions changed. When doing a chmod on a symbolic link, the chmod operation is performed on the file it references.

A little background information is in order. When a symbolic link is created, it sets its permissions depending on the current umask. So if you have a umask set to 027, it will create a link like this:
# umask 027
# ln -s /stand/vmunix /tmp/link1
# ls -al /tmp/link1
lrwxr-x--- 1 root sys 13 Dec 9 14:50 /tmp/link1 -> /stand/vmunix

While a very restrictive umask such as 777 will do this:
# umask 777
# ln -s /stand/vmunix /tmp/link1
# ls -la /tmp/link1
l--------- 1 root sys 13 Dec 9 14:50 /tmp/link1 -> /stand/vmunix

So what do you do if someone created a bunch of symbolic links with a umask of 000, and you have scattered symlinks that look like they're world-writable files?

The technical answer would be to ignore them. As most file operations except the link()-related apply to the file referenced by the symbolic link itself, I do not think this is a security problem. But of course, when you're being scrutinized by a security auditor, explanations like this one often don't have any merit. It's less hassle to just satisfy whatever the auditor wants, and correct these symbolic links.

The problem arises when you notice that chmod doesn't work on a symbolic link. And this isn't specific to HP-UX; Linux doesn't allow this either, but I found that FreeBSD has a "-h" option to chmod that addresses the issue. How can you fix that?

The only solution I found by looking into the ITRC forums is to delete the symlink, and re-create it with an appropriate umask. This can be done really quickly but the process won't be atomic so I can't garantee this will be completely unnoticed by your applications.

Here is a short script I've written named lchmod which will ease the operation:

#!/bin/sh
if [ "${1}" = "" -o "${2}" = "" ]
then
echo "Usage: lchmod "
return 1
fi
umask=${1}
symlink=${2}

if [ ! -h ${symlink} ]
then
echo "Symlink '${symlink}' does not exist"
return 1
fi
destination=$(/bin/ls -l ${symlink} | sed 's/.*-> //g')
umask ${umask}
rm ${symlink}
ln -s ${destination} ${symlink}


Say you've got this link:
# ls -la /tmp/link3
lrwxrwxrwx 1 root sys 13 Dec 9 14:59 /tmp/link3 -> /stand/vmunix

Simply run lchmod like this and the link will be recreated with a umask set to 027:
# lchmod 027 /tmp/link3
# ls -al /tmp/link3
lrwxr-x--- 1 root sys 13 Dec 9 15:00 /tmp/link3 -> /stand/vmunix*

O.

1 comment:

addicted said...

you should use "exit 1" instead return 1 :)

but thanks a lot mate you saved my life today :p