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