## Writing polkit applications

polkit applications are privileged mechanisms using the polkit authority as a decider component. To do this, a mechanism use either the GObject API, the D-Bus API or the pkcheck command to communicate with the polkit Authority.

Note that clients normally doesn't use the polkit API directly – it is intended for privileged mechanisms. If a client needs to disable, modify or remove UI elements to e.g. convey to the user that a certain action cannot be carried out (because e.g. the user is not authorized) or authentication is needed (by e.g. displaying a padlock icon in the UI), it is usually better to have the mechanism provide an API for this.

If a polkit application wants to handle the case where no authentication agent exists (for example if the app is launched via a ssh(1) login), it is trivial for the application to use the PolkitAgentTextListener class to spawn its own authentication agent as needed. Alternatively, the pkttyagent(1) helper can be used to do this.

As an example of code using the GObject API, see Example 1, “Querying the Authority”. For an example using the D-Bus API, see Example 2, “Accessing the Authority via D-Bus”.

Example 1. Querying the Authority

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187

    /*
     * Copyright (C) 2009 Red Hat, Inc.
     *
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2 of the License, or (at your option) any later version.
     *
     * This library is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General
     * Public License along with this library; if not, write to the
     * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     * Boston, MA 02111-1307, USA.
     *
     * Author: David Zeuthen <davidz@redhat.com>
     */

    /* Simple example that shows how to check for an authorization
     * including cancelling the check.
     *
     * Cancelling an authorization check is desirable in situations where
     * the object/action to check for vanishes.
     *
     * One concrete example of this is a disks service in which the user
     * needs to authenticate to modify a disk. If the disk is removed
     * while the authentication dialog is shown, the disks service should
     * cancel the authorization check. A side effect of this, is that the
     * authentication dialog is removed.
     */

    #include <polkit/polkit.h>

    static gboolean
    on_tensec_timeout (gpointer user_data)
    {
      GMainLoop *loop = user_data;
      g_print ("Ten seconds has passed. Now exiting.\n");
      g_main_loop_quit (loop);
      return FALSE;
    }

    static void
    check_authorization_cb (PolkitAuthority *authority,
                            GAsyncResult    *res,
                            gpointer         user_data)
    {
      GMainLoop *loop = user_data;
      PolkitAuthorizationResult *result;
      GError *error;

      error = NULL;
      result = polkit_authority_check_authorization_finish (authority, res, &error);
      if (error != NULL)
        {
          g_print ("Error checking authorization: %s\n", error->message);
          g_error_free (error);
        }
      else
        {
          const gchar *result_str;
          if (polkit_authorization_result_get_is_authorized (result))
            {
              result_str = "authorized";
            }
          else if (polkit_authorization_result_get_is_challenge (result))
            {
              result_str = "challenge";
            }
          else
            {
              result_str = "not authorized";
            }

          g_print ("Authorization result: %s\n", result_str);
        }

      g_print ("Authorization check has been cancelled and the dialog should now be hidden.\n"
               "This process will exit in ten seconds.\n");
      g_timeout_add (10000, on_tensec_timeout, loop);
    }

    static gboolean
    do_cancel (GCancellable *cancellable)
    {
      g_print ("Timer has expired; cancelling authorization check\n");
      g_cancellable_cancel (cancellable);
      return FALSE;
    }

    int
    main (int argc, char *argv[])
    {
      pid_t parent_pid;
      const gchar *action_id;
      GMainLoop *loop;
      PolkitSubject *subject;
      PolkitAuthority *authority;
      GCancellable *cancellable;

      g_type_init ();

      if (argc != 2)
        {
          g_printerr ("usage: %s <action_id>\n", argv[0]);
          return 1;
        }
      action_id = argv[1];

      loop = g_main_loop_new (NULL, FALSE);

      authority = polkit_authority_get_sync (NULL, NULL);

      /* Typically mechanisms will use a PolkitSystemBusName since most
       * clients communicate with the mechanism via D-Bus. However for
       * this simple example we use the process id of the calling process.
       *
       * Note that if the parent was reaped we have to be careful not to
       * check if init(1) is authorized (it always is).
       */
      parent_pid = getppid ();
      if (parent_pid == 1)
        {
          g_printerr ("Parent process was reaped by init(1)\n");
          return 1;
        }
      subject = polkit_unix_process_new (parent_pid);

      cancellable = g_cancellable_new ();

      g_print ("Will cancel authorization check in 10 seconds\n");

      /* Set up a 10 second timer to cancel the check */
      g_timeout_add (10 * 1000,
                     (GSourceFunc) do_cancel,
                     cancellable);

      polkit_authority_check_authorization (authority,
                                            subject,
                                            action_id,
                                            NULL, /* PolkitDetails */
                                            POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
                                            cancellable,
                                            (GAsyncReadyCallback) check_authorization_cb,
                                            loop);

      g_main_loop_run (loop);

      g_object_unref (authority);
      g_object_unref (subject);
      g_object_unref (cancellable);
      g_main_loop_unref (loop);

      return 0;
    }

Example 2. Accessing the Authority via D-Bus

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    FIXME: MISSING XINCLUDE CONTENT