Tuesday, February 1, 2011

How to use PolicyKit authorization for DBus methods and properties in GLib.

Disclaimer: I'm beginner with GLib. So please forgive me if I wrote something wrong.

During my work on DBus support in matahari I have runned into need to use PolicyKit for handling authorization of users.

Methods


Checking authorization for methods is quite easy. Methods must be declared as asynchronous (maybe it is possible to use synchrnous methods as well, but I didn't manage to get it working). It can be done in DBus interface XML file with this line in the method tag:

<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>

Then this XML needs to be transformed to header file using dbus-binding-tool from package dbus-glib [1].

dbus-binding-tool --mode=glib-server 
                  --prefix=something my-object.xml 
                  > my-object-glue.h

In the generated file my-object-glue.h is array of DBusGMethodInfo structs where you can look which methods you need to implement and decrypt their prototypes from something like dbus_glib_marshal_something_NONE__STRING_POINTER.

So the method declaration will look like

gboolean interface_method(Object *object, 
                          const char *param, 
                          DBusGMethodInvocation *context);

And now the polkit authorization. This is just the important code without error handling and freeing memory.

gboolean
check_authorization(const gchar *action, GError** error, DBusGMethodInvocation *context)
{
  char *action = "org.foo.bar.action"
  GError *err = NULL;
  PolkitAuthorizationResult *result;
  // Get the subject from context
  PolkitSubject *subject = polkit_system_bus_name_new(dbus_g_method_get_sender(context));
  // Get the authority
  PolkitAuthority *authority = polkit_authority_get_sync(NULL, &err);
  // Check the authorization for the subject
  result = polkit_authority_check_authorization_sync(authority, 
              subject, action, NULL,
              POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, 
              NULL, &err);
  // Return TRUE if subject is authorized
  return polkit_authorization_result_get_is_authorized(result);
}

Properties


I haven't found any tutorial or existing code for handling properties (that's why I'm writing this), so I have to figure it out myself.

First step is to create properties in something_class_init and set functions for getting and setting properties.

static void something_class_init(SomethingClass *something_class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS(something_class);
  GParamSpec *pspec = NULL;
  GType value_type;

  g_type_class_add_private(something_class, sizeof (SomethingPrivate));

  gobject_class->set_property = something_set_property;
  gobject_class->get_property = something_get_property;

  // Do this for each property
  pspec = g_param_spec_string("Name", "Nick", "Description of the property", NULL, G_PARAM_WRITABLE);
  g_object_class_install_property(gobject_class, /*property_id*/ 42, pspec);

  dbus_g_object_type_install_info(SOMETHING_TYPE, &dbus_glib_something_object_info);
}

Getter and setter are simple:

static void
something_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
  switch (property_id)
  {
    case 42:
      g_value_set_string(value, "42");
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

static void
something_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
  switch (property_id)
  {
    case 42:
      my_private_value = g_value_get_string(value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

And finally the PolicyKit. Add this code to DBus XML interface:

<interface name="org.freedesktop.DBus.Properties">
  <method name="Get">
    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
    <arg name="interface" direction="in" type="s"/>
    <arg name="property" direction="in" type="s"/>
    <arg name="value" direction="out" type="v"/>
  </method>
  <method name="Set">
    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
    <arg name="interface" direction="in" type="s"/>
    <arg name="property" direction="in" type="s"/>
    <arg name="value" direction="in" type="v"/>
  </method>
</interface>

And this methods to the implementation of methods:

gboolean
something_get(Something* something, const char *interface, const char *name, DBusGMethodInvocation *context)
{
  GError* error = NULL;
  char *action = malloc((strlen(interface) + strlen(name) + 2) * sizeof(char));
  sprintf(action, "%s.%s", interface, name);
  if (!check_authorization(action, &error, context))
  {
    dbus_g_method_return_error(context, error);
    free(action);
    return FALSE;
  }
  free(action);

  GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(something), name);
  GValue value = {0, };
  g_value_init(&value, spec->value_type);
  g_object_get_property(G_OBJECT(something), name, &value);
  dbus_g_method_return(context, &value);
  return TRUE;
}

gboolean
something_set(Something *something, const char *interface, const char *name, GValue *value, DBusGMethodInvocation *context)
{
  GError* error = NULL;
  char *action = malloc((strlen(interface) + strlen(name) + 2) * sizeof(char));
  sprintf(action, "%s.%s", interface, name);
  if (!check_authorization(action, &error, context))
  {
    dbus_g_method_return_error(context, error);
    free(action);
    return FALSE;
  }
  free(action);

  g_object_set_property(G_OBJECT(matahari), name, value);
  return TRUE;
}

And that should be all you need for authorization via polkit. Please let me know if I screwed up something.

[1] http://dbus.freedesktop.org/doc/dbus-tutorial.html#glib-server

No comments:

Post a Comment