diff options
author | David Zeuthen <davidz@redhat.com> | 2007-02-17 17:24:54 -0500 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2007-02-17 17:24:54 -0500 |
commit | d7d9e1100f9f26da1004597ee52814fd41610c08 (patch) | |
tree | a8803d3a4b73dc0173879a9b4399cca347337d36 | |
parent | 573c40932cb08837d75f1981904e493734765338 (diff) |
more ACL management work; now it's actually usuable
-rw-r--r-- | fdi/policy/10osvendor/20-acl-management.fdi | 35 | ||||
-rw-r--r-- | hald/ck-tracker.c | 8 | ||||
-rw-r--r-- | hald/hald_dbus.c | 42 | ||||
-rw-r--r-- | tools/Makefile.am | 6 | ||||
-rw-r--r-- | tools/hal-acl-add.c | 97 | ||||
-rw-r--r-- | tools/hal-acl-remove.c | 16 | ||||
-rw-r--r-- | tools/hal-acl-tool.c | 1051 | ||||
-rw-r--r-- | tools/hal-storage-shared.c | 1 |
8 files changed, 1121 insertions, 135 deletions
diff --git a/fdi/policy/10osvendor/20-acl-management.fdi b/fdi/policy/10osvendor/20-acl-management.fdi index ca1f21a8..8033521d 100644 --- a/fdi/policy/10osvendor/20-acl-management.fdi +++ b/fdi/policy/10osvendor/20-acl-management.fdi @@ -5,33 +5,40 @@ <!-- allow local sessions to access ALSA device files --> <match key="info.capabilities" contains="alsa"> - <merge key="acl.is_managed" type="bool">true</merge> - <merge key="acl.grant_local_session" type="bool">true</merge> - <merge key="acl.file" type="copy_property">alsa.device_file</merge> + <append key="info.capabilities" type="strlist">access_control</append> + <merge key="access_control.grant_local_session" type="bool">true</merge> + <merge key="access_control.file" type="copy_property">alsa.device_file</merge> </match> <!-- allow local sessions to access OSS device files --> <match key="info.capabilities" contains="oss"> - <merge key="acl.is_managed" type="bool">true</merge> - <merge key="acl.grant_local_session" type="bool">true</merge> - <merge key="acl.file" type="copy_property">oss.device_file</merge> + <append key="info.capabilities" type="strlist">access_control</append> + <merge key="access_control.grant_local_session" type="bool">true</merge> + <merge key="access_control.file" type="copy_property">oss.device_file</merge> </match> <!-- TODO: add more rules for adding/removing ACL's (e.g. webcam, cd drive etc.) --> - <!-- add and remove ACL's when devices are added and removed --> - <match key="acl.is_managed" bool="true"> - <append key="info.callouts.add" type="strlist">hal-acl-add --device</append> - <append key="info.callouts.remove" type="strlist">hal-acl-remove --device</append> + <!-- add / remove ACL's when devices are added and removed --> + <match key="info.capabilities" contains="access_control"> + <append key="info.callouts.add" type="strlist">hal-acl-tool --add-device</append> + <append key="info.callouts.remove" type="strlist">hal-acl-tool --remove-device</append> </match> <match key="info.udi" string="/org/freedesktop/Hal/devices/computer"> <!-- remove all previously added ACL's on start-up --> - <append key="info.callouts.add" type="strlist">hal-acl-remove --remove-all</append> + <append key="info.callouts.add" type="strlist">hal-acl-tool --remove-all</append> + + <!-- reconfigure all ACL's sessions are added and removed --> + <append key="info.callouts.session_add" type="strlist">hal-acl-tool --reconfigure</append> + <append key="info.callouts.session_remove" type="strlist">hal-acl-tool --reconfigure</append> + + <!-- reconfigure all ACL's when a session becomes active --> + <append key="info.callouts.session_active" type="strlist">hal-acl-tool --reconfigure</append> + + <!-- reconfigure all ACL's when a session becomes inactive --> + <append key="info.callouts.session_inactive" type="strlist">hal-acl-tool --reconfigure</append> - <!-- add and remove ACL's when sessions are added and removed --> - <append key="info.callouts.session_add" type="strlist">hal-acl-add --session</append> - <append key="info.callouts.session_remove" type="strlist">hal-acl-remove --session</append> </match> </device> diff --git a/hald/ck-tracker.c b/hald/ck-tracker.c index b9437299..7276ad63 100644 --- a/hald/ck-tracker.c +++ b/hald/ck-tracker.c @@ -496,10 +496,6 @@ ck_tracker_process_system_bus_message (CKTracker *tracker, DBusMessage *message) session = (CKSession *) i->data; if (strcmp (session->session_objpath, session_objpath) == 0) { - if (tracker->session_removed_cb != NULL) { - tracker->session_removed_cb (tracker, session, tracker->user_data); - } - if (session->seat == NULL) { HAL_ERROR (("Session '%s' to be removed is not attached to a seat", session_objpath)); @@ -509,6 +505,10 @@ ck_tracker_process_system_bus_message (CKTracker *tracker, DBusMessage *message) tracker->sessions = g_slist_remove (tracker->sessions, session); ck_session_unref (session); + if (tracker->session_removed_cb != NULL) { + tracker->session_removed_cb (tracker, session, tracker->user_data); + } + break; } } diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c index 51089a55..24d0d534 100644 --- a/hald/hald_dbus.c +++ b/hald/hald_dbus.c @@ -4215,7 +4215,7 @@ hald_dbus_filter_function (DBusConnection * connection, goto out; } - HAL_INFO (("NameOwnerChanged name=%s old=%s new=%s", name, old_service_name, new_service_name)); + /*HAL_INFO (("NameOwnerChanged name=%s old=%s new=%s", name, old_service_name, new_service_name));*/ ci_tracker_name_owner_changed (name, old_service_name, new_service_name); @@ -4497,9 +4497,49 @@ out: static void hald_dbus_session_active_changed (CKTracker *tracker, CKSession *session, void *user_data) { + HalDevice *d; + char **programs; + char *extra_env[5] = {"HALD_ACTION=session_remove", + NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=" */, + NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=" */, + NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=" */, + NULL}; + HAL_INFO (("In hald_dbus_session_active_changed for session '%s': %s", ck_session_get_id (session), ck_session_is_active (session) ? "ACTIVE" : "INACTIVE")); + + d = hal_device_store_find (hald_get_gdl (), "/org/freedesktop/Hal/devices/computer"); + if (d == NULL) { + d = hal_device_store_find (hald_get_tdl (), "/org/freedesktop/Hal/devices/computer"); + } + if (d == NULL) { + goto out; + } + + programs = hal_device_property_dup_strlist_as_strv (d, + ck_session_is_active (session) ? + "info.callouts.session_active" : + "info.callouts.session_inactive"); + if (programs == NULL) { + goto out; + } + + extra_env[1] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=%s", ck_session_get_id (session)); + extra_env[2] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=%d", ck_session_get_user (session)); + extra_env[3] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=%s", + ck_session_is_active (session) ? "true" : "false"); + hal_callout_device (d, + NULL /* callback */, + NULL /* userdata1 */, + NULL /* userdata2 */, + programs, + extra_env); + g_free (extra_env[1]); + g_free (extra_env[2]); + g_free (extra_env[3]); +out: + ; } gboolean diff --git a/tools/Makefile.am b/tools/Makefile.am index 96844ce6..b5332e39 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -63,10 +63,10 @@ libexec_PROGRAMS += \ endif if HAVE_ACLMGMT -libexec_PROGRAMS += hal-acl-add hal-acl-remove +libexec_PROGRAMS += hal-acl-tool -hal_acl_add_SOURCES = hal-acl-add.c -hal_acl_remove_SOURCES = hal-acl-remove.c +hal_acl_tool_SOURCES = hal-acl-tool.c +hal_acl_tool_LDADD = @GLIB_LIBS@ $(top_builddir)/libhal/libhal.la endif diff --git a/tools/hal-acl-add.c b/tools/hal-acl-add.c deleted file mode 100644 index 4d358d51..00000000 --- a/tools/hal-acl-add.c +++ /dev/null @@ -1,97 +0,0 @@ - -#include <stdio.h> -#include <stdlib.h> -#include <sys/file.h> -#include <sys/types.h> -#include <unistd.h> -#include <errno.h> - -#include <glib.h> - -#if 0 -static int lock_acl_fd = -1; - -static gboolean -lock_hal_acl (void) -{ - if (lock_acl_fd >= 0) - return TRUE; - - printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ()); - - lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR); - - if (lock_acl_fd < 0) - return FALSE; - -tryagain: -#if sun - if (lockf (lock_acl_fd, F_LOCK, 0) != 0) { -#else - if (flock (lock_acl_fd, LOCK_EX) != 0) { -#endif - if (errno == EINTR) - goto tryagain; - return FALSE; - } - - printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ()); - - - return TRUE; -} - -static void -unlock_hal_acl (void) -{ -#if sun - lockf (lock_acl_fd, F_ULOCK, 0); -#else - flock (lock_acl_fd, LOCK_UN); -#endif - close (lock_acl_fd); - lock_acl_fd = -1; - printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ()); -} - -/* Each entry here represents a line in the acl-list file - * - * <device-file>\t<hal-udi>\t<uid-as-number>\t<gid-as-number>\t<session-id> - * - * example: - * - * /dev/snd/controlC0\t/org/freedesktop/Hal/devices/pci_8086_27d8_alsa_control__1\t500\t\t/org/freedesktop/ConsoleKit/Session0 - * - * This means that the - */ -typedef struct HalACL_s { - const char *device; - const char *uid; - uid_t uid; /* 0 if unset */ - gid_t gid; /* 0 if unset */ - const char *session; /* NULL if unset */ -} HalACL; - -/* hal-acl-grant can run in two modes of operation; - * - * 1) as a hal callout via info.callouts.add - this will set ACL's on the device file pointed to by acl.file - * using the information provided by the other acl.* properties to determine what ACL's to add to the given - * device file - * - * 2) - * - */ -#endif - -int -main (int argc, char *argv[]) -{ - int i; - - fprintf (stderr, "hal-acl-add %d\n", argc); - for (i = 0; i < argc; i++) { - fprintf (stderr, " arg %2d: %s\n", i, argv[i]); - } - system ("env |sort"); - return 0; -} diff --git a/tools/hal-acl-remove.c b/tools/hal-acl-remove.c deleted file mode 100644 index 83f1c7c0..00000000 --- a/tools/hal-acl-remove.c +++ /dev/null @@ -1,16 +0,0 @@ - -#include <stdio.h> -#include <stdlib.h> - -int -main (int argc, char *argv[]) -{ - int i; - - fprintf (stderr, "hal-acl-remove %d\n", argc); - for (i = 0; i < argc; i++) { - fprintf (stderr, " arg %2d: %s\n", i, argv[i]); - } - system ("env |sort"); - return 0; -} diff --git a/tools/hal-acl-tool.c b/tools/hal-acl-tool.c new file mode 100644 index 00000000..d49d0977 --- /dev/null +++ b/tools/hal-acl-tool.c @@ -0,0 +1,1051 @@ +/*************************************************************************** + * + * hal-acl-tool.c : Manage ACL's on device nodes + * + * Copyright (C) 2007 David Zeuthen, <david@fubar.dk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/file.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> + +#include <glib.h> +#include <libhal.h> + +/* How this works (or "An introduction to this code") + * + * - all ACL's granted by this tool is kept in /var/lib/hal/acl-list + * + * - every time tool is launched we read this file and keep each line + * as an ACLCurrent instance. These are kept in a list. + * + * - we do locking so only one instance of this tool is doing work + * at one time. This is essential as we maintain ACL's in a file. + * + * - there's an overarching --reconfigure method that basically + * - finds all devices of capability 'access_control' + * - computes what ACL's these devices should have + * - modifies the in-memory acl-current-list + * - ACL's to be removed are tagged with setting remove -> TRUE + * - ACL's to be added are appended to the list and add -> TRUE + * - we then compute the argument vector to setfacl(1) for adding / + * removing ACL's + * - if setfacl(1) succeeds (rc == 0) then we write the new acl-current-list + * + * Notably, the HAL daemon will invoke us with --reconfigure on every + * - session add + * - session remove + * - session inactive + * - session active + * + * event. Also, when devices are added we're invoked with --add-device + * respectively --remove-device. When the HAL daemon starts we're invoked + * with --remove-all. + * + * Optimizations + * + * - the HAL daemon exports the ConsoleKit seat + session + * configuration in CK_* environment variables. So we don't + * need to do IPC to the ConsoleKit daemon to learn about + * about seats and sessions. + * + * - special casing for --add-device and --remove-device; here we + * don't need roundtrips to the HAL daemon. Only --reconfigure + * requires that. As such no IPC is required for these cases + * which is fortunate as --add-device is invoked quite a lot + * on startup + * + */ + +/* Each entry here represents a line in the /var/lib/hal/acl-list file + * of ACL's that have been set by HAL and as such are currently + * applied + * + * <device-file> <type> <uid-or-gid> + * + * where <type>='u'|'g' for respectively uid and gid (the spacing represent tabs). + * + * Example: + * + * /dev/snd/controlC0 u 500 + * /dev/snd/controlC0 u 501 + * /dev/snd/controlC0 g 1001 + */ +typedef struct ACLCurrent_s { + char *device; + int type; + union { + uid_t uid; + gid_t gid; + } v; + + gboolean remove; + gboolean add; +} ACLCurrent; + +/* used for type member in ACLCurrent */ +enum { + HAL_ACL_UID, + HAL_ACL_GID +}; + +static void +hal_acl_free (ACLCurrent *ha) +{ + g_free (ha->device); + g_free (ha); +} + +static int +ha_sort (ACLCurrent *a, ACLCurrent *b) +{ + return strcmp (a->device, b->device); +} + +static gboolean +acl_apply_changes (GSList *new_acl_list, gboolean only_update_acllist, gboolean missing_files_ok) +{ + GString *str; + GString *setfacl_cmdline; + char *new_acl_file_contents = NULL; + char *setfacl_cmdline_str = NULL; + GSList *i; + gboolean ret; + GError *error = NULL; + int exit_status; + + ret = FALSE; + + new_acl_list = g_slist_sort (new_acl_list, (GCompareFunc) ha_sort); + + /* first compute the contents of the new acl-file + * and build up the command line for setfacl(1) + */ + str = g_string_new (""); + setfacl_cmdline = g_string_new (""); + for (i = new_acl_list; i != NULL; i = g_slist_next (i)) { + ACLCurrent *ha = (ACLCurrent *) i->data; + + if (ha->remove) { + if (setfacl_cmdline->len > 0) { + g_string_append_c (setfacl_cmdline, ' '); + } + g_string_append_printf (setfacl_cmdline, "-x %c:%d %s", + (ha->type == HAL_ACL_UID) ? 'u' : 'g', + (ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid, + ha->device); + continue; + } + + if (ha->add) { + if (setfacl_cmdline->len > 0) { + g_string_append_c (setfacl_cmdline, ' '); + } + g_string_append_printf (setfacl_cmdline, "-m %c:%d:rw %s", + (ha->type == HAL_ACL_UID) ? 'u' : 'g', + (ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid, + ha->device); + } + + g_string_append_printf (str, + "%s\t%c\t%d\n", + ha->device, + (ha->type == HAL_ACL_UID) ? 'u' : 'g', + (ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid); + } + new_acl_file_contents = g_string_free (str, FALSE); + + /* TODO FIXME NOTE XXX WARNING: + * + * The variable 'only_update_acllist' is set to TRUE only on + * device_remove events. It effectively means "only update the + * /var/lib/hal/acl-list, do not apply ACL's on disk". So this + * is done for systems where /dev is dynamic and we know for + * sure that the device file is gone. + * + * So if you're running a static /dev you need to comment the + * only_update_acllist conditional out below so the ACL's are + * removed accordingly. As this is uncommon and against all + * recommendations for a system using HAL (we depend on recent + * udev versions!) we don't bother the syscall overhead of + * stat(2)'ing every file. + */ + + if (setfacl_cmdline->len == 0 || only_update_acllist) { + g_string_free (setfacl_cmdline, TRUE); + setfacl_cmdline_str = NULL; + } else { + g_string_prepend (setfacl_cmdline, "setfacl "); + setfacl_cmdline_str = g_string_free (setfacl_cmdline, FALSE); + + printf ("%d: invoking '%s'\n", getpid (), setfacl_cmdline_str); + if (!g_spawn_command_line_sync (setfacl_cmdline_str, NULL, NULL, &exit_status, &error)) { + printf ("%d: Error - couldn't invoke setfacl(1): %s\n", getpid (), error->message); + g_error_free (error); + goto out; + } + + if (exit_status != 0) { + if (!missing_files_ok) { + printf ("%d: setfacl(1) exit code != 0 but OK as some missing files are expected\n", + getpid ()); + } else { + printf ("%d: Error - setfacl(1) failed\n", getpid ()); + goto out; + } + } + } + + /* success; now atomically set the new list */ + g_file_set_contents ("/var/lib/hal/acl-list", + new_acl_file_contents, + strlen (new_acl_file_contents), + NULL); + + ret = TRUE; + +out: + if (new_acl_file_contents != NULL) + g_free (new_acl_file_contents); + + if (setfacl_cmdline_str != NULL) + g_free (setfacl_cmdline_str); + + return ret; +} + + +static gboolean +get_current_acl_list (GSList **l) +{ + FILE *f; + gboolean ret; + char buf[1024]; + + *l = NULL; + f = NULL; + ret = FALSE; + + f = fopen ("/var/lib/hal/acl-list", "r"); + if (f == NULL) { + printf ("%d: cannot open /var/lib/hal/acl-list\n", getpid ()); + goto out; + } + + while (fgets (buf, sizeof(buf), f) != NULL) { + ACLCurrent *ha; + char **val; + char *endptr; + + ha = g_new0 (ACLCurrent, 1); + val = g_strsplit(buf, "\t", 0); + if (g_strv_length (val) != 3) { + printf ("Line is malformed: '%s'\n", buf); + g_strfreev (val); + goto out; + } + + ha->device = g_strdup (val[0]); + if (strcmp (val[1], "u") == 0) { + ha->type = HAL_ACL_UID; + ha->v.uid = strtol (val[2], &endptr, 10); + if (*endptr != '\0' && *endptr != '\n') { + printf ("Line is malformed: '%s'\n", buf); + g_strfreev (val); + goto out; + } + } else if (strcmp (val[1], "g") == 0) { + ha->type = HAL_ACL_GID; + ha->v.gid = strtol (val[2], &endptr, 10); + if (*endptr != '\0' && *endptr != '\n') { + printf ("Line is malformed: '%s'\n", buf); + g_strfreev (val); + goto out; + } + } else { + printf ("Line is malformed: '%s'\n", buf); + g_strfreev (val); + goto out; + } + g_strfreev (val); + + /*printf (" ACL '%s' %d %d\n", ha->device, ha->type, ha->v.uid);*/ + + *l = g_slist_prepend (*l, ha); + } + + ret = TRUE; + +out: + if (f != NULL) + fclose (f); + return ret; +} + +typedef void (*SeatSessionVisitor) (const char *seat_id, + gboolean seat_is_local, + int num_sessions_on_seat, + const char *session_id, /* may be NULL */ + uid_t session_uid, + gboolean session_is_active, + gpointer user_data); + +/* Visits all seats and sessions. + * + * NOTE: when a seat is visited session_id will be NULL and session_uid, session_is_active are undefined. + */ +static gboolean +visit_seats_and_sessions (SeatSessionVisitor visitor_cb, gpointer user_data) +{ + int i; + int j; + char *s; + char *p; + char **seats; + gboolean ret; + + ret = FALSE; + + if ((s = getenv ("CK_SEATS")) == NULL) { + printf ("%d: CK_SEATS is not set!\n", getpid()); + goto out; + } + seats = g_strsplit (s, "\t", 0); + /* for all seats */ + for (i = 0; seats[i] != NULL; i++) { + char *seat = seats[i]; + char **sessions; + int num_sessions_on_seat; + gboolean seat_is_local; + + p = g_strdup_printf ("CK_SEAT_IS_LOCAL_%s", seat); + if ((s = getenv (p)) == NULL) { + printf ("%d: CK_SEAT_IS_LOCAL_%s is not set!\n", getpid(), seat); + g_free (p); + goto out; + } + g_free (p); + seat_is_local = (strcmp (s, "true") == 0); + + p = g_strdup_printf ("CK_SEAT_%s", seat); + if ((s = getenv (p)) == NULL) { + printf ("%d: CK_SEAT_%s is not set!\n", getpid(), seat); + g_free (p); + goto out; + } + g_free (p); + sessions = g_strsplit (s, "\t", 0); + num_sessions_on_seat = g_strv_length (sessions); + + visitor_cb (seat, seat_is_local, num_sessions_on_seat, NULL, 0, FALSE, user_data); + + /* for all sessions on seat */ + for (j = 0; sessions[j] != NULL; j++) { + char *session = sessions[j]; + gboolean session_is_active; + uid_t session_uid; + char *endptr; + + p = g_strdup_printf ("CK_SESSION_IS_ACTIVE_%s", session); + if ((s = getenv (p)) == NULL) { + printf ("%d: CK_SESSION_IS_ACTIVE_%s is not set!\n", getpid(), session); + g_free (p); + goto out; + } + g_free (p); + session_is_active = (strcmp (s, "true") == 0); + + p = g_strdup_printf ("CK_SESSION_UID_%s", session); + if ((s = getenv (p)) == NULL) { + printf ("%d: CK_SESSION_UID_%s is not set!\n", getpid(), session); + g_free (p); + goto out; + } + g_free (p); + session_uid = strtol (s, &endptr, 10); + if (*endptr != '\0') { + printf ("%d: CK_SESSION_UID_%s set to '%s' is malformed!\n", getpid(), session, s); + goto out; + } + + visitor_cb (seat, seat_is_local, num_sessions_on_seat, + session, session_uid, session_is_active, user_data); + + } + g_strfreev (sessions); + } + g_strfreev (seats); + + ret = TRUE; + +out: + return ret; +} + +/* this data structure is for collecting what ACL's a device should have */ +typedef struct { + /* identifying the device */ + char *udi; /* HAL UDI of device */ + char *device; /* device file */ + + /* policy for how to apply ACL's (must be set by the caller prior to visiting the device) */ + + /* access is granted to any session on a local seat */ + gboolean grant_to_local_seat; + + /* access is granted only to active sessions on local seats */ + gboolean grant_to_local_seat_active_only; + + /* will be set by the visitor */ + GSList *uid; /* list of uid's (int) that should have access to this device */ + GSList *gid; /* list of gid's (int) that should have access to this device */ +} ACLForDevice; + +static void +afd_grant_to_uid (ACLForDevice *afd, uid_t uid) +{ + GSList *i; + + for (i = afd->uid; i != NULL; i = g_slist_next (i)) { + uid_t iuid = GPOINTER_TO_INT (i->data); + if (uid == iuid) + goto out; + } + afd->uid = g_slist_prepend (afd->uid, GINT_TO_POINTER (uid)); +out: + ; +} + +static void +afd_grant_to_gid (ACLForDevice *afd, gid_t gid) +{ + GSList *i; + + for (i = afd->gid; i != NULL; i = g_slist_next (i)) { + gid_t igid = GPOINTER_TO_INT (i->data); + if (gid == igid) + goto out; + } + afd->gid = g_slist_prepend (afd->gid, GINT_TO_POINTER (gid)); +out: + ; +} + +static uid_t +util_name_to_uid (const char *username, gid_t *default_gid) +{ + int rc; + uid_t res; + char *buf = NULL; + unsigned int bufsize; + struct passwd pwd; + struct passwd *pwdp; + + res = (uid_t) -1; + + bufsize = sysconf (_SC_GETPW_R_SIZE_MAX); + buf = g_new0 (char, bufsize); + + rc = getpwnam_r (username, &pwd, buf, bufsize, &pwdp); + if (rc != 0 || pwdp == NULL) { + /*g_warning ("getpwnam_r() returned %d", rc);*/ + goto out; + } + + res = pwdp->pw_uid; + if (default_gid != NULL) + *default_gid = pwdp->pw_gid; + +out: + g_free (buf); + return res; +} + +static gid_t +util_name_to_gid (const char *groupname) +{ + int rc; + gid_t res; + char *buf = NULL; + unsigned int bufsize; + struct group gbuf; + struct group *gbufp; + + res = (gid_t) -1; + + bufsize = sysconf (_SC_GETGR_R_SIZE_MAX); + buf = g_new0 (char, bufsize); + + rc = getgrnam_r (groupname, &gbuf, buf, bufsize, &gbufp); + if (rc != 0 || gbufp == NULL) { + /*g_warning ("getgrnam_r() returned %d", rc);*/ + goto out; + } + + res = gbufp->gr_gid; + +out: + g_free (buf); + return res; +} + +static void +afd_grant_to_uid_from_userlist (ACLForDevice *afd, char **sv) +{ + int i; + uid_t uid; + char *endptr; + + for (i = 0; sv[i] != NULL; i++) { + uid = strtol (sv[i], &endptr, 10); + if (*endptr != '\0') { + uid = util_name_to_uid (sv[i], NULL); + if ((int) uid == -1) { + printf ("%d: warning; username '%s' is unknown\n", getpid (), sv[i]); + continue; + } + } + afd_grant_to_uid (afd, uid); + } +} + +static void +afd_grant_to_gid_from_grouplist (ACLForDevice *afd, char **sv) +{ + int i; + gid_t gid; + char *endptr; + + for (i = 0; sv[i] != NULL; i++) { + gid = strtol (sv[i], &endptr, 10); + if (*endptr != '\0') { + gid = util_name_to_gid (sv[i]); + if ((int) gid == -1) { + printf ("%d: warning; group '%s' is unknown\n", getpid (), sv[i]); + continue; + } + } + afd_grant_to_gid (afd, gid); + } +} + +static ACLForDevice * +acl_for_device_new (const char *udi) +{ + ACLForDevice *afd; + + afd = g_new0 (ACLForDevice, 1); + afd->udi = g_strdup (udi); + + return afd; +} + +static void +acl_for_device_set_device (ACLForDevice *afd, const char *device) +{ + afd->device = g_strdup (device); +} + +static void +acl_for_device_free (ACLForDevice* afd) +{ + g_free (afd->udi); + g_free (afd->device); + g_slist_free (afd->uid); + g_slist_free (afd->gid); + g_free (afd); +} + +static void +acl_device_added_visitor (const char *seat_id, + gboolean seat_is_local, + int num_sessions_on_seat, + const char *session_id, + uid_t session_uid, + gboolean session_is_active, + gpointer user_data) +{ + GSList *i; + GSList *afd_list = (GSList *) user_data; + +#if 0 + if (session_id == NULL) { + /* means we're just visiting the seat; each session on the seat will be visited accordingly */ + printf ("Visiting seat '%s' (is_local=%d) with %d sessions\n", + seat_id, seat_is_local, num_sessions_on_seat); + } else { + printf (" %s: Visiting session '%s' with uid %d (is_active=%d)\n", + seat_id, session_id, session_uid, session_is_active); + } +#endif + + /* for each entry ACLForDevice in afd_list, add to the uid and + * gid lists for users and groups that should have access to + * the device in question + */ + for (i = afd_list; i != NULL; i = g_slist_next (i)) { + ACLForDevice *afd = (ACLForDevice *) i->data; + + if (session_id == NULL) { + /* we only grant access to sessions - someone suggested that if a device is tied + * to a seat.. the owner might want to give access to user 'dilbert' if, and only + * if, no sessions is occuring at that seat. Cuz then the user 'dilbert' could + * have a cron job running that takes a webcam shot every 5 minutes or so.. + * + * this can be achieved by testing num_sessions_on_seat==0 + */ + continue; + } + + + /* apply the policy defined by grant_to_local_seat and grant_to_local_seat_active_only */ + + /* we only grant access to local seats... */ + if (!seat_is_local) + continue; + + if (afd->grant_to_local_seat) + afd_grant_to_uid (afd, session_uid); + else { + if (afd->grant_to_local_seat_active_only) { + if (session_is_active) { + afd_grant_to_uid (afd, session_uid); + } + } + } + } + +} + +static void +acl_compute_changes (GSList *afd_list, gboolean only_update_acllist) +{ + GSList *current_acl_list = NULL; + GSList *i; + GSList *j; + GSList *k; + + /* get the list of ACL's currently applied */ + if (!get_current_acl_list (¤t_acl_list)) { + printf ("Error getting ACL's currently applied\n"); + goto out; + } + + /* for each entry in ACLForDevice, we need to modify current_acl_list + * such that it matches the entry. This is achieved by + * + * - setting the 'remove' boolean to TRUE for existing + * entries that should be removed + * + * - adding a new entry with the 'add' boolean set to TRUE, + * in the front, for entries we need to have added + */ + for (i = afd_list; i != NULL; i = g_slist_next (i)) { + ACLForDevice *afd = (ACLForDevice *) i->data; + + /* OK, so we have the ACL's we want for a device - this is expressed in afd + * + * go through all the ACL's that we've *already* + * added.. + * + * if an ACL we want is already there + * we simply remove it from afd (the ACL's we want). + * + * If we've already got an ACL that we don't want + * tag the current list with this for removal. + * + * At the end the afd will express the ACL's that we + * need to add.. so make entries in the current_list + * with 'add' set to TRUE. + */ + for (j = current_acl_list; j != NULL; j = g_slist_next (j)) { + ACLCurrent *ha = (ACLCurrent *) j->data; + + if (strcmp (afd->device, ha->device) == 0) { + switch (ha->type) { + case HAL_ACL_UID: + /* see if this is already in the ACLForDevice entry */ + for (k = afd->uid; k != NULL; k = g_slist_next (k)) { + uid_t uid = GPOINTER_TO_INT (k->data); + if (uid == ha->v.uid) { + /* yup, so we're all good - remove it from the afd_list + * since we don't need it to be added later... + */ + afd->uid = g_slist_delete_link (afd->uid, k); + break; + } + } + if (k == NULL) { + /* nope, element wasn't there so this ACLCurrent should be removed */ + ha->remove = TRUE; + } + break; + case HAL_ACL_GID: + /* see if this is already in the ACLForDevice entry */ + for (k = afd->gid; k != NULL; k = g_slist_next (k)) { + gid_t gid = GPOINTER_TO_INT (k->data); + if (gid == ha->v.gid) { + /* yup, so we're all good - remove it from the afd_list + * since we don't need it to be added later... + */ + afd->gid = g_slist_delete_link (afd->gid, k); + break; + } + } + if (k == NULL) { + /* nope, element wasn't there so this ACLCurrent should be removed */ + ha->remove = TRUE; + } + break; + } + } + + } + + /* now go through remaining entries in afd->uid and afd->gid + * and create ACLCurrent entries + */ + for (j = afd->uid; j != NULL; j = g_slist_next (j)) { + ACLCurrent *ha; + ha = g_new0 (ACLCurrent, 1); + ha->add = TRUE; + ha->device = g_strdup (afd->device); + ha->type = HAL_ACL_UID; + ha->v.uid = GPOINTER_TO_INT (j->data); + current_acl_list = g_slist_prepend (current_acl_list, ha); + } + for (j = afd->gid; j != NULL; j = g_slist_next (j)) { + ACLCurrent *ha; + ha = g_new0 (ACLCurrent, 1); + ha->add = TRUE; + ha->device = g_strdup (afd->device); + ha->type = HAL_ACL_GID; + ha->v.gid = GPOINTER_TO_INT (j->data); + current_acl_list = g_slist_prepend (current_acl_list, ha); + } + } + +#if 0 + printf ("====================================\n"); + for (i = current_acl_list; i != NULL; i = g_slist_next (i)) { + ACLCurrent *ha = (ACLCurrent *) i->data; + + printf (" ACL '%s' %d %d rem=%d add=%d\n", ha->device, ha->type, ha->v.uid, ha->remove, ha->add); + + } + printf ("====================================\n"); +#endif + + acl_apply_changes (current_acl_list, only_update_acllist, FALSE); + +out: + if (current_acl_list != NULL) { + g_slist_foreach (current_acl_list, (GFunc) hal_acl_free, NULL); + g_slist_free (current_acl_list); + } +} + + +static void +acl_device_added (void) +{ + char *s; + char *udi; + char *device; + GSList *afd_list = NULL; + ACLForDevice *afd = NULL; + + /* we can avoid round-trips to the HAL daemon by using what's in the environment */ + + if ((udi = getenv ("UDI")) == NULL) + goto out; + + if ((device = getenv ("HAL_PROP_ACCESS_CONTROL_FILE")) == NULL) + goto out; + + afd = acl_for_device_new (udi); + acl_for_device_set_device (afd, device); + afd_list = g_slist_prepend (NULL, afd); + + /* get ACL granting policy from HAL properties */ + if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_LOCAL_SESSION")) != NULL) { + afd->grant_to_local_seat = (strcmp (s, "true") == 0); + } + if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_LOCAL_ACTIVE_SESSION")) != NULL) { + afd->grant_to_local_seat_active_only = (strcmp (s, "true") == 0); + } + if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_USER")) != NULL) { + char **sv; + sv = g_strsplit (s, "\t", 0); + afd_grant_to_uid_from_userlist (afd, sv); + g_strfreev (sv); + } + if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_GROUP")) != NULL) { + char **sv; + sv = g_strsplit (s, "\t", 0); + afd_grant_to_gid_from_grouplist (afd, sv); + g_strfreev (sv); + } + + /* determine what ACL's we want to put on the given device + * files; e.g. apply the seat / session policy + * + * (entries in afd_list will be modified, see ACLForDevice + * data structure) + */ + if (!visit_seats_and_sessions (acl_device_added_visitor, (gpointer) afd_list)) { + printf ("Error visiting seats and sessions\n"); + goto out; + } + + printf ("%d: adding ACL's for %s\n", getpid (), device); + + acl_compute_changes (afd_list, FALSE); + +out: + if (afd != NULL) + acl_for_device_free (afd); + if (afd_list != NULL) + g_slist_free (afd_list); +} + +static void +acl_device_removed (void) +{ + char *udi; + char *device; + GSList *afd_list = NULL; + ACLForDevice *afd = NULL; + + /* we can avoid round-trips to the HAL daemon by using what's in the environment */ + + if ((udi = getenv ("UDI")) == NULL) + goto out; + + if ((device = getenv ("HAL_PROP_ACCESS_CONTROL_FILE")) == NULL) + goto out; + + afd = acl_for_device_new (udi); + acl_for_device_set_device (afd, device); + afd_list = g_slist_prepend (NULL, afd); + + /* since this device is to be removed don't set policy - this means "grant it to no-one"; and since + * there is no-one to grant it to.. we don't need to visit any seats + */ + printf ("%d: removing ACL's for %s\n", getpid (), device); + + /* only update the ACL list, don't invoke setfacl(1) on the + * files (see note in acl_apply_changes()) + */ + acl_compute_changes (afd_list, TRUE); + +out: + if (afd != NULL) + acl_for_device_free (afd); + if (afd_list != NULL) + g_slist_free (afd_list); +} + +static void +acl_reconfigure_all (void) +{ + int i; + int num_devices; + char **udis; + DBusError error; + LibHalContext *hal_ctx; + GSList *afd_list = NULL; + + printf ("%d: reconfiguring all ACL's\n", getpid ()); + + dbus_error_init (&error); + if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) { + printf ("%d: Cannot connect to hald: %s: %s\n", getpid (), error.name, error.message); + LIBHAL_FREE_DBUS_ERROR (&error); + goto out; + } + + if ((udis = libhal_find_device_by_capability (hal_ctx, "access_control", &num_devices, &error)) == NULL) { + printf ("%d: Cannot get list of devices of capability 'acl'\n", getpid ()); + goto out; + } + + for (i = 0; udis[i] != NULL; i++) { + LibHalPropertySet *props; + LibHalPropertySetIterator psi; + char *device = NULL; + ACLForDevice *afd; + char **sv; + + if ((props = libhal_device_get_all_properties (hal_ctx, udis[i], &error)) == NULL) { + printf ("%d: Cannot get list properties for '%s'\n", getpid (), udis[1]); + goto out; + } + + afd = acl_for_device_new (udis[i]); + + libhal_psi_init (&psi, props); + while (libhal_psi_has_more (&psi)) { + char *key; + key = libhal_psi_get_key (&psi); + if (strcmp (key, "access_control.file") == 0) { + device = libhal_psi_get_string (&psi); + } else if (strcmp (key, "access_control.grant_local_session") == 0) { + afd->grant_to_local_seat = libhal_psi_get_bool (&psi); + } else if (strcmp (key, "access_control.grant_local_active_session") == 0) { + afd->grant_to_local_seat_active_only = libhal_psi_get_bool (&psi); + } else if (strcmp (key, "access_control.grant_user") == 0) { + sv = libhal_psi_get_strlist (&psi); + afd_grant_to_uid_from_userlist (afd, sv); + } else if (strcmp (key, "access_control.grant_group") == 0) { + sv = libhal_psi_get_strlist (&psi); + afd_grant_to_gid_from_grouplist (afd, sv); + } + libhal_psi_next (&psi); + } + + if (device == NULL) { + printf ("%d: access_control.file not set for '%s'\n", getpid (), udis[i]); + goto out; + } else { + acl_for_device_set_device (afd, device); + afd_list = g_slist_prepend (afd_list, afd); + } + + libhal_free_property_set (props); + } + libhal_free_string_array (udis); + + if (g_slist_length (afd_list) > 0) { + if (!visit_seats_and_sessions (acl_device_added_visitor, (gpointer) afd_list)) { + printf ("Error visiting seats and sessions\n"); + goto out; + } + acl_compute_changes (afd_list, FALSE); + } + +out: + ; +} + +static void +acl_remove_all (void) +{ + GSList *current_acl_list = NULL; + GSList *i; + + if (!get_current_acl_list (¤t_acl_list)) { + printf ("Error getting ACL's currently applied\n"); + goto out; + } + + for (i = current_acl_list; i != NULL; i = g_slist_next (i)) { + ACLCurrent *ha = (ACLCurrent *) i->data; + ha->remove = TRUE; + } + + acl_apply_changes (current_acl_list, FALSE, FALSE); + +out: + if (current_acl_list != NULL) { + g_slist_foreach (current_acl_list, (GFunc) hal_acl_free, NULL); + g_slist_free (current_acl_list); + } +} + +static int lock_acl_fd = -1; + +static gboolean +acl_lock (void) +{ + if (lock_acl_fd >= 0) + return TRUE; + + printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ()); + + lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR); + /* TODO: set correct mode, owner etc. */ + + if (lock_acl_fd < 0) { + printf ("%d: error opening/creating /var/lib/hal/acl-list\n", getpid ()); + return FALSE; + } + +tryagain: +#if sun + if (lockf (lock_acl_fd, F_LOCK, 0) != 0) +#else + if (flock (lock_acl_fd, LOCK_EX) != 0) +#endif + { + if (errno == EINTR) + goto tryagain; + return FALSE; + } + + printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ()); + return TRUE; +} + +static void +acl_unlock (void) +{ +#if sun + lockf (lock_acl_fd, F_ULOCK, 0); +#else + flock (lock_acl_fd, LOCK_UN); +#endif + close (lock_acl_fd); + lock_acl_fd = -1; + printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ()); +} + + +int +main (int argc, char *argv[]) +{ + if (argc != 2) { + printf ("hal-acl-tool should only be invoked by hald\n"); + goto out; + } + + if (!acl_lock ()) { + goto out; + } + + if (strcmp (argv[1], "--add-device") == 0) { + acl_device_added (); + } else if (strcmp (argv[1], "--remove-device") == 0) { + acl_device_removed (); + } else if (strcmp (argv[1], "--reconfigure") == 0) { + acl_reconfigure_all (); + } else if (strcmp (argv[1], "--remove-all") == 0) { + acl_remove_all (); + } + + acl_unlock (); + +out: + return 0; +} diff --git a/tools/hal-storage-shared.c b/tools/hal-storage-shared.c index 19e18198..11ec3d1c 100644 --- a/tools/hal-storage-shared.c +++ b/tools/hal-storage-shared.c @@ -573,6 +573,7 @@ lock_hal_mtab (void) printf ("%d: XYA attempting to get lock on /media/.hal-mtab-lock\n", getpid ()); lock_mtab_fd = open ("/media/.hal-mtab-lock", O_CREAT | O_RDWR); + /* TODO: set correct mode, owner etc. */ if (lock_mtab_fd < 0) return FALSE; |