PolicyKit: Branch 'master'
David Zeuthen
david at kemper.freedesktop.org
Thu Dec 6 22:38:53 PST 2007
doc/TODO | 4
src/polkit-dbus/Makefile.am | 16
src/polkit-dbus/polkit-resolve-exe-helper.c | 161 ++++++++++
src/polkit-grant/polkit-authorization-db-write.c | 7
src/polkit/polkit-authorization-constraint.c | 370 +++++++++++++++++++++--
src/polkit/polkit-authorization-constraint.h | 20 +
src/polkit/polkit-private.h | 2
src/polkit/polkit-sysdeps.c | 135 ++++++++
src/polkit/polkit-sysdeps.h | 2
tools/polkit-auth.c | 8
10 files changed, 687 insertions(+), 38 deletions(-)
New commits:
commit a8e46ceb39137582b0fbacb69fdfda72ae7139f8
Author: David Zeuthen <davidz at redhat.com>
Date: Fri Dec 7 01:35:30 2007 -0500
add constraints for exe and SELinux context when granting an authorization
The way it works is that added constraints now look like this
scope=always:action-id=org.pulseaudio.acquire-high-priority:when=1197004781:auth-as=0:constraint=local:constraint=active:constraint=exe%3A%2Fusr%2Fbin%2Fpulseaudio:constraint=selinux_context%3Asystem_u%3Asystem_r%3Aunconfined_t
or if not using SELinux like this
scope=always:action-id=org.freedesktop.hal.storage.mount-fixed:when=1197008218:auth-as=0:constraint=local:constraint=active:constraint=exe%3A%2Fusr%2Fbin%2Fgnome-mount
This is a bit icky to implement for mechanisms, like HAL, running as
an unprivileged user. The problem is that we can't resolve the symlink
/proc/pid/exe. On the other hands such mechanisms has the
authorization org.freedesktop.policykit.read already. So use that.
Note that this is what some people call snake-oil. The reason is in the
docs for polkit_sysdeps_get_pid_for_exe(); copying it here so I can point
people to this commit in the future
Get the name of the binary a given process was started from.
Note that this is not necessary reliable information and as such
shouldn't be relied on 100% to make a security decision. In fact,
this information is only trustworthy in situations where the given
binary is securely locked down meaning that 1) it can't be
ptrace(2)'d; 2) libc secure mode kicks in (e.g LD_PRELOAD won't
work); 3) there are no other attack vectors (e.g. GTK_MODULES, X11,
CORBA, D-Bus) to patch running code into the process.
In other words: the risk of relying on constraining an authorization
to the output of this function is high. Suppose that the program
/usr/bin/gullible obtains an authorization via authentication for
the action org.example.foo. We add a constraint to say that the
gained authorization only applies to processes for whom
/proc/pid/exe points to /usr/bin/gullible. Now enter
/usr/bin/evil. It knows that the program /usr/bin/gullible is not
"securely locked down" (per the definition in the above
paragraph). So /usr/bin/evil simply sets LD_PRELOAD and execs
/usr/bin/gullible and it can now run code in a process where
/proc/pid/exe points to /usr/bin/gullible. Thus, the recently gained
authorization for org.example.foo applies. Also, /usr/bin/evil could
use a host of other attack vectors to run it's own code under the
disguise of pretending to be /usr/bin/gullible.
Specifically for interpreted languages like Python and Mono it is
the case that /proc/pid/exe always points to /usr/bin/python
resp. /usr/bin/mono. Thus, it's not very useful to rely on that the
result for this function if you want to constrain an authorization
to e.g. /usr/bin/tomboy or /usr/bin/banshee.
However. Once we have a framework for running secure desktop apps this
will start to make sense. Such a framework includes securing X (using
e.g. XACE with SELinux) and making the UI toolkit secure as well. It's
a lot of work.
Until then these constraints at least makes it harder to for malicious
apps to abuse PolicyKit authorizations gained by other users.
diff --git a/doc/TODO b/doc/TODO
index b865d2c..fd51180 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -54,3 +54,7 @@
- Include the patch from Piter PUNK to optionally avoid the PAM
dependency (manually checks against /etc/shadow instead)
+
+ - To avoid work we should maintain a cache in the get_exe_for_pid()
+ functions. The key into the cache should be (pid, pid_start_time)
+ and the values should be the exe-paths
diff --git a/src/polkit-dbus/Makefile.am b/src/polkit-dbus/Makefile.am
index 2d4d60b..19601a7 100644
--- a/src/polkit-dbus/Makefile.am
+++ b/src/polkit-dbus/Makefile.am
@@ -29,8 +29,14 @@ libpolkit_dbus_la_LIBADD = @DBUS_LIBS@ $(top_builddir)/src/polkit/libpolkit.la $
libpolkit_dbus_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) @R_DYNAMIC_LDFLAG@
+libexec_PROGRAMS = polkit-resolve-exe-helper
+
+polkit_resolve_exe_helper_SOURCES = polkit-resolve-exe-helper.c
+polkit_resolve_exe_helper_CFLAGS = @DBUS_CFLAGS@
+polkit_resolve_exe_helper_LDADD = $(top_builddir)/src/polkit/libpolkit.la libpolkit-dbus.la
+
if POLKIT_AUTHDB_DEFAULT
-libexec_PROGRAMS = polkit-read-auth-helper polkit-set-default-helper
+libexec_PROGRAMS += polkit-read-auth-helper polkit-set-default-helper
polkit_read_auth_helper_SOURCES = polkit-read-auth-helper.c
polkit_read_auth_helper_CFLAGS = @DBUS_CFLAGS@
@@ -47,13 +53,21 @@ polkit_set_default_helper_LDADD = $(top_builddir)/src/polkit/libpolkit.la libpol
# polkit-set-default-helper needs to be setgid $POLKIT_GROUP to be able
# to write .override files in /var/lib/PolicyKit-public
#
+# polkit-resolve-exe-helper needs to be setuid root to be able to resolve
+# /proc/$pid/exe symlinks.
+#
install-exec-hook:
-chgrp $(POLKIT_GROUP) $(DESTDIR)$(libexecdir)/polkit-read-auth-helper
-chmod 2755 $(DESTDIR)$(libexecdir)/polkit-read-auth-helper
-chgrp $(POLKIT_GROUP) $(DESTDIR)$(libexecdir)/polkit-set-default-helper
-chmod 2755 $(DESTDIR)$(libexecdir)/polkit-set-default-helper
+ -chmod 4755 $(DESTDIR)$(libexecdir)/polkit-resolve-exe-helper
+else
+install-exec-hook:
+ -chmod 4755 $(DESTDIR)$(libexecdir)/polkit-resolve-exe-helper
endif
+
## note that TESTS has special meaning (stuff to use in make check)
## so if adding tests not to be run in make check, don't add them to
## TESTS
diff --git a/src/polkit-dbus/polkit-resolve-exe-helper.c b/src/polkit-dbus/polkit-resolve-exe-helper.c
new file mode 100644
index 0000000..181e483
--- /dev/null
+++ b/src/polkit-dbus/polkit-resolve-exe-helper.c
@@ -0,0 +1,161 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/***************************************************************************
+ *
+ * polkit-resolve-exe-helper.c : setuid root helper for PolicyKit to
+ * resolve /proc/$pid/exe symlinks
+ *
+ * Copyright (C) 2007 David Zeuthen, <david at fubar.dk>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <security/pam_appl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <polkit-dbus/polkit-dbus.h>
+#include <polkit/polkit-private.h>
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret;
+ uid_t caller_uid;
+ pid_t requesting_info_for_pid;
+ char *endp;
+ uid_t uid_for_polkit_user;
+ struct passwd *pw;
+ gid_t egid;
+ struct group *group;
+ int n;
+ char buf[PATH_MAX];
+ polkit_bool_t is_setgid_polkit;
+
+ ret = 1;
+
+ /* clear the entire environment to avoid attacks using with libraries honoring environment variables */
+ if (clearenv () != 0)
+ goto out;
+ /* set a minimal environment */
+ setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
+
+ openlog ("polkit-resolve-exe-helper", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+
+ /* check for correct invocation */
+ if (argc != 2) {
+ syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ());
+ fprintf (stderr, "polkit-resolve-exe-helper: wrong number of arguments. This incident has been logged.\n");
+ goto out;
+ }
+
+ caller_uid = getuid ();
+
+ /* check we're running with a non-tty stdin */
+ if (isatty (STDIN_FILENO) != 0) {
+ syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ());
+ fprintf (stderr, "polkit-resolve-exe-helper: inappropriate use of helper, stdin is a tty. This incident has been logged.\n");
+ goto out;
+ }
+
+ pw = getpwnam (POLKIT_USER);
+ if (pw == NULL) {
+ fprintf (stderr, "polkit-resolve-exe-helper: cannot lookup uid for " POLKIT_USER "\n");
+ goto out;
+ }
+ uid_for_polkit_user = pw->pw_uid;
+
+ /* check if we are setgid polkituser */
+ egid = getegid ();
+ group = getgrgid (egid);
+ if (group == NULL) {
+ fprintf (stderr, "polkit-resolve-exe-helper: cannot lookup group info for gid %d\n", egid);
+ goto out;
+ }
+ if (strcmp (group->gr_name, POLKIT_GROUP) == 0) {
+ is_setgid_polkit = TRUE;
+ } else {
+ is_setgid_polkit = FALSE;
+ }
+
+ /*----------------------------------------------------------------------------------------------------*/
+
+ requesting_info_for_pid = strtoul (argv[1], &endp, 10);
+ if (*endp != '\0') {
+ fprintf (stderr, "polkit-resolve-exe-helper: requesting_info_for_pid malformed\n");
+ goto out;
+ }
+
+ /* user polkituser is allowed to resolve anything. So is any program that is setgid polkituser. */
+ if (caller_uid != uid_for_polkit_user && !is_setgid_polkit) {
+ pid_t ppid;
+
+ ppid = getppid ();
+ if (ppid == 1)
+ goto out;
+
+ /* need to set the real uid of the process to root ... otherwise D-Bus won't work */
+ if (setuid (0) != 0) {
+ fprintf (stderr, "polkit-resolve-exe-helper: cannot do setuid(0): %m\n");
+ goto out;
+ }
+
+ if (polkit_check_auth (ppid,
+ "org.freedesktop.policykit.read", NULL) == 0) {
+ fprintf (stderr, "polkit-resolve-exe-helper: not authorized for org.freedesktop.policykit.read\n");
+ goto out;
+ }
+ }
+
+ n = polkit_sysdeps_get_exe_for_pid (requesting_info_for_pid, buf, sizeof (buf));
+ if (n == -1 || n >= (int) sizeof (buf)) {
+ fprintf (stderr, "polkit-resolve-exe-helper: Cannot resolve link for pid %d\n",
+ requesting_info_for_pid);
+ goto out;
+ }
+
+ printf ("%s", buf);
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
diff --git a/src/polkit-grant/polkit-authorization-db-write.c b/src/polkit-grant/polkit-authorization-db-write.c
index a6e8099..6aa8ce2 100644
--- a/src/polkit-grant/polkit-authorization-db-write.c
+++ b/src/polkit-grant/polkit-authorization-db-write.c
@@ -267,14 +267,17 @@ static polkit_bool_t
_add_caller_constraints (char *buf, size_t buf_size, PolKitCaller *caller)
{
PolKitAuthorizationConstraint *constraints[64];
- size_t num_constraints;
+ int num_constraints;
polkit_bool_t ret;
int num_written;
- unsigned int n;
+ int n;
ret = FALSE;
num_constraints = polkit_authorization_constraint_get_from_caller (caller, constraints, 64);
+ if (num_constraints == -1)
+ goto out;
+
if (num_constraints >= 64) {
goto out;
}
diff --git a/src/polkit/polkit-authorization-constraint.c b/src/polkit/polkit-authorization-constraint.c
index f864d72..49f6018 100644
--- a/src/polkit/polkit-authorization-constraint.c
+++ b/src/polkit/polkit-authorization-constraint.c
@@ -40,6 +40,7 @@
#include <grp.h>
#include <unistd.h>
#include <errno.h>
+#include <limits.h>
#include "polkit-debug.h"
#include "polkit-authorization-constraint.h"
@@ -72,6 +73,15 @@ struct _PolKitAuthorizationConstraint
{
int refcount;
PolKitAuthorizationConstraintType type;
+
+ union {
+ struct {
+ char *path;
+ } exe;
+ struct {
+ char *context;
+ } selinux_context;
+ };
};
static PolKitAuthorizationConstraint _local_constraint = {-1,
@@ -80,8 +90,8 @@ static PolKitAuthorizationConstraint _local_constraint = {-1,
static PolKitAuthorizationConstraint _active_constraint = {-1,
POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE};
-PolKitAuthorizationConstraint *
-_polkit_authorization_constraint_new (const char *entry_in_auth_file)
+static PolKitAuthorizationConstraint *
+_polkit_authorization_constraint_new (void)
{
PolKitAuthorizationConstraint *authc;
authc = kit_new0 (PolKitAuthorizationConstraint, 1);
@@ -132,6 +142,20 @@ polkit_authorization_constraint_unref (PolKitAuthorizationConstraint *authc)
if (authc->refcount > 0)
return;
+ switch (authc->type) {
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_LOCAL:
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE:
+ break;
+
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE:
+ kit_free (authc->exe.path);
+ break;
+
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT:
+ kit_free (authc->selinux_context.context);
+ break;
+ }
+
kit_free (authc);
}
@@ -229,6 +253,10 @@ polkit_bool_t
polkit_authorization_constraint_check_caller (PolKitAuthorizationConstraint *authc,
PolKitCaller *caller)
{
+ int n;
+ pid_t pid;
+ char *selinux_context;
+ char buf[PATH_MAX];
polkit_bool_t ret;
PolKitSession *session;
@@ -238,13 +266,48 @@ polkit_authorization_constraint_check_caller (PolKitAuthorizationConstraint *aut
ret = FALSE;
/* caller may not be in a session */
- if (polkit_caller_get_ck_session (caller, &session) && session != NULL) {
- ret = polkit_authorization_constraint_check_session (authc, session);
- } else {
- if (authc->type != POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_LOCAL &&
- authc->type != POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE) {
+
+ switch (authc->type) {
+ /* explicit fallthrough */
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_LOCAL:
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE:
+ if (polkit_caller_get_ck_session (caller, &session) && session != NULL) {
+ ret = polkit_authorization_constraint_check_session (authc, session);
+ }
+ break;
+
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE:
+ if (polkit_caller_get_pid (caller, &pid)) {
+
+ /* we may be running unprivileged.. so optionally use the helper. Requires the calling
+ * process (this one) to have the org.freedesktop.policykit.read authorization.
+ *
+ * An example of this is HAL (running as user 'haldaemon').
+ */
+ n = polkit_sysdeps_get_exe_for_pid_with_helper (pid, buf, sizeof (buf));
+
+ if (n != -1 && n < (int) sizeof (buf)) {
+ if (strcmp (authc->exe.path, buf) == 0) {
+ ret = TRUE;
+ }
+ }
+ }
+
+ break;
+
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT:
+ if (polkit_caller_get_selinux_context (caller, &selinux_context) && selinux_context != NULL) {
+ if (strcmp (authc->selinux_context.context, selinux_context) == 0) {
+ ret = TRUE;
+ }
+ } else {
+ /* if SELinux context is not set then SELinux is not enabled (or the
+ * caller made a mistake and didn't set it); thus, the authorization can
+ * never apply
+ */
ret = TRUE;
}
+ break;
}
return ret;
@@ -258,7 +321,7 @@ polkit_authorization_constraint_check_caller (PolKitAuthorizationConstraint *aut
* authorization to present information to the user (e.g. as
* polkit-auth(1) does).
*
- * Returns: type from #PolKitAuthorizationConstraintFlags
+ * Returns: type from #PolKitAuthorizationConstraintType
*
* Since: 0.7
*/
@@ -270,12 +333,54 @@ polkit_authorization_constraint_type (PolKitAuthorizationConstraint *authc)
}
/**
+ * polkit_authorization_constraint_get_exe:
+ * @authc: the object
+ *
+ * Get the exe path for the constraint.
+ *
+ * Returns: The exe path or #NULL if type isn't
+ * #POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE. Caller shall not
+ * free this string.
+ *
+ * Since: 0.8
+ */
+const char *
+polkit_authorization_constraint_get_exe (PolKitAuthorizationConstraint *authc)
+{
+ kit_return_val_if_fail (authc != NULL, NULL);
+ kit_return_val_if_fail (authc->type == POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE, NULL);
+
+ return authc->exe.path;
+}
+
+/**
+ * polkit_authorization_constraint_get_selinux_context:
+ * @authc: the object
+ *
+ * Get the SELinux context for the constraint.
+ *
+ * Returns: The selinux context or #NULL if type isn't
+ * #POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT. Caller
+ * shall not free this string.
+ *
+ * Since: 0.8
+ */
+const char *
+polkit_authorization_constraint_get_selinux_context (PolKitAuthorizationConstraint *authc)
+{
+ kit_return_val_if_fail (authc != NULL, NULL);
+ kit_return_val_if_fail (authc->type == POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT, NULL);
+
+ return authc->selinux_context.context;
+}
+
+/**
* polkit_authorization_constraint_get_require_local:
*
* Get a #PolKitAuthorizationConstraint object that represents the
* constraint that the session or caller must be local.
*
- * Returns: the constraint; the caller shall not unref this object
+ * Returns: the constraint
*
* Since: 0.7
*/
@@ -291,7 +396,7 @@ polkit_authorization_constraint_get_require_local (void)
* Get a #PolKitAuthorizationConstraint object that represents the
* constraint that the session or caller must be active.
*
- * Returns: the constraint; the caller shall not unref this object
+ * Returns: the constraint
*
* Since: 0.7
*/
@@ -302,6 +407,70 @@ polkit_authorization_constraint_get_require_active (void)
}
/**
+ * polkit_authorization_constraint_get_require_exe:
+ * @path: path to program
+ *
+ * Get a #PolKitAuthorizationConstraint object that represents the
+ * constraint that the caller must be a specific program
+ *
+ * Returns: the constraint or #NULL on OOM
+ *
+ * Since: 0.8
+ */
+PolKitAuthorizationConstraint *
+polkit_authorization_constraint_get_require_exe (const char *path)
+{
+ PolKitAuthorizationConstraint *authc;
+
+ kit_return_val_if_fail (path != NULL, NULL);
+
+ authc = _polkit_authorization_constraint_new ();
+ if (authc == NULL)
+ goto out;
+ authc->type = POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE;
+ authc->exe.path = kit_strdup (path);
+ if (authc->exe.path == NULL) {
+ polkit_authorization_constraint_unref (authc);
+ authc = NULL;
+ }
+
+out:
+ return authc;
+}
+
+/**
+ * polkit_authorization_constraint_get_require_selinux_context:
+ * @context: SELinux context
+ *
+ * Get a #PolKitAuthorizationConstraint object that represents the
+ * constraint that the caller must be in a specific SELinux context.
+ *
+ * Returns: the constraint or #NULL on OOM
+ *
+ * Since: 0.8
+ */
+PolKitAuthorizationConstraint *
+polkit_authorization_constraint_get_require_selinux_context (const char *context)
+{
+ PolKitAuthorizationConstraint *authc;
+
+ kit_return_val_if_fail (context != NULL, NULL);
+
+ authc = _polkit_authorization_constraint_new ();
+ if (authc == NULL)
+ goto out;
+ authc->type = POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT;
+ authc->selinux_context.context = kit_strdup (context);
+ if (authc->selinux_context.context == NULL) {
+ polkit_authorization_constraint_unref (authc);
+ authc = NULL;
+ }
+
+out:
+ return authc;
+}
+
+/**
* polkit_authorization_constraint_to_string:
* @authc: the object
* @out_buf: buffer to store the string representation in
@@ -324,15 +493,20 @@ polkit_authorization_constraint_to_string (PolKitAuthorizationConstraint *authc,
kit_return_val_if_fail (authc != NULL, buf_size);
switch (authc->type) {
- default:
- return snprintf (out_buf, buf_size, "none");
-
case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_LOCAL:
return snprintf (out_buf, buf_size, "local");
case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE:
return snprintf (out_buf, buf_size, "active");
+
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE:
+ return snprintf (out_buf, buf_size, "exe:%s", authc->exe.path);
+
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT:
+ return snprintf (out_buf, buf_size, "selinux_context:%s", authc->selinux_context.context);
}
+
+ return 0;
}
/**
@@ -359,6 +533,12 @@ polkit_authorization_constraint_from_string (const char *str)
} else if (strcmp (str, "active") == 0) {
ret = polkit_authorization_constraint_get_require_active ();
goto out;
+ } else if (strncmp (str, "exe:", 4) == 0) {
+ ret = polkit_authorization_constraint_get_require_exe (str + 4);
+ goto out;
+ } else if (strncmp (str, "selinux_context:", 16) == 0) {
+ ret = polkit_authorization_constraint_get_require_selinux_context (str + 16);
+ goto out;
}
out:
@@ -383,22 +563,29 @@ out:
* The caller must unref all the created objects using
* polkit_authorization_constraint_unref().
*
- * Returns: This function do not create more than @array_size constraints
- * (including the trailing %NULL). If the output was truncated due to
- * this limit then the return value is the number of objects (not
- * including the trailing %NULL) which would have been written to the
- * final array if enough space had been available. Thus, a return
- * value of @array_size or more means that the output was truncated.
+ * Returns: If OOM -1 is returned. This function do not create more
+ * than @array_size constraints (including the trailing %NULL). If the
+ * output was truncated due to this limit then the return value is the
+ * number of objects (not including the trailing %NULL) which would
+ * have been written to the final array if enough space had been
+ * available. Thus, a return value of @array_size or more means that
+ * the output was truncated.
+ *
+ * Since: 0.7
*/
-size_t
+int
polkit_authorization_constraint_get_from_caller (PolKitCaller *caller,
PolKitAuthorizationConstraint **out_array,
size_t array_size)
{
- unsigned int ret;
+ pid_t pid;
+ char *selinux_context;
+ int ret;
polkit_bool_t is_local;
polkit_bool_t is_active;
PolKitSession *session;
+ char path[PATH_MAX];
+ int n;
kit_return_val_if_fail (caller != NULL, 0);
kit_return_val_if_fail (out_array != NULL, 0);
@@ -413,22 +600,67 @@ polkit_authorization_constraint_get_from_caller (PolKitCaller *caller,
polkit_session_get_ck_is_active (session, &is_active);
if (is_local) {
- if (ret < array_size)
+ if (ret < (int) array_size)
out_array[ret] = polkit_authorization_constraint_get_require_local ();
ret++;
}
if (is_active) {
- if (ret < array_size)
+ if (ret < (int) array_size)
out_array[ret] = polkit_authorization_constraint_get_require_active ();
ret++;
}
+ /* constrain to callers program */
+ if (polkit_caller_get_pid (caller, &pid)) {
+ /* So the program to receive a constraint may besetuid root... so we may need some
+ * help to get the exepath.. Therefore use _with_helper().
+ *
+ * This works because this function is normally only called from polkit-grant-helper which
+ * is setgid polkituser.. this means that _with_helper will succeed.
+ *
+ * An example of this is pulseaudio...
+ */
+ n = polkit_sysdeps_get_exe_for_pid_with_helper (pid, path, sizeof (path));
+ if (n != -1 && n < (int) sizeof (path)) {
+ PolKitAuthorizationConstraint *c;
+
+ c = polkit_authorization_constraint_get_require_exe (path);
+ if (c == NULL)
+ goto oom;
+
+ if (ret < (int) array_size)
+ out_array[ret] = c;
+
+ ret++;
+ }
+ }
+
+ /* constrain to callers SELinux context */
+ if (polkit_caller_get_selinux_context (caller, &selinux_context) && selinux_context != NULL) {
+ PolKitAuthorizationConstraint *c;
+
+ c = polkit_authorization_constraint_get_require_selinux_context (selinux_context);
+ if (c == NULL)
+ goto oom;
+
+ if (ret < (int) array_size)
+ out_array[ret] = c;
+
+ ret++;
+ }
+
out:
- if (ret < array_size)
+ if (ret < (int) array_size)
out_array[ret] = NULL;
return ret;
+
+oom:
+ for (n = 0; n < ret; n++) {
+ polkit_authorization_constraint_unref (out_array[n]);
+ }
+ return -1;
}
/**
@@ -445,11 +677,28 @@ out:
polkit_bool_t
polkit_authorization_constraint_equal (PolKitAuthorizationConstraint *a, PolKitAuthorizationConstraint *b)
{
+ polkit_bool_t ret;
+
kit_return_val_if_fail (a != NULL, FALSE);
kit_return_val_if_fail (b != NULL, FALSE);
- /* When we add more types this needs expansion */
- return a->type == b->type;
+ ret = FALSE;
+
+ if (a->type != b->type)
+ goto out;
+
+ if (a->type == POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE) {
+ if (strcmp (a->exe.path, b->exe.path) != 0)
+ goto out;
+ } else if (a->type == POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT) {
+ if (strcmp (a->selinux_context.context, b->selinux_context.context) != 0)
+ goto out;
+ }
+
+ ret = TRUE;
+
+out:
+ return ret;
}
#ifdef POLKIT_BUILD_TESTS
@@ -538,12 +787,14 @@ static polkit_bool_t
_run_test (void)
{
PolKitAuthorizationConstraint *ac;
+ PolKitAuthorizationConstraint *ac2;
PolKitAuthorizationConstraintType type;
PolKitSession *s_active;
PolKitSession *s_inactive;
PolKitSession *s_active_remote;
PolKitSession *s_inactive_remote;
polkit_bool_t ret;
+ char buf[256];
if ((s_active = polkit_session_new ()) != NULL) {
if (!polkit_session_set_ck_objref (s_active, "/session1")) {
@@ -609,6 +860,9 @@ _run_test (void)
#if 0
+ /* TODO: redo these tests; they are supposed to to verify that
+ * polkit_authorization_constraint_get_from_caller() works()
+ */
for (n = 0; n < 4; n++) {
PolKitSession *s;
polkit_bool_t expected[4];
@@ -651,13 +905,75 @@ _run_test (void)
}
#endif
- if ((ac = _polkit_authorization_constraint_new ("local")) != NULL) {
+ if ((ac = _polkit_authorization_constraint_new ()) != NULL) {
polkit_authorization_constraint_validate (ac);
polkit_authorization_constraint_debug (ac);
polkit_authorization_constraint_ref (ac);
polkit_authorization_constraint_unref (ac);
polkit_authorization_constraint_unref (ac);
}
+
+ char our_exe[256];
+ int n;
+ n = polkit_sysdeps_get_exe_for_pid (getpid (), our_exe, sizeof (our_exe));
+ kit_assert (n != -1);
+ kit_assert (n < (int) sizeof (our_exe));
+
+ if ((ac = polkit_authorization_constraint_get_require_exe (our_exe)) != NULL) {
+ const char *s;
+ PolKitCaller *c;
+
+ kit_assert ((s = polkit_authorization_constraint_get_exe (ac)) != NULL && strcmp (s, our_exe) == 0);
+ kit_assert (polkit_authorization_constraint_to_string (ac, buf, sizeof (buf)) < sizeof (buf));
+ if ((ac2 = polkit_authorization_constraint_from_string (buf)) != NULL) {
+ kit_assert (polkit_authorization_constraint_equal (ac, ac2));
+ polkit_authorization_constraint_unref (ac2);
+ }
+
+ if ((c = polkit_caller_new ()) != NULL) {
+ kit_assert (polkit_caller_set_pid (c, getpid ()));
+ kit_assert (polkit_authorization_constraint_check_caller (ac, c));
+ kit_assert (polkit_caller_set_pid (c, getppid ()));
+ kit_assert (! polkit_authorization_constraint_check_caller (ac, c));
+ polkit_caller_unref (c);
+ }
+
+
+ polkit_authorization_constraint_unref (ac);
+ }
+
+ if ((ac = polkit_authorization_constraint_get_require_selinux_context ("httpd_exec_t")) != NULL) {
+ const char *s;
+ PolKitCaller *c;
+
+ kit_assert ((s = polkit_authorization_constraint_get_selinux_context (ac)) != NULL &&
+ strcmp (s, "httpd_exec_t") == 0);
+ kit_assert (polkit_authorization_constraint_to_string (ac, buf, sizeof (buf)) < sizeof (buf));
+ if ((ac2 = polkit_authorization_constraint_from_string (buf)) != NULL) {
+ kit_assert (polkit_authorization_constraint_equal (ac, ac2));
+ polkit_authorization_constraint_unref (ac2);
+ }
+
+ if ((c = polkit_caller_new ()) != NULL) {
+ kit_assert (polkit_caller_set_pid (c, getpid ()));
+
+ if (polkit_caller_set_selinux_context (c, "httpd_exec_t")) {
+ kit_assert (polkit_authorization_constraint_check_caller (ac, c));
+ } else {
+ kit_assert (errno == ENOMEM);
+ }
+
+ if (polkit_caller_set_selinux_context (c, "hald_exec_t")) {
+ kit_assert (! polkit_authorization_constraint_check_caller (ac, c));
+ } else {
+ kit_assert (errno == ENOMEM);
+ }
+
+ polkit_caller_unref (c);
+ }
+
+ polkit_authorization_constraint_unref (ac);
+ }
if (s_active != NULL)
polkit_session_unref (s_active);
diff --git a/src/polkit/polkit-authorization-constraint.h b/src/polkit/polkit-authorization-constraint.h
index 9c0dd80..a778693 100644
--- a/src/polkit/polkit-authorization-constraint.h
+++ b/src/polkit/polkit-authorization-constraint.h
@@ -49,13 +49,23 @@ POLKIT_BEGIN_DECLS
* caller must be local
* @POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE: the session or
* caller must be in an active session
+ * @POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE: the caller must
+ * be a specific program; use
+ * polkit_authorization_constraint_get_exe() to get the path of the
+ * program.
+ * @POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT: the
+ * caller must be in a specific security context
+ * polkit_authorization_constraint_get_selinux_context() to get the
+ * security context.
*
* This enumeration describes the type of the authorization
* constraint.
*/
typedef enum {
POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_LOCAL,
- POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE
+ POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE,
+ POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE,
+ POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT,
} PolKitAuthorizationConstraintType;
struct _PolKitAuthorizationConstraint;
@@ -63,6 +73,8 @@ typedef struct _PolKitAuthorizationConstraint PolKitAuthorizationConstraint;
PolKitAuthorizationConstraint *polkit_authorization_constraint_get_require_local (void);
PolKitAuthorizationConstraint *polkit_authorization_constraint_get_require_active (void);
+PolKitAuthorizationConstraint *polkit_authorization_constraint_get_require_exe (const char *path);
+PolKitAuthorizationConstraint *polkit_authorization_constraint_get_require_selinux_context (const char *context);
PolKitAuthorizationConstraint *polkit_authorization_constraint_ref (PolKitAuthorizationConstraint *authc);
void polkit_authorization_constraint_unref (PolKitAuthorizationConstraint *authc);
@@ -71,6 +83,10 @@ polkit_bool_t polkit_authorization_constraint_validate (PolKitA
PolKitAuthorizationConstraintType polkit_authorization_constraint_type (PolKitAuthorizationConstraint *authc);
+const char *polkit_authorization_constraint_get_exe (PolKitAuthorizationConstraint *authc);
+
+const char *polkit_authorization_constraint_get_selinux_context (PolKitAuthorizationConstraint *authc);
+
polkit_bool_t polkit_authorization_constraint_check_session (PolKitAuthorizationConstraint *authc,
PolKitSession *session);
@@ -80,7 +96,7 @@ polkit_bool_t polkit_authorization_constraint_check_caller (PolKitAuthorizationC
size_t polkit_authorization_constraint_to_string (PolKitAuthorizationConstraint *authc, char *out_buf, size_t buf_size);
PolKitAuthorizationConstraint *polkit_authorization_constraint_from_string (const char *str);
-size_t polkit_authorization_constraint_get_from_caller (PolKitCaller *caller, PolKitAuthorizationConstraint **out_array, size_t array_size);
+int polkit_authorization_constraint_get_from_caller (PolKitCaller *caller, PolKitAuthorizationConstraint **out_array, size_t array_size);
polkit_bool_t polkit_authorization_constraint_equal (PolKitAuthorizationConstraint *a,
PolKitAuthorizationConstraint *b);
diff --git a/src/polkit/polkit-private.h b/src/polkit/polkit-private.h
index dd8f741..934bcb0 100644
--- a/src/polkit/polkit-private.h
+++ b/src/polkit/polkit-private.h
@@ -55,8 +55,6 @@ void _polkit_memory_fail_nth_alloc (int number);
PolKitAuthorization *_polkit_authorization_new_for_uid (const char *entry_in_auth_file, uid_t uid);
const char *_polkit_authorization_get_authfile_entry (PolKitAuthorization *auth);
-PolKitAuthorizationConstraint *_polkit_authorization_constraint_new (const char *entry_in_auth_file);
-
polkit_bool_t _polkit_authorization_db_auth_file_add (polkit_bool_t transient, uid_t uid, char *str_to_add);
PolKitAuthorizationDB *_polkit_authorization_db_new (void);
diff --git a/src/polkit/polkit-sysdeps.c b/src/polkit/polkit-sysdeps.c
index 6ef9da5..29e57c8 100644
--- a/src/polkit/polkit-sysdeps.c
+++ b/src/polkit/polkit-sysdeps.c
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
@@ -131,10 +132,51 @@ out:
* @out_buf: buffer to store the string representation in
* @buf_size: size of buffer
*
- * 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. If the information could not be obtained 0 is
- * returned and out_buf will be set to "(unknown)".
+ * Get the name of the binary a given process was started from.
+ *
+ * Note that this is not necessary reliable information and as such
+ * shouldn't be relied on 100% to make a security decision. In fact,
+ * this information is only trustworthy in situations where the given
+ * binary is securely locked down meaning that 1) it can't be
+ * <literal>ptrace(2)</literal>'d; 2) libc secure mode kicks in (e.g
+ * <literal>LD_PRELOAD</literal> won't work); 3) there are no other
+ * attack vectors (e.g. GTK_MODULES, X11, CORBA, D-Bus) to patch
+ * running code into the process.
+ *
+ * In other words: the risk of relying on constraining an
+ * authorization to the output of this function is high. Suppose that
+ * the program <literal>/usr/bin/gullible</literal> obtains an
+ * authorization via authentication for the action
+ * <literal>org.example.foo</literal>. We add a constraint to say that
+ * the gained authorization only applies to processes for whom
+ * <literal>/proc/pid/exe</literal> points to
+ * <literal>/usr/bin/gullible</literal>. Now enter
+ * <literal>/usr/bin/evil</literal>. It knows that the program
+ * <literal>/usr/bin/gullible</literal> is not "securely locked down"
+ * (per the definition in the above paragraph). So
+ * <literal>/usr/bin/evil</literal> simply sets
+ * <literal>LD_PRELOAD</literal> and execs
+ * <literal>/usr/bin/gullible</literal> and it can now run code in a
+ * process where <literal>/proc/pid/exe</literal> points to
+ * <literal>/usr/bin/gullible</literal>. Thus, the recently gained
+ * authorization for <literal>org.example.foo</literal> applies. Also,
+ * <literal>/usr/bin/evil</literal> could use a host of other attack
+ * vectors to run it's own code under the disguise of pretending to be
+ * <literal>/usr/bin/gullible</literal>.
+ *
+ * Specifically for interpreted languages like Python and Mono it is
+ * the case that <literal>/proc/pid/exe</literal> always points to
+ * <literal>/usr/bin/python</literal>
+ * resp. <literal>/usr/bin/mono</literal>. Thus, it's not very useful
+ * to rely on that the result for this function if you want to
+ * constrain an authorization to
+ * e.g. <literal>/usr/bin/tomboy</literal> or
+ * <literal>/usr/bin/banshee</literal>.
+ *
+ * If the information could not be obtained, such as if the given
+ * process is owned by another user than the caller, -1 is returned
+ * and out_buf will be set to "(unknown)". See also the function
+ * polkit_sysdeps_get_exe_for_pid_with_helper().
*
* Returns: Number of characters written (not including trailing
* '\0'). If the output was truncated due to the buffer being too
@@ -151,6 +193,11 @@ polkit_sysdeps_get_exe_for_pid (pid_t pid, char *out_buf, size_t buf_size)
int ret;
char proc_name[32];
+ /* TODO: to avoid work we should maintain a cache. The key
+ * into the cache should be (pid, pid_start_time) and the
+ * values should be the exe-paths
+ */
+
ret = 0;
snprintf (proc_name, sizeof (proc_name), "/proc/%d/exe", pid);
@@ -166,6 +213,86 @@ out:
return ret;
}
+/**
+ * polkit_sysdeps_get_exe_for_pid_with_helper:
+ * @pid: process id
+ * @out_buf: buffer to store the string representation in
+ * @buf_size: size of buffer
+ *
+ * Like polkit_sysdeps_get_exe_for_pid() but if the given process is
+ * owned by another user, a setuid root helper is used to obtain the
+ * information. This helper only works if 1) the caller is authorized
+ * for the org.freedesktop.policykit.read authorization; or 2) the
+ * calling user is polkituser; or 3) the calling user is setegid
+ * polkituser.
+ *
+ * So -1 might still be returned (the process might also have exited).
+ *
+ * Returns: See polkit_sysdeps_get_exe_for_pid().
+ *
+ * Since: 0.8
+ */
+int
+polkit_sysdeps_get_exe_for_pid_with_helper (pid_t pid, char *out_buf, size_t buf_size)
+{
+ int ret;
+
+ /* TODO: to avoid work we should maintain a cache. The key
+ * into the cache should be (pid, pid_start_time) and the
+ * values should be the exe-paths
+ */
+
+ ret = polkit_sysdeps_get_exe_for_pid (pid, out_buf, buf_size);
+ if (ret == -1) {
+ char buf[32];
+ char *helper_argv[3] = {PACKAGE_LIBEXEC_DIR "/polkit-resolve-exe-helper", buf, NULL};
+ char *standard_output;
+ int exit_status;
+
+ /* Uh uh.. This means that we don't have permission to read /proc/$pid/exe for
+ * the given process id... this can happen if the mechanism in question runs
+ * as an unprivileged user instead of uid 0 (e.g. user 'haldaemon').
+ *
+ * This blows.
+ *
+ * To work around this we use a setuid root helper that
+ *
+ * 1. checks whether the caller (us) has the 1) org.freedesktop.policykit.read
+ * authorization; or 2) is $POLKIT_USER; or 3) is group $POLKIT_USER
+ *
+ * 2. If so, resolves /prod/$pid/exe and writes it to stdout
+ */
+
+ snprintf (buf, sizeof (buf), "%d", pid);
+
+ if (!kit_spawn_sync (NULL, /* const char *working_directory */
+ 0, /* flags */
+ helper_argv, /* char **argv */
+ NULL, /* char **envp */
+ NULL, /* char *stdin */
+ &standard_output, /* char **stdout */
+ NULL, /* char **stderr */
+ &exit_status)) { /* int *exit_status */
+ goto out;
+ }
+
+ if (!WIFEXITED (exit_status)) {
+ kit_warning ("resolve exe helper crashed!");
+ goto out;
+ } else if (WEXITSTATUS(exit_status) != 0) {
+ goto out;
+ }
+
+ strncpy (out_buf, standard_output, buf_size);
+ out_buf[buf_size - 1] = '\0';
+ ret = strlen (standard_output);
+ }
+
+out:
+ return ret;
+}
+
+
#ifdef POLKIT_BUILD_TESTS
static polkit_bool_t
diff --git a/src/polkit/polkit-sysdeps.h b/src/polkit/polkit-sysdeps.h
index cc9c35f..6203bc2 100644
--- a/src/polkit/polkit-sysdeps.h
+++ b/src/polkit/polkit-sysdeps.h
@@ -43,6 +43,8 @@ polkit_uint64_t polkit_sysdeps_get_start_time_for_pid (pid_t pid);
int polkit_sysdeps_get_exe_for_pid (pid_t pid, char *out_buf, size_t buf_size);
+int polkit_sysdeps_get_exe_for_pid_with_helper (pid_t pid, char *out_buf, size_t buf_size);
+
POLKIT_END_DECLS
diff --git a/tools/polkit-auth.c b/tools/polkit-auth.c
index 2bf5248..76b18bd 100644
--- a/tools/polkit-auth.c
+++ b/tools/polkit-auth.c
@@ -397,6 +397,14 @@ _print_constraint (PolKitAuthorization *auth, PolKitAuthorizationConstraint *aut
case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_ACTIVE:
printf (" Constraint: Session must be active\n");
break;
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_EXE:
+ printf (" Constraint: Only allowed for program %s\n",
+ polkit_authorization_constraint_get_exe (authc));
+ break;
+ case POLKIT_AUTHORIZATION_CONSTRAINT_TYPE_REQUIRE_SELINUX_CONTEXT:
+ printf (" Constraint: Only allowed for SELinux Context %s\n",
+ polkit_authorization_constraint_get_selinux_context (authc));
+ break;
}
return FALSE;
}
More information about the hal-commit
mailing list