2 * Copyright (C) 2009 Kay Sievers <kay.sievers@vrfy.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details:
27 #include <acl/libacl.h>
39 static int set_facl(const char* filename, uid_t uid, int add)
43 acl_entry_t entry = NULL;
45 acl_permset_t permset;
48 /* read current record */
49 acl = acl_get_file(filename, ACL_TYPE_ACCESS);
53 /* locate ACL_USER entry for uid */
54 get = acl_get_entry(acl, ACL_FIRST_ENTRY, &e);
58 acl_get_tag_type(e, &t);
62 u = (uid_t*)acl_get_qualifier(e);
75 get = acl_get_entry(acl, ACL_NEXT_ENTRY, &e);
78 /* remove ACL_USER entry for uid */
84 acl_delete_entry(acl, entry);
88 /* create ACL_USER entry for uid */
90 ret = acl_create_entry(&acl, &entry);
93 acl_set_tag_type(entry, ACL_USER);
94 acl_set_qualifier(entry, &uid);
97 /* add permissions for uid */
98 acl_get_permset(entry, &permset);
99 acl_add_perm(permset, ACL_READ|ACL_WRITE);
103 printf("%c%u %s\n", add ? '+' : '-', uid, filename);
105 ret = acl_set_file(filename, ACL_TYPE_ACCESS, acl);
113 /* check if a given uid is listed */
114 static int uid_in_list(GSList *list, uid_t uid)
118 for (l = list; l != NULL; l = g_slist_next(l))
119 if (uid == GPOINTER_TO_UINT(l->data))
124 /* return list of current uids of local active sessions */
125 static GSList *uids_with_local_active_session(const char *own_id)
130 keyfile = g_key_file_new();
131 if (g_key_file_load_from_file(keyfile, "/var/run/ConsoleKit/database", 0, NULL)) {
134 groups = g_key_file_get_groups(keyfile, NULL);
135 if (groups != NULL) {
138 for (i = 0; groups[i] != NULL; i++) {
141 if (!g_str_has_prefix(groups[i], "Session "))
143 if (own_id != NULL &&g_str_has_suffix(groups[i], own_id))
145 if (!g_key_file_get_boolean(keyfile, groups[i], "is_local", NULL))
147 if (!g_key_file_get_boolean(keyfile, groups[i], "is_active", NULL))
149 u = g_key_file_get_integer(keyfile, groups[i], "uid", NULL);
150 if (u > 0 && !uid_in_list(list, u))
151 list = g_slist_prepend(list, GUINT_TO_POINTER(u));
156 g_key_file_free(keyfile);
161 /* ConsoleKit calls us with special variables */
162 static int consolekit_called(const char *ck_action, uid_t *uid, uid_t *uid2, const char **remove_session_id, int *action)
169 const char *old_session = NULL;
171 if (ck_action == NULL || strcmp(ck_action, "seat_active_session_changed") != 0)
174 /* We can have one of: remove, add, change, no-change */
175 s = getenv("CK_SEAT_OLD_SESSION_ID");
176 s2 = getenv("CK_SEAT_SESSION_ID");
177 if (s == NULL && s2 == NULL) {
179 } else if (s2 == NULL) {
181 } else if (s == NULL) {
189 s = getenv("CK_SEAT_SESSION_USER_UID");
192 u = strtoul(s, NULL, 10);
196 s = getenv("CK_SEAT_SESSION_IS_LOCAL");
199 if (strcmp(s, "true") != 0)
204 s = getenv("CK_SEAT_OLD_SESSION_USER_UID");
207 u = strtoul(s, NULL, 10);
211 s = getenv("CK_SEAT_OLD_SESSION_IS_LOCAL");
214 if (strcmp(s, "true") != 0)
217 old_session = getenv("CK_SEAT_OLD_SESSION_ID");
218 if (old_session == NULL)
223 s = getenv("CK_SEAT_OLD_SESSION_USER_UID");
226 u = strtoul(s, NULL, 10);
229 s = getenv("CK_SEAT_SESSION_USER_UID");
232 u2 = strtoul(s, NULL, 10);
236 s = getenv("CK_SEAT_OLD_SESSION_IS_LOCAL");
237 s2 = getenv("CK_SEAT_SESSION_IS_LOCAL");
238 if (s == NULL || s2 == NULL)
240 /* don't process non-local session changes */
241 if (strcmp(s, "true") != 0 && strcmp(s2, "true") != 0)
244 if (strcmp(s, "true") == 0 && strcmp(s, "true") == 0) {
245 /* process the change */
247 /* special case: we noop if we are
248 * changing between local sessions for
252 old_session = getenv("CK_SEAT_OLD_SESSION_ID");
253 if (old_session == NULL)
255 } else if (strcmp(s, "true") == 0) {
256 /* only process the removal */
258 old_session = getenv("CK_SEAT_OLD_SESSION_ID");
259 if (old_session == NULL)
261 } else if (strcmp(s2, "true") == 0) {
262 /* only process the addition */
271 g_assert_not_reached ();
275 *remove_session_id = old_session;
282 /* add or remove a ACL for a given uid from all matching devices */
283 static void apply_acl_to_devices(uid_t uid, int add)
286 struct udev_enumerate *enumerate;
287 struct udev_list_entry *list_entry;
289 /* iterate over all devices tagged with ACL_SET */
291 enumerate = udev_enumerate_new(udev);
292 udev_enumerate_add_match_property(enumerate, "ACL_MANAGE", "1");
293 udev_enumerate_scan_devices(enumerate);
294 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
295 struct udev_device *device;
298 device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate),
299 udev_list_entry_get_name(list_entry));
302 node = udev_device_get_devnode(device);
305 set_facl(node, uid, add);
306 udev_device_unref(device);
308 udev_enumerate_unref(enumerate);
313 remove_uid (uid_t uid, const char *remove_session_id)
316 * Remove ACL for given uid from all matching devices
317 * when there is currently no local active session.
321 list = uids_with_local_active_session(remove_session_id);
322 if (!uid_in_list(list, uid))
323 apply_acl_to_devices(uid, 0);
327 int main (int argc, char* argv[])
329 static const struct option options[] = {
330 { "action", required_argument, NULL, 'a' },
331 { "device", required_argument, NULL, 'D' },
332 { "user", required_argument, NULL, 'u' },
333 { "debug", no_argument, NULL, 'd' },
334 { "help", no_argument, NULL, 'h' },
338 const char *device = NULL;
341 const char* remove_session_id = NULL;
344 /* valgrind is more important to us than a slice allocator */
345 g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, 1);
350 option = getopt_long(argc, argv, "+a:D:u:dh", options, NULL);
356 if (strcmp(optarg, "add") == 0 || strcmp(optarg, "change") == 0)
358 else if (strcmp(optarg, "remove") == 0)
359 action = ACTION_REMOVE;
367 uid = strtoul(optarg, NULL, 10);
373 printf("Usage: udev-acl --action=ACTION [--device=DEVICEFILE] [--user=UID]\n\n");
379 if (action < 0 && device == NULL && uid == 0)
380 consolekit_called(argv[optind], &uid, &uid2, &remove_session_id, &action);
383 fprintf(stderr, "missing action\n\n");
388 if (device != NULL && uid != 0) {
389 fprintf(stderr, "only one option, --device=DEVICEFILE or --user=UID expected\n\n");
397 /* Add ACL for given uid to all matching devices. */
398 apply_acl_to_devices(uid, 1);
401 remove_uid(uid, remove_session_id);
404 remove_uid(uid, remove_session_id);
405 apply_acl_to_devices(uid2, 1);
411 g_assert_not_reached();
414 } else if (device != NULL) {
416 * Add ACLs for all current session uids to a given device.
418 * Or remove ACLs for uids which do not have any current local
419 * active session. Remove is not really interesting, because in
420 * most cases the device node is removed anyway.
425 list = uids_with_local_active_session(NULL);
426 for (l = list; l != NULL; l = g_slist_next(l)) {
429 u = GPOINTER_TO_UINT(l->data);
430 if (action == ACTION_ADD || !uid_in_list(list, u))
431 set_facl(device, u, action == ACTION_ADD);
435 fprintf(stderr, "--device=DEVICEFILE or --user=UID expected\n\n");