Commit ff9f8745 authored by David Zeuthen's avatar David Zeuthen

define abstract Authentication Agent interface and make polkit-auth(1) use it

Also provide a convenience function to access it: polkit_auth_obtain().
parent 7c5fa7dd
......@@ -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 *~
......
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- This file is provided by the PolicyKit project -->
<node>
<interface name="org.freedesktop.PolicyKit.AuthenticationAgent">
<method name="ObtainAuthorization">
<!-- IN: PolicyKit action identifier; see PolKitAction -->
<arg name="action_id" direction="in" type="s"/>
<!-- IN: X11 window ID for the top-level X11 window the dialog will be transient for (pass zero if no window) -->
<arg name="xid" direction="in" type="u"/>
<!-- IN: Process ID to grant authorization to -->
<arg name="pid" direction="in" type="u"/>
<!-- OUT: whether the user gained the authorization -->
<arg name="gained_authorization" direction="out" type="b"/>
</method>
</interface>
</node>
......@@ -47,12 +47,20 @@
<term><option>--obtain <replaceable>action</replaceable></option></term>
<listitem>
<para>
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
<literal>auth_*</literal>.
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
<literal>auth_*</literal>.
</para><para>
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).
</para>
</listitem>
</varlistentry>
......
......@@ -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 <ulink type="http"
process. PolicyKit defines an abstract interface to
interact with the an Authentication Agent, see the
<link linkend="model-authentication-agent">Authentication
Agent section</link> for details. For details on the
Authentication Agent for the GNOME desktop, please see
the <ulink type="http"
url="http://hal.freedesktop.org/docs/PolicyKit-gnome/ref-auth-daemon.html">PolicyKit-gnome</ulink>
documentation.
</para>
......@@ -532,7 +536,34 @@
active sessions and always require authentication for the Action
<literal>dialup-connect-untrusted</literal>.
</para>
</sect1>
<sect1 id="model-authentication-agent">
<title>Authentication Agent</title>
<para>
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.
</para>
<para>
The interface is quite simple. Basically, a PolicyKit
Authentication Agent must provide the D-Bus session service with
the unique
name <literal>org.freedesktop.PolicyKit.AuthenticationAgent</literal>
that exposes a single object with the path <literal>/</literal> that exports the
<literal>org.freedesktop.PolicyKit.AuthenticationAgent</literal>
D-Bus interface. The interface is defined by the following D-Bus
introspection data:
<programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../data/org.freedesktop.PolicyKit.AuthenticationAgent.xml" parse="text"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting>
This file is available
as <literal>/usr/share/dbus-1/interfaces/org.freedesktop.PolicyKit.AuthenticationAgent.xml</literal>
on a system with PolicyKit development packages installed. It
can be used to generating client glue code.
</para>
</sect1>
</chapter>
......@@ -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",
......
......@@ -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 <literal>/dev/null</literal>
* @KIT_SPAWN_STDOUT_TO_DEV_NULL: If set childs output will be sent to <literal>/dev/null</literal>
* @KIT_SPAWN_STDERR_TO_DEV_NULL: If set childs error output will be sent to <literal>/dev/null</literal>
*
* 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
......
......@@ -40,6 +40,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
......@@ -47,6 +48,7 @@
#include <polkit/polkit-private.h>
#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
......
......@@ -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 */
......@@ -52,7 +52,6 @@
*/
#undef PGH_DEBUG
/* #define PGH_DEBUG */
#define PGH_DEBUG
/* synopsis: polkit-grant-helper <pid> <action-name>
*
......
......@@ -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 */
......
......@@ -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 */
......
......@@ -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);
......
<
......@@ -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",