From 11b54f3385619268ff70402e1fd3a2f2e0e77785 Mon Sep 17 00:00:00 2001 From: Matthew Monaco Date: Sat, 3 Nov 2012 14:14:56 -0600 Subject: [PATCH] Bug 56729: add a configuration file to accountsservice --- data/Makefile.am | 4 + data/accountsservice.conf | 12 +++ data/org.freedesktop.Accounts.User.xml | 41 +++++++ src/Makefile.am | 3 + src/config.c | 191 +++++++++++++++++++++++++++++++++ src/daemon.c | 125 +++------------------ src/daemon.h | 3 - src/user.c | 137 ++++++++++++++++++++++- src/user.h | 4 + src/util.h | 10 ++ 10 files changed, 416 insertions(+), 114 deletions(-) create mode 100644 data/accountsservice.conf create mode 100644 src/config.c diff --git a/data/Makefile.am b/data/Makefile.am index 521c6c2..7078fba 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -7,6 +7,9 @@ dbusif_DATA = \ dbusconfdir = $(sysconfdir)/dbus-1/system.d dbusconf_DATA = org.freedesktop.Accounts.conf +configdir = $(pkglibdir) +config_DATA = accountsservice.conf + servicedir = $(datadir)/dbus-1/system-services service_in_files = org.freedesktop.Accounts.service.in service_DATA = $(service_in_files:.service.in=.service) @@ -32,6 +35,7 @@ endif EXTRA_DIST = \ $(dbusif_DATA) \ $(dbusconf_DATA) \ + $(config_DATA) \ $(service_in_files) \ $(policy_in_files) \ accounts-daemon.service.in diff --git a/data/accountsservice.conf b/data/accountsservice.conf new file mode 100644 index 0000000..67cbc0b --- /dev/null +++ b/data/accountsservice.conf @@ -0,0 +1,12 @@ +# +# Configuration for AccountsService +# +# MinimumUID: The minimum UID to not be considered a system account. +# Exclude: Users to exclude from ListCachedUsers(), which is used by +# display managers to select a list of users for the greeter. +# This is cumulative across config files. +# + +[Accounts] +MinimumUID = 500 +Exclude = root daemon adm lp sync shutdown halt mail news uucp operator nobody nobody4 noaccess postgres pvm rpm nfs pcap mysql ftp games man at diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml index 88198d8..7229ffe 100644 --- a/data/org.freedesktop.Accounts.User.xml +++ b/data/org.freedesktop.Accounts.User.xml @@ -533,6 +533,37 @@ + + + + + + Whether the user is excluded from ListCachedUsers(). + + + + + + + Most display managers use ListCachedUsers to populate the user list in the greeter.. + + + + The caller needs one of the following PolicyKit authorizations: + + + org.freedesktop.accounts.user-administration + To change the password of a user + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + @@ -766,6 +797,16 @@ + + + + + Whether the user is a excluded from ListCachedUsers(). + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index c368c02..94c22ff 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,6 +4,8 @@ INCLUDES = \ -DDATADIR=\""$(datadir)"\" \ -DICONDIR=\"$(localstatedir)/lib/AccountsService/icons\" \ -DUSERDIR=\"$(localstatedir)/lib/AccountsService/users\" \ + -DCONFDIR=\"$(sysconfdir)\" \ + -DPKGLIBDIR=\"$(pkglibdir)\" \ $(DBUS_GLIB_CFLAGS) \ $(GIO_CFLAGS) \ $(GLIB_CFLAGS) \ @@ -28,6 +30,7 @@ accounts_daemon_SOURCES = \ $(enums_h_sources) \ $(BUILT_SOURCES) \ types.h \ + config.c \ daemon.h \ daemon.c \ user.h \ diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..ad4226b --- /dev/null +++ b/src/config.c @@ -0,0 +1,191 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Matthew Monaco + * + * 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 3 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 + * + */ + +/* + * AccountsService config file handler + * + * This is designed so that cfg_init may return NULL (due to error...) yet the + * getters should still work. + * + */ + +#include "util.h" + +#define CFG_PATH_LIB PKGLIBDIR "/accountsservice.conf" +#define CFG_PATH_ETC CONFDIR "/accountsservice.conf" +#define CFG_DEFAULT_MIN_UID 500 + +struct _cfg_struct { + GKeyFile* file_etc; + GKeyFile* file_lib; + uid_t min_uid; + GHashTable* excludes; +}; + +static void _cfg_parse_min_uid(Config*); +static void _cfg_parse_excludes(Config*); + +/** + * Alloc/Free +**/ + +Config *cfg_init() +{ + Config* cfg = NULL; + + if ((cfg = g_malloc(sizeof(Config))) == NULL) + return NULL; + + cfg->file_etc = g_key_file_new(); + cfg->file_lib = g_key_file_new(); + cfg->min_uid = CFG_DEFAULT_MIN_UID; + cfg->excludes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + /* load config from /etc */ + + if (g_key_file_load_from_file(cfg->file_etc, CFG_PATH_ETC, G_KEY_FILE_NONE, NULL)) { + + g_key_file_set_list_separator(cfg->file_etc, ' '); + + } else { + + g_key_file_free(cfg->file_etc); + cfg->file_etc = NULL; + } + + /* load config from /lib */ + + if (g_key_file_load_from_file(cfg->file_lib, CFG_PATH_LIB, G_KEY_FILE_NONE, NULL)) { + + g_key_file_set_list_separator(cfg->file_lib, ' '); + + } else { + + g_key_file_free(cfg->file_lib); + cfg->file_lib = NULL; + } + + /* parse options */ + + _cfg_parse_min_uid(cfg); + _cfg_parse_excludes(cfg); + + /* with the above code, these aren't needed anymore */ + + g_key_file_free(cfg->file_etc); + g_key_file_free(cfg->file_lib); + cfg->file_etc = NULL; + cfg->file_lib = NULL; + + return cfg; +} + +void cfg_free(Config *cfg) +{ + g_hash_table_destroy(cfg->excludes); + g_key_file_free(cfg->file_etc); + g_key_file_free(cfg->file_lib); + g_free(cfg); +} + +/** + * Getters +**/ + +uid_t cfg_get_min_uid(const Config *cfg) +{ + if (cfg) + return cfg->min_uid; + else + return CFG_DEFAULT_MIN_UID; +} + +gboolean cfg_get_user_excluded(const Config *cfg, const gchar* user_name) +{ + if (cfg) + return g_hash_table_contains(cfg->excludes, user_name); + else + return FALSE; +} + +/** + * Parsers +**/ + +static void _cfg_parse_min_uid(Config* cfg) +{ + GError* err; + int i; + + /* etc takes priority over lib */ + + if (cfg->file_etc) { + err = NULL; + i = g_key_file_get_integer(cfg->file_etc, "Accounts", "MinimumUID", &err); + if (err == NULL) { + cfg->min_uid = (uid_t) ( (i >= 0) ? i : 0 ); + return; + } + } + + if (cfg->file_lib) { + err = NULL; + i = g_key_file_get_integer(cfg->file_lib, "Accounts", "MinimumUID", &err); + if (err == NULL) { + cfg->min_uid = (uid_t) ( (i >= 0) ? i : 0 ); + return; + } + } +} + +static void _cfg_hash_insert_strings(GHashTable* ht, gchar** list) +{ + int i; + + if (list == NULL) + return; + + for (i = 0; list[i] != NULL; i++) { + g_hash_table_insert(ht, list[i], NULL); + } +} + +static void _cfg_parse_excludes(Config* cfg) +{ + gchar** tmp = NULL; + + /* etc and lib are cumulative */ + + if (cfg->file_etc) { + tmp = g_key_file_get_string_list(cfg->file_etc, "Accounts", "Exclude", NULL, NULL); + _cfg_hash_insert_strings(cfg->excludes, tmp); + /* only free the array, the strings now live in the hash table */ + g_free(tmp); + } + + if (cfg->file_lib) { + tmp = g_key_file_get_string_list(cfg->file_lib, "Accounts", "Exclude", NULL, NULL); + _cfg_hash_insert_strings(cfg->excludes, tmp); + /* only free the array, the strings now live in the hash table */ + g_free(tmp); + } +} + +/* vim: set ts=8 sw=8 sts=8 et : */ diff --git a/src/daemon.c b/src/daemon.c index f2ee7ac..c16e6d2 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -47,39 +47,8 @@ #define PATH_PASSWD "/etc/passwd" #define PATH_SHADOW "/etc/shadow" -#define PATH_NOLOGIN "/sbin/nologin" -#define PATH_FALSE "/bin/false" #define PATH_GDM_CUSTOM "/etc/gdm/custom.conf" -static const char *default_excludes[] = { - "bin", - "root", - "daemon", - "adm", - "lp", - "sync", - "shutdown", - "halt", - "mail", - "news", - "uucp", - "operator", - "nobody", - "nobody4", - "noaccess", - "postgres", - "pvm", - "rpm", - "nfsnobody", - "pcap", - "mysql", - "ftp", - "games", - "man", - "at", - NULL -}; - enum { PROP_0, PROP_DAEMON_VERSION @@ -90,10 +59,11 @@ struct DaemonPrivate { GDBusProxy *bus_proxy; GHashTable *users; - GHashTable *exclusions; User *autologin; + Config *cfg; + GFileMonitor *passwd_monitor; GFileMonitor *shadow_monitor; GFileMonitor *gdm_monitor; @@ -157,53 +127,6 @@ error_get_type (void) return etype; } -gboolean -daemon_local_user_is_excluded (Daemon *daemon, const gchar *username, const gchar *shell) -{ - int ret; - - if (g_hash_table_lookup (daemon->priv->exclusions, username)) { - return TRUE; - } - - ret = FALSE; - - if (shell != NULL) { - char *basename, *nologin_basename, *false_basename; - -#ifdef HAVE_GETUSERSHELL - char *valid_shell; - - ret = TRUE; - setusershell (); - while ((valid_shell = getusershell ()) != NULL) { - if (g_strcmp0 (shell, valid_shell) != 0) - continue; - ret = FALSE; - } - endusershell (); -#endif - - basename = g_path_get_basename (shell); - nologin_basename = g_path_get_basename (PATH_NOLOGIN); - false_basename = g_path_get_basename (PATH_FALSE); - - if (shell[0] == '\0') { - ret = TRUE; - } else if (g_strcmp0 (basename, nologin_basename) == 0) { - ret = TRUE; - } else if (g_strcmp0 (basename, false_basename) == 0) { - ret = TRUE; - } - - g_free (basename); - g_free (nologin_basename); - g_free (false_basename); - } - - return ret; -} - #ifdef HAVE_UTMPX_H typedef struct { @@ -480,14 +403,8 @@ load_entries (Daemon *daemon, if (pwent == NULL) break; - /* Skip system users... */ - if (daemon_local_user_is_excluded (daemon, pwent->pw_name, pwent->pw_shell)) { - g_debug ("skipping user: %s", pwent->pw_name); - continue; - } - /* ignore duplicate entries */ - if (g_hash_table_lookup (users, pwent->pw_name)) { + if (g_hash_table_contains (users, pwent->pw_name)) { continue; } @@ -501,6 +418,13 @@ load_entries (Daemon *daemon, /* freeze & update users not already in the new list */ g_object_freeze_notify (G_OBJECT (user)); user_update_from_pwent (user, pwent); + user_update_from_config (user, daemon->priv->cfg); + + /* skip system accounts */ + if (user_get_system_account(user)) { + g_object_thaw_notify (G_OBJECT (user)); + continue; + } g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user); g_debug ("loaded user: %s", user_get_user_name (user)); @@ -711,25 +635,15 @@ on_gdm_monitor_changed (GFileMonitor *monitor, static void daemon_init (Daemon *daemon) { - gint i; GFile *file; GError *error; daemon->priv = DAEMON_GET_PRIVATE (daemon); - daemon->priv->exclusions = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - NULL); - - for (i = 0; default_excludes[i] != NULL; i++) { - g_hash_table_insert (daemon->priv->exclusions, - g_strdup (default_excludes[i]), - GUINT_TO_POINTER (TRUE)); - } - daemon->priv->users = create_users_hash_table (); + daemon->priv->cfg = cfg_init(); + file = g_file_new_for_path (PATH_PASSWD); daemon->priv->passwd_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, @@ -798,6 +712,8 @@ daemon_finalize (GObject *object) g_hash_table_destroy (daemon->priv->users); + cfg_free(daemon->priv->cfg); + G_OBJECT_CLASS (daemon_parent_class)->finalize (object); } @@ -1015,26 +931,19 @@ finish_list_cached_users (gpointer user_data) const gchar *name; User *user; uid_t uid; - const gchar *shell; object_paths = g_ptr_array_new (); g_hash_table_iter_init (&iter, data->daemon->priv->users); while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) { uid = user_get_uid (user); - shell = user_get_shell (user); - - if (user_get_system_account (user)) { - g_debug ("user %s %ld is system account, so excluded\n", name, (long) uid); - continue; - } - if (daemon_local_user_is_excluded (data->daemon, name, shell)) { - g_debug ("user %s %ld excluded\n", name, (long) uid); + if (user_get_excluded (user)) { + g_debug ("user %s %ld is excluded", name, (long) uid); continue; } - g_debug ("user %s %ld not excluded\n", name, (long) uid); + g_debug ("user %s %ld not excluded", name, (long) uid); g_ptr_array_add (object_paths, (gpointer) user_get_object_path (user)); } g_ptr_array_add (object_paths, NULL); diff --git a/src/daemon.h b/src/daemon.h index 996b452..3975652 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -75,9 +75,6 @@ User *daemon_local_find_user_by_id (Daemon *daemon, uid_t uid); User *daemon_local_find_user_by_name (Daemon *daemon, const gchar *name); -gboolean daemon_local_user_is_excluded (Daemon *daemon, - const gchar *name, - const gchar *shell); typedef void (*AuthorizedCallback) (Daemon *daemon, User *user, diff --git a/src/user.c b/src/user.c index 9d84e11..f78d385 100644 --- a/src/user.c +++ b/src/user.c @@ -68,6 +68,7 @@ enum { PROP_AUTOMATIC_LOGIN, PROP_SYSTEM_ACCOUNT, PROP_LOCAL_ACCOUNT, + PROP_EXCLUDED, }; struct User { @@ -100,6 +101,7 @@ struct User { gboolean automatic_login; gboolean system_account; gboolean local_account; + gboolean excluded; }; typedef struct UserClass @@ -146,6 +148,32 @@ account_type_from_pwent (struct passwd *pwent) return ACCOUNT_TYPE_STANDARD; } +static gboolean +user_has_system_shell (User *user) +{ + gboolean ret = FALSE; + + /* HAVE_GETUSERSHELL doesn't seem to be available... */ + + if (user->shell != NULL) { + char *valid_shell; + ret = TRUE; + g_debug ("determine_sys_account: %s: shell is %s", + user->user_name, user->shell); + setusershell (); + while ((valid_shell = getusershell ()) != NULL) { + if (g_strcmp0 (user->shell, valid_shell) == 0) + ret = FALSE; + } + endusershell (); + } + + g_debug ("determine_sys_account: %s: is%s a system account", + user->user_name, (ret) ? "" : " not"); + + return ret; +} + void user_update_from_pwent (User *user, struct passwd *pwent) @@ -285,9 +313,13 @@ user_update_from_pwent (User *user, g_object_notify (G_OBJECT (user), "password-mode"); } - user->system_account = daemon_local_user_is_excluded (user->daemon, - user->user_name, - pwent->pw_shell); + if (!user->system_account && + user_has_system_shell (user)) { + + user->system_account = TRUE; + user->excluded = TRUE; + changed = TRUE; + } g_object_thaw_notify (G_OBJECT (user)); @@ -296,10 +328,41 @@ user_update_from_pwent (User *user, } void +user_update_from_config (User *user, + const Config *cfg) +{ + gboolean changed = FALSE; + + g_object_freeze_notify (G_OBJECT (user)); + + if (!user->system_account && + (user->uid < cfg_get_min_uid (cfg))) { + + user->system_account = TRUE; + user->excluded = TRUE; + changed = TRUE; + } + + if (!user->excluded && + cfg_get_user_excluded (cfg, user->user_name)) { + + user->excluded = TRUE; + changed = TRUE; + } + + g_object_thaw_notify (G_OBJECT (user)); + + if (changed) + accounts_user_emit_changed (ACCOUNTS_USER (user)); +} + +void user_update_from_keyfile (User *user, GKeyFile *keyfile) { gchar *s; + gboolean b; + GError *err = NULL; g_object_freeze_notify (G_OBJECT (user)); @@ -346,6 +409,11 @@ user_update_from_keyfile (User *user, g_object_notify (G_OBJECT (user), "icon-file"); } + b = g_key_file_get_boolean (keyfile, "User", "Excluded", &err); + if (err == NULL) { + user->excluded = b; + } + g_object_thaw_notify (G_OBJECT (user)); } @@ -380,6 +448,8 @@ user_save_to_keyfile (User *user, if (user->icon_file) g_key_file_set_string (keyfile, "User", "Icon", user->icon_file); + + g_key_file_set_boolean (keyfile, "User", "Excluded", user->excluded); } static void @@ -522,6 +592,12 @@ user_get_shell(User *user) return user->shell; } +gboolean +user_get_excluded(User *user) +{ + return user->excluded; +} + static void throw_error (GDBusMethodInvocation *context, gint error_code, @@ -1627,6 +1703,46 @@ user_set_automatic_login (AccountsUser *auser, return TRUE; } +static void +user_change_excluded_authorized_cb (Daemon *daemon, + User *user, + GDBusMethodInvocation *context, + gpointer data) + +{ + gboolean excluded = GPOINTER_TO_INT (data); + + if (user->excluded != excluded) { + sys_log (context, + "%s account of user '%s' (%d)", + excluded ? "excluding" : "including", user->user_name, user->uid); + + user->excluded = excluded; + accounts_user_emit_changed (ACCOUNTS_USER (user)); + g_object_notify (G_OBJECT (user), "excluded"); + } + + accounts_user_complete_set_excluded (ACCOUNTS_USER (user), context); +} + +static gboolean +user_set_excluded (AccountsUser *auser, + GDBusMethodInvocation *context, + gboolean excluded) +{ + User *user = (User*)auser; + daemon_local_check_auth (user->daemon, + user, + "org.freedesktop.accounts.user-administration", + TRUE, + user_change_excluded_authorized_cb, + context, + GINT_TO_POINTER (excluded), + NULL); + + return TRUE; +} + static guint64 user_real_get_uid (AccountsUser *user) { @@ -1744,6 +1860,12 @@ user_real_get_system_account (AccountsUser *user) return USER (user)->system_account; } +static gboolean +user_real_get_excluded (AccountsUser *user) +{ + return USER (user)->excluded; +} + static void user_finalize (GObject *object) { @@ -1804,6 +1926,9 @@ user_set_property (GObject *object, case PROP_SYSTEM_ACCOUNT: user->system_account = g_value_get_boolean (value); break; + case PROP_EXCLUDED: + user->excluded = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -1886,6 +2011,9 @@ user_get_property (GObject *object, case PROP_LOCAL_ACCOUNT: g_value_set_boolean (value, user->local_account); break; + case PROP_EXCLUDED: + g_value_set_boolean (value, user->excluded); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -1923,6 +2051,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface) iface->handle_set_shell = user_set_shell; iface->handle_set_user_name = user_set_user_name; iface->handle_set_xsession = user_set_x_session; + iface->handle_set_excluded = user_set_excluded; iface->get_uid = user_real_get_uid; iface->get_user_name = user_real_get_user_name; iface->get_real_name = user_real_get_real_name; @@ -1942,6 +2071,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface) iface->get_password_hint = user_real_get_password_hint; iface->get_automatic_login = user_real_get_automatic_login; iface->get_system_account = user_real_get_system_account; + iface->get_excluded = user_real_get_excluded; } static void @@ -1966,4 +2096,5 @@ user_init (User *user) user->automatic_login = FALSE; user->system_account = FALSE; user->login_history = NULL; + user->excluded = FALSE; } diff --git a/src/user.h b/src/user.h index 5362556..c939d1b 100644 --- a/src/user.h +++ b/src/user.h @@ -27,6 +27,7 @@ #include #include "types.h" +#include "util.h" G_BEGIN_DECLS @@ -55,6 +56,8 @@ User * user_new (Daemon *daemon, void user_update_from_pwent (User *user, struct passwd *pwent); +void user_update_from_config (User *user, + const Config* cfg); void user_update_from_keyfile (User *user, GKeyFile *keyfile); void user_update_local_account_property (User *user, @@ -69,6 +72,7 @@ gboolean user_get_local_account (User *user); const gchar * user_get_object_path (User *user); uid_t user_get_uid (User *user); const gchar * user_get_shell (User *user); +gboolean user_get_excluded (User *user); G_END_DECLS diff --git a/src/util.h b/src/util.h index 41ba545..cb020dc 100644 --- a/src/util.h +++ b/src/util.h @@ -23,9 +23,19 @@ #define __UTIL_H__ #include +#include +#include G_BEGIN_DECLS +struct _cfg_struct; +typedef struct _cfg_struct Config; + +Config* cfg_init(void); +void cfg_free(Config*); +uid_t cfg_get_min_uid(const Config*); +gboolean cfg_get_user_excluded(const Config*, const gchar*); + void sys_log (GDBusMethodInvocation *context, const gchar *format, ...); -- 1.9.1