From ff9f8745cd9d7f22c80a6c6967d5f4014bac537e Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Tue, 20 Nov 2007 16:38:44 -0500 Subject: [PATCH] define abstract Authentication Agent interface and make polkit-auth(1) use it Also provide a convenience function to access it: polkit_auth_obtain(). --- data/Makefile.am | 5 +- ...g.freedesktop.PolicyKit.AuthenticationAgent.xml | 24 ++++ doc/man/polkit-auth.xml | 20 ++- doc/spec/polkit-spec-model.xml | 35 ++++- src/kit/kit-spawn.c | 67 +++++++--- src/kit/kit-spawn.h | 30 ++++- src/polkit-dbus/polkit-simple.c | 147 +++++++++++++++++++++ src/polkit-dbus/polkit-simple.h | 2 + src/polkit-grant/polkit-grant-helper.c | 1 - src/polkit/polkit-authorization-db.c | 2 + src/polkit/polkit-policy-file-entry.c | 1 + src/polkit/polkit-sysdeps.c | 4 +- tools/polkit-auth.c | 35 ++++- 13 files changed, 331 insertions(+), 42 deletions(-) create mode 100644 data/org.freedesktop.PolicyKit.AuthenticationAgent.xml diff --git a/data/Makefile.am b/data/Makefile.am index 59d24e3..36bd7c0 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -16,9 +16,12 @@ conf_DATA = PolicyKit.conf dtddir = $(datadir)/PolicyKit dtd_DATA = config.dtd +dbusifdir = $(datadir)/dbus-1/interfaces +dbusif_DATA = org.freedesktop.PolicyKit.AuthenticationAgent.xml + DISTCLEANFILES = polkit.pc polkit-dbus.pc polkit-grant.pc PolicyKit.conf -EXTRA_DIST = polkit.in polkit.pc.in polkit-dbus.pc.in polkit-grant.pc.in PolicyKit.conf.in config.dtd +EXTRA_DIST = polkit.in polkit.pc.in polkit-dbus.pc.in polkit-grant.pc.in PolicyKit.conf.in config.dtd org.freedesktop.PolicyKit.AuthenticationAgent.xml clean-local : rm -f *~ diff --git a/data/org.freedesktop.PolicyKit.AuthenticationAgent.xml b/data/org.freedesktop.PolicyKit.AuthenticationAgent.xml new file mode 100644 index 0000000..9101d19 --- /dev/null +++ b/data/org.freedesktop.PolicyKit.AuthenticationAgent.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/man/polkit-auth.xml b/doc/man/polkit-auth.xml index 638f3bb..f10b8e5 100644 --- a/doc/man/polkit-auth.xml +++ b/doc/man/polkit-auth.xml @@ -47,12 +47,20 @@ - Attempt to obtain the authorization to do an action. This - is only useful for implicit authorizations requiring - authentication; e.g. when an appropriate stanza in the - defaults section of the .policy file for the action - specifies - auth_*. + Attempt to obtain an authorization through authentication + for the given action. This is only useful for implicit + authorizations requiring authentication; e.g. when an + appropriate stanza in the defaults section of the .policy + file for the action specifies + auth_*. + + If an Authentication Agent (such as the one from + PolicyKit-gnome) is available in the session, it will used + for authentication unless the environment variable + POLKIT_AUTH_FORCE_TEXT is set. If the environment variable + POLKIT_AUTH_GRANT_TO_PID is set, the authorization will be + granted to that process id instead of the invoking process + (e.g. the shell from which polkit-auth is launched). diff --git a/doc/spec/polkit-spec-model.xml b/doc/spec/polkit-spec-model.xml index ccc4173..4d3b885 100644 --- a/doc/spec/polkit-spec-model.xml +++ b/doc/spec/polkit-spec-model.xml @@ -350,8 +350,12 @@ trivial) and security reasons (it is typically a good idea to have password handling in as few processes as possible) it is preferable to have this done in a separate - process. For details on the Authentication Agent for the - GNOME desktop, please see the Authentication + Agent section for details. For details on the + Authentication Agent for the GNOME desktop, please see + the PolicyKit-gnome documentation. @@ -532,7 +536,34 @@ active sessions and always require authentication for the Action dialup-connect-untrusted. + + + Authentication Agent + + To gain authorizations through authentication, an Authentication + Agent is used. The section defines an abstract interface that + applications can use to interact with such an agent. This allows + different desktop environments to implement different agents + with native look and feel. + + + The interface is quite simple. Basically, a PolicyKit + Authentication Agent must provide the D-Bus session service with + the unique + name org.freedesktop.PolicyKit.AuthenticationAgent + that exposes a single object with the path / that exports the + org.freedesktop.PolicyKit.AuthenticationAgent + D-Bus interface. The interface is defined by the following D-Bus + introspection data: + +FIXME: MISSING XINCLUDE CONTENT + + This file is available + as /usr/share/dbus-1/interfaces/org.freedesktop.PolicyKit.AuthenticationAgent.xml + on a system with PolicyKit development packages installed. It + can be used to generating client glue code. + diff --git a/src/kit/kit-spawn.c b/src/kit/kit-spawn.c index 9fe7739..6fb803a 100644 --- a/src/kit/kit-spawn.c +++ b/src/kit/kit-spawn.c @@ -166,13 +166,14 @@ out: * occured and errno will be set. */ kit_bool_t -kit_spawn_sync (const char *working_directory, - char **argv, - char **envp, - char *stdin, - char **stdout, - char **stderr, - int *out_exit_status) +kit_spawn_sync (const char *working_directory, + KitSpawnFlags flags, + char **argv, + char **envp, + char *stdin, + char **stdout, + char **stderr, + int *out_exit_status) { kit_bool_t ret; pid_t pid; @@ -186,6 +187,9 @@ kit_spawn_sync (const char *working_directory, kit_return_val_if_fail (argv != NULL, FALSE); kit_return_val_if_fail (out_exit_status != NULL, FALSE); + kit_return_val_if_fail (! ((flags & KIT_SPAWN_CHILD_INHERITS_STDIN) && stdin != NULL), FALSE); + kit_return_val_if_fail (! ((flags & KIT_SPAWN_STDOUT_TO_DEV_NULL) && stdout != NULL), FALSE); + kit_return_val_if_fail (! ((flags & KIT_SPAWN_STDERR_TO_DEV_NULL) && stderr != NULL), FALSE); if (stdout != NULL) *stdout = NULL; @@ -221,8 +225,19 @@ kit_spawn_sync (const char *working_directory, } if (pid == 0) { + int fd_null = -1; + /* child */ + if ( (!(flags & KIT_SPAWN_CHILD_INHERITS_STDIN)) || + (flags & KIT_SPAWN_STDOUT_TO_DEV_NULL) || + (flags & KIT_SPAWN_STDERR_TO_DEV_NULL)) { + fd_null = open ("/dev/null", O_RDONLY); + if (fd_null < 0) { + exit (128 + errno); + } + } + signal (SIGPIPE, SIG_DFL); /* close unused ends */ @@ -248,32 +263,39 @@ kit_spawn_sync (const char *working_directory, /* set stdin, stdout and stderr */ - if (stdin == NULL) { - int fd_null; - fd_null = open ("/dev/null", O_RDONLY); - if (fd_null < 0) { + if (stdin != NULL) { + if (_sane_dup2 (stdin_pipe[0], 0) < 0) { exit (128 + errno); } + } else if (! (flags & KIT_SPAWN_CHILD_INHERITS_STDIN)) { if (_sane_dup2 (fd_null, 0) < 0) { exit (128 + errno); } - } else { - if (_sane_dup2 (stdin_pipe[0], 0) < 0) { - exit (128 + errno); - } } + if (stdout != NULL) { if (_sane_dup2 (stdout_pipe[1], 1) < 0) { exit (128 + errno); } + } else if (flags & KIT_SPAWN_STDOUT_TO_DEV_NULL) { + if (_sane_dup2 (fd_null, 1) < 0) { + exit (128 + errno); + } } if (stderr != NULL) { if (_sane_dup2 (stderr_pipe[1], 2) < 0) { exit (128 + errno); } + } else if (flags & KIT_SPAWN_STDERR_TO_DEV_NULL) { + if (_sane_dup2 (fd_null, 2) < 0) { + exit (128 + errno); + } } + if (fd_null != -1) + close (fd_null); + /* finally, execute the child */ if (execve (argv[0], argv, envp_to_use) == -1) { exit (128 + errno); @@ -330,7 +352,6 @@ kit_spawn_sync (const char *working_directory, NULL); if (ret < 0 && errno != EINTR) { - kit_warning ("4"); goto out; } @@ -338,7 +359,6 @@ kit_spawn_sync (const char *working_directory, num_written = _write_to (stdin_pipe[1], wp); if (num_written == -1) { - kit_warning ("3"); goto out; } @@ -355,7 +375,6 @@ kit_spawn_sync (const char *working_directory, close (stdout_pipe[0]); stdout_pipe[0] = -1; } else if (num_read == -1) { - kit_warning ("2"); goto out; } } @@ -366,14 +385,12 @@ kit_spawn_sync (const char *working_directory, close (stderr_pipe[0]); stderr_pipe[0] = -1; } else if (num_read == -1) { - kit_warning ("1"); goto out; } } } if (waitpid (pid, out_exit_status, 0) == -1) { - kit_warning ("0"); goto out; } pid = -1; @@ -386,7 +403,6 @@ kit_spawn_sync (const char *working_directory, } else { ret = FALSE; errno = WEXITSTATUS (*out_exit_status) - 128; - kit_warning ("kiddo died with errno %d: %m!", errno); } out: @@ -463,6 +479,7 @@ _run_test (void) /* script echoing to stdout and stderr */ if (kit_file_set_contents (path, 0700, script1, strlen (script1))) { if (kit_spawn_sync ("/", + 0, argv, NULL, NULL, @@ -477,6 +494,7 @@ _run_test (void) } if (kit_spawn_sync ("/", + 0, argv, NULL, NULL, @@ -492,6 +510,7 @@ _run_test (void) /* silent script */ if (kit_file_set_contents (path, 0700, script2, strlen (script2))) { if (kit_spawn_sync ("/", + 0, argv, NULL, NULL, @@ -511,6 +530,7 @@ _run_test (void) char *envp[] = {"KIT_TEST_VAR=some_value", NULL}; if (kit_spawn_sync ("/", + 0, argv, envp, NULL, @@ -532,6 +552,7 @@ _run_test (void) kit_assert (setenv ("KIT_TEST_VAR", "foobar", 1) == 0); if (kit_spawn_sync ("/", + 0, argv, envp, NULL, @@ -549,6 +570,7 @@ _run_test (void) if (kit_file_set_contents (path, 0700, script5, strlen (script5))) { kit_assert (stat ("/tmp", &statbuf) == 0 && S_ISDIR (statbuf.st_mode)); if (kit_spawn_sync ("/tmp", + 0, argv, NULL, NULL, @@ -562,6 +584,7 @@ _run_test (void) kit_assert (stat ("/usr", &statbuf) == 0 && S_ISDIR (statbuf.st_mode)); if (kit_spawn_sync ("/usr", + 0, argv, NULL, NULL, @@ -579,6 +602,7 @@ _run_test (void) /* check bogus working directory */ kit_assert (stat ("/org/freedesktop/PolicyKit/bogus-fs-path", &statbuf) != 0); kit_assert (kit_spawn_sync ("/org/freedesktop/PolicyKit/bogus-fs-path", + 0, argv, NULL, NULL, @@ -590,6 +614,7 @@ _run_test (void) /* check for writing to stdin */ if (kit_file_set_contents (path, 0700, script6, strlen (script6))) { if (kit_spawn_sync (NULL, + 0, argv, NULL, "foobar0\nfoobar1", diff --git a/src/kit/kit-spawn.h b/src/kit/kit-spawn.h index 72b76f7..9a680c5 100644 --- a/src/kit/kit-spawn.h +++ b/src/kit/kit-spawn.h @@ -34,13 +34,29 @@ KIT_BEGIN_DECLS -kit_bool_t kit_spawn_sync (const char *working_directory, - char **argv, - char **envp, - char *stdin, - char **stdout, - char **stderr, - int *out_exit_status); +/** + * KitSpawnFlags: + * @KIT_SPAWN_CHILD_INHERITS_STDIN: If not set, child's stdin will be attached to /dev/null + * @KIT_SPAWN_STDOUT_TO_DEV_NULL: If set childs output will be sent to /dev/null + * @KIT_SPAWN_STDERR_TO_DEV_NULL: If set childs error output will be sent to /dev/null + * + * Flags passed to kit_spawn_sync(). + */ +typedef enum { + KIT_SPAWN_CHILD_INHERITS_STDIN = 1 << 0, + KIT_SPAWN_STDOUT_TO_DEV_NULL = 1 << 1, + KIT_SPAWN_STDERR_TO_DEV_NULL = 1 << 2, +} KitSpawnFlags; + + +kit_bool_t kit_spawn_sync (const char *working_directory, + KitSpawnFlags flags, + char **argv, + char **envp, + char *stdin, + char **stdout, + char **stderr, + int *out_exit_status); KIT_END_DECLS diff --git a/src/polkit-dbus/polkit-simple.c b/src/polkit-dbus/polkit-simple.c index af1c3c9..d6160dc 100644 --- a/src/polkit-dbus/polkit-simple.c +++ b/src/polkit-dbus/polkit-simple.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include "polkit-simple.h" +#include "polkit-dbus.h" /** @@ -231,6 +233,151 @@ out: return ret; } +extern char **environ; + +static polkit_bool_t +_auth_show_dialog_text (const char *action_id, pid_t pid, DBusError *error) +{ + unsigned int n; + polkit_bool_t ret; + int exit_status; + char *helper_argv[] = {PACKAGE_BIN_DIR "/polkit-auth", "--obtain", NULL, NULL}; + char **envp; + size_t envsize; + char buf[256]; + + ret = FALSE; + + if (isatty (STDOUT_FILENO) != 1 || isatty (STDIN_FILENO) != 1) { + dbus_set_error (error, + "org.freedesktop.PolicyKit.LocalError", + "stdout and/or stdin is not a tty"); + goto out; + } + + envsize = kit_strv_length (environ); + envp = kit_new0 (char *, envsize + 3); + if (envp == NULL) + goto out; + for (n = 0; n < envsize; n++) + envp[n] = environ[n]; + envp[envsize] = "POLKIT_AUTH_FORCE_TEXT=1"; + snprintf (buf, sizeof (buf), "POLKIT_AUTH_GRANT_TO_PID=%d", pid); + envp[envsize+1] = buf; + + helper_argv[2] = (char *) action_id; + + if (!kit_spawn_sync (NULL, /* const char *working_directory */ + KIT_SPAWN_CHILD_INHERITS_STDIN, /* flags */ + helper_argv, /* char **argv */ + envp, /* char **envp */ + NULL, /* char *stdin */ + NULL, /* char **stdout */ + NULL, /* char **stderr */ + &exit_status)) { /* int *exit_status */ + dbus_set_error (error, + "org.freedesktop.PolicyKit.LocalError", + "Error spawning polkit-auth: %m"); + goto out; + } + + if (!WIFEXITED (exit_status)) { + dbus_set_error (error, + "org.freedesktop.PolicyKit.LocalError", + "polkit-auth crashed!"); + goto out; + } else if (WEXITSTATUS(exit_status) != 0) { + goto out; + } + + ret = TRUE; + +out: + return ret; +} + +/** + * polkit_auth_obtain: + * @action_id: The action_id for the #PolKitAction to make the user + * authenticate for + * @xid: X11 window ID for the window that the dialog will be + * transient for. If there is no window, pass 0. + * @pid: Process ID of process to grant authorization to. Normally one wants to pass result of getpid(). + * @error: return location for error; cannot be %NULL + * + * Convenience function to prompt the user to authenticate to gain an + * authorization for the given action. First, an attempt to reach an + * Authentication Agent on the session message bus is made. If that + * doesn't work and stdout/stdin are both tty's, polkit-auth(1) is + * invoked. + * + * This is a blocking call. If you're using GTK+ see + * polkit_gnome_auth_obtain() for a non-blocking version. + * + * Returns: %TRUE if, and only if, the user successfully + * authenticated. %FALSE if the user failed to authenticate or if + * error is set + * + * Since: 0.7 + */ +polkit_bool_t +polkit_auth_obtain (const char *action_id, polkit_uint32_t xid, pid_t pid, DBusError *error) +{ + polkit_bool_t ret; + DBusConnection *bus; + DBusMessage *message; + DBusMessage *reply; + + kit_return_val_if_fail (action_id != NULL, FALSE); + kit_return_val_if_fail (error != NULL, FALSE); + kit_return_val_if_fail (!dbus_error_is_set (error), FALSE); + + bus = NULL; + message = NULL; + reply = NULL; + ret = FALSE; + + bus = dbus_bus_get (DBUS_BUS_SESSION, error); + if (bus == NULL) { + dbus_error_init (error); + ret = _auth_show_dialog_text (action_id, pid, error); + goto out; + } + + message = dbus_message_new_method_call ("org.freedesktop.PolicyKit.AuthenticationAgent", /* service */ + "/", /* object path */ + "org.freedesktop.PolicyKit.AuthenticationAgent", /* interface */ + "ObtainAuthorization"); + dbus_message_append_args (message, + DBUS_TYPE_STRING, &action_id, + DBUS_TYPE_UINT32, &xid, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block (bus, message, -1, error); + if (reply == NULL || dbus_error_is_set (error)) { + ret = _auth_show_dialog_text (action_id, pid, error); + goto out; + } + if (!dbus_message_get_args (reply, NULL, + DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID)) { + dbus_error_init (error); + ret = _auth_show_dialog_text (action_id, pid, error); + goto out; + } + +out: + if (bus != NULL) + dbus_connection_unref (bus); + if (message != NULL) + dbus_message_unref (message); + if (reply != NULL) + dbus_message_unref (reply); + + return ret; +} + + #ifdef POLKIT_BUILD_TESTS static polkit_bool_t diff --git a/src/polkit-dbus/polkit-simple.h b/src/polkit-dbus/polkit-simple.h index c982621..7d8b938 100644 --- a/src/polkit-dbus/polkit-simple.h +++ b/src/polkit-dbus/polkit-simple.h @@ -37,6 +37,8 @@ POLKIT_BEGIN_DECLS polkit_uint64_t polkit_check_auth (pid_t pid, ...); polkit_uint64_t polkit_check_authv (pid_t pid, const char **action_ids); +polkit_bool_t polkit_auth_obtain (const char *action_id, polkit_uint32_t xid, pid_t pid, DBusError *error); + POLKIT_END_DECLS #endif /* POLKIT_SIMPLE_H */ diff --git a/src/polkit-grant/polkit-grant-helper.c b/src/polkit-grant/polkit-grant-helper.c index d1694b1..e3b904f 100644 --- a/src/polkit-grant/polkit-grant-helper.c +++ b/src/polkit-grant/polkit-grant-helper.c @@ -52,7 +52,6 @@ */ #undef PGH_DEBUG /* #define PGH_DEBUG */ -#define PGH_DEBUG /* synopsis: polkit-grant-helper * diff --git a/src/polkit/polkit-authorization-db.c b/src/polkit/polkit-authorization-db.c index 7322355..c3b5c1b 100644 --- a/src/polkit/polkit-authorization-db.c +++ b/src/polkit/polkit-authorization-db.c @@ -278,6 +278,7 @@ _authdb_get_auths_for_uid (PolKitAuthorizationDB *authdb, * polkituser. */ if (!kit_spawn_sync (NULL, /* const char *working_directory */ + 0, /* flags */ helper_argv, /* char **argv */ NULL, /* char **envp */ NULL, /* char *stdin */ @@ -882,6 +883,7 @@ polkit_authorization_db_revoke_entry (PolKitAuthorizationDB *authdb, helper_argv[3] = kit_strdup_printf ("%d", polkit_authorization_get_uid (auth)); if (!kit_spawn_sync (NULL, /* const char *working_directory */ + 0, /* flags */ helper_argv, /* char **argv */ NULL, /* char **envp */ NULL, /* char *stdin */ diff --git a/src/polkit/polkit-policy-file-entry.c b/src/polkit/polkit-policy-file-entry.c index 5ef3702..6c23c2a 100644 --- a/src/polkit/polkit-policy-file-entry.c +++ b/src/polkit/polkit-policy-file-entry.c @@ -514,6 +514,7 @@ polkit_policy_file_entry_set_default (PolKitPolicyFileEntry *policy_file_entry, } if (!kit_spawn_sync (NULL, /* const char *working_directory */ + 0, /* flags */ helper_argv, /* char **argv */ NULL, /* char **envp */ NULL, /* char *stdin */ diff --git a/src/polkit/polkit-sysdeps.c b/src/polkit/polkit-sysdeps.c index 5d923d7..5a34ee9 100644 --- a/src/polkit/polkit-sysdeps.c +++ b/src/polkit/polkit-sysdeps.c @@ -129,7 +129,8 @@ out: * * Get the name of the binary a given process was started from. Note * that this is not reliable information; it should not be part of any - * security decision. + * security decision. If the information could not be obtained 0 is + * returned and out_buf will be set to "(unknown)". * * Returns: Number of characters written (not including trailing * '\0'). If the output was truncated due to the buffer being too @@ -151,6 +152,7 @@ polkit_sysdeps_get_exe_for_pid (pid_t pid, char *out_buf, size_t buf_size) snprintf (proc_name, sizeof (proc_name), "/proc/%d/exe", pid); ret = readlink (proc_name, out_buf, buf_size - 1); if (ret == -1) { + strncpy (out_buf, "(unknown)", buf_size); goto out; } kit_assert (ret >= 0 && ret < (int) buf_size - 1); diff --git a/tools/polkit-auth.c b/tools/polkit-auth.c index 4d73c0a..772b31a 100644 --- a/tools/polkit-auth.c +++ b/tools/polkit-auth.c @@ -652,6 +652,8 @@ main (int argc, char *argv[]) DBusError dbus_error; struct passwd *pw; uid_t uid; + pid_t pid; + char *s; ret = 1; @@ -672,6 +674,12 @@ main (int argc, char *argv[]) * we need to be able to run even when D-Bus and/or ConsoleKit aren't available... */ + if ((s = getenv ("POLKIT_AUTH_GRANT_TO_PID")) != NULL) { + pid = atoi (s); + } else { + pid = getppid (); + } + dbus_error_init (&dbus_error); system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error); if (system_bus != NULL) { @@ -679,7 +687,7 @@ main (int argc, char *argv[]) polkit_tracker_set_system_bus_connection (pk_tracker, system_bus); polkit_tracker_init (pk_tracker); - pk_caller = polkit_caller_new_from_pid (system_bus, getppid (), &dbus_error); + pk_caller = polkit_caller_new_from_pid (system_bus, pid, &dbus_error); if (pk_caller == NULL) { if (dbus_error_is_set (&dbus_error)) { fprintf (stderr, "polkit-auth: polkit_caller_new_from_dbus_name(): %s: %s\n", @@ -782,8 +790,29 @@ main (int argc, char *argv[]) if (!ensure_dbus_and_ck ()) goto out; - if (!obtain_authorization (opt_obtain_action_id)) - goto out; + if (getenv ("POLKIT_AUTH_FORCE_TEXT") != NULL) { + if (!obtain_authorization (opt_obtain_action_id)) + goto out; + } else { + DBusError dbus_error; + + dbus_error_init (&dbus_error); + if (!polkit_auth_obtain (opt_obtain_action_id, 0, pid, &dbus_error)) { + if (dbus_error_is_set (&dbus_error)) { + + /* fall back to text mode */ + if (!obtain_authorization (opt_obtain_action_id)) + goto out; + + //fprintf (stderr, + // "polkit-auth: failed to use session service: %s: %s\n", + // dbus_error.name, dbus_error.message); + } else { + goto out; + } + } + } + ret = 0; } else if (opt_grant_action_id != NULL || opt_block_action_id != NULL) { -- 2.7.4