KAuth in use: privileged KCModules

Hello people,

Today I want to write a more extensive introduction and how-to, with the KAuth API being finalized, on writing a privileged KCModule. Please note that this information will be soon on Techbase as well.

So, before we start, why should you read this post?

  • Because you’re a developer who needs to develop an application requiring high privileges
  • Because you’re a developer who needs to develop an application requiring authorization
  • Because you’re a curious developer
  • Because you’re a curious user

So, defined the target audience, here we go :)

Back in the KDE3 days, we had an “Administration mode” button, that basically removed the kcm widget, XEmbedded another widget running as root into the kcmshell, and that’s it. Pretty ugly and unsecure, but still working. Most of all, the developer did not know what was happening under the hood, since what happened is in no way different than issuing right now “sudo kcmshell4 <module>”. In KDE4(.4), things are different.

We do not have an “Administrator mode” button, neither widgets being embedded. We do have small helper applications running in the background with high privileges, performing the requested action only if explicitely authorized by the underlying policy system. And we have an easy API, that extends throughout KDEUI and KCModule to make things transparent, customizable, yet extremely easy to the developer.

So let’s start. We are trying to create “fookcm”, a kcmodule for writing to /etc/foo the lines “foo”. So important. As you, experienced developer should know, the first lines will look like this:

K_PLUGIN_FACTORY(FooKCMFactory, registerPlugin<FooKCM>();) K_EXPORT_PLUGIN(FooKCMFactory("fookcm"))
FooKCM::FooKCM(QWidget *parent, const QVariantList &)
: KCModule(FooKCMFactory::componentData(), parent)
{
KAboutData *about =
new KAboutData(I18N_NOOP("fookcm"), 0, ki18n("Da foo KCM"),
0, KLocalizedString(), KAboutData::License_GPL, ki18n("(c) foo"));

Remember well the name we gave to the module (fookcm), we will need it later. So you wonder what we need to add to the constructor to make the magic happen. A single line:

setNeedsAuthorization(true);

What does that do? A variety of things:

  • It maps the KCM to the action org.kde.kcontrol.<modulename>.save. So our save action is named org.kde.kcontrol.fookcm.save. Keep this in mind, we’ll need it later
  • It turns kcmshell/and or systemsettings Ok and Apply buttons authAction property to the newly mapped action, so that buttons reflect the action state (eg: they are disabled, show a key instead of the icon, etc)
  • It sets some additional visual feedback to the kcmodule.

After this line, your header (obviously after the action install phase, which I’ll show you later on) when you are required to authenticate will look something like this:

This is how the header will look like

Neat, isn’t it? But to achieve this, a small thing needs to be done. We need to create an action file. So let’s just open your favorite editor on fookcm.actions (the “actions” extension is mandatory to let scripty translate your file successfully) and let’s write this down:

[Domain]
Name=Foo configuration
Icon=foo

[org.kde.kcontrol.fookcm.save]
Name=Save the foo
Description=Authentication is required for saving the foo
Policy=auth_admin

It is pretty straightforward, but let’s see this in detail. The structure is a standard ini file. You can define an unlimited number of actions in each actions file, provided they belong to the same domain (in this case org.kde.kcontrol.fookcm). The group name is the action name, Name is what will appear when you will browse the PolicyKit Authorization module, and Description is the message that will appear in the auth dialog. Policy is mandatory as well, and defines the default policy. It can be yes, no, auth_self or auth_admin. There is also an optional field, Persistence, that will define the default persistence of the authentication. There is also a special “Domain” group: this defines some additional settings that will affect all the actions under the domain you’re configuring. You can set the Name, that will appear as the folder name in the PolicyKit authorization module, and an icon that will be shown both in the authorization module and dialog. The whole Domain group is advised for better feedback, but not mandatory.

Done that, we need a small magic in our CMakeLists.txt. We need to register the actions like this:

kde4_install_auth_actions(org.kde.kcontrol.fookcm fookcm.actions)

Straightforward as well. Just define the helper ID (which basically is the action domain) and the action file. This will take care of generating and installing the actions for you.

So now we have our beautiful kcmodule with all bells and whistles and our save() function will be called just if the user is authorized to save the module. But we’re missing one step, huh? We actually want to save stuff in /etc, so we need high privileges. Then it’s time to create our helper.

Creating an helper is really easy with KAuth, since with the tools the library provides you, you won’t even know another application is running in the background. The helper has also remote debugging, so if you issue a qDebug() from the helper application, you will see the output in your main application. So you really don’t have to care about the fact it’s separate. But let’s get to the point.

Let’s create a new class for the helper, let’s name it FooHelper. The header file would look like this:

#include <kauth.h>

using namespace KAuth;

class FooHelper : public QObject
{
Q_OBJECT

public slots:
ActionReply save(const QVariantMap &map);
};

There are some small but important details here. For every action our helper implements, we need to define a slot that has EXACTLY the same name as the action itself (so save), returns an ActionReply and accepts a QVariantMap. It’s EXTREMELY important that the return type is an ActionReply and not a KAuth::ActionReply, eg you have to use using namespace KAuth. Short explaination: otherwise things won’t work. Long explaination: we’re using QMetaObject::invokeMethod for calling the actions, and it is extremely picky about namespaces.

To understand better the arguments, let’s have a look at how to implement save() in our kcm

void FooKCM::save() {
KAuth::Action *action = authAction();
QVariantMap args;
args["path"] = "/etc/foo";
args["contents"] = "foooooo";
action->setArguments(args);
KAuth::ActionReply reply = action->execute();

I did not write the followup, where you actually check the result of the reply and act accordingly. So what we do here is grabbing the authAction of our KCModule, attaching some arguments, and executing it synchronously. I think you can now realize the reason for the arguments of the save function of the helper. ActionReply can also carry a QVariantMap, so you have a way of returning data back from the helper. In any case, returning ActionReply::SuccessReply will just tell the main application that everything went well.

Almost done (I’m just skipping the save implementation in the helper, as it is really stupid). We need two more small things, one in the helper implementation (our foohelper.cpp) and one of course in cmake. The first one is a macro, and it goes like this:

KDE4_AUTH_HELPER_MAIN(“org.kde.kcontrol.fookcm”, FooHelper)

What does it do? It accepts our helper ID (so the actions’ domain), and the class which represents the helper itself. Under the hood, it creates a main function for the helper instantiating and registering your class. CMake stuff is pretty straightforward as well:

kde4_add_executable(fookcmhelper foohelper.cpp)
target_link_libraries(fookcmhelper ${KDE4_KDECORE_LIBS})
install(TARGETS fookcmhelper DESTINATION ${LIBEXEC_INSTALL_DIR})
kde4_install_auth_helper_files(fookcmhelper org.kde.kcontrol.fookcm root)

It adds a new executable, that has to be installed (this is mandatory) in LIBEXEC_INSTALL_DIR. Installing it elsewhere will make things fail. The additional macro you surely spotted takes as arguments the target, the helper ID associated to it, and the user under which the helper will run on. Yes, you can also create helpers not running as root! This can be extremely useful to improve security in some cases. Under the hood, this macro generates and installs a lot of files. It creates a dbus policy for your helper, so that it will be able to be exposed on the system bus, and an activation file, to make it start up automatically. As you probably have realized by now, the helper ID is nothing else but the service name of the helper. But as you noticed, you (as a developer) do not even know that DBus is running under the hood, as you don’t see/have to write a single line of DBus related code.

That’s it. You now have a nicely integrated, rock-solid, clean and extremely secure KCModule that saves data with high privileges. I’m not covering why it is secure (you can read about Polkit and/or Authorization Services if you are interested) as this post is already long enough.

Also, what I’ve shown you is just a very basic usage of KAuth. You can, for example, execute actions asynchronously, and stream progress of an action, and even data from your helper. To learn more about KAuth, go now to the KDE API Documentation for a complete overview of KAuth and its capabilities, and be sure to have a look at the KAuth namespace documentation, which features a really extensive introduction/tutorial that will be reformatted as well to be included in Techbase.

Now, before you start yelling this is the best thing you’ve ever seen, there are also some caveats you might want to be aware of and, of course, avoid:

  • If you are using a non-standard type in your QVariant, Q_DECLARE_METATYPE is not enough. Data is streamed through the bus in a binary datastream, so you have to implement custom operator<< and operator>> of QDataStream for your class. Please read QDataStream documentation for more details about this.
  • KAuth needs to install some files in privileged and specific locations. In particular, if you are installing stuff in a different prefix than /usr, things will not work out of the box. You have to tweak DBus configuration and move PolicyKit files to /usr (even though the PolicyKit guys are fixing that) to make things work. But be careful in editing DBus configuration as you might end up screwing your system. More details on that will follow on the TechBase tutorial. If you are installing KDE from packages you can simply ignore this, or blame your packager.
  • I noticed (at least here) that sometimes DBus does not recognize immediately new policies/autostart services. Before screaming about things not working, you might want to try restarting DBus first.

That’s all, folks. Hope you enjoyed this, please give me some feedback before I push a refined version of this tutorial on TechBase.

About these ads

~ by Dario on 15 September, 2009.

8 Responses to “KAuth in use: privileged KCModules”

  1. Great!! I’m happy to see my work goes on :D

  2. Maybe I didn’t understand correctly, but did you say you need a privileged application running in the background all the time, for every KCM module?

    It doesn’t sound right.

  3. You didn’t understand right. The helper is activated by DBus, so it starts up only when it’s needed, and KAuth shuts it down after 10 seconds of inactivity. Maybe I should have made this clearer in the post. Also, please note that even if the helper runs privileged, you need to be authorized to make it do something, so it’s completely secure

  4. Interesting stuff! This seams like a great improvement to KDE, and I like what I’ve seen here wrt to the API.

    One question: does the helper application also have access to the X11 display, or is it a “QtCore” application?

  5. Nope, it’s a core application :)

  6. Great work!
    As far as I can understand, the whole point of PolicyKit and this library is to minimize the amount of root-privileged code. Do you know how secure is this framework compared to what other OSes use? (just wonder)
    BTW when viewing this blog post aggregated on Planet KDE in Safari 4, the lines with K_PLUGIN_FACTORY and K_EXPORT_PLUGIN appear far far away on the right – please, look into this. (not the first time embedded code snippets behaves like this in safari)

  7. A while ago I was trying to figure out how to hack together some method to get a few GTK admin apps (mandriva’s draktool’s) for my distro to display / launch from kde4s systemsettings gui…

    Is there a simple way to do this?

    Perhaps a generic kcmodule that could be called on by different .desktop files to launch thier specific app??

    I’m not a coder, but any help would be appreciated!

    mypclinuxos.com/forum/index.php?topic=2670.0

  8. I’m experimenting with KAuth using Kubuntu and the KDE SC 4.4 Beta 2 packages, but CMake is failing on the kde4_install_auth_actions macro complaining: “Could not find module FindPolkitQt.cmake or a configuration file for package PolkitQt.” I have libpolkit-qt-dev and libpolkit-qt0 installed so I’m at a loss. My CMake skills are lacking. Any ideas?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: