Monday, January 25, 2010

Update without root password?

In the old days of openSUSE 10.x I used sudo and its configuration to install updates and new software in GUI without the most-annoying (I know, secure) need to enter password of root. Since version 11, this is no longer possible.

Since I am too lazy to enter root password (I've already logged-in, it's me, right?), I spent quite some time figuring out how to solve it. And I finally did.

In KDE the kupdateapplet is used to check for updates. In default installation kupdate applet uses PackageKit backend. To make things more complicated, PackageKit uses another framework: PolicyKit. (From What's PackageKit?: "The primary design goal is to unify all the software graphical tools used in different distributions, and use some of the latest technology like PolicyKit to make the process suck less." - WTF?! I wanna sudo!).

How does it work? When the update applet issue update command to PackageKit, PackageKit asks PolicyKit for permission. PolicyKit checks it's configuration and either allows it, denies it, or ask for authentication (enter password). If you want to know how it works, read the PolicyKit and PackageKit documentation.

What we need to do is configure PolicyKit in a way that it gives permission to update action of PackageKit.

Default permissions are stored in file /etc/polkit-default-privs.standard (or /etc/polkit-default-privs.restrictive according to value of POLKIT_DEFAULT_PRIVS in /etc/sysconfig/security). The file have following syntax:

action_id permissions

or (one line)

action_id  perm_any_user:perm_user_not_on_the_active_console:perm_user_on_the_active_console

In first case, same permission are applied. In second case different permission are used for user on active console, etc.

Possible permissions are:

  • yes allowed
  • no denied
  • auth_admin need root password
  • auth_admin_keep same as above, but will not require password again for some time
  • auth_user need user password
  • auth_user_keep same as above, but will not require password again for some time

Action IDs and their descriptions can be found in files located in /usr/share/polkit-1/actions/. The one we're looking for is: org.freedesktop.packagekit.system-update.

In our case, we want that user on active console don't have to enter root password for installation of updates. So we add following rule to /etc/polkit-default-privs.local (do not modify polkit-default-privs.standard and polkit-default-privs.restrictive since they may be overwritten during some update!):

org.freedesktop.packagekit.system-update auth_admin:auth_admin:yes

And we need to install the privileges to the system (without following command, it won't work):

/sbin/set_polkit_default_privs

Yes! Now you should be able to update your system without the need to enter root password.

Same way can be used for package installation, removal, etc. You can also ask user to enter his password instead of root password. The only thing I miss is Vista-like way: make user explicitly click "I know what I'm doing" without the need to enter password. Why? Because this way some script can install a package on your system without you even knowing it (If you have passwordless installation allowed for user on active console and the user runs the script without knowing what it exactly does. But I didn't test it, maybe it doesn't work this way? Maybe there's some check?)

Special thanks to Ludwig Nussel for pointing me to /sbin/set_polkit_default_privs.

Monday, November 9, 2009

OpenSUSE without the mess

I've just learned out, that openSUSE zypp has wery useful configuration file /etc/zypp/zypp.conf, which contains crucial option solver.onlyRequires. If you set it to True, only really needed dependencies will be installed (of course you can install other packages manually).

This should be also possible to set during the installation. Just need to set the value from console before the libzypp is initialize - during first few screens of installation. But don't forget that it must be set after reboot/kexec as well so it is also valid for 2nd stage of installation and installed system.

Thursday, February 26, 2009

RTFM!

Everybody knows. It's written in all programming languages textbooks (I hope). Always check function return values. As you learn more and more, you learn that there are two types of functions: those that need to be checked (I'll refer to them as important functions) and other functions.

Even though it is sometimes discutable (and depending of lazyness and carelessness of specific developer) which group a function belongs to, there are many functions where no such discussion is needed.

One example of important function is EVP_VerifyFinal() which is part of openSSL library. The function is used as part of signature verification, so (hopefully) only complete idiot would dare not to check the return value.

Unfortunatelly the function is so complicated, that it can return three possible values: verification success, verification fail and error. This is cleanly written in manual page. Since this part of the documentation is extreamply clear, I wonder why search for EVP_VerifyFinal on National Vulnerability Database displays 10 results of (somehow) important applications which do not check the return values of the function.

Since return values are described clearly in the documentation, I'm surprised that the return values are not (correctly) checked. The only reason I can think about so far is that the author did not read the documentation. Since the function is really important (it's part of secure connection initialization!), author should use it in a proper way. Which means - according to documentation...

Conclusion: RTFM!

Friday, February 20, 2009

NTP With Autokey Authentication

Recently I had to verify the functionality of NTP server with autokey authentication. Though I thought that it would be simple, it took me several days to find out how to configure the authentication. I found out dozens of different howtos, but none of them worked for me. So when I finally came to one that worked, I created several shell scripts that helped me to configure different kind of (unicast) NTP authentication.

The shell scripts are paired: setup_server_XYZ.sh and setup_client_XYZ.sh where XYZ is used identity scheme. MV identity scheme seems not to work on sles9.The usage of the scripts is:

  1. Edit PASS/SPASS/CPASS variables in both client and server script! These are password for generated keys
  2. Edit SERVER variable in setup_server_XYZ.sh - that is the server which will your server use for own synchronization.
  3. Copy setup_server_XYZ.sh to the ntp server.
  4. Run setup_server_XYZ.sh on the server. If XYZ is mv, script takes one argument N, which is number of client keys to generate + 1 (N-1 clients will be able to use the server).
  5. Copy setup_client_XYZ.sh to the client.
  6. Run setup_client_XYZ.sh . If XYZ is mv, script takes additional argument X which is client id - number between 1 and N-1 - which specifies which key client will use. The script will ask for server root's password twice - first time to identify the key to download from the server, and the second time to actually download the key.
  7. Make sure that both server and client are not configured to run in chroot (or copy/move /etc/ntp/ and /etc/ntp.conf to appropriate chroot directory).
  8. Start ntpd (manually or with initscript) on server and clients. It takes some time to establish the link between client and server (approximately 15 minutes).

When ntp is set up and running, the status can be checked with several commands:

  • ntpq -p - prints a list of server the host has configured as its NTP servers. If there's a star '*' before the server name, host uses the server for synchronization.
  • ntp -c as - print list of associations - including authentication status. Following output shows successfully authenticated association:
ind assID status  conf reach auth condition  last_event cnt
===========================================================
1 26132   f694   yes   yes   ok  sys.peer   reachable   9

The scripts have following limitations:

  • Server is configured to use one server - which is not a good idea - it should use more than one (same for clients). To fix this just add more server lines to /etc/ntp.conf.
  • Usage of /dev/urandom is not the best one as well, /dev/random (or file generated by command openssl rand) should probably be used for maximal security.
  • Configuration is saved to /etc but ntpd usually run in chroot.
  • The script setup_server_mv.sh always endlessly loops while generating client keys - seems to continuously reject duplicate keys - this is probably not problem in the script. So, try to use it but don't be surprised ;-)

setup_server_gq.sh:

#!/bin/bash

PASS=lala
SERVER=ntp2.suse.de

rm -fr /etc/ntp
mkdir -p /etc/ntp

cat <<> /etc/ntp.conf
server $SERVER
keysdir /etc/ntp
crypto pw $PASS randfile /dev/urandom
logfile /var/log/ntp
logconfig =all
EOF

cd /etc/ntp
RANDFILE=/dev/urandom ntp-keygen -T -G -p $PASS

echo server is set up. Start it with \'ntpd -u ntp [-d]\'

setup_client_gq.sh:

#!/bin/bash

PASS=lala
ping -c1 "$1" > /dev/null
if [ $? -ne 0 -o $# -ne 1 ] ; then
  echo "Needs one argument - ntp server address"
  exit 1
fi

rm -fr /etc/ntp
mkdir -p /etc/ntp

cat <<> /etc/ntp.conf
server $1 autokey
crypto pw $PASS randfile /dev/urandom
logfile /var/log/ntp
logconfig =all
keysdir /etc/ntp
EOF

cd /etc/ntp
RANDFILE=/dev/urandom ntp-keygen -H -p $PASS
KEY="`ssh root@$1 ls /etc/ntp/ntpkey_GQpar_\* | sed 's,.*/,,'`"

if [ "x$KEY" = "x" ] ; then
  echo Error while fetching key name
  exit 1
fi

scp root@$1:/etc/ntp/$KEY .
LINK="`echo $KEY | sed 's/^ntpkey_GQpar_\(.*\)\.[0-9]\+$/ntpkey_gp_\1/'`"
ln -s $KEY $LINK

echo client is set up. Start it with \'ntpd -u ntp [-d]\'

setup_server_iff.sh:

#!/bin/bash

SPASS=lala
CPASS=alal
SERVER=ntp2.suse.de

rm -fr /etc/ntp
mkdir -p /etc/ntp

cat <<> /etc/ntp.conf
server $SERVER
keysdir /etc/ntp
crypto pw $SPASS randfile /dev/urandom
logfile /var/log/ntp
logconfig =all
EOF

cd /etc/ntp
RANDFILE=/dev/urandom ntp-keygen -T -I -p $SPASS
RANDFILE=/dev/urandom ntp-keygen -e -q $SPASS -p $CPASS > tmp_key

echo keyname "'`head -n1 tmp_key | sed 's/^# *//'`'"
mv tmp_key `head -n1 tmp_key | sed 's/^# *//'`

echo server is set up. Start it with \'ntpd -u ntp [-d]\'

setup_client_iff.sh:

#!/bin/bash

CPASS=alal
ping -c1 "$1" > /dev/null
if [ $? -ne 0 -o $# -ne 1 ] ; then
  echo "Needs one argument - ntp server address"
  exit 1
fi

rm -fr /etc/ntp
mkdir -p /etc/ntp

cat <<> /etc/ntp.conf
server $1 autokey
crypto pw $CPASS randfile /dev/urandom
logfile /var/log/ntp
logconfig =all
keysdir /etc/ntp
EOF

cd /etc/ntp
RANDFILE=/dev/urandom ntp-keygen -H -p $CPASS
KEY="`ssh root@$1 ls /etc/ntp/ntpkey_IFFkey_\* | sed 's,.*/,,'`"

if [ "x$KEY" = "x" ] ; then
  echo Error while fetching key name
  exit 1
fi

scp root@$1:/etc/ntp/$KEY .
LINK="`echo $KEY | sed 's/^ntpkey_IFFkey_\(.*\)\.[0-9]\+$/ntpkey_iff_\1/'`"
ln -s $KEY $LINK

echo client is set up. Start it with \'ntpd -u ntp [-d]\'

setup_server_mv.sh (loops!!!):

#!/bin/bash

PASS=lala
SERVER=ntp2.suse.de

if [ "x`echo $1 | grep '^[1-9][0-9]*$'`" = "x" ] ; then
  echo Needs one argument n - number of client certificates to create + 1.
  exit 1
fi

rm -fr /etc/ntp
mkdir -p /etc/ntp

cat <<> /etc/ntp.conf
server $SERVER
keysdir /etc/ntp
crypto pw $PASS randfile /dev/urandom
logfile /var/log/ntp
logconfig =all
EOF

cd /etc/ntp
RANDFILE=/dev/urandom ntp-keygen -V $1 -p $PASS

echo server is set up. Start it with \'ntpd -u ntp [-d]\'

setup_client_mv.sh (untested, because setup_server_mv.sh loops!):

#!/bin/bash

PASS=lala
ping -c1 "$1" > /dev/null
if [ $? -ne 0 -o $# -ne 2 -o "x`echo $2 | grep '^[1-9][0-9]*$'`" = "x" ] ; then
  echo "Needs two arguments:"
  echo "  1. ntp server address"
  echo "  2. ntp client id 1..n-1 - which certificate to get."
  exit 1
fi

rm -fr /etc/ntp
mkdir -p /etc/ntp

cat <<> /etc/ntp.conf
server $1 autokey
crypto pw $PASS randfile /dev/urandom
logfile /var/log/ntp
logconfig =all
keysdir /etc/ntp
EOF

cd /etc/ntp
RANDFILE=/dev/urandom ntp-keygen -H -p $PASS
KEY="`ssh root@$1 ls /etc/ntp/ntpkey_MVkey$2_\* | sed 's,.*/,,'`"

if [ "x$KEY" = "x" ] ; then
  echo Error while fetching key name
  exit 1
fi

scp root@$1:/etc/ntp/$KEY .
LINK="`echo $KEY | sed 's/^ntpkey_MVkey[1-9][0-9]*_\(.*\)\.[0-9]\+$/ntpkey_mv_\1/'`"
ln -s $KEY $LINK

echo client is set up. Start it with \'ntpd -u ntp [-d]\'

That's all, I hope this can spare some time...

(This text has also been published on openSUSE wiki)

Wednesday, February 18, 2009

Overloading library function

Sometimes during testing (especially while testing maintenance update), I come to a problem how to test the reaction of the program to an unusual function return value (e.g. special error), data, etc. The problem is, that I need to test the unmodified program binary and that it is almost impossible to set up a situation when the error "naturally" occurs.

If the function is in a shared library, there is a simple way to force the program to use another function instead of the one from the library. The program will call the new function, which will return the specific value (unusual error I need to test).

The magic "tool" is LD_PRELOAD variable. When the program is loaded to memory, libraries specified in LD_PRELOAD are loaded before libraries needed by the program. If I have function strdup() in my custom library (loaded by LD_PRELOAD), it will be used instead of strdup() from standard C library.

So for (very simplified) example, let's say I need to test program prog correctly checks whether strdup() returns NULL (I assume that prog segfaults on NULL access): I'll write simple library libexploit which will only contains my new strdup().

exploit.c:

char *strdup(const char *s)
{
      return NULL;
}

Makefile:

all: libexploit.so.1.0

clean:
      rm -f libexploit.so.1.0 *.o

libexploit.so.1.0: exploit.o
      gcc -shared -Wl,-soname,libexploit.so.1 -o libexploit.so.1.0 exploit.o

exploit.o: exploit.c
      gcc -Wall -fPIC -c exploit.c

Now when I make the simple library, I'll get result libexploit.so.1.0 which I'll preload to the program:

LD_PRELOAD="`pwd`/libexploit.so.1.0" prog

And it is all - the program got NULL from all calls to strdup().

Simple, right? There is one catch, however. Or two.

First problem is that the original function is replaced by the new one, so you cannot call the original one (not even from your "new" function).

Second problem is that even the original library will start using your function. So if the library is using the overloaded function, it uses the new one. This may result in unexpected library behavior.

Both these problems are not (AFAIK) solvable in a easy way. The only way I've found out so far is the re-factoring of the original library in following steps:

  1. Rename the original function - e.g. from strdup() to __strdup().
  2. Replace all calls of function strdup() with calls of function __strdup() in the library source - this will ensure that the library code will always call the correct function.
  3. Create new function strdup() which will contain the testing code (and can also call the original __strdup() function).
  4. Compile the library and load it with LD_PRELOAD as described above.

Now, that's finally all.

(This text has also been published on openSUSE wiki)