From af39b832e3bf2e46fe58af74304d7f6f85e853bf Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 13 Aug 2007 13:44:33 -0400 Subject: [PATCH] export PolKitConfig and provide a config file directive Also change the libpolkit-grant API a bit to work with these changes. --- 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 +- 11 files changed, 616 insertions(+), 121 deletions(-) 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 @@ - + 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 following values .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 future. .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 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 + * 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 (PolKitResult *result) 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_grant) * @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_grant) **/ 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, 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 *polkit_grant, pid_t pid, int exit_code) 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 *polkit_grant, pid_t pid, int exit_code) 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 *polkit_grant, int fd) 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 *polkit_grant, int fd) 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 *polkit_grant, int fd) 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 *polkit_grant, int fd) 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 *polkit_grant) { 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 *polkit_grant) 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, 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) (PolKitGrant *polkit_grant, 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 (PolKitGrant *polkit_grant, 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, unsigned int indent) 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, const char **attr) 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, const char **attr) _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, const char **attr) } 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_config) 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; - result = POLKIT_RESULT_UNKNOWN; - recurse = FALSE; + match = FALSE; + str1 = NULL; + str2 = NULL; + switch (node->data.node_match.match_type) { - switch (node->node_type) { - case NODE_TYPE_TOP: - recurse = TRUE; + case MATCH_TYPE_ACTION: + if (!polkit_action_get_action_id (action, &str)) + goto out; + str1 = g_strdup (str); 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)) + + case MATCH_TYPE_USER: + if (caller != NULL) { + if (!polkit_caller_get_uid (caller, &uid)) 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 + } else if (session != NULL) { + if (!polkit_session_get_uid (session, &uid)) 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; + } 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 (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; +} - if (match) - recurse = TRUE; - g_free (str1); - g_free (str2); +/* 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; + + recurse = FALSE; + result = POLKIT_RESULT_UNKNOWN; + + switch (node->node_type) { + case NODE_TYPE_TOP: + recurse = TRUE; + break; + case NODE_TYPE_MATCH: + if (config_node_match (node, action, caller, session)) + recurse = TRUE; 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 (PolKitConfig *pk_config, 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 +#include #include #include #include -#include #include #include #include @@ -55,6 +56,28 @@ polkit_config_can_caller_do_action (PolKitConfig *pk_config, 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 #include #include +#include struct PolKitContext; typedef struct PolKitContext PolKitContext; @@ -154,16 +155,16 @@ void polkit_context_io_func (PolKitContext *pk_context, 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 */ -- 2.7.4