PolicyKit: Branch 'master' - 2 commits

David Zeuthen david at kemper.freedesktop.org
Mon Aug 20 14:55:03 PDT 2007


 doc/TODO                           |    9 -
 doc/api/polkit/polkit-docs.xml     |    2 
 doc/man/PolicyKit.conf.5.in        |   44 +++++
 polkit-grant/polkit-grant-helper.c |  237 ++++++++++++++++++++++++++++--
 polkit-grant/polkit-grant.c        |   85 +++++++----
 polkit-grant/polkit-grant.h        |   20 ++
 polkit/Makefile.am                 |    3 
 polkit/polkit-config.c             |  283 ++++++++++++++++++++++++++++---------
 polkit/polkit-config.h             |   25 +++
 polkit/polkit-context.c            |   20 ++
 polkit/polkit-context.h            |    9 -
 polkit/polkit-policy-file.c        |  206 +++++++++++++++++++++-----
 12 files changed, 782 insertions(+), 161 deletions(-)

New commits:
diff-tree 07bd50776b1be13f506f9a75763ecc32a469d408 (from af39b832e3bf2e46fe58af74304d7f6f85e853bf)
Author: David Zeuthen <davidz at redhat.com>
Date:   Mon Aug 20 17:51:02 2007 -0400

    properly support i18n'ed messages from .policy files
    
    Ugh, I'm not sure if there's a smarter way of dealing with xml:lang
    when using expat (google searches for this suggests no) but the way I
    fixed this is surely a bitch.

diff --git a/polkit/polkit-policy-file.c b/polkit/polkit-policy-file.c
index 1efdbee..b5224f6 100644
--- a/polkit/polkit-policy-file.c
+++ b/polkit/polkit-policy-file.c
@@ -85,6 +85,7 @@ enum {
 typedef struct {
         XML_Parser parser;
         int state;
+
         char *group_id;
         char *action_id;
 
@@ -95,12 +96,56 @@ typedef struct {
 
         polkit_bool_t load_descriptions;
 
-        char *group_description;
-        char *policy_description;
-        char *policy_message;
+        GHashTable *group_descriptions;
+        GHashTable *policy_descriptions;
+        GHashTable *policy_messages;
+
+        char *group_description_nolang;
+        char *policy_description_nolang;
+        char *policy_message_nolang;
+
+        /* the language according to $LANG (e.g. en_US, da_DK, fr, en_CA minus the encoding) */
+        char *lang;
+
+        /* the value of xml:lang for the thing we're reading in _cdata() */
+        char *elem_lang;
 } ParserData;
 
 static void
+pd_unref_action_data (ParserData *pd)
+{
+        g_free (pd->action_id);
+        pd->action_id = NULL;
+        g_free (pd->policy_description_nolang);
+        pd->policy_description_nolang = NULL;
+        g_free (pd->policy_message_nolang);
+        pd->policy_message_nolang = NULL;
+        if (pd->policy_descriptions != NULL) {
+                g_hash_table_destroy (pd->policy_descriptions);
+                pd->policy_descriptions = NULL;
+        }
+        if (pd->policy_messages != NULL) {
+                g_hash_table_destroy (pd->policy_messages);
+                pd->policy_messages = NULL;
+        }
+}
+
+static void
+pd_unref_group_data (ParserData *pd)
+{
+        pd_unref_action_data (pd);
+
+        g_free (pd->group_id);
+        pd->group_id = NULL;
+        g_free (pd->group_description_nolang);
+        pd->group_description_nolang = NULL;
+        if (pd->group_descriptions != NULL) {
+                g_hash_table_destroy (pd->group_descriptions);
+                pd->group_descriptions = NULL;
+        }
+}
+
+static void
 _start (void *data, const char *el, const char **attr)
 {
         int state;
@@ -122,41 +167,50 @@ _start (void *data, const char *el, cons
                 if (strcmp (el, "group") == 0) {
                         if (num_attr != 2 || strcmp (attr[0], "id") != 0)
                                 goto error;
-                        g_free (pd->group_id);
-                        pd->group_id = g_strdup (attr[1]);
                         state = STATE_IN_GROUP;
 
-                        g_free (pd->group_description);
-                        pd->group_description = NULL;
+                        pd_unref_group_data (pd);
+                        pd->group_id = g_strdup (attr[1]);
+                        pd->group_descriptions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
                 }
                 break;
         case STATE_IN_GROUP:
                 if (strcmp (el, "policy") == 0) {
                         if (num_attr != 2 || strcmp (attr[0], "id") != 0)
                                 goto error;
-                        g_free (pd->action_id);
-                        pd->action_id = g_strdup (attr[1]);
                         state = STATE_IN_POLICY;
 
-                        pd->policy_description = NULL;
-                        pd->policy_message = NULL;
+                        pd_unref_action_data (pd);
+                        pd->action_id = g_strdup (attr[1]);
+                        pd->policy_descriptions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+                        pd->policy_messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
                         /* initialize defaults */
                         pd->defaults_allow_inactive = POLKIT_RESULT_NO;
                         pd->defaults_allow_active = POLKIT_RESULT_NO;
-                }
-                else if (strcmp (el, "description") == 0)
+                } else if (strcmp (el, "description") == 0) {
+                        if (num_attr == 2 && strcmp (attr[0], "xml:lang") == 0) {
+                                pd->elem_lang = g_strdup (attr[1]);
+                        }
                         state = STATE_IN_GROUP_DESCRIPTION;
+                }
                 break;
         case STATE_IN_GROUP_DESCRIPTION:
                 break;
         case STATE_IN_POLICY:
                 if (strcmp (el, "defaults") == 0)
                         state = STATE_IN_DEFAULTS;
-                else if (strcmp (el, "description") == 0)
+                else if (strcmp (el, "description") == 0) {
+                        if (num_attr == 2 && strcmp (attr[0], "xml:lang") == 0) {
+                                pd->elem_lang = g_strdup (attr[1]);
+                        }
                         state = STATE_IN_POLICY_DESCRIPTION;
-                else if (strcmp (el, "message") == 0)
+                } else if (strcmp (el, "message") == 0) {
+                        if (num_attr == 2 && strcmp (attr[0], "xml:lang") == 0) {
+                                pd->elem_lang = g_strdup (attr[1]);
+                        }
                         state = STATE_IN_POLICY_MESSAGE;
+                }
                 break;
         case STATE_IN_POLICY_DESCRIPTION:
                 break;
@@ -197,25 +251,35 @@ _cdata (void *data, const char *s, int l
 
         case STATE_IN_GROUP_DESCRIPTION:
                 if (pd->load_descriptions) {
-                        if (pd->group_description != NULL)
-                                g_free (pd->group_description);
-                        pd->group_description = g_strdup (str);
+
+                        if (pd->elem_lang == NULL) {
+                                g_free (pd->group_description_nolang);
+                                pd->group_description_nolang = g_strdup (str);
+                        } else {
+                                g_hash_table_insert (pd->group_descriptions, g_strdup (pd->elem_lang), g_strdup (str));
+                        }
                 }
                 break;
                 
         case STATE_IN_POLICY_DESCRIPTION:
                 if (pd->load_descriptions) {
-                        if (pd->policy_description != NULL)
-                                g_free (pd->policy_description);
-                        pd->policy_description = g_strdup (str);
+                        if (pd->elem_lang == NULL) {
+                                g_free (pd->policy_description_nolang);
+                                pd->policy_description_nolang = g_strdup (str);
+                        } else {
+                                g_hash_table_insert (pd->policy_descriptions, g_strdup (pd->elem_lang), g_strdup (str));
+                        }
                 }
                 break;
 
         case STATE_IN_POLICY_MESSAGE:
                 if (pd->load_descriptions) {
-                        if (pd->policy_message != NULL)
-                                g_free (pd->policy_message);
-                        pd->policy_message = g_strdup (str);
+                        if (pd->elem_lang == NULL) {
+                                g_free (pd->policy_message_nolang);
+                                pd->policy_message_nolang = g_strdup (str);
+                        } else {
+                                g_hash_table_insert (pd->policy_messages, g_strdup (pd->elem_lang), g_strdup (str));
+                        }
                 }
                 break;
 
@@ -243,6 +307,53 @@ extern void _polkit_policy_file_entry_se
                                                         const char *policy_description,
                                                         const char *policy_message);
 
+/**
+ * _localize:
+ * @translations: a mapping from xml:lang to the value, e.g. 'da' -> 'Smadre', 'en_CA' -> 'Punch, Aye!'
+ * @untranslated: the untranslated value, e.g. 'Punch'
+ * @lang: the locale we're interested in, e.g. 'da_DK', 'da', 'en_CA', 'en_US'; basically just $LANG
+ * with the encoding cut off. Maybe be NULL.
+ *
+ * Pick the correct translation to use.
+ *
+ * Returns: the localized string to use
+ */
+static const char *
+_localize (GHashTable *translations, const char *untranslated, const char *lang)
+{
+        const char *result;
+        char *lang2;
+        int n;
+
+        if (lang == NULL) {
+                result = untranslated;
+                goto out;
+        }
+
+        /* first see if we have the translation */
+        result = g_hash_table_lookup (translations, lang);
+        if (result != NULL)
+                goto out;
+
+        /* we could have a translation for 'da' but lang=='da_DK'; cut off the last part and try again */
+        lang2 = g_strdup (lang);
+        for (n = 0; lang2[n] != '\0'; n++) {
+                if (lang2[n] == '_') {
+                        lang2[n] = '\0';
+                        break;
+                }
+        }
+        result = g_hash_table_lookup (translations, lang2);
+        g_free (lang2);
+        if (result != NULL)
+                goto out;
+
+        /* fall back to untranslated */
+        result = untranslated;
+out:
+        return result;
+}
+
 static void
 _end (void *data, const char *el)
 {
@@ -251,6 +362,9 @@ _end (void *data, const char *el)
 
         state = STATE_NONE;
 
+        g_free (pd->elem_lang);
+        pd->elem_lang = NULL;
+
         switch (pd->state) {
         case STATE_NONE:
                 break;
@@ -265,6 +379,9 @@ _end (void *data, const char *el)
                 break;
         case STATE_IN_POLICY:
         {
+                const char *group_description;
+                const char *policy_description;
+                const char *policy_message;
                 PolKitPolicyFileEntry *pfe;
 
                 pfe = _polkit_policy_file_entry_new (pd->group_id, pd->action_id, 
@@ -273,11 +390,15 @@ _end (void *data, const char *el)
                 if (pfe == NULL)
                         goto error;
 
+                group_description = _localize (pd->group_descriptions, pd->group_description_nolang, pd->lang);
+                policy_description = _localize (pd->policy_descriptions, pd->policy_description_nolang, pd->lang);
+                policy_message = _localize (pd->policy_messages, pd->policy_message_nolang, pd->lang);
+
                 if (pd->load_descriptions)
                         _polkit_policy_file_entry_set_descriptions (pfe,
-                                                                    pd->group_description,
-                                                                    pd->policy_description,
-                                                                    pd->policy_message);
+                                                                    group_description,
+                                                                    policy_description,
+                                                                    policy_message);
 
                 pd->pf->entries = g_slist_prepend (pd->pf->entries, pfe);
 
@@ -327,6 +448,7 @@ polkit_policy_file_new (const char *path
         PolKitPolicyFile *pf;
         ParserData pd;
         int xml_res;
+        char *lang;
 
         pf = NULL;
 
@@ -351,6 +473,9 @@ polkit_policy_file_new (const char *path
 		goto error;
         }
 
+        /* clear parser data */
+        memset (&pd, 0, sizeof (ParserData));
+
         pd.parser = XML_ParserCreate (NULL);
         if (pd.parser == NULL) {
                 polkit_error_set_error (error, POLKIT_ERROR_OUT_OF_MEMORY,
@@ -366,23 +491,24 @@ polkit_policy_file_new (const char *path
         pf = g_new0 (PolKitPolicyFile, 1);
         pf->refcount = 1;
 
+        /* init parser data */
         pd.state = STATE_NONE;
-        pd.group_id = NULL;
-        pd.action_id = NULL;
-        pd.group_description = NULL;
-        pd.policy_description = NULL;
-        pd.policy_message = NULL;
         pd.pf = pf;
         pd.load_descriptions = load_descriptions;
+        lang = getenv ("LANG");
+        if (lang != NULL) {
+                int n;
+                pd.lang = g_strdup (lang);
+                for (n = 0; pd.lang[n] != '\0'; n++) {
+                        if (pd.lang[n] == '.') {
+                                pd.lang[n] = '\0';
+                                break;
+                        }
+                }
+        }
 
         xml_res = XML_Parse (pd.parser, buf, buflen, 1);
 
-        g_free (pd.group_id);
-        g_free (pd.action_id);
-        g_free (pd.group_description);
-        g_free (pd.policy_description);
-        g_free (pd.policy_message);
-
 	if (xml_res == 0) {
                 polkit_error_set_error (error, POLKIT_ERROR_POLICY_FILE_INVALID,
                                         "%s:%d: parse error: %s",
@@ -396,12 +522,12 @@ polkit_policy_file_new (const char *path
 	}
 	XML_ParserFree (pd.parser);
 	g_free (buf);
-
+        pd_unref_group_data (&pd);
         return pf;
 error:
         if (pf != NULL)
                 polkit_policy_file_unref (pf);
-
+        pd_unref_group_data (&pd);
         return NULL;
 }
 
diff-tree af39b832e3bf2e46fe58af74304d7f6f85e853bf (from 7034e0c24e37963d72a50371ed6ad6a51a740d02)
Author: David Zeuthen <davidz at redhat.com>
Date:   Mon Aug 13 13:44:33 2007 -0400

    export PolKitConfig and provide a <define_admin_auth/> config file directive
    
    Also change the libpolkit-grant API a bit to work with these changes.

diff --git a/doc/TODO b/doc/TODO
index e4495aa..311a44b 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -3,7 +3,8 @@
 
  - Verify the security model
 
- - Audit all code; especially the setgid helper
+ - Audit all code; especially the setgid polkit_user helper and setuid
+   root pam specific helper
 
  - Granted privileges are currently world-visible; see
    https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=244941
@@ -24,12 +25,6 @@
 
  - Make sure API coverage is 100%
 
- - Have support for systems that don't use the root account; e.g.
-   instead of authenticating as root, authenticate a user in
-   e.g. the 'wheel' group. Probably means we need a combobox in the UI
-   bits (e.g. PolicyKit-gnome + friends) for selecting the user to
-   auth as.
-
  - Potentially drop the glib dependency (it's not visible in the
    external API). This is mainly to be able to handle OOM for
    mechanisms that will need this (such as dbus-daemon)
diff --git a/doc/api/polkit/polkit-docs.xml b/doc/api/polkit/polkit-docs.xml
index 8358039..fd5f811 100644
--- a/doc/api/polkit/polkit-docs.xml
+++ b/doc/api/polkit/polkit-docs.xml
@@ -76,7 +76,7 @@
     <xi:include href="xml/polkit-seat.xml"/>
     <xi:include href="xml/polkit-session.xml"/>
     <xi:include href="xml/polkit-caller.xml"/>
-    <xi:include href="xml/polkit-module.xml"/>
+    <xi:include href="xml/polkit-config.xml"/>
   </reference>
 
   <index>
diff --git a/doc/man/PolicyKit.conf.5.in b/doc/man/PolicyKit.conf.5.in
index c75d307..1374b29 100644
--- a/doc/man/PolicyKit.conf.5.in
+++ b/doc/man/PolicyKit.conf.5.in
@@ -100,15 +100,15 @@ is supported and it can assume the follo
 .B no
 Access denied.
 .TP
-.B auth_root
+.B auth_admin
 Access denied, but authentication of the caller as root will grant
 access to only that caller.
 .TP
-.B auth_root_keep_session
+.B auth_admin_keep_session
 Access denied, but authentication of the caller as root will grant
 access for the remainder of the session the caller stems from.
 .TP
-.B auth_root_keep_always
+.B auth_admin_keep_always
 Access denied, but authentication of the caller as root will grant
 access to the user of the caller in the future.
 .TP
@@ -127,6 +127,44 @@ access to the user of the caller in the 
 .B yes
 Access granted.
 
+.PP
+
+.I define_admin_auth
+
+This element is used to specify the meaning of "authenticate as
+administrator". It is normally used at the top-level but can also be
+used deep inside a number of 
+.I match
+elements for conditional behavior. 
+
+There can only be a single attribute in each
+.I define_admin_auth
+element. POSIX Extended Regular Expression syntax are 
+.B not 
+supported in the value part, however multiple values to match on can
+be separated with the bar (|) character. The following attributes
+are supported:
+
+.TP
+.B user
+Administrator authentication means authenticate as the given user.
+If no
+.I define_admin_auth
+element is given, the default is to use
+.B user="root"
+e.g. administrator authentication mean authenticate as the super user.
+
+.TP
+.B group
+Administrator authentication means that any user in the groups matching
+the given value can be used to authenticate. Typically, on a system 
+with the root account disabled one wants to use something like
+.B group="wheel"
+to e.g. enable all UNIX users in the UNIX group
+.B wheel
+to be able to authentication whenever administrator authentication
+is required.
+
 .SH EXAMPLES
 
 For brevity the standard XML and doctype headers are omitted in the
diff --git a/polkit-grant/polkit-grant-helper.c b/polkit-grant/polkit-grant-helper.c
index 049d10c..0358973 100644
--- a/polkit-grant/polkit-grant-helper.c
+++ b/polkit-grant/polkit-grant-helper.c
@@ -78,13 +78,24 @@
  *                                   the right to do the Action.
  *
  *                      <-- Tell libpolkit-grant about grant details, e.g.
- *                          {self,admin}_{,keep_session,keep_always}
- *                          using stdout
+ *                          {self,admin}_{,keep_session,keep_always} +
+ *                          what users can authenticate using stdout
  *
  *   Receive grant details on stdin.
  *   Caller prepares UI dialog depending
  *   on grant details.
  *
+ *                                     if admin_users is not empty, wait for
+ *                                     user name of admin user to auth on stdin
+ *
+ *   if admin_users is not empty, write
+ *   user name of admin user to auth on stdout -->
+ *
+ *
+ *                                       verify that given username is
+ *                                       in admin_users
+ *
+ *
  *                                       Spawn polkit-grant-helper-pam
  *                                       with no args -->
  *
@@ -132,8 +143,13 @@
  */
 
 
-/* the authentication itself is done via a setuid root helper; this is
- * to make the code running as uid 0 easier to audit. */
+/** 
+ * do_auth:
+ * 
+ * the authentication itself is done via a setuid root helper; this is
+ * to make the code running as uid 0 easier to audit. 
+ *
+ */
 static polkit_bool_t
 do_auth (const char *user_to_auth)
 {
@@ -208,6 +224,7 @@ do_auth (const char *user_to_auth)
                 /* read from parent */
                 if (fgets (buf, sizeof buf, stdin) == NULL)
                         goto out;
+
 #ifdef PGH_DEBUG
                 fprintf (stderr, "received: '%s' from parent; sending to child\n", buf);
 #endif /* PGH_DEBUG */
@@ -224,11 +241,32 @@ out:
         return ret;
 }
 
+/**
+ * verify_with_polkit:
+ * @caller_pid: the process id of the caller
+ * @action_name: name of the action
+ * @result: return location for result AKA how the user can auth
+ * @out_session_objpath: return location for ConsoleKit session identifier
+ * @out_admin_users: return location for a NULL-terminated array of
+ * strings that can be user to auth as admin. Is set to NULL if the
+ * super user (e.g. uid 0) should be user to auth as admin.
+ *
+ * Verify that the given caller can authenticate to gain a privilege
+ * to do the given action. If the authentication requires
+ * administrator privileges, also return a list of users that can be
+ * used to do this cf. the <define_admin_auth/> element in the
+ * configuration file; see the PolicyKit.conf(5) manual page for
+ * details.
+ *
+ * Returns: TRUE if, and only if, the given caller can authenticate to
+ * gain a privilege to do the given action.
+ */
 static polkit_bool_t
 verify_with_polkit (pid_t caller_pid,
                     const char *action_name,
                     PolKitResult *result,
-                    char **out_session_objpath)
+                    char **out_session_objpath,
+                    char ***out_admin_users)
 {
         PolKitCaller *caller;
         PolKitSession *session;
@@ -287,6 +325,103 @@ verify_with_polkit (pid_t caller_pid,
                 goto error;
         }
 
+        *out_admin_users = NULL;
+
+        /* for admin auth, get a list of users that can be used - this is basically evaluating the
+         * <define_admin_auth/> directives in the config file...
+         */
+        if (*result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
+            *result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
+            *result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS) {
+                PolKitConfig *pk_config;
+                PolKitConfigAdminAuthType admin_auth_type;
+                const char *admin_auth_data;
+
+                pk_config = polkit_context_get_config (pol_ctx);
+                if (polkit_config_determine_admin_auth_type (pk_config, 
+                                                             action, 
+                                                             caller, 
+                                                             &admin_auth_type, 
+                                                             &admin_auth_data)) {
+#ifdef PGH_DEBUG
+                        fprintf (stderr, "polkit-grant-helper: admin_auth_type=%d data='%s'\n", admin_auth_type, admin_auth_data);
+#endif /* PGH_DEBUG */
+                        switch (admin_auth_type) {
+                        case POLKIT_CONFIG_ADMIN_AUTH_TYPE_USER:
+                                if (admin_auth_data != NULL)
+                                        *out_admin_users = g_strsplit (admin_auth_data, "|", 0);
+                                break;
+                        case POLKIT_CONFIG_ADMIN_AUTH_TYPE_GROUP:
+                                if (admin_auth_data != NULL) {
+                                        int n;
+                                        char **groups;
+                                        GSList *i;
+                                        GSList *users;
+
+
+                                        users = NULL;
+                                        groups = g_strsplit (admin_auth_data, "|", 0);
+                                        for (n = 0; groups[n] != NULL; n++)  {
+                                                int m;
+                                                struct group *group;
+
+                                                /* This is fine; we're a single-threaded app */
+                                                if ((group = getgrnam (groups[n])) == NULL)
+                                                        continue;
+
+                                                for (m = 0; group->gr_mem[m] != NULL; m++) {
+                                                        const char *user;
+                                                        gboolean found;
+
+                                                        user = group->gr_mem[m];
+                                                        found = FALSE;
+
+#ifdef PGH_DEBUG
+                                                        fprintf (stderr, "polkit-grant-helper: examining member '%s' of group '%s'\n", user, groups[n]);
+#endif /* PGH_DEBUG */
+
+                                                        /* skip user 'root' since he is often member of 'wheel' etc. */
+                                                        if (strcmp (user, "root") == 0)
+                                                                continue;
+                                                        /* TODO: we should probably only consider users with an uid
+                                                         * in a given "safe" range, e.g. between 500 and 32000 or
+                                                         * something like that...
+                                                         */
+
+                                                        for (i = users; i != NULL; i = g_slist_next (i)) {
+                                                                if (strcmp (user, (const char *) i->data) == 0) {
+                                                                        found = TRUE;
+                                                                        break;
+                                                                }
+                                                        }
+                                                        if (found)
+                                                                continue;
+
+#ifdef PGH_DEBUG
+                                                        fprintf (stderr, "polkit-grant-helper: added user '%s'\n", user);
+#endif /* PGH_DEBUG */
+
+                                                        users = g_slist_prepend (users, g_strdup (user));
+                                                }
+
+                                        }
+                                        g_strfreev (groups);
+
+                                        users = g_slist_sort (users, (GCompareFunc) strcmp);
+
+                                        *out_admin_users = g_new0 (char *, g_slist_length (users) + 1);
+                                        for (i = users, n = 0; i != NULL; i = g_slist_next (i)) {
+                                                (*out_admin_users)[n++] = i->data;
+                                        }
+
+                                        g_slist_free (users);
+                                }
+                                break;
+                        }
+                }
+        }
+        
+
         /* TODO: we should probably clean up */
 
         return TRUE;
@@ -298,6 +433,7 @@ static polkit_bool_t
 get_and_validate_override_details (PolKitResult *result)
 {
         char buf[256];
+        char *textual_result;
         PolKitResult desired_result;
 
         if (fgets (buf, sizeof buf, stdin) == NULL)
@@ -305,10 +441,17 @@ get_and_validate_override_details (PolKi
         if (strlen (buf) > 0 &&
             buf[strlen (buf) - 1] == '\n')
                 buf[strlen (buf) - 1] = '\0';
-        
-        fprintf (stderr, "polkit-grant-helper: caller said '%s'\n", buf);
 
-        if (!polkit_result_from_string_representation (buf, &desired_result))
+        if (strncmp (buf, 
+                     "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE ", 
+                     sizeof "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE " - 1) != 0) {
+                goto error;
+        }
+        textual_result = buf + sizeof "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE " - 1;
+
+        fprintf (stderr, "polkit-grant-helper: caller said '%s'\n", textual_result);
+
+        if (!polkit_result_from_string_representation (textual_result, &desired_result))
                 goto error;
 
         fprintf (stderr, "polkit-grant-helper: testing for voluntarily downgrade from '%s' to '%s'\n",
@@ -386,6 +529,7 @@ main (int argc, char *argv[])
         char *session_objpath;
         struct passwd *pw;
         polkit_bool_t dbres;
+        char **admin_users;
 
         ret = 3;
 
@@ -461,10 +605,20 @@ main (int argc, char *argv[])
          * - figure out if the caller can really auth to do the action
          * - learn what ConsoleKit session the caller belongs to
          */
-        if (!verify_with_polkit (caller_pid, action_name, &result, &session_objpath))
+        if (!verify_with_polkit (caller_pid, action_name, &result, &session_objpath, &admin_users))
                 goto out;
 
 #ifdef PGH_DEBUG
+        if (admin_users != NULL) {
+                int n;
+                fprintf (stderr, "polkit-grant-helper: admin_users: ");
+                for (n = 0; admin_users[n] != NULL; n++)
+                        fprintf (stderr, "'%s' ", admin_users[n]);
+                fprintf (stderr, "\n");
+        }
+#endif /* PGH_DEBUG */
+
+#ifdef PGH_DEBUG
         fprintf (stderr, "polkit-grant-helper: polkit result   = '%s'\n", 
                  polkit_result_to_string_representation (result));
         fprintf (stderr, "polkit-grant-helper: session_objpath = '%s'\n", session_objpath);
@@ -477,21 +631,70 @@ main (int argc, char *argv[])
                  polkit_result_to_string_representation (result));
         fflush (stdout);
 
-        /* figure out what user to auth */
-        if (result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
-            result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
-            result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS) {
-                /* TODO: with wheel support, figure out what user to auth */
-                user_to_auth = "root";
+        /* if admin auth is required, tell caller about possible users */
+        if (admin_users != NULL) {
+                int n;
+                fprintf (stdout, "POLKIT_GRANT_HELPER_TELL_ADMIN_USERS");
+                for (n = 0; admin_users[n] != NULL; n++)
+                        fprintf (stdout, " %s", admin_users[n]);
+                fprintf (stdout, "\n");
+                fflush (stdout);
+        }
+
+
+        /* wait for libpolkit-grant to tell us what user to use */
+        if (admin_users != NULL) {
+                int n;
+                char buf[256];
+
+#ifdef PGH_DEBUG
+                fprintf (stderr, "waiting for admin user name...\n");
+#endif /* PGH_DEBUG */
+
+                /* read from parent */
+                if (fgets (buf, sizeof buf, stdin) == NULL)
+                        goto out;
+                if (strlen (buf) > 0 && buf[strlen (buf) - 1] == '\n')
+                        buf[strlen (buf) - 1] = '\0';
+
+                if (strncmp (buf, 
+                             "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER ", 
+                             sizeof "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER " - 1) != 0) {
+                        goto out;
+                }
+
+                user_to_auth = strdup (buf) + sizeof "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER " - 1;
+#ifdef PGH_DEBUG
+                fprintf (stderr, "libpolkit-grant wants to auth as '%s'\n", user_to_auth);
+#endif /* PGH_DEBUG */
+
+                /* now sanity check that returned user is actually in admin_users */
+                for (n = 0; admin_users[n] != NULL; n++) {
+                        if (strcmp (admin_users[n], user_to_auth) == 0)
+                                break;
+                }
+                if (admin_users[n] == NULL) {
+                        ret = 2;
+                        goto out;
+                }
+
         } else {
-                user_to_auth = invoking_user_name;
+                /* figure out what user to auth */
+                if (result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
+                    result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
+                    result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS) {
+                        user_to_auth = "root";
+                } else {
+                        user_to_auth = invoking_user_name;
+                }
         }
 
         ret = 1;
 
         /* Start authentication */
-        if (!do_auth (user_to_auth))
+        if (!do_auth (user_to_auth)) {
                 goto out;
+        }
 
         /* Ask caller if he want to slim down grant type...  e.g. he
          * might want to go from auth_self_keep_always to
diff --git a/polkit-grant/polkit-grant.c b/polkit-grant/polkit-grant.c
index 1217217..a79c86b 100644
--- a/polkit-grant/polkit-grant.c
+++ b/polkit-grant/polkit-grant.c
@@ -60,6 +60,7 @@ struct PolKitGrant
         PolKitGrantAddChildWatch func_add_child_watch;
         PolKitGrantRemoveWatch func_remove_watch;
         PolKitGrantType func_type;
+        PolKitGrantSelectAdminUser func_select_admin_user;
         PolKitGrantConversationPromptEchoOff func_prompt_echo_off;
         PolKitGrantConversationPromptEchoOn func_prompt_echo_on;
         PolKitGrantConversationErrorMessage func_error_message;
@@ -77,7 +78,7 @@ struct PolKitGrant
         int io_watch_id;
 
         gboolean success;
-        gboolean auth_in_progress;
+        gboolean helper_is_running;
 };
 
 /**
@@ -162,6 +163,7 @@ polkit_grant_unref (PolKitGrant *polkit_
  * @func_add_child_watch: Callback function
  * @func_remove_watch: Callback function
  * @func_type: Callback function
+ * @func_select_admin_user: Callback function
  * @func_prompt_echo_off: Callback function
  * @func_prompt_echo_on: Callback function
  * @func_error_message: Callback function
@@ -174,23 +176,25 @@ polkit_grant_unref (PolKitGrant *polkit_
  **/
 void
 polkit_grant_set_functions (PolKitGrant *polkit_grant,
-                               PolKitGrantAddIOWatch func_add_io_watch,
-                               PolKitGrantAddChildWatch func_add_child_watch,
-                               PolKitGrantRemoveWatch func_remove_watch,
-                               PolKitGrantType func_type,
-                               PolKitGrantConversationPromptEchoOff func_prompt_echo_off,
-                               PolKitGrantConversationPromptEchoOn func_prompt_echo_on,
-                               PolKitGrantConversationErrorMessage func_error_message,
-                               PolKitGrantConversationTextInfo func_text_info,
-                               PolKitGrantOverrideGrantType func_override_grant_type,
-                               PolKitGrantDone func_done,
-                               void *user_data)
+                            PolKitGrantAddIOWatch func_add_io_watch,
+                            PolKitGrantAddChildWatch func_add_child_watch,
+                            PolKitGrantRemoveWatch func_remove_watch,
+                            PolKitGrantType func_type,
+                            PolKitGrantSelectAdminUser func_select_admin_user,
+                            PolKitGrantConversationPromptEchoOff func_prompt_echo_off,
+                            PolKitGrantConversationPromptEchoOn func_prompt_echo_on,
+                            PolKitGrantConversationErrorMessage func_error_message,
+                            PolKitGrantConversationTextInfo func_text_info,
+                            PolKitGrantOverrideGrantType func_override_grant_type,
+                            PolKitGrantDone func_done,
+                            void *user_data)
 {
         g_return_if_fail (polkit_grant != NULL);
         g_return_if_fail (func_add_io_watch != NULL);
         g_return_if_fail (func_add_child_watch != NULL);
         g_return_if_fail (func_remove_watch != NULL);
         g_return_if_fail (func_type != NULL);
+        g_return_if_fail (func_select_admin_user != NULL);
         g_return_if_fail (func_prompt_echo_off != NULL);
         g_return_if_fail (func_prompt_echo_on != NULL);
         g_return_if_fail (func_error_message != NULL);
@@ -200,6 +204,7 @@ polkit_grant_set_functions (PolKitGrant 
         polkit_grant->func_add_child_watch = func_add_child_watch;
         polkit_grant->func_remove_watch = func_remove_watch;
         polkit_grant->func_type = func_type;
+        polkit_grant->func_select_admin_user = func_select_admin_user;
         polkit_grant->func_prompt_echo_off = func_prompt_echo_off;
         polkit_grant->func_prompt_echo_on = func_prompt_echo_on;
         polkit_grant->func_error_message = func_error_message;
@@ -227,7 +232,7 @@ polkit_grant_child_func (PolKitGrant *po
         polkit_bool_t input_was_bogus;
 
         g_return_if_fail (polkit_grant != NULL);
-        g_return_if_fail (polkit_grant->auth_in_progress);
+        g_return_if_fail (polkit_grant->helper_is_running);
 
         g_debug ("pid %d terminated", pid);
         waitpid (pid, &status, 0);
@@ -238,6 +243,7 @@ polkit_grant_child_func (PolKitGrant *po
                 input_was_bogus = FALSE;
 
         polkit_grant->success = (exit_code == 0);
+        polkit_grant->helper_is_running = FALSE;
         polkit_grant->func_done (polkit_grant, polkit_grant->success, input_was_bogus, polkit_grant->user_data);
 }
 
@@ -259,22 +265,23 @@ polkit_grant_io_func (PolKitGrant *polki
         char *id;
         size_t id_len;
         char *response;
+        char *response_prefix;
 
         g_return_if_fail (polkit_grant != NULL);
-        g_return_if_fail (polkit_grant->auth_in_progress);
+        g_return_if_fail (polkit_grant->helper_is_running);
 
         while (getline (&line, &line_len, polkit_grant->child_stdout_f) != -1) {
                 if (strlen (line) > 0 &&
                     line[strlen (line) - 1] == '\n')
                         line[strlen (line) - 1] = '\0';
                 
-                //printf ("from child '%s'\n", line);
-                
                 response = NULL;
+                response_prefix = NULL;
                 
                 id = "PAM_PROMPT_ECHO_OFF ";
                 if (g_str_has_prefix (line, id)) {
                         id_len = strlen (id);
+                        response_prefix = "";
                         response = polkit_grant->func_prompt_echo_off (polkit_grant, 
                                                                        line + id_len, 
                                                                        polkit_grant->user_data);
@@ -284,6 +291,7 @@ polkit_grant_io_func (PolKitGrant *polki
                 id = "PAM_PROMPT_ECHO_ON ";
                 if (g_str_has_prefix (line, id)) {
                         id_len = strlen (id);
+                        response_prefix = "";
                         response = polkit_grant->func_prompt_echo_on (polkit_grant, 
                                                                       line + id_len, 
                                                                       polkit_grant->user_data);
@@ -311,16 +319,36 @@ polkit_grant_io_func (PolKitGrant *polki
                 id = "POLKIT_GRANT_HELPER_TELL_TYPE ";
                 if (g_str_has_prefix (line, id)) {
                         PolKitResult result;
+                        char *result_textual;
+
                         id_len = strlen (id);
-                        if (!polkit_result_from_string_representation (line + id_len, &result)) {
+                        result_textual = line + id_len;
+                        if (!polkit_result_from_string_representation (result_textual, &result)) {
                                 /* TODO: danger will robinson */
                         }
+
                         polkit_grant->func_type (polkit_grant, 
                                                  result,
                                                  polkit_grant->user_data);
                         goto processed;
                 }
 
+                id = "POLKIT_GRANT_HELPER_TELL_ADMIN_USERS ";
+                if (g_str_has_prefix (line, id)) {
+                        char **admin_users;
+
+                        id_len = strlen (id);
+                        admin_users = g_strsplit (line + id_len, " ", 0);
+
+                        response_prefix = "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER ";
+                        response = polkit_grant->func_select_admin_user (polkit_grant, 
+                                                                         admin_users,
+                                                                         polkit_grant->user_data);
+                        g_strfreev (admin_users);
+
+                        goto processed;
+                }
+
                 id = "POLKIT_GRANT_HELPER_ASK_OVERRIDE_GRANT_TYPE ";
                 if (g_str_has_prefix (line, id)) {
                         PolKitResult override;
@@ -332,19 +360,27 @@ polkit_grant_io_func (PolKitGrant *polki
                         override = polkit_grant->func_override_grant_type (polkit_grant, 
                                                                            result, 
                                                                            polkit_grant->user_data);
+                        response_prefix = "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE ";
                         response = g_strdup (polkit_result_to_string_representation (override));
                         goto processed;
                 }
 
         processed:
-                if (response != NULL) {
+                if (response != NULL && response_prefix != NULL) {
+                        char *buf;
+                        gboolean add_newline;
+
                         /* add a newline if there isn't one already... */
+                        add_newline = FALSE;
                         if (response[strlen (response) - 1] != '\n') {
-                                char *old = response;
-                                response = g_strdup_printf ("%s\n", response);
-                                g_free (old);
+                                add_newline = TRUE;
                         }
-                        write (polkit_grant->child_stdin, response, strlen (response));
+                        buf = g_strdup_printf ("%s%s%c",
+                                               response_prefix,
+                                               response,
+                                               add_newline ? '\n' : '\0');
+                        write (polkit_grant->child_stdin, buf, strlen (buf));
+                        g_free (buf);
                         free (response);
                 }
         }
@@ -364,7 +400,7 @@ polkit_grant_cancel_auth (PolKitGrant *p
 {
         GPid pid;
         g_return_if_fail (polkit_grant != NULL);
-        g_return_if_fail (polkit_grant->auth_in_progress);
+        g_return_if_fail (polkit_grant->helper_is_running);
 
         pid = polkit_grant->child_pid;
         polkit_grant->child_pid = 0;
@@ -372,6 +408,7 @@ polkit_grant_cancel_auth (PolKitGrant *p
                 int status;
                 kill (pid, SIGTERM);
                 waitpid (pid, &status, 0);
+                polkit_grant->helper_is_running = FALSE;
         }
         polkit_grant->func_done (polkit_grant, FALSE, FALSE, polkit_grant->user_data);        
 }
@@ -463,7 +500,7 @@ polkit_grant_initiate_auth (PolKitGrant 
         
         polkit_grant->success = FALSE;
 
-        polkit_grant->auth_in_progress = TRUE;
+        polkit_grant->helper_is_running = TRUE;
 
         return TRUE;
 error:
diff --git a/polkit-grant/polkit-grant.h b/polkit-grant/polkit-grant.h
index 5211b6f..e619625 100644
--- a/polkit-grant/polkit-grant.h
+++ b/polkit-grant/polkit-grant.h
@@ -50,6 +50,25 @@ typedef void (*PolKitGrantType) (PolKitG
                                  void *user_data);
 
 /**
+ * PolKitGrantSelectAdminUser:
+ * @polkit_grant: the grant object
+ * @admin_users: a NULL-terminated array of users that can be used for
+ * authentication for admin grants.
+ * @user_data: user data pointed as passed into polkit_grant_set_functions()
+ *
+ * Type for callback function that describes the possible users that
+ * can be chosen for authentication when administrator privileges are
+ * required. 
+ *
+ * Returns: the chosen user; must be allocated with malloc(3) and will
+ * be freed by the #PolKitGrant class.
+ **/
+typedef char* (*PolKitGrantSelectAdminUser) (PolKitGrant *polkit_grant,
+                                             char **admin_users,
+                                             void *user_data);
+
+
+/**
  * PolKitGrantConversationPromptEchoOff:
  * @polkit_grant: the grant object
  * @prompt: prompt passed by the authentication layer; do not free this string
@@ -323,6 +342,7 @@ void          polkit_grant_set_functions
                                           PolKitGrantAddChildWatch func_add_child_watch,
                                           PolKitGrantRemoveWatch func_remove_watch,
                                           PolKitGrantType func_type,
+                                          PolKitGrantSelectAdminUser func_select_admin_user,
                                           PolKitGrantConversationPromptEchoOff func_prompt_echo_off,
                                           PolKitGrantConversationPromptEchoOn func_prompt_echo_on,
                                           PolKitGrantConversationErrorMessage func_error_message,
diff --git a/polkit/Makefile.am b/polkit/Makefile.am
index 8099be8..155367a 100644
--- a/polkit/Makefile.am
+++ b/polkit/Makefile.am
@@ -36,7 +36,8 @@ libpolkitinclude_HEADERS =              
 	polkit-policy-file-entry.h			\
 	polkit-policy-file.h				\
 	polkit-policy-cache.h				\
-	polkit-policy-default.h
+	polkit-policy-default.h				\
+	polkit-config.h
 
 libpolkit_la_SOURCES =                                			\
 	polkit.h							\
diff --git a/polkit/polkit-config.c b/polkit/polkit-config.c
index d0de655..782796c 100644
--- a/polkit/polkit-config.c
+++ b/polkit/polkit-config.c
@@ -49,7 +49,10 @@
  * SECTION:polkit-config
  * @short_description: Configuration file.
  *
- * This class is used to represent the /etc/PolicyKit/PolicyKit.conf configuration file.
+ * This class is used to represent the /etc/PolicyKit/PolicyKit.conf
+ * configuration file. Applications using PolicyKit should never use
+ * this class; it's only here for integration with other PolicyKit
+ * components.
  **/
 
 enum {
@@ -57,6 +60,7 @@ enum {
         STATE_IN_CONFIG,
         STATE_IN_MATCH,
         STATE_IN_RETURN,
+        STATE_IN_DEFINE_ADMIN_AUTH,
 };
 
 struct ConfigNode;
@@ -65,7 +69,10 @@ typedef struct ConfigNode ConfigNode;
 /**
  * PolKitConfig:
  *
- * This class represents the system-wide configuration file for PolicyKit.
+ * This class represents the system-wide configuration file for
+ * PolicyKit. Applications using PolicyKit should never use this
+ * class; it's only here for integration with other PolicyKit
+ * components.
  **/
 struct PolKitConfig
 {
@@ -90,6 +97,7 @@ enum {
         NODE_TYPE_TOP,
         NODE_TYPE_MATCH,
         NODE_TYPE_RETURN,
+        NODE_TYPE_DEFINE_ADMIN_AUTH,
 };
 
 enum {
@@ -103,6 +111,12 @@ static const char * const match_names[] 
         "user",
 };
 
+static const char * const define_admin_auth_names[] = 
+{
+        "user",
+        "group",
+};
+
 struct ConfigNode
 {
         int node_type;
@@ -119,6 +133,11 @@ struct ConfigNode
                         PolKitResult result;
                 } node_return;
 
+                struct {
+                        PolKitConfigAdminAuthType admin_type;
+                        char *data;
+                } node_define_admin_auth;
+
         } data;
 
         GSList *children;
@@ -161,6 +180,14 @@ config_node_dump_real (ConfigNode *node,
                            polkit_result_to_string_representation (node->data.node_return.result),
                            node->data.node_return.result);
                 break;
+        case NODE_TYPE_DEFINE_ADMIN_AUTH:
+                _pk_debug ("%sDEFINE_ADMIN_AUTH %s (%d) with '%s'", 
+                           buf, 
+                           define_admin_auth_names[node->data.node_define_admin_auth.admin_type],
+                           node->data.node_define_admin_auth.admin_type,
+                           node->data.node_define_admin_auth.data);
+                break;
+                break;
         }
 
         for (i = node->children; i != NULL; i = g_slist_next (i)) {
@@ -190,6 +217,9 @@ config_node_unref (ConfigNode *node)
                 break;
         case NODE_TYPE_RETURN:
                 break;
+        case NODE_TYPE_DEFINE_ADMIN_AUTH:
+                g_free (node->data.node_define_admin_auth.data);
+                break;
         }
 
         for (i = node->children; i != NULL; i = g_slist_next (i)) {
@@ -208,7 +238,7 @@ _start (void *data, const char *el, cons
         ParserData *pd = data;
         ConfigNode *node;
 
-        _pk_debug ("_start for node '%s'", el);
+        _pk_debug ("_start for node '%s' (at depth=%d)", el, pd->stack_depth);
 
         for (num_attr = 0; attr[num_attr] != NULL; num_attr++)
                 ;
@@ -280,6 +310,28 @@ _start (void *data, const char *el, cons
                         _pk_debug ("parsed return node ('%s' (%d))",
                                    attr[1],
                                    node->data.node_return.result);
+                } else if ((strcmp (el, "define_admin_auth") == 0) && (num_attr == 2)) {
+
+                        node = config_node_new ();
+                        node->node_type = NODE_TYPE_DEFINE_ADMIN_AUTH;
+                        if (strcmp (attr[0], "user") == 0) {
+                                node->data.node_define_admin_auth.admin_type = POLKIT_CONFIG_ADMIN_AUTH_TYPE_USER;
+                        } else if (strcmp (attr[0], "group") == 0) {
+                                node->data.node_define_admin_auth.admin_type = POLKIT_CONFIG_ADMIN_AUTH_TYPE_GROUP;
+                        } else {
+                                _pk_debug ("Unknown define_admin_auth rule '%s'", attr[0]);
+                                goto error;
+                        }
+
+                        node->data.node_define_admin_auth.data = g_strdup (attr[1]);
+
+                        state = STATE_IN_DEFINE_ADMIN_AUTH;
+                        _pk_debug ("parsed define_admin_auth node ('%s' (%d) -> '%s')", 
+                                   attr[0], 
+                                   node->data.node_define_admin_auth.admin_type,
+                                   node->data.node_define_admin_auth.data);
+
+
                 }
                 break;
         }
@@ -301,7 +353,7 @@ _start (void *data, const char *el, cons
         }
 
         pd->stack_depth++;
-        _pk_debug ("state = %d", pd->state);
+        _pk_debug ("now in state=%d (after _start, depth=%d)", pd->state, pd->stack_depth);
         return;
 
 error:
@@ -321,15 +373,18 @@ _end (void *data, const char *el)
 {
         ParserData *pd = data;
 
-        _pk_debug ("_end for node '%s'", el);
+        _pk_debug ("_end for node '%s' (at depth=%d)", el, pd->stack_depth);
 
         --pd->stack_depth;
         if (pd->stack_depth < 0 || pd->stack_depth >= PARSER_MAX_DEPTH) {
                 _pk_debug ("reached max depth?");
                 goto error;
         }
-        pd->state = pd->state_stack[pd->stack_depth];
-        _pk_debug ("state = %d", pd->state);
+        if (pd->stack_depth > 0)
+                pd->state = pd->state_stack[pd->stack_depth - 1];
+        else
+                pd->state = STATE_NONE;
+        _pk_debug ("now in state=%d (after _end, depth=%d)", pd->state, pd->stack_depth);
         return;
 error:
         XML_StopParser (pd->parser, FALSE);
@@ -456,79 +511,96 @@ polkit_config_unref (PolKitConfig *pk_co
         g_free (pk_config);
 }
 
-/* exactly one of the parameters caller and session must be NULL */
-static PolKitResult
-config_node_test (ConfigNode *node, PolKitAction *action, PolKitCaller *caller, PolKitSession *session)
+static gboolean
+config_node_match (ConfigNode *node, 
+                  PolKitAction *action, 
+                  PolKitCaller *caller, 
+                  PolKitSession *session)
 {
-        gboolean match;
-        gboolean recurse;
-        PolKitResult result;
         char *str;
         char *str1;
         char *str2;
         uid_t uid;
+        gboolean match;
+
+        match = FALSE;
+        str1 = NULL;
+        str2 = NULL;
+        switch (node->data.node_match.match_type) {
+
+        case MATCH_TYPE_ACTION:
+                if (!polkit_action_get_action_id (action, &str))
+                        goto out;
+                str1 = g_strdup (str);
+                break;
+
+        case MATCH_TYPE_USER:
+                if (caller != NULL) {
+                        if (!polkit_caller_get_uid (caller, &uid))
+                                goto out;
+                } else if (session != NULL) {
+                        if (!polkit_session_get_uid (session, &uid))
+                                goto out;
+                } else
+                        goto out;
+                
+                str1 = g_strdup_printf ("%d", uid);
+                {
+                        struct passwd pd;
+                        struct passwd* pwdptr=&pd;
+                        struct passwd* tempPwdPtr;
+                        char pwdbuffer[256];
+                        int  pwdlinelen = sizeof(pwdbuffer);
+                        
+                        if ((getpwuid_r (uid, pwdptr, pwdbuffer, pwdlinelen, &tempPwdPtr)) !=0 )
+                                goto out;
+                        str2 = g_strdup (pd.pw_name);
+                }
+                break;
+        }
+        
+        if (str1 != NULL) {
+                if (regexec (&(node->data.node_match.preq), str1, 0, NULL, 0) == 0)
+                        match = TRUE;
+        }
+        if (!match && str2 != NULL) {
+                if (regexec (&(node->data.node_match.preq), str2, 0, NULL, 0) == 0)
+                        match = TRUE;
+        }
+
+out:
+        g_free (str1);
+        g_free (str2);
+        return match;
+}
+
+
+/* exactly one of the parameters caller and session must be NULL */
+static PolKitResult
+config_node_test (ConfigNode *node, 
+                  PolKitAction *action, 
+                  PolKitCaller *caller, 
+                  PolKitSession *session)
+{
+        gboolean recurse;
+        PolKitResult result;
 
-        result = POLKIT_RESULT_UNKNOWN;
         recurse = FALSE;
+        result = POLKIT_RESULT_UNKNOWN;
 
         switch (node->node_type) {
         case NODE_TYPE_TOP:
                 recurse = TRUE;
                 break;
         case NODE_TYPE_MATCH:
-                match = FALSE;
-                str1 = NULL;
-                str2 = NULL;
-                switch (node->data.node_match.match_type) {
-                case MATCH_TYPE_ACTION:
-                        if (!polkit_action_get_action_id (action, &str))
-                                goto out;
-                        str1 = g_strdup (str);
-                        break;
-                case MATCH_TYPE_USER:
-                        if (caller != NULL) {
-                                if (!polkit_caller_get_uid (caller, &uid))
-                                        goto out;
-                        } else if (session != NULL) {
-                                if (!polkit_session_get_uid (session, &uid))
-                                        goto out;
-                        } else
-                                goto out;
-
-                        str1 = g_strdup_printf ("%d", uid);
-                        {
-                                struct passwd pd;
-                                struct passwd* pwdptr=&pd;
-                                struct passwd* tempPwdPtr;
-                                char pwdbuffer[256];
-                                int  pwdlinelen = sizeof(pwdbuffer);
-
-                                if ((getpwuid_r (uid, pwdptr, pwdbuffer, pwdlinelen, &tempPwdPtr)) !=0 )
-                                        goto out;
-                                str2 = g_strdup (pd.pw_name);
-                        }
-                        break;
-                }
-
-                if (str1 != NULL) {
-                        if (regexec (&(node->data.node_match.preq), str1, 0, NULL, 0) == 0)
-                                match = TRUE;
-                }
-                if (!match && str2 != NULL) {
-                        if (regexec (&(node->data.node_match.preq), str2, 0, NULL, 0) == 0)
-                                match = TRUE;
-                }
-              
-
-                if (match)
+                if (config_node_match (node, action, caller, session))
                         recurse = TRUE;
-
-                g_free (str1);
-                g_free (str2);
                 break;
         case NODE_TYPE_RETURN:
                 result = node->data.node_return.result;
                 break;
+        default:
+                break;
         }
 
         if (recurse) {
@@ -553,7 +625,7 @@ out:
  * @session: the session in question
  *
  * Determine if the /etc/PolicyKit/PolicyKit.conf configuration file
- * says that a given session can do a given action.
+ * says that a given session can do a given action. 
  *
  * Returns: A #PolKitResult - returns #POLKIT_RESULT_UNKNOWN if there
  * was no match in the configuration file.
@@ -595,3 +667,88 @@ polkit_config_can_caller_do_action (PolK
                 result = POLKIT_RESULT_UNKNOWN;
         return result;
 }
+
+
+static polkit_bool_t
+config_node_determine_admin_auth (ConfigNode *node, 
+                                  PolKitAction                *action,
+                                  PolKitCaller                *caller,
+                                  PolKitConfigAdminAuthType   *out_admin_auth_type,
+                                  const char                 **out_data)
+{
+        gboolean recurse;
+        gboolean result_set;
+
+        recurse = FALSE;
+        result_set = FALSE;
+
+        switch (node->node_type) {
+        case NODE_TYPE_TOP:
+                recurse = TRUE;
+                break;
+        case NODE_TYPE_MATCH:
+                if (config_node_match (node, action, caller, NULL))
+                        recurse = TRUE;
+                break;
+        case NODE_TYPE_DEFINE_ADMIN_AUTH:
+                if (out_admin_auth_type != NULL)
+                        *out_admin_auth_type = node->data.node_define_admin_auth.admin_type;
+                if (out_data != NULL)
+                        *out_data = node->data.node_define_admin_auth.data;
+                result_set = TRUE;
+                break;
+        default:
+                break;
+        }
+
+        if (recurse) {
+                GSList *i;
+                for (i = node->children; i != NULL; i = g_slist_next (i)) {
+                        ConfigNode *child_node = i->data;
+
+                        result_set = config_node_determine_admin_auth (child_node, 
+                                                                       action, 
+                                                                       caller, 
+                                                                       out_admin_auth_type,
+                                                                       out_data) || result_set;
+                }
+        }
+
+        return result_set;
+}
+
+/**
+ * polkit_config_determine_auth_type:
+ * @pk_config: the PolicyKit context
+ * @action: the type of access to check for
+ * @caller: the caller in question
+ * @out_admin_auth_type: return location for the authentication type
+ * @out_data: return location for the match value of the given
+ * authentication type. Caller shall not manipulate or free this
+ * string.
+ *
+ * Determine what "Authenticate as admin" means for a given caller and
+ * a given action. This basically returns the result of the
+ * "define_admin_auth" in the configuration file when drilling down
+ * for a specific caller / action.
+ *
+ * Returns: TRUE if value was returned
+ */
+polkit_bool_t
+polkit_config_determine_admin_auth_type (PolKitConfig                *pk_config,
+                                         PolKitAction                *action,
+                                         PolKitCaller                *caller,
+                                         PolKitConfigAdminAuthType   *out_admin_auth_type,
+                                         const char                 **out_data)
+{
+        if (pk_config->top_config_node != NULL) {
+                return config_node_determine_admin_auth (pk_config->top_config_node,
+                                                         action, 
+                                                         caller, 
+                                                         out_admin_auth_type,
+                                                         out_data);
+        } else {
+                return FALSE;
+        }
+}
+
diff --git a/polkit/polkit-config.h b/polkit/polkit-config.h
index 24f5658..8a9a3a5 100644
--- a/polkit/polkit-config.h
+++ b/polkit/polkit-config.h
@@ -30,10 +30,11 @@
 #ifndef POLKIT_CONFIG_H
 #define POLKIT_CONFIG_H
 
+#include <sys/types.h>
+#include <regex.h>
 #include <polkit/polkit-error.h>
 #include <polkit/polkit-types.h>
 #include <polkit/polkit-result.h>
-#include <polkit/polkit-context.h>
 #include <polkit/polkit-action.h>
 #include <polkit/polkit-session.h>
 #include <polkit/polkit-caller.h>
@@ -55,6 +56,28 @@ polkit_config_can_caller_do_action      
                                                      PolKitAction    *action,
                                                      PolKitCaller    *caller);
 
+/**
+ * PolKitConfigAdminAuthType:
+ * @POLKIT_CONFIG_ADMIN_AUTH_TYPE_USER: Authentication as
+ * administrator matches one or more users
+ * @POLKIT_CONFIG_ADMIN_AUTH_TYPE_GROUP: Authentication as
+ * administrator matches users from one or more groups
+ *
+ * This enumeration reflects results defined in the
+ * "define_admin_auth" configuration element.
+ */
+typedef enum
+{
+        POLKIT_CONFIG_ADMIN_AUTH_TYPE_USER,
+        POLKIT_CONFIG_ADMIN_AUTH_TYPE_GROUP
+} PolKitConfigAdminAuthType;
+
+polkit_bool_t polkit_config_determine_admin_auth_type (PolKitConfig                *pk_config,
+                                                       PolKitAction                *action,
+                                                       PolKitCaller                *caller,
+                                                       PolKitConfigAdminAuthType   *out_admin_auth_type,
+                                                       const char                 **out_data);
+
 #endif /* POLKIT_CONFIG_H */
 
 
diff --git a/polkit/polkit-context.c b/polkit/polkit-context.c
index d8f2ed0..073b306 100644
--- a/polkit/polkit-context.c
+++ b/polkit/polkit-context.c
@@ -564,3 +564,23 @@ out:
         _pk_debug ("... result was %s", polkit_result_to_string_representation (result));
         return result;
 }
+
+/**
+ * polkit_context_get_config:
+ * @pk_context: the PolicyKit context
+ *
+ * Returns an object that provides access to the
+ * /etc/PolicyKit/PolicyKit.conf configuration files. Applications
+ * using PolicyKit should never use this method; it's only here for
+ * integration with other PolicyKit components.
+ *
+ * Returns: A #PolKitConfig object
+ */
+PolKitConfig *
+polkit_context_get_config (PolKitContext *pk_context)
+{
+        g_return_val_if_fail (pk_context != NULL, NULL);
+        return pk_context->config;
+}
+
+
diff --git a/polkit/polkit-context.h b/polkit/polkit-context.h
index 3e4fe29..b75f5a3 100644
--- a/polkit/polkit-context.h
+++ b/polkit/polkit-context.h
@@ -39,6 +39,7 @@
 #include <polkit/polkit-session.h>
 #include <polkit/polkit-caller.h>
 #include <polkit/polkit-policy-cache.h>
+#include <polkit/polkit-config.h>
 
 struct PolKitContext;
 typedef struct PolKitContext PolKitContext;
@@ -154,16 +155,16 @@ void           polkit_context_io_func   
 
 PolKitPolicyCache *polkit_context_get_policy_cache   (PolKitContext *pk_context);
 
-PolKitResult
-polkit_context_can_session_do_action                 (PolKitContext   *pk_context,
+PolKitResult polkit_context_can_session_do_action    (PolKitContext   *pk_context,
                                                       PolKitAction    *action,
                                                       PolKitSession   *session);
 
-PolKitResult
-polkit_context_can_caller_do_action                  (PolKitContext   *pk_context,
+PolKitResult polkit_context_can_caller_do_action     (PolKitContext   *pk_context,
                                                       PolKitAction    *action,
                                                       PolKitCaller    *caller);
 
+PolKitConfig *polkit_context_get_config (PolKitContext *pk_context);
+
 #endif /* POLKIT_CONTEXT_H */
 
 


More information about the hal-commit mailing list